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