github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/worker/provisioner/provisioner_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner_test 5 6 import ( 7 "fmt" 8 "strings" 9 "time" 10 11 gc "launchpad.net/gocheck" 12 13 "launchpad.net/juju-core/constraints" 14 "launchpad.net/juju-core/environs" 15 "launchpad.net/juju-core/environs/config" 16 "launchpad.net/juju-core/errors" 17 "launchpad.net/juju-core/instance" 18 "launchpad.net/juju-core/juju/testing" 19 "launchpad.net/juju-core/names" 20 "launchpad.net/juju-core/provider/dummy" 21 "launchpad.net/juju-core/state" 22 "launchpad.net/juju-core/state/api" 23 "launchpad.net/juju-core/state/api/params" 24 apiprovisioner "launchpad.net/juju-core/state/api/provisioner" 25 coretesting "launchpad.net/juju-core/testing" 26 jc "launchpad.net/juju-core/testing/checkers" 27 "launchpad.net/juju-core/utils" 28 "launchpad.net/juju-core/utils/set" 29 "launchpad.net/juju-core/worker/provisioner" 30 ) 31 32 type CommonProvisionerSuite struct { 33 testing.JujuConnSuite 34 op <-chan dummy.Operation 35 cfg *config.Config 36 // // defaultConstraints are used when adding a machine and then later in test assertions. 37 defaultConstraints constraints.Value 38 39 st *api.State 40 provisioner *apiprovisioner.State 41 } 42 43 type ProvisionerSuite struct { 44 CommonProvisionerSuite 45 } 46 47 var _ = gc.Suite(&ProvisionerSuite{}) 48 49 var veryShortAttempt = utils.AttemptStrategy{ 50 Total: 1 * time.Second, 51 Delay: 80 * time.Millisecond, 52 } 53 54 func (s *CommonProvisionerSuite) SetUpSuite(c *gc.C) { 55 s.JujuConnSuite.SetUpSuite(c) 56 s.defaultConstraints = constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G") 57 } 58 59 func (s *CommonProvisionerSuite) SetUpTest(c *gc.C) { 60 // Disable the default state policy, because the 61 // provisioner needs to be able to test pathological 62 // scenarios where a machine exists in state with 63 // invalid environment config. 64 dummy.SetStatePolicy(nil) 65 66 s.JujuConnSuite.SetUpTest(c) 67 // Create the operations channel with more than enough space 68 // for those tests that don't listen on it. 69 op := make(chan dummy.Operation, 500) 70 dummy.Listen(op) 71 s.op = op 72 73 cfg, err := s.State.EnvironConfig() 74 c.Assert(err, gc.IsNil) 75 s.cfg = cfg 76 } 77 78 func (s *CommonProvisionerSuite) APILogin(c *gc.C, machine *state.Machine) { 79 if s.st != nil { 80 c.Assert(s.st.Close(), gc.IsNil) 81 } 82 password, err := utils.RandomPassword() 83 c.Assert(err, gc.IsNil) 84 err = machine.SetPassword(password) 85 c.Assert(err, gc.IsNil) 86 err = machine.SetProvisioned("i-fake", "fake_nonce", nil) 87 c.Assert(err, gc.IsNil) 88 s.st = s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") 89 c.Assert(s.st, gc.NotNil) 90 c.Logf("API: login as %q successful", machine.Tag()) 91 s.provisioner = s.st.Provisioner() 92 c.Assert(s.provisioner, gc.NotNil) 93 } 94 95 // breakDummyProvider changes the environment config in state in a way 96 // that causes the given environMethod of the dummy provider to return 97 // an error, which is also returned as a message to be checked. 98 func breakDummyProvider(c *gc.C, st *state.State, environMethod string) string { 99 oldCfg, err := st.EnvironConfig() 100 c.Assert(err, gc.IsNil) 101 cfg, err := oldCfg.Apply(map[string]interface{}{"broken": environMethod}) 102 c.Assert(err, gc.IsNil) 103 err = st.SetEnvironConfig(cfg, oldCfg) 104 c.Assert(err, gc.IsNil) 105 return fmt.Sprintf("dummy.%s is broken", environMethod) 106 } 107 108 // setupEnvironment adds an environment manager machine and login to the API. 109 func (s *CommonProvisionerSuite) setupEnvironmentManager(c *gc.C) { 110 machine, err := s.State.AddMachine("quantal", state.JobManageEnviron) 111 c.Assert(err, gc.IsNil) 112 c.Assert(machine.Id(), gc.Equals, "0") 113 err = machine.SetAddresses([]instance.Address{ 114 instance.NewAddress("0.1.2.3"), 115 }) 116 c.Assert(err, gc.IsNil) 117 s.APILogin(c, machine) 118 } 119 120 // invalidateEnvironment alters the environment configuration 121 // so the Settings returned from the watcher will not pass 122 // validation. 123 func (s *CommonProvisionerSuite) invalidateEnvironment(c *gc.C) { 124 attrs := s.cfg.AllAttrs() 125 attrs["type"] = "unknown" 126 invalidCfg, err := config.New(config.NoDefaults, attrs) 127 c.Assert(err, gc.IsNil) 128 err = s.State.SetEnvironConfig(invalidCfg, s.cfg) 129 c.Assert(err, gc.IsNil) 130 } 131 132 // fixEnvironment undoes the work of invalidateEnvironment. 133 func (s *CommonProvisionerSuite) fixEnvironment() error { 134 cfg, err := s.State.EnvironConfig() 135 if err != nil { 136 return err 137 } 138 return s.State.SetEnvironConfig(s.cfg, cfg) 139 } 140 141 // stopper is stoppable. 142 type stopper interface { 143 Stop() error 144 } 145 146 // stop stops a stopper. 147 func stop(c *gc.C, s stopper) { 148 c.Assert(s.Stop(), gc.IsNil) 149 } 150 151 func (s *CommonProvisionerSuite) startUnknownInstance(c *gc.C, id string) instance.Instance { 152 instance, _ := testing.AssertStartInstance(c, s.Conn.Environ, id) 153 select { 154 case o := <-s.op: 155 switch o := o.(type) { 156 case dummy.OpStartInstance: 157 default: 158 c.Fatalf("unexpected operation %#v", o) 159 } 160 case <-time.After(coretesting.LongWait): 161 c.Fatalf("timed out waiting for startinstance operation") 162 } 163 return instance 164 } 165 166 func (s *CommonProvisionerSuite) checkStartInstance(c *gc.C, m *state.Machine) instance.Instance { 167 return s.checkStartInstanceCustom(c, m, "pork", s.defaultConstraints) 168 } 169 170 func (s *CommonProvisionerSuite) checkStartInstanceCustom(c *gc.C, m *state.Machine, secret string, cons constraints.Value) (inst instance.Instance) { 171 s.BackingState.StartSync() 172 for { 173 select { 174 case o := <-s.op: 175 switch o := o.(type) { 176 case dummy.OpStartInstance: 177 inst = o.Instance 178 s.waitInstanceId(c, m, inst.Id()) 179 180 // Check the instance was started with the expected params. 181 c.Assert(o.MachineId, gc.Equals, m.Id()) 182 nonceParts := strings.SplitN(o.MachineNonce, ":", 2) 183 c.Assert(nonceParts, gc.HasLen, 2) 184 c.Assert(nonceParts[0], gc.Equals, names.MachineTag("0")) 185 c.Assert(nonceParts[1], jc.Satisfies, utils.IsValidUUIDString) 186 c.Assert(o.Secret, gc.Equals, secret) 187 c.Assert(o.Constraints, gc.DeepEquals, cons) 188 189 // All provisioned machines in this test suite have their hardware characteristics 190 // attributes set to the same values as the constraints due to the dummy environment being used. 191 hc, err := m.HardwareCharacteristics() 192 c.Assert(err, gc.IsNil) 193 c.Assert(*hc, gc.DeepEquals, instance.HardwareCharacteristics{ 194 Arch: cons.Arch, 195 Mem: cons.Mem, 196 RootDisk: cons.RootDisk, 197 CpuCores: cons.CpuCores, 198 CpuPower: cons.CpuPower, 199 Tags: cons.Tags, 200 }) 201 return 202 default: 203 c.Logf("ignoring unexpected operation %#v", o) 204 } 205 case <-time.After(2 * time.Second): 206 c.Fatalf("provisioner did not start an instance") 207 return 208 } 209 } 210 return 211 } 212 213 // checkNoOperations checks that the environ was not operated upon. 214 func (s *CommonProvisionerSuite) checkNoOperations(c *gc.C) { 215 s.BackingState.StartSync() 216 select { 217 case o := <-s.op: 218 c.Fatalf("unexpected operation %#v", o) 219 case <-time.After(coretesting.ShortWait): 220 return 221 } 222 } 223 224 // checkStopInstances checks that an instance has been stopped. 225 func (s *CommonProvisionerSuite) checkStopInstances(c *gc.C, instances ...instance.Instance) { 226 s.checkStopSomeInstances(c, instances, nil) 227 } 228 229 // checkStopSomeInstances checks that instancesToStop are stopped while instancesToKeep are not. 230 func (s *CommonProvisionerSuite) checkStopSomeInstances(c *gc.C, 231 instancesToStop []instance.Instance, instancesToKeep []instance.Instance) { 232 233 s.BackingState.StartSync() 234 instanceIdsToStop := set.NewStrings() 235 for _, instance := range instancesToStop { 236 instanceIdsToStop.Add(string(instance.Id())) 237 } 238 instanceIdsToKeep := set.NewStrings() 239 for _, instance := range instancesToKeep { 240 instanceIdsToKeep.Add(string(instance.Id())) 241 } 242 // Continue checking for stop instance calls until all the instances we 243 // are waiting on to finish, actually finish, or we time out. 244 for !instanceIdsToStop.IsEmpty() { 245 select { 246 case o := <-s.op: 247 switch o := o.(type) { 248 case dummy.OpStopInstances: 249 for _, stoppedInstance := range o.Instances { 250 instId := string(stoppedInstance.Id()) 251 instanceIdsToStop.Remove(instId) 252 if instanceIdsToKeep.Contains(instId) { 253 c.Errorf("provisioner unexpectedly stopped instance %s", instId) 254 } 255 } 256 default: 257 c.Fatalf("unexpected operation %#v", o) 258 return 259 } 260 case <-time.After(2 * time.Second): 261 c.Fatalf("provisioner did not stop an instance") 262 return 263 } 264 } 265 } 266 267 func (s *CommonProvisionerSuite) waitMachine(c *gc.C, m *state.Machine, check func() bool) { 268 // TODO(jam): We need to grow a new method on NotifyWatcherC 269 // that calls StartSync while waiting for changes, then 270 // waitMachine and waitHardwareCharacteristics can use that 271 // instead 272 w := m.Watch() 273 defer stop(c, w) 274 timeout := time.After(coretesting.LongWait) 275 resync := time.After(0) 276 for { 277 select { 278 case <-w.Changes(): 279 if check() { 280 return 281 } 282 case <-resync: 283 resync = time.After(coretesting.ShortWait) 284 s.BackingState.StartSync() 285 case <-timeout: 286 c.Fatalf("machine %v wait timed out", m) 287 } 288 } 289 } 290 291 func (s *CommonProvisionerSuite) waitHardwareCharacteristics(c *gc.C, m *state.Machine, check func() bool) { 292 w := m.WatchHardwareCharacteristics() 293 defer stop(c, w) 294 timeout := time.After(coretesting.LongWait) 295 resync := time.After(0) 296 for { 297 select { 298 case <-w.Changes(): 299 if check() { 300 return 301 } 302 case <-resync: 303 resync = time.After(coretesting.ShortWait) 304 s.BackingState.StartSync() 305 case <-timeout: 306 c.Fatalf("hardware characteristics for machine %v wait timed out", m) 307 } 308 } 309 } 310 311 // waitRemoved waits for the supplied machine to be removed from state. 312 func (s *CommonProvisionerSuite) waitRemoved(c *gc.C, m *state.Machine) { 313 s.waitMachine(c, m, func() bool { 314 err := m.Refresh() 315 if errors.IsNotFoundError(err) { 316 return true 317 } 318 c.Assert(err, gc.IsNil) 319 c.Logf("machine %v is still %s", m, m.Life()) 320 return false 321 }) 322 } 323 324 // waitInstanceId waits until the supplied machine has an instance id, then 325 // asserts it is as expected. 326 func (s *CommonProvisionerSuite) waitInstanceId(c *gc.C, m *state.Machine, expect instance.Id) { 327 s.waitHardwareCharacteristics(c, m, func() bool { 328 if actual, err := m.InstanceId(); err == nil { 329 c.Assert(actual, gc.Equals, expect) 330 return true 331 } else if !state.IsNotProvisionedError(err) { 332 // We don't expect any errors. 333 panic(err) 334 } 335 c.Logf("machine %v is still unprovisioned", m) 336 return false 337 }) 338 } 339 340 func (s *ProvisionerSuite) SetUpTest(c *gc.C) { 341 s.CommonProvisionerSuite.SetUpTest(c) 342 s.CommonProvisionerSuite.setupEnvironmentManager(c) 343 } 344 345 func (s *ProvisionerSuite) newEnvironProvisioner(c *gc.C) provisioner.Provisioner { 346 machineTag := "machine-0" 347 agentConfig := s.AgentConfigForTag(c, machineTag) 348 return provisioner.NewEnvironProvisioner(s.provisioner, agentConfig) 349 } 350 351 func (s *ProvisionerSuite) TestProvisionerStartStop(c *gc.C) { 352 p := s.newEnvironProvisioner(c) 353 c.Assert(p.Stop(), gc.IsNil) 354 } 355 356 func (s *ProvisionerSuite) addMachine() (*state.Machine, error) { 357 return s.BackingState.AddOneMachine(state.MachineTemplate{ 358 Series: config.DefaultSeries, 359 Jobs: []state.MachineJob{state.JobHostUnits}, 360 Constraints: s.defaultConstraints, 361 }) 362 } 363 364 func (s *ProvisionerSuite) TestSimple(c *gc.C) { 365 p := s.newEnvironProvisioner(c) 366 defer stop(c, p) 367 368 // Check that an instance is provisioned when the machine is created... 369 m, err := s.addMachine() 370 c.Assert(err, gc.IsNil) 371 instance := s.checkStartInstance(c, m) 372 373 // ...and removed, along with the machine, when the machine is Dead. 374 c.Assert(m.EnsureDead(), gc.IsNil) 375 s.checkStopInstances(c, instance) 376 s.waitRemoved(c, m) 377 } 378 379 func (s *ProvisionerSuite) TestConstraints(c *gc.C) { 380 // Create a machine with non-standard constraints. 381 m, err := s.addMachine() 382 c.Assert(err, gc.IsNil) 383 cons := constraints.MustParse("mem=8G arch=amd64 cpu-cores=2 root-disk=10G") 384 err = m.SetConstraints(cons) 385 c.Assert(err, gc.IsNil) 386 387 // Start a provisioner and check those constraints are used. 388 p := s.newEnvironProvisioner(c) 389 defer stop(c, p) 390 s.checkStartInstanceCustom(c, m, "pork", cons) 391 } 392 393 func (s *ProvisionerSuite) TestProvisionerSetsErrorStatusWhenStartInstanceFailed(c *gc.C) { 394 brokenMsg := breakDummyProvider(c, s.State, "StartInstance") 395 p := s.newEnvironProvisioner(c) 396 defer stop(c, p) 397 398 // Check that an instance is not provisioned when the machine is created... 399 m, err := s.addMachine() 400 c.Assert(err, gc.IsNil) 401 s.checkNoOperations(c) 402 403 t0 := time.Now() 404 for time.Since(t0) < coretesting.LongWait { 405 // And check the machine status is set to error. 406 status, info, _, err := m.Status() 407 c.Assert(err, gc.IsNil) 408 if status == params.StatusPending { 409 time.Sleep(coretesting.ShortWait) 410 continue 411 } 412 c.Assert(status, gc.Equals, params.StatusError) 413 c.Assert(info, gc.Equals, brokenMsg) 414 break 415 } 416 417 // Unbreak the environ config. 418 err = s.fixEnvironment() 419 c.Assert(err, gc.IsNil) 420 421 // Restart the PA to make sure the machine is skipped again. 422 stop(c, p) 423 p = s.newEnvironProvisioner(c) 424 defer stop(c, p) 425 s.checkNoOperations(c) 426 } 427 428 func (s *ProvisionerSuite) TestProvisioningDoesNotOccurForContainers(c *gc.C) { 429 p := s.newEnvironProvisioner(c) 430 defer stop(c, p) 431 432 // create a machine to host the container. 433 m, err := s.addMachine() 434 c.Assert(err, gc.IsNil) 435 inst := s.checkStartInstance(c, m) 436 437 // make a container on the machine we just created 438 template := state.MachineTemplate{ 439 Series: config.DefaultSeries, 440 Jobs: []state.MachineJob{state.JobHostUnits}, 441 } 442 container, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC) 443 c.Assert(err, gc.IsNil) 444 445 // the PA should not attempt to create it 446 s.checkNoOperations(c) 447 448 // cleanup 449 c.Assert(container.EnsureDead(), gc.IsNil) 450 c.Assert(container.Remove(), gc.IsNil) 451 c.Assert(m.EnsureDead(), gc.IsNil) 452 s.checkStopInstances(c, inst) 453 s.waitRemoved(c, m) 454 } 455 456 func (s *ProvisionerSuite) TestProvisioningDoesNotOccurWithAnInvalidEnvironment(c *gc.C) { 457 s.invalidateEnvironment(c) 458 459 p := s.newEnvironProvisioner(c) 460 defer stop(c, p) 461 462 // try to create a machine 463 _, err := s.addMachine() 464 c.Assert(err, gc.IsNil) 465 466 // the PA should not create it 467 s.checkNoOperations(c) 468 } 469 470 func (s *ProvisionerSuite) TestProvisioningOccursWithFixedEnvironment(c *gc.C) { 471 s.invalidateEnvironment(c) 472 473 p := s.newEnvironProvisioner(c) 474 defer stop(c, p) 475 476 // try to create a machine 477 m, err := s.addMachine() 478 c.Assert(err, gc.IsNil) 479 480 // the PA should not create it 481 s.checkNoOperations(c) 482 483 err = s.fixEnvironment() 484 c.Assert(err, gc.IsNil) 485 486 s.checkStartInstance(c, m) 487 } 488 489 func (s *ProvisionerSuite) TestProvisioningDoesOccurAfterInvalidEnvironmentPublished(c *gc.C) { 490 p := s.newEnvironProvisioner(c) 491 defer stop(c, p) 492 493 // place a new machine into the state 494 m, err := s.addMachine() 495 c.Assert(err, gc.IsNil) 496 497 s.checkStartInstance(c, m) 498 499 s.invalidateEnvironment(c) 500 501 // create a second machine 502 m, err = s.addMachine() 503 c.Assert(err, gc.IsNil) 504 505 // the PA should create it using the old environment 506 s.checkStartInstance(c, m) 507 } 508 509 func (s *ProvisionerSuite) TestProvisioningDoesNotProvisionTheSameMachineAfterRestart(c *gc.C) { 510 p := s.newEnvironProvisioner(c) 511 defer stop(c, p) 512 513 // create a machine 514 m, err := s.addMachine() 515 c.Assert(err, gc.IsNil) 516 s.checkStartInstance(c, m) 517 518 // restart the PA 519 stop(c, p) 520 p = s.newEnvironProvisioner(c) 521 defer stop(c, p) 522 523 // check that there is only one machine provisioned. 524 machines, err := s.State.AllMachines() 525 c.Assert(err, gc.IsNil) 526 c.Check(len(machines), gc.Equals, 2) 527 c.Check(machines[0].Id(), gc.Equals, "0") 528 c.Check(machines[1].CheckProvisioned("fake_nonce"), jc.IsFalse) 529 530 // the PA should not create it a second time 531 s.checkNoOperations(c) 532 } 533 534 func (s *ProvisionerSuite) TestProvisioningStopsInstances(c *gc.C) { 535 p := s.newEnvironProvisioner(c) 536 defer stop(c, p) 537 538 // create a machine 539 m0, err := s.addMachine() 540 c.Assert(err, gc.IsNil) 541 i0 := s.checkStartInstance(c, m0) 542 543 // create a second machine 544 m1, err := s.addMachine() 545 c.Assert(err, gc.IsNil) 546 i1 := s.checkStartInstance(c, m1) 547 stop(c, p) 548 549 // mark the first machine as dead 550 c.Assert(m0.EnsureDead(), gc.IsNil) 551 552 // remove the second machine entirely 553 c.Assert(m1.EnsureDead(), gc.IsNil) 554 c.Assert(m1.Remove(), gc.IsNil) 555 556 // start a new provisioner to shut them both down 557 p = s.newEnvironProvisioner(c) 558 defer stop(c, p) 559 s.checkStopInstances(c, i0, i1) 560 s.waitRemoved(c, m0) 561 } 562 563 func (s *ProvisionerSuite) TestDyingMachines(c *gc.C) { 564 p := s.newEnvironProvisioner(c) 565 defer stop(c, p) 566 567 // provision a machine 568 m0, err := s.addMachine() 569 c.Assert(err, gc.IsNil) 570 s.checkStartInstance(c, m0) 571 572 // stop the provisioner and make the machine dying 573 stop(c, p) 574 err = m0.Destroy() 575 c.Assert(err, gc.IsNil) 576 577 // add a new, dying, unprovisioned machine 578 m1, err := s.addMachine() 579 c.Assert(err, gc.IsNil) 580 err = m1.Destroy() 581 c.Assert(err, gc.IsNil) 582 583 // start the provisioner and wait for it to reap the useless machine 584 p = s.newEnvironProvisioner(c) 585 defer stop(c, p) 586 s.checkNoOperations(c) 587 s.waitRemoved(c, m1) 588 589 // verify the other one's still fine 590 err = m0.Refresh() 591 c.Assert(err, gc.IsNil) 592 c.Assert(m0.Life(), gc.Equals, state.Dying) 593 } 594 595 func (s *ProvisionerSuite) TestProvisioningRecoversAfterInvalidEnvironmentPublished(c *gc.C) { 596 p := s.newEnvironProvisioner(c) 597 defer stop(c, p) 598 599 // place a new machine into the state 600 m, err := s.addMachine() 601 c.Assert(err, gc.IsNil) 602 s.checkStartInstance(c, m) 603 604 s.invalidateEnvironment(c) 605 s.BackingState.StartSync() 606 607 // create a second machine 608 m, err = s.addMachine() 609 c.Assert(err, gc.IsNil) 610 611 // the PA should create it using the old environment 612 s.checkStartInstance(c, m) 613 614 err = s.fixEnvironment() 615 c.Assert(err, gc.IsNil) 616 617 // insert our observer 618 cfgObserver := make(chan *config.Config, 1) 619 provisioner.SetObserver(p, cfgObserver) 620 621 oldcfg, err := s.State.EnvironConfig() 622 c.Assert(err, gc.IsNil) 623 attrs := oldcfg.AllAttrs() 624 attrs["secret"] = "beef" 625 cfg, err := config.New(config.NoDefaults, attrs) 626 c.Assert(err, gc.IsNil) 627 err = s.State.SetEnvironConfig(cfg, oldcfg) 628 629 s.BackingState.StartSync() 630 631 // wait for the PA to load the new configuration 632 select { 633 case <-cfgObserver: 634 case <-time.After(coretesting.LongWait): 635 c.Fatalf("PA did not action config change") 636 } 637 638 // create a third machine 639 m, err = s.addMachine() 640 c.Assert(err, gc.IsNil) 641 642 // the PA should create it using the new environment 643 s.checkStartInstanceCustom(c, m, "beef", s.defaultConstraints) 644 } 645 646 func (s *ProvisionerSuite) TestProvisioningSafeMode(c *gc.C) { 647 p := s.newEnvironProvisioner(c) 648 defer stop(c, p) 649 650 // create a machine 651 m0, err := s.addMachine() 652 c.Assert(err, gc.IsNil) 653 i0 := s.checkStartInstance(c, m0) 654 655 // create a second machine 656 m1, err := s.addMachine() 657 c.Assert(err, gc.IsNil) 658 i1 := s.checkStartInstance(c, m1) 659 stop(c, p) 660 661 // mark the first machine as dead 662 c.Assert(m0.EnsureDead(), gc.IsNil) 663 664 // remove the second machine entirely from state 665 c.Assert(m1.EnsureDead(), gc.IsNil) 666 c.Assert(m1.Remove(), gc.IsNil) 667 668 // turn on safe mode 669 oldcfg, err := s.State.EnvironConfig() 670 c.Assert(err, gc.IsNil) 671 attrs := oldcfg.AllAttrs() 672 attrs["provisioner-safe-mode"] = true 673 cfg, err := config.New(config.NoDefaults, attrs) 674 c.Assert(err, gc.IsNil) 675 err = s.State.SetEnvironConfig(cfg, oldcfg) 676 677 // start a new provisioner to shut down only the machine still in state. 678 p = s.newEnvironProvisioner(c) 679 defer stop(c, p) 680 s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1}) 681 s.waitRemoved(c, m0) 682 } 683 684 func (s *ProvisionerSuite) TestProvisioningSafeModeChange(c *gc.C) { 685 p := s.newEnvironProvisioner(c) 686 defer stop(c, p) 687 688 // First check that safe mode is initially off. 689 690 // create a machine 691 m0, err := s.addMachine() 692 c.Assert(err, gc.IsNil) 693 i0 := s.checkStartInstance(c, m0) 694 695 // create a second machine 696 m1, err := s.addMachine() 697 c.Assert(err, gc.IsNil) 698 i1 := s.checkStartInstance(c, m1) 699 700 // mark the first machine as dead 701 c.Assert(m0.EnsureDead(), gc.IsNil) 702 703 // remove the second machine entirely from state 704 c.Assert(m1.EnsureDead(), gc.IsNil) 705 c.Assert(m1.Remove(), gc.IsNil) 706 707 s.checkStopInstances(c, i0, i1) 708 s.waitRemoved(c, m0) 709 710 // insert our observer 711 cfgObserver := make(chan *config.Config, 1) 712 provisioner.SetObserver(p, cfgObserver) 713 714 // turn on safe mode 715 oldcfg, err := s.State.EnvironConfig() 716 c.Assert(err, gc.IsNil) 717 attrs := oldcfg.AllAttrs() 718 attrs["provisioner-safe-mode"] = true 719 cfg, err := config.New(config.NoDefaults, attrs) 720 c.Assert(err, gc.IsNil) 721 err = s.State.SetEnvironConfig(cfg, oldcfg) 722 723 s.BackingState.StartSync() 724 725 // wait for the PA to load the new configuration 726 select { 727 case <-cfgObserver: 728 case <-time.After(coretesting.LongWait): 729 c.Fatalf("PA did not action config change") 730 } 731 732 // Now check that the provisioner has noticed safe mode is on. 733 734 // create a machine 735 m3, err := s.addMachine() 736 c.Assert(err, gc.IsNil) 737 i3 := s.checkStartInstance(c, m3) 738 739 // create an instance out of band 740 i4 := s.startUnknownInstance(c, "999") 741 742 // mark the machine as dead 743 c.Assert(m3.EnsureDead(), gc.IsNil) 744 745 // check the machine's instance is stopped, and the other isn't 746 s.checkStopSomeInstances(c, []instance.Instance{i3}, []instance.Instance{i4}) 747 s.waitRemoved(c, m3) 748 } 749 750 func (s *ProvisionerSuite) newProvisionerTask(c *gc.C, safeMode bool) provisioner.ProvisionerTask { 751 env := s.APIConn.Environ 752 watcher, err := s.provisioner.WatchEnvironMachines() 753 c.Assert(err, gc.IsNil) 754 auth, err := environs.NewAPIAuthenticator(s.provisioner) 755 c.Assert(err, gc.IsNil) 756 return provisioner.NewProvisionerTask("machine-0", safeMode, s.provisioner, watcher, env, auth) 757 } 758 759 func (s *ProvisionerSuite) TestTurningOffSafeModeReapsUnknownInstances(c *gc.C) { 760 task := s.newProvisionerTask(c, true) 761 defer stop(c, task) 762 763 // Initially create a machine, and an unknown instance, with safe mode on. 764 m0, err := s.addMachine() 765 c.Assert(err, gc.IsNil) 766 i0 := s.checkStartInstance(c, m0) 767 i1 := s.startUnknownInstance(c, "999") 768 769 // mark the first machine as dead 770 c.Assert(m0.EnsureDead(), gc.IsNil) 771 772 // with safe mode on, only one of the machines is stopped. 773 s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1}) 774 s.waitRemoved(c, m0) 775 776 // turn off safe mode and check that the other machine is now stopped also. 777 task.SetSafeMode(false) 778 s.checkStopInstances(c, i1) 779 }