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