github.com/kaisenlinux/docker@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/deallocator/deallocator_test.go (about)

     1  package deallocator
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/docker/swarmkit/api"
    10  	"github.com/docker/swarmkit/manager/state/store"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestDeallocatorInit(t *testing.T) {
    16  	// start up the memory store
    17  	s := store.NewMemoryStore(nil)
    18  	require.NotNil(t, s)
    19  	defer s.Close()
    20  
    21  	// create a service that's pending deletion, with no tasks remaining
    22  	// this one should be deleted by the deallocator
    23  	// additionally, that service is using a network that's also marked for
    24  	// deletion, and another that's not
    25  	network1 := newNetwork("network1", true)
    26  	network2 := newNetwork("network2", false)
    27  	service1 := newService("service1", true, network1, network2)
    28  
    29  	// now let's create another service that's also pending deletion, but still
    30  	// has one task associated with it (in any state) - and also uses a network
    31  	// that's also marked for deletion
    32  	// none of those should get deleted
    33  	network3 := newNetwork("network3", true)
    34  	service2 := newService("service2", true, network3)
    35  	task1 := newTask("task1", service2)
    36  
    37  	// let's also have a network that's pending deletion,
    38  	// but isn't used by any existing service
    39  	// this one should be gone after the init
    40  	network4 := newNetwork("network4", true)
    41  
    42  	// and finally a network that's not pending deletion, not
    43  	// used by any service
    44  	network5 := newNetwork("network5", false)
    45  
    46  	createDBObjects(t, s, service1, service2,
    47  		network1, network2, network3, network4, network5, task1)
    48  
    49  	// create and start the deallocator
    50  	deallocator, ran := startNewDeallocator(t, s, true)
    51  
    52  	// and then stop it immediately - we're just interested in the init
    53  	// phase for this test
    54  	stopDeallocator(t, deallocator, ran)
    55  
    56  	// now let's check that the DB is in the state we expect
    57  	s.View(func(tx store.ReadTx) {
    58  		assert.Nil(t, store.GetService(tx, service1.ID))
    59  		assert.Nil(t, store.GetNetwork(tx, network1.ID))
    60  		assert.NotNil(t, store.GetNetwork(tx, network2.ID))
    61  
    62  		assert.NotNil(t, store.GetService(tx, service2.ID))
    63  		assert.NotNil(t, store.GetNetwork(tx, network3.ID))
    64  
    65  		assert.Nil(t, store.GetNetwork(tx, network4.ID))
    66  
    67  		assert.NotNil(t, store.GetNetwork(tx, network5.ID))
    68  	})
    69  }
    70  
    71  // this tests what happens when a service is marked for deletion
    72  func TestServiceDelete(t *testing.T) {
    73  	// we test services with respectively 1, 2, 5 and 10 tasks
    74  	for _, taskCount := range []int{1, 2, 5, 10} {
    75  		t.Run("service delete with "+strconv.Itoa(taskCount)+" tasks",
    76  			func(t *testing.T) {
    77  				// start up the memory store
    78  				s := store.NewMemoryStore(nil)
    79  				require.NotNil(t, s)
    80  				defer s.Close()
    81  
    82  				// let's create the task and services
    83  				service := newService("service", false)
    84  				createDBObjects(t, s, service)
    85  
    86  				taskIDs := make([]string, taskCount)
    87  				tasks := make([]interface{}, taskCount)
    88  				for i := 0; i < taskCount; i++ {
    89  					taskIDs[i] = "task" + strconv.Itoa(i+1)
    90  					tasks[i] = newTask(taskIDs[i], service)
    91  				}
    92  				createDBObjects(t, s, tasks...)
    93  
    94  				// now let's start the deallocator
    95  				deallocator, ran := startNewDeallocator(t, s, false)
    96  				defer stopDeallocator(t, deallocator, ran)
    97  
    98  				// then let's mark the service for deletion...
    99  				updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   100  					service.PendingDelete = true
   101  					require.NoError(t, store.UpdateService(tx, service))
   102  				}, false)
   103  
   104  				// and then let's remove all tasks
   105  				for i, taskID := range taskIDs {
   106  					lastTask := i == len(taskIDs)-1
   107  
   108  					updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   109  						require.NoError(t, store.DeleteTask(tx, taskID))
   110  					}, lastTask)
   111  
   112  					// and after each but the last one, the service should still
   113  					// be there - after the last one it should be gone
   114  					s.View(func(tx store.ReadTx) {
   115  						if lastTask {
   116  							require.Nil(t, store.GetService(tx, service.ID))
   117  						} else {
   118  							require.NotNil(t, store.GetService(tx, service.ID))
   119  						}
   120  					})
   121  
   122  				}
   123  			})
   124  	}
   125  }
   126  
   127  // this tests what happens when a service is marked for deletion,
   128  // along with its network, _before_ the service has had time to
   129  // fully shut down
   130  func TestServiceAndNetworkDelete(t *testing.T) {
   131  	s := store.NewMemoryStore(nil)
   132  	require.NotNil(t, s)
   133  	defer s.Close()
   134  
   135  	// let's create a couple of networks, a service, and a couple of tasks
   136  	network1 := newNetwork("network1", false)
   137  	network2 := newNetwork("network2", false)
   138  	service := newService("service", false, network1, network2)
   139  	task1 := newTask("task1", service)
   140  	task2 := newTask("task2", service)
   141  
   142  	createDBObjects(t, s, network1, network2, service, task1, task2)
   143  
   144  	deallocator, ran := startNewDeallocator(t, s, false)
   145  	defer stopDeallocator(t, deallocator, ran)
   146  
   147  	// then let's mark the service and network2 for deletion
   148  	updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   149  		service.PendingDelete = true
   150  		require.NoError(t, store.UpdateService(tx, service))
   151  	}, false)
   152  
   153  	updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   154  		network2.PendingDelete = true
   155  		require.NoError(t, store.UpdateNetwork(tx, network2))
   156  	}, false)
   157  
   158  	// then let's delete one task
   159  	updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   160  		require.NoError(t, store.DeleteTask(tx, task2.ID))
   161  	}, false)
   162  
   163  	// the service and network2 should still exist
   164  	s.View(func(tx store.ReadTx) {
   165  		require.NotNil(t, store.GetService(tx, service.ID))
   166  		require.NotNil(t, store.GetNetwork(tx, network2.ID))
   167  	})
   168  
   169  	// now let's delete the other task
   170  	updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   171  		require.NoError(t, store.DeleteTask(tx, task1.ID))
   172  	}, true)
   173  
   174  	// now the service and network2 should be gone
   175  	s.View(func(tx store.ReadTx) {
   176  		require.Nil(t, store.GetService(tx, service.ID))
   177  		require.Nil(t, store.GetNetwork(tx, network2.ID))
   178  
   179  		// quick sanity check, the first service should be
   180  		// unaffected
   181  		require.NotNil(t, store.GetNetwork(tx, network1.ID))
   182  	})
   183  }
   184  
   185  // this tests that an update to a service that is _not_ marked it for deletion
   186  // doesn't do anything
   187  func TestServiceNotMarkedForDeletion(t *testing.T) {
   188  	s := store.NewMemoryStore(nil)
   189  	require.NotNil(t, s)
   190  	defer s.Close()
   191  
   192  	service := newService("service", false)
   193  	createDBObjects(t, s, service)
   194  
   195  	deallocator, ran := startNewDeallocator(t, s, false)
   196  	defer stopDeallocator(t, deallocator, ran)
   197  
   198  	updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   199  		service.Meta = api.Meta{Version: api.Version{Index: 12}}
   200  		require.NoError(t, store.UpdateService(tx, service))
   201  	},
   202  		// the deallocator shouldn't do any DB updates based on this event
   203  		false)
   204  }
   205  
   206  // this tests that an update to a network that is _not_ marked it for deletion
   207  // doesn't do anything
   208  func TestNetworkNotMarkedForDeletion(t *testing.T) {
   209  	s := store.NewMemoryStore(nil)
   210  	require.NotNil(t, s)
   211  	defer s.Close()
   212  
   213  	network := newNetwork("network", false)
   214  	service := newService("service", false, network)
   215  	createDBObjects(t, s, network, service)
   216  
   217  	deallocator, ran := startNewDeallocator(t, s, false)
   218  	defer stopDeallocator(t, deallocator, ran)
   219  
   220  	updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   221  		network.IPAM = &api.IPAMOptions{Driver: &api.Driver{Name: "test_driver"}}
   222  		require.NoError(t, store.UpdateNetwork(tx, network))
   223  	},
   224  		// the deallocator shouldn't do any DB updates based on this event
   225  		false)
   226  }
   227  
   228  // this test that the deallocator also works with the "old" style of storing
   229  // networks directly on the  service spec (instead of the task spec)
   230  // TODO: as said in the source file, we should really add a helper
   231  // on services objects itself, and test it there instead
   232  func TestDeallocatorWithOldStyleNetworks(t *testing.T) {
   233  	s := store.NewMemoryStore(nil)
   234  	require.NotNil(t, s)
   235  	defer s.Close()
   236  
   237  	service := newService("service", true)
   238  
   239  	// add a couple of networks with the old style
   240  	network1 := newNetwork("network1", true)
   241  	network2 := newNetwork("network2", false)
   242  	service.Spec.Networks = append(service.Spec.Networks, newNetworkConfigs(network1, network2)...)
   243  	task := newTask("task", service)
   244  
   245  	createDBObjects(t, s, service, network1, network2, task)
   246  
   247  	deallocator, ran := startNewDeallocator(t, s,
   248  		// nothing should have been deleted
   249  		false)
   250  	defer stopDeallocator(t, deallocator, ran)
   251  
   252  	// now let's delete the one task saving it all from oblivion
   253  	updateStoreAndWaitForEvent(t, deallocator, func(tx store.Tx) {
   254  		require.NoError(t, store.DeleteTask(tx, task.ID))
   255  	}, true)
   256  
   257  	// the deallocator should have removed both the service and
   258  	// the first network, but not the second
   259  	s.View(func(tx store.ReadTx) {
   260  		require.Nil(t, store.GetService(tx, service.ID))
   261  		require.Nil(t, store.GetNetwork(tx, network1.ID))
   262  		require.NotNil(t, store.GetNetwork(tx, network2.ID))
   263  	})
   264  }
   265  
   266  // Helpers below
   267  
   268  // starts a new deallocator, and also creates a channel to retrieve the return
   269  // value, so that we can check later than there was no error
   270  func startNewDeallocator(t *testing.T, s *store.MemoryStore, expectedUpdates bool) (deallocator *Deallocator, ran chan error) {
   271  	deallocator = New(s)
   272  	deallocator.eventChan = make(chan bool)
   273  	ran = make(chan error)
   274  
   275  	go func() {
   276  		returnValue := deallocator.Run(context.Background())
   277  		// allows checking that `Run` does return after we've stopped
   278  		ran <- returnValue
   279  		close(ran)
   280  	}()
   281  	waitForDeallocatorEvent(t, deallocator, expectedUpdates)
   282  
   283  	return
   284  }
   285  
   286  // stops the deallocator started by `startNewDeallocator` above
   287  func stopDeallocator(t *testing.T, deallocator *Deallocator, ran chan error) {
   288  	stopped := make(chan struct{})
   289  	go func() {
   290  		deallocator.Stop()
   291  		close(stopped)
   292  	}()
   293  
   294  	// it shouldn't take too long to stop
   295  	select {
   296  	case <-stopped:
   297  	case <-time.After(time.Second):
   298  		t.Fatal("Waited for too long for the deallocator to stop")
   299  	}
   300  
   301  	// `Run` should have returned, too
   302  	select {
   303  	case returnValue := <-ran:
   304  		require.NoError(t, returnValue)
   305  	case <-time.After(time.Second):
   306  		t.Fatal("Run hasn't returned")
   307  	}
   308  
   309  	ensureNoDeallocatorEvent(t, deallocator)
   310  }
   311  
   312  func waitForDeallocatorEvent(t *testing.T, deallocator *Deallocator, expectedUpdates bool) {
   313  	select {
   314  	case updates := <-deallocator.eventChan:
   315  		if updates != expectedUpdates {
   316  			t.Errorf("Expected updates %v VS actual %v", expectedUpdates, updates)
   317  		}
   318  		ensureNoDeallocatorEvent(t, deallocator)
   319  	case <-time.After(time.Second):
   320  		t.Fatal("Waited for too long for the deallocator to process new events")
   321  	}
   322  }
   323  
   324  func ensureNoDeallocatorEvent(t *testing.T, deallocator *Deallocator) {
   325  	select {
   326  	case <-deallocator.eventChan:
   327  		t.Fatal("Found unexpected event")
   328  	default:
   329  	}
   330  }
   331  
   332  func createDBObjects(t *testing.T, s *store.MemoryStore, objects ...interface{}) {
   333  	err := s.Update(func(tx store.Tx) (e error) {
   334  		for _, object := range objects {
   335  			switch typedObject := object.(type) {
   336  			case *api.Service:
   337  				e = store.CreateService(tx, typedObject)
   338  			case *api.Task:
   339  				e = store.CreateTask(tx, typedObject)
   340  			case *api.Network:
   341  				e = store.CreateNetwork(tx, typedObject)
   342  			}
   343  			require.NoError(t, e, "Unable to create DB object %v", object)
   344  		}
   345  		return
   346  	})
   347  	require.NoError(t, err, "Error setting up test fixtures")
   348  }
   349  
   350  func updateStore(t *testing.T, s *store.MemoryStore, cb func(x store.Tx)) {
   351  	require.NoError(t, s.Update(func(tx store.Tx) error {
   352  		cb(tx)
   353  		return nil
   354  	}))
   355  }
   356  
   357  func updateStoreAndWaitForEvent(t *testing.T, deallocator *Deallocator, cb func(x store.Tx), expectedUpdates bool) {
   358  	updateStore(t, deallocator.store, cb)
   359  	waitForDeallocatorEvent(t, deallocator, expectedUpdates)
   360  }
   361  
   362  func newService(id string, pendingDelete bool, networks ...*api.Network) *api.Service {
   363  	return &api.Service{
   364  		ID: id,
   365  		Spec: api.ServiceSpec{
   366  			Annotations: api.Annotations{
   367  				Name: id,
   368  			},
   369  			Task: api.TaskSpec{
   370  				Networks: newNetworkConfigs(networks...),
   371  			},
   372  		},
   373  		PendingDelete: pendingDelete,
   374  	}
   375  }
   376  
   377  func newNetwork(id string, pendingDelete bool) *api.Network {
   378  	return &api.Network{
   379  		ID: id,
   380  		Spec: api.NetworkSpec{
   381  			Annotations: api.Annotations{
   382  				Name: id,
   383  			},
   384  		},
   385  		PendingDelete: pendingDelete,
   386  	}
   387  }
   388  
   389  func newNetworkConfigs(networks ...*api.Network) []*api.NetworkAttachmentConfig {
   390  	networkConfigs := make([]*api.NetworkAttachmentConfig, len(networks))
   391  
   392  	for i := 0; i < len(networks); i++ {
   393  		networkConfigs[i] = &api.NetworkAttachmentConfig{
   394  			Target: networks[i].ID,
   395  		}
   396  	}
   397  
   398  	return networkConfigs
   399  }
   400  
   401  func newTask(id string, service *api.Service) *api.Task {
   402  	return &api.Task{
   403  		ID:        id,
   404  		ServiceID: service.ID,
   405  	}
   406  }