github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/cluster/executor/container/adapter_test.go (about)

     1  package container // import "github.com/docker/docker/daemon/cluster/executor/container"
     2  
     3  import (
     4  	"testing"
     5  
     6  	"context"
     7  	"time"
     8  
     9  	"github.com/docker/docker/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  }