123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- package container // import "github.com/docker/docker/daemon/cluster/executor/container"
- import (
- "context"
- "testing"
- "time"
- "github.com/docker/docker/daemon"
- "github.com/moby/swarmkit/v2/api"
- )
- // TestWaitNodeAttachment tests that the waitNodeAttachment method successfully
- // blocks until the required node attachment becomes available.
- func TestWaitNodeAttachment(t *testing.T) {
- emptyDaemon := &daemon.Daemon{}
- // the daemon creates an attachment store as an object, which means it's
- // initialized to an empty store by default. get that attachment store here
- // and add some attachments to it
- attachmentStore := emptyDaemon.GetAttachmentStore()
- // create a set of attachments to put into the attahcment store
- attachments := map[string]string{
- "network1": "10.1.2.3/24",
- }
- // this shouldn't fail, but check it anyway just in case
- err := attachmentStore.ResetAttachments(attachments)
- if err != nil {
- t.Fatalf("error resetting attachments: %v", err)
- }
- // create a containerConfig to put in the adapter. we don't need the task,
- // actually; only the networkAttachments are needed.
- container := &containerConfig{
- task: nil,
- networksAttachments: map[string]*api.NetworkAttachment{
- // network1 is already present in the attachment store.
- "network1": {
- Network: &api.Network{
- ID: "network1",
- DriverState: &api.Driver{
- Name: "overlay",
- },
- },
- },
- // network2 is not yet present in the attachment store, and we
- // should block while waiting for it.
- "network2": {
- Network: &api.Network{
- ID: "network2",
- DriverState: &api.Driver{
- Name: "overlay",
- },
- },
- },
- // localnetwork is not and will never be in the attachment store,
- // but we should not block on it, because it is not an overlay
- // network
- "localnetwork": {
- Network: &api.Network{
- ID: "localnetwork",
- DriverState: &api.Driver{
- Name: "bridge",
- },
- },
- },
- },
- }
- // we don't create an adapter using the newContainerAdapter package,
- // because it does a bunch of checks and validations. instead, create one
- // "from scratch" so we only have the fields we need.
- adapter := &containerAdapter{
- backend: emptyDaemon,
- container: container,
- }
- // create a context to do call the method with
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- // create a channel to allow the goroutine that we run the method call in
- // to signal that it's done.
- doneChan := make(chan struct{})
- // store the error return value of waitNodeAttachments in this variable
- var waitNodeAttachmentsErr error
- // NOTE(dperny): be careful running goroutines in test code. if a test
- // terminates with ie t.Fatalf or a failed requirement, runtime.Goexit gets
- // called, which does run defers but does not clean up child goroutines.
- // we defer canceling the context here, which should stop this goroutine
- // from leaking
- go func() {
- waitNodeAttachmentsErr = adapter.waitNodeAttachments(ctx)
- // signal that we've completed
- close(doneChan)
- }()
- // wait 200ms to allow the waitNodeAttachments call to spin for a bit
- time.Sleep(200 * time.Millisecond)
- select {
- case <-doneChan:
- if waitNodeAttachmentsErr == nil {
- t.Fatalf("waitNodeAttachments exited early with no error")
- } else {
- t.Fatalf(
- "waitNodeAttachments exited early with an error: %v",
- waitNodeAttachmentsErr,
- )
- }
- default:
- // allow falling through; this is the desired case
- }
- // now update the node attachments to include another network attachment
- attachments["network2"] = "10.3.4.5/24"
- err = attachmentStore.ResetAttachments(attachments)
- if err != nil {
- t.Fatalf("error resetting attachments: %v", err)
- }
- // now wait 200 ms for waitNodeAttachments to pick up the change
- time.Sleep(200 * time.Millisecond)
- // and check that waitNodeAttachments has exited with no error
- select {
- case <-doneChan:
- if waitNodeAttachmentsErr != nil {
- t.Fatalf(
- "waitNodeAttachments returned an error: %v",
- waitNodeAttachmentsErr,
- )
- }
- default:
- t.Fatalf("waitNodeAttachments did not exit yet, but should have")
- }
- }
|