github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/caasunitprovisioner/worker_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasunitprovisioner_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock/testclock" 10 "github.com/juju/errors" 11 "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/juju/names.v2" 16 "gopkg.in/juju/worker.v1" 17 "gopkg.in/juju/worker.v1/workertest" 18 apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 19 20 apicaasunitprovisioner "github.com/juju/juju/api/caasunitprovisioner" 21 "github.com/juju/juju/apiserver/params" 22 "github.com/juju/juju/caas" 23 "github.com/juju/juju/core/application" 24 "github.com/juju/juju/core/constraints" 25 "github.com/juju/juju/core/life" 26 "github.com/juju/juju/core/status" 27 "github.com/juju/juju/core/watcher/watchertest" 28 "github.com/juju/juju/storage" 29 coretesting "github.com/juju/juju/testing" 30 "github.com/juju/juju/worker/caasunitprovisioner" 31 ) 32 33 type WorkerSuite struct { 34 testing.IsolationSuite 35 36 config caasunitprovisioner.Config 37 applicationGetter mockApplicationGetter 38 applicationUpdater mockApplicationUpdater 39 serviceBroker mockServiceBroker 40 containerBroker mockContainerBroker 41 podSpecGetter mockProvisioningInfoGetterGetter 42 lifeGetter mockLifeGetter 43 unitUpdater mockUnitUpdater 44 statusSetter mockProvisioningStatusSetter 45 46 applicationChanges chan []string 47 applicationScaleChanges chan struct{} 48 caasUnitsChanges chan struct{} 49 caasOperatorChanges chan struct{} 50 containerSpecChanges chan struct{} 51 serviceDeleted chan struct{} 52 serviceEnsured chan struct{} 53 serviceUpdated chan struct{} 54 clock *testclock.Clock 55 } 56 57 var _ = gc.Suite(&WorkerSuite{}) 58 59 var ( 60 containerSpec = ` 61 containers: 62 - name: gitlab 63 image: gitlab/latest 64 ports: 65 - containerPort: 80 66 protocol: TCP 67 - containerPort: 443 68 config: 69 attr: foo=bar; fred=blogs 70 foo: bar 71 `[1:] 72 73 parsedSpec = caas.PodSpec{ 74 Containers: []caas.ContainerSpec{{ 75 Name: "gitlab", 76 Image: "gitlab/latest", 77 Ports: []caas.ContainerPort{ 78 {ContainerPort: 80, Protocol: "TCP"}, 79 {ContainerPort: 443}, 80 }, 81 Config: map[string]interface{}{ 82 "attr": "foo=bar; fred=blogs", 83 "foo": "bar", 84 }}, 85 }} 86 87 expectedServiceParams = &caas.ServiceParams{ 88 PodSpec: &parsedSpec, 89 ResourceTags: map[string]string{"foo": "bar"}, 90 Placement: "placement", 91 Constraints: constraints.MustParse("mem=4G"), 92 Filesystems: []storage.KubernetesFilesystemParams{{ 93 StorageName: "database", 94 Size: 100, 95 }}, 96 } 97 ) 98 99 func (s *WorkerSuite) SetUpTest(c *gc.C) { 100 s.IsolationSuite.SetUpTest(c) 101 102 s.applicationChanges = make(chan []string) 103 s.applicationScaleChanges = make(chan struct{}) 104 s.caasUnitsChanges = make(chan struct{}) 105 s.caasOperatorChanges = make(chan struct{}) 106 s.containerSpecChanges = make(chan struct{}, 1) 107 s.serviceDeleted = make(chan struct{}) 108 s.serviceEnsured = make(chan struct{}) 109 s.serviceUpdated = make(chan struct{}) 110 111 s.applicationGetter = mockApplicationGetter{ 112 watcher: watchertest.NewMockStringsWatcher(s.applicationChanges), 113 scaleWatcher: watchertest.NewMockNotifyWatcher(s.applicationScaleChanges), 114 } 115 s.applicationUpdater = mockApplicationUpdater{ 116 updated: s.serviceUpdated, 117 } 118 119 s.podSpecGetter = mockProvisioningInfoGetterGetter{ 120 watcher: watchertest.NewMockNotifyWatcher(s.containerSpecChanges), 121 } 122 s.podSpecGetter.setProvisioningInfo(apicaasunitprovisioner.ProvisioningInfo{ 123 PodSpec: containerSpec, 124 Tags: map[string]string{"foo": "bar"}, 125 Placement: "placement", 126 Constraints: constraints.MustParse("mem=4G"), 127 Filesystems: []storage.KubernetesFilesystemParams{{ 128 StorageName: "database", 129 Size: 100, 130 }}, 131 }) 132 133 s.unitUpdater = mockUnitUpdater{} 134 135 s.containerBroker = mockContainerBroker{ 136 unitsWatcher: watchertest.NewMockNotifyWatcher(s.caasUnitsChanges), 137 operatorWatcher: watchertest.NewMockNotifyWatcher(s.caasOperatorChanges), 138 podSpec: &parsedSpec, 139 } 140 s.lifeGetter = mockLifeGetter{} 141 s.lifeGetter.setLife(life.Alive) 142 s.serviceBroker = mockServiceBroker{ 143 ensured: s.serviceEnsured, 144 deleted: s.serviceDeleted, 145 podSpec: &parsedSpec, 146 } 147 s.statusSetter = mockProvisioningStatusSetter{} 148 149 s.config = caasunitprovisioner.Config{ 150 ApplicationGetter: &s.applicationGetter, 151 ApplicationUpdater: &s.applicationUpdater, 152 ServiceBroker: &s.serviceBroker, 153 ContainerBroker: &s.containerBroker, 154 ProvisioningInfoGetter: &s.podSpecGetter, 155 LifeGetter: &s.lifeGetter, 156 UnitUpdater: &s.unitUpdater, 157 ProvisioningStatusSetter: &s.statusSetter, 158 } 159 } 160 161 func (s *WorkerSuite) sendContainerSpecChange(c *gc.C) { 162 select { 163 case s.containerSpecChanges <- struct{}{}: 164 case <-time.After(coretesting.LongWait): 165 c.Fatal("timed out sending pod spec change") 166 } 167 } 168 169 func (s *WorkerSuite) TestValidateConfig(c *gc.C) { 170 s.testValidateConfig(c, func(config *caasunitprovisioner.Config) { 171 config.ApplicationGetter = nil 172 }, `missing ApplicationGetter not valid`) 173 174 s.testValidateConfig(c, func(config *caasunitprovisioner.Config) { 175 config.ApplicationUpdater = nil 176 }, `missing ApplicationUpdater not valid`) 177 178 s.testValidateConfig(c, func(config *caasunitprovisioner.Config) { 179 config.ServiceBroker = nil 180 }, `missing ServiceBroker not valid`) 181 182 s.testValidateConfig(c, func(config *caasunitprovisioner.Config) { 183 config.ContainerBroker = nil 184 }, `missing ContainerBroker not valid`) 185 186 s.testValidateConfig(c, func(config *caasunitprovisioner.Config) { 187 config.ProvisioningInfoGetter = nil 188 }, `missing ProvisioningInfoGetter not valid`) 189 190 s.testValidateConfig(c, func(config *caasunitprovisioner.Config) { 191 config.LifeGetter = nil 192 }, `missing LifeGetter not valid`) 193 s.testValidateConfig(c, func(config *caasunitprovisioner.Config) { 194 config.ProvisioningStatusSetter = nil 195 }, `missing ProvisioningStatusSetter not valid`) 196 } 197 198 func (s *WorkerSuite) testValidateConfig(c *gc.C, f func(*caasunitprovisioner.Config), expect string) { 199 config := s.config 200 f(&config) 201 w, err := caasunitprovisioner.NewWorker(config) 202 if err == nil { 203 workertest.DirtyKill(c, w) 204 } 205 c.Check(err, gc.ErrorMatches, expect) 206 } 207 208 func (s *WorkerSuite) TestStartStop(c *gc.C) { 209 w, err := caasunitprovisioner.NewWorker(s.config) 210 c.Assert(err, jc.ErrorIsNil) 211 workertest.CheckAlive(c, w) 212 workertest.CleanKill(c, w) 213 } 214 215 func (s *WorkerSuite) setupNewUnitScenario(c *gc.C) worker.Worker { 216 w, err := caasunitprovisioner.NewWorker(s.config) 217 c.Assert(err, jc.ErrorIsNil) 218 219 s.podSpecGetter.SetErrors(nil, errors.NotFoundf("spec")) 220 221 select { 222 case s.applicationChanges <- []string{"gitlab"}: 223 case <-time.After(coretesting.LongWait): 224 c.Fatal("timed out sending applications change") 225 } 226 227 s.applicationGetter.scale = 1 228 select { 229 case s.applicationScaleChanges <- struct{}{}: 230 case <-time.After(coretesting.LongWait): 231 c.Fatal("timed out sending scale change") 232 } 233 234 // We seed a "not found" error above to indicate that 235 // there is not yet a pod spec; the broker should 236 // not be invoked. 237 s.sendContainerSpecChange(c) 238 select { 239 case <-s.serviceEnsured: 240 c.Fatal("service ensured unexpectedly") 241 case <-time.After(coretesting.ShortWait): 242 } 243 244 s.sendContainerSpecChange(c) 245 s.podSpecGetter.assertSpecRetrieved(c) 246 select { 247 case <-s.serviceEnsured: 248 case <-time.After(coretesting.LongWait): 249 c.Fatal("timed out waiting for service to be ensured") 250 } 251 s.statusSetter.CheckCall(c, 0, "SetOperatorStatus", "gitlab", status.Waiting, "ensuring", map[string]interface{}{"foo": "bar"}) 252 select { 253 case <-s.serviceUpdated: 254 case <-time.After(coretesting.LongWait): 255 c.Fatal("timed out waiting for service to be updated") 256 } 257 return w 258 } 259 260 func (s *WorkerSuite) TestScaleChanged(c *gc.C) { 261 w := s.setupNewUnitScenario(c) 262 defer workertest.CleanKill(c, w) 263 264 s.applicationGetter.CheckCallNames(c, "WatchApplications", "WatchApplicationScale", "ApplicationScale", "ApplicationConfig") 265 s.podSpecGetter.CheckCallNames(c, "WatchPodSpec", "ProvisioningInfo", "ProvisioningInfo") 266 s.podSpecGetter.CheckCall(c, 0, "WatchPodSpec", "gitlab") 267 s.podSpecGetter.CheckCall(c, 1, "ProvisioningInfo", "gitlab") // not found 268 s.podSpecGetter.CheckCall(c, 2, "ProvisioningInfo", "gitlab") 269 s.lifeGetter.CheckCallNames(c, "Life") 270 s.lifeGetter.CheckCall(c, 0, "Life", "gitlab") 271 s.serviceBroker.CheckCallNames(c, "EnsureService", "Service") 272 s.serviceBroker.CheckCall(c, 0, "EnsureService", 273 "gitlab", expectedServiceParams, 1, application.ConfigAttributes{"juju-external-hostname": "exthost"}) 274 s.serviceBroker.CheckCall(c, 1, "Service", "gitlab") 275 276 s.serviceBroker.ResetCalls() 277 // Add another unit. 278 s.applicationGetter.scale = 2 279 select { 280 case s.applicationScaleChanges <- struct{}{}: 281 case <-time.After(coretesting.LongWait): 282 c.Fatal("timed out sending scale change") 283 } 284 285 select { 286 case <-s.serviceEnsured: 287 case <-time.After(coretesting.LongWait): 288 c.Fatal("timed out waiting for service to be ensured") 289 } 290 291 newExpectedParams := *expectedServiceParams 292 newExpectedParams.PodSpec = &parsedSpec 293 s.serviceBroker.CheckCallNames(c, "EnsureService") 294 s.serviceBroker.CheckCall(c, 0, "EnsureService", 295 "gitlab", &newExpectedParams, 2, application.ConfigAttributes{"juju-external-hostname": "exthost"}) 296 297 s.serviceBroker.ResetCalls() 298 // Delete a unit. 299 s.applicationGetter.scale = 1 300 select { 301 case s.applicationScaleChanges <- struct{}{}: 302 case <-time.After(coretesting.LongWait): 303 c.Fatal("timed out sending scale change") 304 } 305 306 select { 307 case <-s.serviceEnsured: 308 case <-time.After(coretesting.LongWait): 309 c.Fatal("timed out waiting for service to be ensured") 310 } 311 312 s.serviceBroker.CheckCallNames(c, "EnsureService") 313 s.serviceBroker.CheckCall(c, 0, "EnsureService", 314 "gitlab", &newExpectedParams, 1, application.ConfigAttributes{"juju-external-hostname": "exthost"}) 315 } 316 317 func (s *WorkerSuite) TestNewPodSpecChange(c *gc.C) { 318 w := s.setupNewUnitScenario(c) 319 defer workertest.CleanKill(c, w) 320 321 s.serviceBroker.ResetCalls() 322 323 // Same spec, nothing happens. 324 s.sendContainerSpecChange(c) 325 s.podSpecGetter.assertSpecRetrieved(c) 326 select { 327 case <-s.serviceEnsured: 328 c.Fatal("service/unit ensured unexpectedly") 329 case <-time.After(coretesting.ShortWait): 330 } 331 332 var ( 333 anotherSpec = ` 334 containers: 335 - name: gitlab 336 image-name: gitlab/latest 337 `[1:] 338 339 anotherParsedSpec = caas.PodSpec{ 340 Containers: []caas.ContainerSpec{{ 341 Name: "gitlab", 342 Image: "gitlab/latest", 343 }}} 344 ) 345 346 s.serviceBroker.podSpec = &anotherParsedSpec 347 348 s.podSpecGetter.setProvisioningInfo(apicaasunitprovisioner.ProvisioningInfo{ 349 PodSpec: anotherSpec, 350 Tags: map[string]string{"foo": "bar"}, 351 }) 352 s.sendContainerSpecChange(c) 353 s.podSpecGetter.assertSpecRetrieved(c) 354 355 select { 356 case <-s.serviceEnsured: 357 case <-time.After(coretesting.LongWait): 358 c.Fatal("timed out waiting for service to be ensured") 359 } 360 361 expectedParams := &caas.ServiceParams{ 362 PodSpec: &anotherParsedSpec, 363 ResourceTags: map[string]string{"foo": "bar"}, 364 } 365 s.serviceBroker.CheckCallNames(c, "EnsureService") 366 s.serviceBroker.CheckCall(c, 0, "EnsureService", 367 "gitlab", expectedParams, 1, application.ConfigAttributes{"juju-external-hostname": "exthost"}) 368 } 369 370 func (s *WorkerSuite) TestNewPodSpecChangeCrd(c *gc.C) { 371 w := s.setupNewUnitScenario(c) 372 defer workertest.CleanKill(c, w) 373 374 s.serviceBroker.ResetCalls() 375 376 // Same spec, nothing happens. 377 s.sendContainerSpecChange(c) 378 s.podSpecGetter.assertSpecRetrieved(c) 379 select { 380 case <-s.serviceEnsured: 381 c.Fatal("service/unit ensured unexpectedly") 382 case <-time.After(coretesting.ShortWait): 383 } 384 385 float64Ptr := func(f float64) *float64 { return &f } 386 387 var ( 388 anotherSpec = ` 389 crd: 390 - group: kubeflow.org 391 version: v1alpha2 392 scope: Namespaced 393 kind: TFJob 394 validation: 395 properties: 396 tfReplicaSpecs: 397 properties: 398 Worker: 399 properties: 400 replicas: 401 type: integer 402 minimum: 1 403 `[1:] 404 405 anotherParsedSpec = caas.PodSpec{ 406 CustomResourceDefinitions: []caas.CustomResourceDefinition{ 407 { 408 Kind: "TFJob", 409 Group: "kubeflow.org", 410 Version: "v1alpha2", 411 Scope: "Namespaced", 412 Validation: caas.CustomResourceDefinitionValidation{ 413 Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ 414 "tfReplicaSpecs": { 415 Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ 416 "Worker": { 417 Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ 418 "replicas": { 419 Type: "integer", 420 Minimum: float64Ptr(1), 421 }, 422 }, 423 }, 424 }, 425 }, 426 }, 427 }, 428 }, 429 }, 430 } 431 ) 432 433 s.serviceBroker.podSpec = &anotherParsedSpec 434 435 s.podSpecGetter.setProvisioningInfo(apicaasunitprovisioner.ProvisioningInfo{ 436 PodSpec: anotherSpec, 437 Tags: map[string]string{"foo": "bar"}, 438 }) 439 s.sendContainerSpecChange(c) 440 s.podSpecGetter.assertSpecRetrieved(c) 441 442 select { 443 case <-s.serviceEnsured: 444 case <-time.After(coretesting.LongWait): 445 c.Fatal("timed out waiting for service to be ensured") 446 } 447 448 s.serviceBroker.CheckCallNames(c, "EnsureCustomResourceDefinition", "EnsureService") 449 s.serviceBroker.CheckCall(c, 0, "EnsureCustomResourceDefinition", "gitlab", &anotherParsedSpec) 450 } 451 452 func (s *WorkerSuite) TestScaleZero(c *gc.C) { 453 w := s.setupNewUnitScenario(c) 454 defer workertest.CleanKill(c, w) 455 456 s.serviceBroker.ResetCalls() 457 // Add another unit. 458 s.applicationGetter.scale = 2 459 select { 460 case s.applicationScaleChanges <- struct{}{}: 461 case <-time.After(coretesting.LongWait): 462 c.Fatal("timed out sending scale change") 463 } 464 465 select { 466 case <-s.serviceEnsured: 467 case <-time.After(coretesting.LongWait): 468 c.Fatal("timed out waiting for service to be ensured") 469 } 470 s.serviceBroker.ResetCalls() 471 472 // Now the scale down to 0. 473 s.applicationGetter.scale = 0 474 select { 475 case s.applicationScaleChanges <- struct{}{}: 476 case <-time.After(coretesting.LongWait): 477 c.Fatal("timed out sending scale change") 478 } 479 480 select { 481 case <-s.serviceEnsured: 482 case <-time.After(coretesting.LongWait): 483 c.Fatal("timed out waiting for service to be ensured") 484 } 485 s.serviceBroker.CheckCallNames(c, "EnsureService") 486 s.serviceBroker.CheckCall(c, 0, "EnsureService", 487 "gitlab", &caas.ServiceParams{}, 0, application.ConfigAttributes(nil)) 488 } 489 490 func (s *WorkerSuite) TestUnitAllRemoved(c *gc.C) { 491 w := s.setupNewUnitScenario(c) 492 defer workertest.CleanKill(c, w) 493 494 s.serviceBroker.ResetCalls() 495 // Add another unit. 496 s.applicationGetter.scale = 2 497 select { 498 case s.applicationScaleChanges <- struct{}{}: 499 case <-time.After(coretesting.LongWait): 500 c.Fatal("timed out sending scale change") 501 } 502 503 select { 504 case <-s.serviceEnsured: 505 case <-time.After(coretesting.LongWait): 506 c.Fatal("timed out waiting for service to be ensured") 507 } 508 s.serviceBroker.ResetCalls() 509 510 s.podSpecGetter.SetErrors(apicaasunitprovisioner.ErrNoUnits) 511 512 select { 513 case s.applicationScaleChanges <- struct{}{}: 514 case <-time.After(coretesting.LongWait): 515 c.Fatal("timed out sending scale change") 516 } 517 select { 518 case <-s.serviceEnsured: 519 case <-time.After(coretesting.LongWait): 520 c.Fatal("timed out waiting for service to be ensured") 521 } 522 s.serviceBroker.CheckCallNames(c, "EnsureService") 523 s.serviceBroker.CheckCall(c, 0, "EnsureService", 524 "gitlab", &caas.ServiceParams{}, 0, application.ConfigAttributes(nil)) 525 } 526 527 func (s *WorkerSuite) TestApplicationDeadRemovesService(c *gc.C) { 528 w := s.setupNewUnitScenario(c) 529 defer workertest.CleanKill(c, w) 530 531 s.serviceBroker.ResetCalls() 532 s.containerBroker.ResetCalls() 533 534 s.lifeGetter.SetErrors(errors.NotFoundf("application")) 535 select { 536 case s.applicationChanges <- []string{"gitlab"}: 537 case <-time.After(coretesting.LongWait): 538 c.Fatal("timed out sending application change") 539 } 540 541 select { 542 case <-s.serviceDeleted: 543 case <-time.After(coretesting.LongWait): 544 c.Fatal("timed out waiting for service to be deleted") 545 } 546 547 s.serviceBroker.CheckCallNames(c, "UnexposeService", "DeleteService") 548 s.serviceBroker.CheckCall(c, 0, "UnexposeService", "gitlab") 549 s.serviceBroker.CheckCall(c, 1, "DeleteService", "gitlab") 550 } 551 552 func (s *WorkerSuite) TestWatchApplicationDead(c *gc.C) { 553 w, err := caasunitprovisioner.NewWorker(s.config) 554 c.Assert(err, jc.ErrorIsNil) 555 defer workertest.CleanKill(c, w) 556 557 s.lifeGetter.setLife(life.Dead) 558 select { 559 case s.applicationChanges <- []string{"gitlab"}: 560 case <-time.After(coretesting.LongWait): 561 c.Fatal("timed out sending applications change") 562 } 563 564 select { 565 case s.applicationScaleChanges <- struct{}{}: 566 c.Fatal("unexpected watch for application scale") 567 case <-time.After(coretesting.ShortWait): 568 } 569 570 workertest.CleanKill(c, w) 571 // There should just be the initial watch call, no subsequent calls to watch/get scale etc. 572 s.applicationGetter.CheckCallNames(c, "WatchApplications") 573 } 574 575 func (s *WorkerSuite) TestRemoveApplicationStopsWatchingApplicationScale(c *gc.C) { 576 w, err := caasunitprovisioner.NewWorker(s.config) 577 c.Assert(err, jc.ErrorIsNil) 578 defer workertest.CleanKill(c, w) 579 580 select { 581 case s.applicationChanges <- []string{"gitlab"}: 582 case <-time.After(coretesting.LongWait): 583 c.Fatal("timed out sending applications change") 584 } 585 586 // Check that the gitlab worker is running or not; 587 // given it time to startup. 588 shortAttempt := &utils.AttemptStrategy{ 589 Total: coretesting.LongWait, 590 Delay: 10 * time.Millisecond, 591 } 592 running := false 593 for a := shortAttempt.Start(); a.Next(); { 594 _, running = caasunitprovisioner.AppWorker(w, "gitlab") 595 if running { 596 break 597 } 598 } 599 c.Assert(running, jc.IsTrue) 600 601 // Add an additional app worker so we can check that the correct one is accessed. 602 caasunitprovisioner.NewAppWorker(w, "mysql") 603 604 s.lifeGetter.SetErrors(errors.NotFoundf("application")) 605 select { 606 case s.applicationChanges <- []string{"gitlab"}: 607 case <-time.After(coretesting.LongWait): 608 c.Fatal("timed out sending applications change") 609 } 610 611 select { 612 case <-s.serviceDeleted: 613 case <-time.After(coretesting.LongWait): 614 c.Fatal("timed out waiting for service to be deleted") 615 } 616 617 // The mysql worker should still be running. 618 _, ok := caasunitprovisioner.AppWorker(w, "mysql") 619 c.Assert(ok, jc.IsTrue) 620 621 // Check that the gitlab worker is running or not; 622 // given it time to shutdown. 623 for a := shortAttempt.Start(); a.Next(); { 624 _, running = caasunitprovisioner.AppWorker(w, "gitlab") 625 if !running { 626 break 627 } 628 } 629 c.Assert(running, jc.IsFalse) 630 workertest.CheckKilled(c, s.applicationGetter.scaleWatcher) 631 } 632 633 func (s *WorkerSuite) TestWatcherErrorStopsWorker(c *gc.C) { 634 w, err := caasunitprovisioner.NewWorker(s.config) 635 c.Assert(err, jc.ErrorIsNil) 636 defer workertest.DirtyKill(c, w) 637 638 s.applicationGetter.scale = 1 639 select { 640 case s.applicationChanges <- []string{"gitlab"}: 641 case <-time.After(coretesting.LongWait): 642 c.Fatal("timed out sending applications change") 643 } 644 645 select { 646 case s.applicationScaleChanges <- struct{}{}: 647 case <-time.After(coretesting.LongWait): 648 c.Fatal("timed out sending scale change") 649 } 650 651 s.podSpecGetter.watcher.KillErr(errors.New("splat")) 652 workertest.CheckKilled(c, s.podSpecGetter.watcher) 653 workertest.CheckKilled(c, s.applicationGetter.watcher) 654 err = workertest.CheckKilled(c, w) 655 c.Assert(err, gc.ErrorMatches, "splat") 656 } 657 658 func (s *WorkerSuite) TestUnitsChange(c *gc.C) { 659 w, err := caasunitprovisioner.NewWorker(s.config) 660 c.Assert(err, jc.ErrorIsNil) 661 defer workertest.DirtyKill(c, w) 662 663 select { 664 case s.applicationChanges <- []string{"gitlab"}: 665 case <-time.After(coretesting.LongWait): 666 c.Fatal("timed out sending applications change") 667 } 668 defer workertest.CleanKill(c, w) 669 670 for a := coretesting.LongAttempt.Start(); a.Next(); { 671 if len(s.containerBroker.Calls()) > 0 { 672 break 673 } 674 } 675 s.containerBroker.CheckCallNames(c, "WatchUnits", "WatchOperator") 676 677 s.assertUnitChange(c, status.Allocating, status.Allocating) 678 s.assertUnitChange(c, status.Allocating, status.Unknown) 679 } 680 681 func (s *WorkerSuite) TestOperatorChange(c *gc.C) { 682 w, err := caasunitprovisioner.NewWorker(s.config) 683 c.Assert(err, jc.ErrorIsNil) 684 defer workertest.DirtyKill(c, w) 685 686 select { 687 case s.applicationChanges <- []string{"gitlab"}: 688 case <-time.After(coretesting.LongWait): 689 c.Fatal("timed out sending applications change") 690 } 691 692 for a := coretesting.LongAttempt.Start(); a.Next(); { 693 if len(s.containerBroker.Calls()) > 0 { 694 break 695 } 696 } 697 s.containerBroker.CheckCallNames(c, "WatchUnits", "WatchOperator") 698 s.containerBroker.ResetCalls() 699 700 select { 701 case s.caasOperatorChanges <- struct{}{}: 702 case <-time.After(coretesting.LongWait): 703 c.Fatal("timed out sending applications change") 704 } 705 s.containerBroker.reportedOperatorStatus = status.Active 706 for a := coretesting.LongAttempt.Start(); a.Next(); { 707 if len(s.containerBroker.Calls()) > 0 { 708 break 709 } 710 } 711 s.containerBroker.CheckCallNames(c, "Operator") 712 c.Assert(s.containerBroker.Calls()[0].Args, jc.DeepEquals, []interface{}{"gitlab"}) 713 714 s.statusSetter.CheckCallNames(c, "SetOperatorStatus") 715 c.Assert(s.statusSetter.Calls()[0].Args, jc.DeepEquals, []interface{}{ 716 "gitlab", status.Active, "testing 1. 2. 3.", map[string]interface{}{"zip": "zap"}, 717 }) 718 } 719 720 func (s *WorkerSuite) assertUnitChange(c *gc.C, reported, expected status.Status) { 721 s.containerBroker.ResetCalls() 722 s.unitUpdater.ResetCalls() 723 s.containerBroker.reportedUnitStatus = reported 724 725 select { 726 case s.caasUnitsChanges <- struct{}{}: 727 case <-time.After(coretesting.LongWait): 728 c.Fatal("timed out sending units change") 729 } 730 731 for a := coretesting.LongAttempt.Start(); a.Next(); { 732 if len(s.containerBroker.Calls()) > 0 { 733 break 734 } 735 } 736 s.containerBroker.CheckCallNames(c, "Units") 737 c.Assert(s.containerBroker.Calls()[0].Args, jc.DeepEquals, []interface{}{"gitlab"}) 738 739 for a := coretesting.LongAttempt.Start(); a.Next(); { 740 if len(s.unitUpdater.Calls()) > 0 { 741 break 742 } 743 } 744 s.unitUpdater.CheckCallNames(c, "UpdateUnits") 745 c.Assert(s.unitUpdater.Calls()[0].Args, jc.DeepEquals, []interface{}{ 746 params.UpdateApplicationUnits{ 747 ApplicationTag: names.NewApplicationTag("gitlab").String(), 748 Units: []params.ApplicationUnitParams{ 749 {ProviderId: "u1", Address: "10.0.0.1", Ports: []string(nil), Status: expected.String(), 750 FilesystemInfo: []params.KubernetesFilesystemInfo{ 751 {StorageName: "database", MountPoint: "/path-to-here", ReadOnly: true, 752 FilesystemId: "fs-id", Size: 100, Pool: "", 753 Volume: params.KubernetesVolumeInfo{ 754 VolumeId: "vol-id", Size: 200, 755 Persistent: true, Status: "error", Info: "vol not ready"}, 756 Status: "attaching", Info: "not ready"}, 757 }}, 758 }, 759 }, 760 }) 761 }