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  }