adapter_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package container // import "github.com/docker/docker/daemon/cluster/executor/container"
  2. import (
  3. "context"
  4. "testing"
  5. "time"
  6. "github.com/docker/docker/daemon"
  7. "github.com/moby/swarmkit/v2/api"
  8. )
  9. // TestWaitNodeAttachment tests that the waitNodeAttachment method successfully
  10. // blocks until the required node attachment becomes available.
  11. func TestWaitNodeAttachment(t *testing.T) {
  12. emptyDaemon := &daemon.Daemon{}
  13. // the daemon creates an attachment store as an object, which means it's
  14. // initialized to an empty store by default. get that attachment store here
  15. // and add some attachments to it
  16. attachmentStore := emptyDaemon.GetAttachmentStore()
  17. // create a set of attachments to put into the attahcment store
  18. attachments := map[string]string{
  19. "network1": "10.1.2.3/24",
  20. }
  21. // this shouldn't fail, but check it anyway just in case
  22. err := attachmentStore.ResetAttachments(attachments)
  23. if err != nil {
  24. t.Fatalf("error resetting attachments: %v", err)
  25. }
  26. // create a containerConfig to put in the adapter. we don't need the task,
  27. // actually; only the networkAttachments are needed.
  28. container := &containerConfig{
  29. task: nil,
  30. networksAttachments: map[string]*api.NetworkAttachment{
  31. // network1 is already present in the attachment store.
  32. "network1": {
  33. Network: &api.Network{
  34. ID: "network1",
  35. DriverState: &api.Driver{
  36. Name: "overlay",
  37. },
  38. },
  39. },
  40. // network2 is not yet present in the attachment store, and we
  41. // should block while waiting for it.
  42. "network2": {
  43. Network: &api.Network{
  44. ID: "network2",
  45. DriverState: &api.Driver{
  46. Name: "overlay",
  47. },
  48. },
  49. },
  50. // localnetwork is not and will never be in the attachment store,
  51. // but we should not block on it, because it is not an overlay
  52. // network
  53. "localnetwork": {
  54. Network: &api.Network{
  55. ID: "localnetwork",
  56. DriverState: &api.Driver{
  57. Name: "bridge",
  58. },
  59. },
  60. },
  61. },
  62. }
  63. // we don't create an adapter using the newContainerAdapter package,
  64. // because it does a bunch of checks and validations. instead, create one
  65. // "from scratch" so we only have the fields we need.
  66. adapter := &containerAdapter{
  67. backend: emptyDaemon,
  68. container: container,
  69. }
  70. // create a context to do call the method with
  71. ctx, cancel := context.WithCancel(context.Background())
  72. defer cancel()
  73. // create a channel to allow the goroutine that we run the method call in
  74. // to signal that it's done.
  75. doneChan := make(chan struct{})
  76. // store the error return value of waitNodeAttachments in this variable
  77. var waitNodeAttachmentsErr error
  78. // NOTE(dperny): be careful running goroutines in test code. if a test
  79. // terminates with ie t.Fatalf or a failed requirement, runtime.Goexit gets
  80. // called, which does run defers but does not clean up child goroutines.
  81. // we defer canceling the context here, which should stop this goroutine
  82. // from leaking
  83. go func() {
  84. waitNodeAttachmentsErr = adapter.waitNodeAttachments(ctx)
  85. // signal that we've completed
  86. close(doneChan)
  87. }()
  88. // wait 200ms to allow the waitNodeAttachments call to spin for a bit
  89. time.Sleep(200 * time.Millisecond)
  90. select {
  91. case <-doneChan:
  92. if waitNodeAttachmentsErr == nil {
  93. t.Fatalf("waitNodeAttachments exited early with no error")
  94. } else {
  95. t.Fatalf(
  96. "waitNodeAttachments exited early with an error: %v",
  97. waitNodeAttachmentsErr,
  98. )
  99. }
  100. default:
  101. // allow falling through; this is the desired case
  102. }
  103. // now update the node attachments to include another network attachment
  104. attachments["network2"] = "10.3.4.5/24"
  105. err = attachmentStore.ResetAttachments(attachments)
  106. if err != nil {
  107. t.Fatalf("error resetting attachments: %v", err)
  108. }
  109. // now wait 200 ms for waitNodeAttachments to pick up the change
  110. time.Sleep(200 * time.Millisecond)
  111. // and check that waitNodeAttachments has exited with no error
  112. select {
  113. case <-doneChan:
  114. if waitNodeAttachmentsErr != nil {
  115. t.Fatalf(
  116. "waitNodeAttachments returned an error: %v",
  117. waitNodeAttachmentsErr,
  118. )
  119. }
  120. default:
  121. t.Fatalf("waitNodeAttachments did not exit yet, but should have")
  122. }
  123. }