github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/agent/worker_test.go (about) 1 package agent 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/docker/swarmkit/agent/exec" 8 "github.com/docker/swarmkit/api" 9 "github.com/docker/swarmkit/log" 10 "github.com/sirupsen/logrus" 11 "github.com/stretchr/testify/assert" 12 bolt "go.etcd.io/bbolt" 13 ) 14 15 type testPublisherProvider struct { 16 } 17 18 func (tpp *testPublisherProvider) Publisher(ctx context.Context, subscriptionID string) (exec.LogPublisher, func(), error) { 19 return exec.LogPublisherFunc(func(ctx context.Context, message api.LogMessage) error { 20 log.G(ctx).WithFields(logrus.Fields{ 21 "subscription": subscriptionID, 22 "task.id": message.Context.TaskID, 23 "node.id": message.Context.NodeID, 24 "service.id": message.Context.ServiceID, 25 }).Info(message.Data) 26 return nil 27 }), func() { 28 }, nil 29 } 30 31 func TestWorkerAssign(t *testing.T) { 32 db, cleanup := storageTestEnv(t) 33 defer cleanup() 34 35 ctx := context.Background() 36 executor := &mockExecutor{dependencies: NewDependencyManager()} 37 worker := newWorker(db, executor, &testPublisherProvider{}) 38 reporter := statusReporterFunc(func(ctx context.Context, taskID string, status *api.TaskStatus) error { 39 log.G(ctx).WithFields(logrus.Fields{"task.id": taskID, "status": status}).Info("status update received") 40 return nil 41 }) 42 43 worker.Listen(ctx, reporter) 44 45 for _, testcase := range []struct { 46 changeSet []*api.AssignmentChange 47 expectedTasks []*api.Task 48 expectedSecrets []*api.Secret 49 expectedConfigs []*api.Config 50 expectedAssigned []*api.Task 51 }{ 52 {}, // handle nil case. 53 { 54 changeSet: []*api.AssignmentChange{ 55 { 56 Assignment: &api.Assignment{ 57 Item: &api.Assignment_Task{ 58 Task: &api.Task{ID: "task-1"}, 59 }, 60 }, 61 Action: api.AssignmentChange_AssignmentActionUpdate, 62 }, 63 { 64 Assignment: &api.Assignment{ 65 Item: &api.Assignment_Secret{ 66 Secret: &api.Secret{ID: "secret-1"}, 67 }, 68 }, 69 Action: api.AssignmentChange_AssignmentActionUpdate, 70 }, 71 { 72 Assignment: &api.Assignment{ 73 Item: &api.Assignment_Config{ 74 Config: &api.Config{ID: "config-1"}, 75 }, 76 }, 77 Action: api.AssignmentChange_AssignmentActionUpdate, 78 }, 79 // these should be ignored 80 { 81 Assignment: &api.Assignment{ 82 Item: &api.Assignment_Secret{ 83 Secret: &api.Secret{ID: "secret-2"}, 84 }, 85 }, 86 Action: api.AssignmentChange_AssignmentActionRemove, 87 }, 88 { 89 Assignment: &api.Assignment{ 90 Item: &api.Assignment_Task{ 91 Task: &api.Task{ID: "task-2"}, 92 }, 93 }, 94 Action: api.AssignmentChange_AssignmentActionRemove, 95 }, 96 { 97 Assignment: &api.Assignment{ 98 Item: &api.Assignment_Config{ 99 Config: &api.Config{ID: "config-2"}, 100 }, 101 }, 102 Action: api.AssignmentChange_AssignmentActionRemove, 103 }, 104 }, 105 expectedTasks: []*api.Task{ 106 {ID: "task-1"}, 107 }, 108 expectedSecrets: []*api.Secret{ 109 {ID: "secret-1"}, 110 }, 111 expectedConfigs: []*api.Config{ 112 {ID: "config-1"}, 113 }, 114 expectedAssigned: []*api.Task{ 115 {ID: "task-1"}, 116 }, 117 }, 118 { // completely replaces the existing tasks and secrets 119 changeSet: []*api.AssignmentChange{ 120 { 121 Assignment: &api.Assignment{ 122 Item: &api.Assignment_Task{ 123 Task: &api.Task{ID: "task-2"}, 124 }, 125 }, 126 Action: api.AssignmentChange_AssignmentActionUpdate, 127 }, 128 { 129 Assignment: &api.Assignment{ 130 Item: &api.Assignment_Secret{ 131 Secret: &api.Secret{ID: "secret-2"}, 132 }, 133 }, 134 Action: api.AssignmentChange_AssignmentActionUpdate, 135 }, 136 { 137 Assignment: &api.Assignment{ 138 Item: &api.Assignment_Config{ 139 Config: &api.Config{ID: "config-2"}, 140 }, 141 }, 142 Action: api.AssignmentChange_AssignmentActionUpdate, 143 }, 144 }, 145 expectedTasks: []*api.Task{ 146 {ID: "task-2"}, 147 }, 148 expectedSecrets: []*api.Secret{ 149 {ID: "secret-2"}, 150 }, 151 expectedConfigs: []*api.Config{ 152 {ID: "config-2"}, 153 }, 154 expectedAssigned: []*api.Task{ 155 // task-1 should be cleaned up and deleted. 156 {ID: "task-2"}, 157 }, 158 }, 159 { 160 // remove assigned tasks, secret and config no longer present 161 // there should be no tasks in the tasks db after this. 162 expectedTasks: nil, 163 }, 164 165 // TODO(stevvooe): There are a few more states here we need to get 166 // covered to ensure correct during code changes. 167 } { 168 assert.NoError(t, worker.Assign(ctx, testcase.changeSet)) 169 170 var ( 171 tasks []*api.Task 172 assigned []*api.Task 173 ) 174 175 assert.NoError(t, worker.db.View(func(tx *bolt.Tx) error { 176 return WalkTasks(tx, func(task *api.Task) error { 177 tasks = append(tasks, task) 178 if TaskAssigned(tx, task.ID) { 179 assigned = append(assigned, task) 180 } 181 return nil 182 }) 183 })) 184 185 assert.Equal(t, testcase.expectedTasks, tasks) 186 assert.Equal(t, testcase.expectedAssigned, assigned) 187 for _, secret := range testcase.expectedSecrets { 188 secret, err := executor.Secrets().Get(secret.ID) 189 assert.NoError(t, err) 190 assert.NotNil(t, secret) 191 } 192 for _, config := range testcase.expectedConfigs { 193 config, err := executor.Configs().Get(config.ID) 194 assert.NoError(t, err) 195 assert.NotNil(t, config) 196 } 197 } 198 } 199 200 func TestWorkerWait(t *testing.T) { 201 db, cleanup := storageTestEnv(t) 202 defer cleanup() 203 204 ctx := context.Background() 205 executor := &mockExecutor{dependencies: NewDependencyManager()} 206 worker := newWorker(db, executor, &testPublisherProvider{}) 207 reporter := statusReporterFunc(func(ctx context.Context, taskID string, status *api.TaskStatus) error { 208 log.G(ctx).WithFields(logrus.Fields{"task.id": taskID, "status": status}).Info("status update received") 209 return nil 210 }) 211 212 worker.Listen(ctx, reporter) 213 214 changeSet := []*api.AssignmentChange{ 215 { 216 Assignment: &api.Assignment{ 217 Item: &api.Assignment_Task{ 218 Task: &api.Task{ID: "task-1"}, 219 }, 220 }, 221 Action: api.AssignmentChange_AssignmentActionUpdate, 222 }, 223 { 224 Assignment: &api.Assignment{ 225 Item: &api.Assignment_Task{ 226 Task: &api.Task{ID: "task-2"}, 227 }, 228 }, 229 Action: api.AssignmentChange_AssignmentActionUpdate, 230 }, 231 { 232 Assignment: &api.Assignment{ 233 Item: &api.Assignment_Secret{ 234 Secret: &api.Secret{ID: "secret-1"}, 235 }, 236 }, 237 Action: api.AssignmentChange_AssignmentActionUpdate, 238 }, 239 { 240 Assignment: &api.Assignment{ 241 Item: &api.Assignment_Config{ 242 Config: &api.Config{ID: "config-1"}, 243 }, 244 }, 245 Action: api.AssignmentChange_AssignmentActionUpdate, 246 }, 247 } 248 249 expectedTasks := []*api.Task{ 250 {ID: "task-1"}, 251 {ID: "task-2"}, 252 } 253 254 expectedSecrets := []*api.Secret{ 255 {ID: "secret-1"}, 256 } 257 258 expectedConfigs := []*api.Config{ 259 {ID: "config-1"}, 260 } 261 262 expectedAssigned := []*api.Task{ 263 {ID: "task-1"}, 264 {ID: "task-2"}, 265 } 266 267 var ( 268 tasks []*api.Task 269 assigned []*api.Task 270 ) 271 assert.NoError(t, worker.Assign(ctx, changeSet)) 272 273 assert.NoError(t, worker.db.View(func(tx *bolt.Tx) error { 274 return WalkTasks(tx, func(task *api.Task) error { 275 tasks = append(tasks, task) 276 if TaskAssigned(tx, task.ID) { 277 assigned = append(assigned, task) 278 } 279 return nil 280 }) 281 })) 282 283 assert.Equal(t, expectedTasks, tasks) 284 assert.Equal(t, expectedAssigned, assigned) 285 for _, secret := range expectedSecrets { 286 secret, err := executor.Secrets().Get(secret.ID) 287 assert.NoError(t, err) 288 assert.NotNil(t, secret) 289 } 290 for _, config := range expectedConfigs { 291 config, err := executor.Configs().Get(config.ID) 292 assert.NoError(t, err) 293 assert.NotNil(t, config) 294 } 295 296 err := worker.Assign(ctx, nil) 297 assert.Nil(t, err) 298 299 err = worker.Wait(ctx) 300 assert.Nil(t, err) 301 302 assigned = assigned[:0] 303 304 assert.NoError(t, worker.db.View(func(tx *bolt.Tx) error { 305 return WalkTasks(tx, func(task *api.Task) error { 306 if TaskAssigned(tx, task.ID) { 307 assigned = append(assigned, task) 308 } 309 return nil 310 }) 311 })) 312 assert.Equal(t, len(assigned), 0) 313 } 314 315 func TestWorkerUpdate(t *testing.T) { 316 db, cleanup := storageTestEnv(t) 317 defer cleanup() 318 319 ctx := context.Background() 320 executor := &mockExecutor{dependencies: NewDependencyManager()} 321 worker := newWorker(db, executor, &testPublisherProvider{}) 322 reporter := statusReporterFunc(func(ctx context.Context, taskID string, status *api.TaskStatus) error { 323 log.G(ctx).WithFields(logrus.Fields{"task.id": taskID, "status": status}).Info("status update received") 324 return nil 325 }) 326 327 worker.Listen(ctx, reporter) 328 329 // create existing task/secret/config 330 assert.NoError(t, worker.Assign(ctx, []*api.AssignmentChange{ 331 { 332 Assignment: &api.Assignment{ 333 Item: &api.Assignment_Task{ 334 Task: &api.Task{ID: "task-1"}, 335 }, 336 }, 337 Action: api.AssignmentChange_AssignmentActionUpdate, 338 }, 339 { 340 Assignment: &api.Assignment{ 341 Item: &api.Assignment_Secret{ 342 Secret: &api.Secret{ID: "secret-1"}, 343 }, 344 }, 345 Action: api.AssignmentChange_AssignmentActionUpdate, 346 }, 347 { 348 Assignment: &api.Assignment{ 349 Item: &api.Assignment_Config{ 350 Config: &api.Config{ID: "config-1"}, 351 }, 352 }, 353 Action: api.AssignmentChange_AssignmentActionUpdate, 354 }, 355 })) 356 357 for _, testcase := range []struct { 358 changeSet []*api.AssignmentChange 359 expectedTasks []*api.Task 360 expectedSecrets []*api.Secret 361 expectedConfigs []*api.Config 362 expectedAssigned []*api.Task 363 }{ 364 { // handle nil changeSet case. 365 expectedTasks: []*api.Task{ 366 {ID: "task-1"}, 367 }, 368 expectedSecrets: []*api.Secret{ 369 {ID: "secret-1"}, 370 }, 371 expectedConfigs: []*api.Config{ 372 {ID: "config-1"}, 373 }, 374 expectedAssigned: []*api.Task{ 375 {ID: "task-1"}, 376 }, 377 }, 378 { 379 // no changes 380 changeSet: []*api.AssignmentChange{ 381 { 382 Assignment: &api.Assignment{ 383 Item: &api.Assignment_Task{ 384 Task: &api.Task{ID: "task-1"}, 385 }, 386 }, 387 Action: api.AssignmentChange_AssignmentActionUpdate, 388 }, 389 }, 390 expectedTasks: []*api.Task{ 391 {ID: "task-1"}, 392 }, 393 expectedSecrets: []*api.Secret{ 394 {ID: "secret-1"}, 395 }, 396 expectedConfigs: []*api.Config{ 397 {ID: "config-1"}, 398 }, 399 expectedAssigned: []*api.Task{ 400 {ID: "task-1"}, 401 }, 402 }, 403 { 404 // adding a secret and task 405 changeSet: []*api.AssignmentChange{ 406 { 407 Assignment: &api.Assignment{ 408 Item: &api.Assignment_Task{ 409 Task: &api.Task{ID: "task-2"}, 410 }, 411 }, 412 Action: api.AssignmentChange_AssignmentActionUpdate, 413 }, 414 { 415 Assignment: &api.Assignment{ 416 Item: &api.Assignment_Secret{ 417 Secret: &api.Secret{ID: "secret-2"}, 418 }, 419 }, 420 Action: api.AssignmentChange_AssignmentActionUpdate, 421 }, 422 { 423 Assignment: &api.Assignment{ 424 Item: &api.Assignment_Config{ 425 Config: &api.Config{ID: "config-2"}, 426 }, 427 }, 428 Action: api.AssignmentChange_AssignmentActionUpdate, 429 }, 430 }, 431 expectedTasks: []*api.Task{ 432 {ID: "task-1"}, 433 {ID: "task-2"}, 434 }, 435 expectedSecrets: []*api.Secret{ 436 {ID: "secret-1"}, 437 {ID: "secret-2"}, 438 }, 439 expectedConfigs: []*api.Config{ 440 {ID: "config-1"}, 441 {ID: "config-2"}, 442 }, 443 expectedAssigned: []*api.Task{ 444 {ID: "task-1"}, 445 {ID: "task-2"}, 446 }, 447 }, 448 { 449 // remove assigned task and secret, updating existing secret 450 changeSet: []*api.AssignmentChange{ 451 { 452 Assignment: &api.Assignment{ 453 Item: &api.Assignment_Task{ 454 Task: &api.Task{ID: "task-1"}, 455 }, 456 }, 457 Action: api.AssignmentChange_AssignmentActionRemove, 458 }, 459 { 460 Assignment: &api.Assignment{ 461 Item: &api.Assignment_Secret{ 462 Secret: &api.Secret{ID: "secret-1"}, 463 }, 464 }, 465 Action: api.AssignmentChange_AssignmentActionRemove, 466 }, 467 { 468 Assignment: &api.Assignment{ 469 Item: &api.Assignment_Secret{ 470 Secret: &api.Secret{ID: "secret-2"}, 471 }, 472 }, 473 Action: api.AssignmentChange_AssignmentActionUpdate, 474 }, 475 { 476 Assignment: &api.Assignment{ 477 Item: &api.Assignment_Config{ 478 Config: &api.Config{ID: "config-1"}, 479 }, 480 }, 481 Action: api.AssignmentChange_AssignmentActionRemove, 482 }, 483 { 484 Assignment: &api.Assignment{ 485 Item: &api.Assignment_Config{ 486 Config: &api.Config{ID: "config-2"}, 487 }, 488 }, 489 Action: api.AssignmentChange_AssignmentActionUpdate, 490 }, 491 }, 492 expectedTasks: []*api.Task{ 493 {ID: "task-2"}, 494 }, 495 expectedSecrets: []*api.Secret{ 496 {ID: "secret-2"}, 497 }, 498 expectedConfigs: []*api.Config{ 499 {ID: "config-2"}, 500 }, 501 expectedAssigned: []*api.Task{ 502 {ID: "task-2"}, 503 }, 504 }, 505 { 506 // removing nonexistent items doesn't fail 507 changeSet: []*api.AssignmentChange{ 508 { 509 Assignment: &api.Assignment{ 510 Item: &api.Assignment_Task{ 511 Task: &api.Task{ID: "task-1"}, 512 }, 513 }, 514 Action: api.AssignmentChange_AssignmentActionRemove, 515 }, 516 { 517 Assignment: &api.Assignment{ 518 Item: &api.Assignment_Secret{ 519 Secret: &api.Secret{ID: "secret-1"}, 520 }, 521 }, 522 Action: api.AssignmentChange_AssignmentActionRemove, 523 }, 524 { 525 Assignment: &api.Assignment{ 526 Item: &api.Assignment_Task{ 527 Task: &api.Task{ID: "task-2"}, 528 }, 529 }, 530 Action: api.AssignmentChange_AssignmentActionRemove, 531 }, 532 { 533 Assignment: &api.Assignment{ 534 Item: &api.Assignment_Secret{ 535 Secret: &api.Secret{ID: "secret-2"}, 536 }, 537 }, 538 Action: api.AssignmentChange_AssignmentActionRemove, 539 }, 540 { 541 Assignment: &api.Assignment{ 542 Item: &api.Assignment_Config{ 543 Config: &api.Config{ID: "config-1"}, 544 }, 545 }, 546 Action: api.AssignmentChange_AssignmentActionRemove, 547 }, 548 { 549 Assignment: &api.Assignment{ 550 Item: &api.Assignment_Config{ 551 Config: &api.Config{ID: "config-2"}, 552 }, 553 }, 554 Action: api.AssignmentChange_AssignmentActionRemove, 555 }, 556 }, 557 }, 558 } { 559 assert.NoError(t, worker.Update(ctx, testcase.changeSet)) 560 561 var ( 562 tasks []*api.Task 563 assigned []*api.Task 564 ) 565 assert.NoError(t, worker.db.View(func(tx *bolt.Tx) error { 566 return WalkTasks(tx, func(task *api.Task) error { 567 tasks = append(tasks, task) 568 if TaskAssigned(tx, task.ID) { 569 assigned = append(assigned, task) 570 } 571 return nil 572 }) 573 })) 574 575 assert.Equal(t, testcase.expectedTasks, tasks) 576 assert.Equal(t, testcase.expectedAssigned, assigned) 577 for _, secret := range testcase.expectedSecrets { 578 secret, err := executor.Secrets().Get(secret.ID) 579 assert.NoError(t, err) 580 assert.NotNil(t, secret) 581 } 582 for _, config := range testcase.expectedConfigs { 583 config, err := executor.Configs().Get(config.ID) 584 assert.NoError(t, err) 585 assert.NotNil(t, config) 586 } 587 } 588 } 589 590 type mockTaskController struct { 591 exec.Controller 592 task *api.Task 593 dependencies exec.DependencyGetter 594 } 595 596 func (mtc *mockTaskController) Remove(ctx context.Context) error { 597 return nil 598 } 599 600 func (mtc *mockTaskController) Close() error { 601 return nil 602 } 603 604 type mockExecutor struct { 605 exec.Executor 606 dependencies exec.DependencyManager 607 } 608 609 func (m *mockExecutor) Controller(task *api.Task) (exec.Controller, error) { 610 return &mockTaskController{task: task, dependencies: Restrict(m.dependencies, task)}, nil 611 } 612 613 func (m *mockExecutor) Secrets() exec.SecretsManager { 614 return m.dependencies.Secrets() 615 } 616 617 func (m *mockExecutor) Configs() exec.ConfigsManager { 618 return m.dependencies.Configs() 619 }