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 }