github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 "strconv" 9 "strings" 10 "time" 11 12 "github.com/juju/errors" 13 "github.com/juju/names" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils" 16 "github.com/juju/utils/set" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/agent" 20 "github.com/juju/juju/api" 21 apiprovisioner "github.com/juju/juju/api/provisioner" 22 "github.com/juju/juju/apiserver/params" 23 apiserverprovisioner "github.com/juju/juju/apiserver/provisioner" 24 "github.com/juju/juju/constraints" 25 "github.com/juju/juju/environmentserver/authentication" 26 "github.com/juju/juju/environs" 27 "github.com/juju/juju/environs/config" 28 "github.com/juju/juju/environs/filestorage" 29 "github.com/juju/juju/environs/imagemetadata" 30 envtesting "github.com/juju/juju/environs/testing" 31 "github.com/juju/juju/environs/tools" 32 "github.com/juju/juju/instance" 33 "github.com/juju/juju/juju/testing" 34 "github.com/juju/juju/mongo" 35 "github.com/juju/juju/network" 36 "github.com/juju/juju/provider/dummy" 37 "github.com/juju/juju/state" 38 "github.com/juju/juju/state/multiwatcher" 39 "github.com/juju/juju/storage" 40 "github.com/juju/juju/storage/poolmanager" 41 dummystorage "github.com/juju/juju/storage/provider/dummy" 42 "github.com/juju/juju/storage/provider/registry" 43 coretesting "github.com/juju/juju/testing" 44 coretools "github.com/juju/juju/tools" 45 "github.com/juju/juju/version" 46 "github.com/juju/juju/worker/provisioner" 47 ) 48 49 type CommonProvisionerSuite struct { 50 testing.JujuConnSuite 51 op <-chan dummy.Operation 52 cfg *config.Config 53 // defaultConstraints are used when adding a machine and then later in test assertions. 54 defaultConstraints constraints.Value 55 56 st *api.State 57 provisioner *apiprovisioner.State 58 } 59 60 func (s *CommonProvisionerSuite) assertProvisionerObservesConfigChanges(c *gc.C, p provisioner.Provisioner) { 61 // Inject our observer into the provisioner 62 cfgObserver := make(chan *config.Config, 1) 63 provisioner.SetObserver(p, cfgObserver) 64 65 // Switch to reaping on All machines. 66 attrs := map[string]interface{}{ 67 config.ProvisionerHarvestModeKey: config.HarvestAll.String(), 68 } 69 err := s.State.UpdateEnvironConfig(attrs, nil, nil) 70 c.Assert(err, jc.ErrorIsNil) 71 72 s.BackingState.StartSync() 73 74 // Wait for the PA to load the new configuration. We wait for the change we expect 75 // like this because sometimes we pick up the initial harvest config (destroyed) 76 // rather than the one we change to (all). 77 received := []string{} 78 for { 79 select { 80 case newCfg := <-cfgObserver: 81 if newCfg.ProvisionerHarvestMode().String() == config.HarvestAll.String() { 82 return 83 } 84 received = append(received, newCfg.ProvisionerHarvestMode().String()) 85 case <-time.After(coretesting.LongWait): 86 if len(received) == 0 { 87 c.Fatalf("PA did not action config change") 88 } else { 89 c.Fatalf("timed out waiting for config to change to '%s', received %+v", 90 config.HarvestAll.String(), received) 91 } 92 } 93 } 94 } 95 96 type ProvisionerSuite struct { 97 CommonProvisionerSuite 98 } 99 100 var _ = gc.Suite(&ProvisionerSuite{}) 101 102 var veryShortAttempt = utils.AttemptStrategy{ 103 Total: 1 * time.Second, 104 Delay: 80 * time.Millisecond, 105 } 106 107 func (s *CommonProvisionerSuite) SetUpSuite(c *gc.C) { 108 s.JujuConnSuite.SetUpSuite(c) 109 s.defaultConstraints = constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G") 110 } 111 112 func (s *CommonProvisionerSuite) SetUpTest(c *gc.C) { 113 // Disable the default state policy, because the 114 // provisioner needs to be able to test pathological 115 // scenarios where a machine exists in state with 116 // invalid environment config. 117 dummy.SetStatePolicy(nil) 118 119 s.JujuConnSuite.SetUpTest(c) 120 121 // Create the operations channel with more than enough space 122 // for those tests that don't listen on it. 123 op := make(chan dummy.Operation, 500) 124 dummy.Listen(op) 125 s.op = op 126 127 cfg, err := s.State.EnvironConfig() 128 c.Assert(err, jc.ErrorIsNil) 129 s.cfg = cfg 130 131 // Create a machine for the dummy bootstrap instance, 132 // so the provisioner doesn't destroy it. 133 insts, err := s.Environ.Instances([]instance.Id{dummy.BootstrapInstanceId}) 134 c.Assert(err, jc.ErrorIsNil) 135 addrs, err := insts[0].Addresses() 136 c.Assert(err, jc.ErrorIsNil) 137 machine, err := s.State.AddOneMachine(state.MachineTemplate{ 138 Addresses: addrs, 139 Series: "quantal", 140 Nonce: agent.BootstrapNonce, 141 InstanceId: dummy.BootstrapInstanceId, 142 Jobs: []state.MachineJob{state.JobManageEnviron}, 143 }) 144 c.Assert(err, jc.ErrorIsNil) 145 c.Assert(machine.Id(), gc.Equals, "0") 146 147 err = machine.SetAgentVersion(version.Current) 148 c.Assert(err, jc.ErrorIsNil) 149 150 password, err := utils.RandomPassword() 151 c.Assert(err, jc.ErrorIsNil) 152 err = machine.SetPassword(password) 153 c.Assert(err, jc.ErrorIsNil) 154 155 s.st = s.OpenAPIAsMachine(c, machine.Tag(), password, agent.BootstrapNonce) 156 c.Assert(s.st, gc.NotNil) 157 c.Logf("API: login as %q successful", machine.Tag()) 158 s.provisioner = s.st.Provisioner() 159 c.Assert(s.provisioner, gc.NotNil) 160 } 161 162 // breakDummyProvider changes the environment config in state in a way 163 // that causes the given environMethod of the dummy provider to return 164 // an error, which is also returned as a message to be checked. 165 func breakDummyProvider(c *gc.C, st *state.State, environMethod string) string { 166 attrs := map[string]interface{}{"broken": environMethod} 167 err := st.UpdateEnvironConfig(attrs, nil, nil) 168 c.Assert(err, jc.ErrorIsNil) 169 return fmt.Sprintf("dummy.%s is broken", environMethod) 170 } 171 172 // invalidateEnvironment alters the environment configuration 173 // so the Settings returned from the watcher will not pass 174 // validation. 175 func (s *CommonProvisionerSuite) invalidateEnvironment(c *gc.C) { 176 st, err := state.Open(s.MongoInfo(c), mongo.DefaultDialOpts(), state.Policy(nil)) 177 c.Assert(err, jc.ErrorIsNil) 178 defer st.Close() 179 attrs := map[string]interface{}{"type": "unknown"} 180 err = st.UpdateEnvironConfig(attrs, nil, nil) 181 c.Assert(err, jc.ErrorIsNil) 182 } 183 184 // fixEnvironment undoes the work of invalidateEnvironment. 185 func (s *CommonProvisionerSuite) fixEnvironment(c *gc.C) error { 186 st, err := state.Open(s.MongoInfo(c), mongo.DefaultDialOpts(), state.Policy(nil)) 187 c.Assert(err, jc.ErrorIsNil) 188 defer st.Close() 189 attrs := map[string]interface{}{"type": s.cfg.AllAttrs()["type"]} 190 return st.UpdateEnvironConfig(attrs, nil, nil) 191 } 192 193 // stopper is stoppable. 194 type stopper interface { 195 Stop() error 196 } 197 198 // stop stops a stopper. 199 func stop(c *gc.C, s stopper) { 200 c.Assert(s.Stop(), jc.ErrorIsNil) 201 } 202 203 func (s *CommonProvisionerSuite) startUnknownInstance(c *gc.C, id string) instance.Instance { 204 instance, _ := testing.AssertStartInstance(c, s.Environ, id) 205 select { 206 case o := <-s.op: 207 switch o := o.(type) { 208 case dummy.OpStartInstance: 209 default: 210 c.Fatalf("unexpected operation %#v", o) 211 } 212 case <-time.After(coretesting.LongWait): 213 c.Fatalf("timed out waiting for startinstance operation") 214 } 215 return instance 216 } 217 218 func (s *CommonProvisionerSuite) checkStartInstance(c *gc.C, m *state.Machine) instance.Instance { 219 return s.checkStartInstanceCustom(c, m, "pork", s.defaultConstraints, nil, nil, nil, true, nil, true) 220 } 221 222 func (s *CommonProvisionerSuite) checkStartInstanceNoSecureConnection(c *gc.C, m *state.Machine) instance.Instance { 223 return s.checkStartInstanceCustom(c, m, "pork", s.defaultConstraints, nil, nil, nil, false, nil, true) 224 } 225 226 func (s *CommonProvisionerSuite) checkStartInstanceCustom( 227 c *gc.C, m *state.Machine, 228 secret string, cons constraints.Value, 229 networks []string, networkInfo []network.InterfaceInfo, 230 volumes []storage.Volume, 231 secureServerConnection bool, 232 checkPossibleTools coretools.List, 233 waitInstanceId bool, 234 ) ( 235 inst instance.Instance, 236 ) { 237 s.BackingState.StartSync() 238 for { 239 select { 240 case o := <-s.op: 241 switch o := o.(type) { 242 case dummy.OpStartInstance: 243 inst = o.Instance 244 if waitInstanceId { 245 s.waitInstanceId(c, m, inst.Id()) 246 } 247 248 // Check the instance was started with the expected params. 249 c.Assert(o.MachineId, gc.Equals, m.Id()) 250 nonceParts := strings.SplitN(o.MachineNonce, ":", 2) 251 c.Assert(nonceParts, gc.HasLen, 2) 252 c.Assert(nonceParts[0], gc.Equals, names.NewMachineTag("0").String()) 253 c.Assert(nonceParts[1], jc.Satisfies, utils.IsValidUUIDString) 254 c.Assert(o.Secret, gc.Equals, secret) 255 c.Assert(o.Networks, jc.DeepEquals, networks) 256 c.Assert(o.NetworkInfo, jc.DeepEquals, networkInfo) 257 c.Assert(o.Volumes, jc.DeepEquals, volumes) 258 c.Assert(o.AgentEnvironment["SECURE_STATESERVER_CONNECTION"], gc.Equals, strconv.FormatBool(secureServerConnection)) 259 260 var jobs []multiwatcher.MachineJob 261 for _, job := range m.Jobs() { 262 jobs = append(jobs, job.ToParams()) 263 } 264 c.Assert(o.Jobs, jc.SameContents, jobs) 265 266 if checkPossibleTools != nil { 267 for _, t := range o.PossibleTools { 268 url := fmt.Sprintf("https://%s/environment/%s/tools/%s", 269 s.st.Addr(), coretesting.EnvironmentTag.Id(), t.Version) 270 c.Check(t.URL, gc.Equals, url) 271 t.URL = "" 272 } 273 for _, t := range checkPossibleTools { 274 t.URL = "" 275 } 276 c.Assert(o.PossibleTools, gc.DeepEquals, checkPossibleTools) 277 } 278 279 // All provisioned machines in this test suite have 280 // their hardware characteristics attributes set to 281 // the same values as the constraints due to the dummy 282 // environment being used. 283 if !constraints.IsEmpty(&cons) { 284 c.Assert(o.Constraints, gc.DeepEquals, cons) 285 hc, err := m.HardwareCharacteristics() 286 c.Assert(err, jc.ErrorIsNil) 287 c.Assert(*hc, gc.DeepEquals, instance.HardwareCharacteristics{ 288 Arch: cons.Arch, 289 Mem: cons.Mem, 290 RootDisk: cons.RootDisk, 291 CpuCores: cons.CpuCores, 292 CpuPower: cons.CpuPower, 293 Tags: cons.Tags, 294 }) 295 } 296 return 297 default: 298 c.Logf("ignoring unexpected operation %#v", o) 299 } 300 case <-time.After(2 * time.Second): 301 c.Fatalf("provisioner did not start an instance") 302 return 303 } 304 } 305 } 306 307 // checkNoOperations checks that the environ was not operated upon. 308 func (s *CommonProvisionerSuite) checkNoOperations(c *gc.C) { 309 s.BackingState.StartSync() 310 select { 311 case o := <-s.op: 312 c.Fatalf("unexpected operation %+v", o) 313 case <-time.After(coretesting.ShortWait): 314 return 315 } 316 } 317 318 // checkStopInstances checks that an instance has been stopped. 319 func (s *CommonProvisionerSuite) checkStopInstances(c *gc.C, instances ...instance.Instance) { 320 s.checkStopSomeInstances(c, instances, nil) 321 } 322 323 // checkStopSomeInstances checks that instancesToStop are stopped while instancesToKeep are not. 324 func (s *CommonProvisionerSuite) checkStopSomeInstances(c *gc.C, 325 instancesToStop []instance.Instance, instancesToKeep []instance.Instance) { 326 327 s.BackingState.StartSync() 328 instanceIdsToStop := set.NewStrings() 329 for _, instance := range instancesToStop { 330 instanceIdsToStop.Add(string(instance.Id())) 331 } 332 instanceIdsToKeep := set.NewStrings() 333 for _, instance := range instancesToKeep { 334 instanceIdsToKeep.Add(string(instance.Id())) 335 } 336 // Continue checking for stop instance calls until all the instances we 337 // are waiting on to finish, actually finish, or we time out. 338 for !instanceIdsToStop.IsEmpty() { 339 select { 340 case o := <-s.op: 341 switch o := o.(type) { 342 case dummy.OpStopInstances: 343 for _, id := range o.Ids { 344 instId := string(id) 345 instanceIdsToStop.Remove(instId) 346 if instanceIdsToKeep.Contains(instId) { 347 c.Errorf("provisioner unexpectedly stopped instance %s", instId) 348 } 349 } 350 default: 351 c.Fatalf("unexpected operation %#v", o) 352 return 353 } 354 case <-time.After(2 * time.Second): 355 c.Fatalf("provisioner did not stop an instance") 356 return 357 } 358 } 359 } 360 361 func (s *CommonProvisionerSuite) waitMachine(c *gc.C, m *state.Machine, check func() bool) { 362 // TODO(jam): We need to grow a new method on NotifyWatcherC 363 // that calls StartSync while waiting for changes, then 364 // waitMachine and waitHardwareCharacteristics can use that 365 // instead 366 w := m.Watch() 367 defer stop(c, w) 368 timeout := time.After(coretesting.LongWait) 369 resync := time.After(0) 370 for { 371 select { 372 case <-w.Changes(): 373 if check() { 374 return 375 } 376 case <-resync: 377 resync = time.After(coretesting.ShortWait) 378 s.BackingState.StartSync() 379 case <-timeout: 380 c.Fatalf("machine %v wait timed out", m) 381 } 382 } 383 } 384 385 func (s *CommonProvisionerSuite) waitHardwareCharacteristics(c *gc.C, m *state.Machine, check func() bool) { 386 w := m.WatchHardwareCharacteristics() 387 defer stop(c, w) 388 timeout := time.After(coretesting.LongWait) 389 resync := time.After(0) 390 for { 391 select { 392 case <-w.Changes(): 393 if check() { 394 return 395 } 396 case <-resync: 397 resync = time.After(coretesting.ShortWait) 398 s.BackingState.StartSync() 399 case <-timeout: 400 c.Fatalf("hardware characteristics for machine %v wait timed out", m) 401 } 402 } 403 } 404 405 // waitRemoved waits for the supplied machine to be removed from state. 406 func (s *CommonProvisionerSuite) waitRemoved(c *gc.C, m *state.Machine) { 407 s.waitMachine(c, m, func() bool { 408 err := m.Refresh() 409 if errors.IsNotFound(err) { 410 return true 411 } 412 c.Assert(err, jc.ErrorIsNil) 413 c.Logf("machine %v is still %s", m, m.Life()) 414 return false 415 }) 416 } 417 418 // waitInstanceId waits until the supplied machine has an instance id, then 419 // asserts it is as expected. 420 func (s *CommonProvisionerSuite) waitInstanceId(c *gc.C, m *state.Machine, expect instance.Id) { 421 s.waitHardwareCharacteristics(c, m, func() bool { 422 if actual, err := m.InstanceId(); err == nil { 423 c.Assert(actual, gc.Equals, expect) 424 return true 425 } else if !errors.IsNotProvisioned(err) { 426 // We don't expect any errors. 427 panic(err) 428 } 429 c.Logf("machine %v is still unprovisioned", m) 430 return false 431 }) 432 } 433 434 func (s *CommonProvisionerSuite) newEnvironProvisioner(c *gc.C) provisioner.Provisioner { 435 machineTag := names.NewMachineTag("0") 436 agentConfig := s.AgentConfigForTag(c, machineTag) 437 return provisioner.NewEnvironProvisioner(s.provisioner, agentConfig) 438 } 439 440 func (s *CommonProvisionerSuite) addMachine() (*state.Machine, error) { 441 return s.addMachineWithRequestedNetworks(nil, s.defaultConstraints) 442 } 443 444 func (s *CommonProvisionerSuite) addMachineWithRequestedNetworks(networks []string, cons constraints.Value) (*state.Machine, error) { 445 return s.BackingState.AddOneMachine(state.MachineTemplate{ 446 Series: coretesting.FakeDefaultSeries, 447 Jobs: []state.MachineJob{state.JobHostUnits}, 448 Constraints: cons, 449 RequestedNetworks: networks, 450 }) 451 } 452 453 func (s *CommonProvisionerSuite) ensureAvailability(c *gc.C, n int) []*state.Machine { 454 changes, err := s.BackingState.EnsureAvailability(n, s.defaultConstraints, coretesting.FakeDefaultSeries, nil) 455 c.Assert(err, jc.ErrorIsNil) 456 added := make([]*state.Machine, len(changes.Added)) 457 for i, mid := range changes.Added { 458 m, err := s.BackingState.Machine(mid) 459 c.Assert(err, jc.ErrorIsNil) 460 added[i] = m 461 } 462 return added 463 } 464 465 func (s *ProvisionerSuite) TestProvisionerStartStop(c *gc.C) { 466 p := s.newEnvironProvisioner(c) 467 c.Assert(p.Stop(), jc.ErrorIsNil) 468 } 469 470 func (s *ProvisionerSuite) TestSimple(c *gc.C) { 471 p := s.newEnvironProvisioner(c) 472 defer stop(c, p) 473 474 // Check that an instance is provisioned when the machine is created... 475 m, err := s.addMachine() 476 c.Assert(err, jc.ErrorIsNil) 477 instance := s.checkStartInstanceNoSecureConnection(c, m) 478 479 // ...and removed, along with the machine, when the machine is Dead. 480 c.Assert(m.EnsureDead(), gc.IsNil) 481 s.checkStopInstances(c, instance) 482 s.waitRemoved(c, m) 483 } 484 485 func (s *ProvisionerSuite) TestConstraints(c *gc.C) { 486 // Create a machine with non-standard constraints. 487 m, err := s.addMachine() 488 c.Assert(err, jc.ErrorIsNil) 489 cons := constraints.MustParse("mem=8G arch=amd64 cpu-cores=2 root-disk=10G") 490 err = m.SetConstraints(cons) 491 c.Assert(err, jc.ErrorIsNil) 492 493 // Start a provisioner and check those constraints are used. 494 p := s.newEnvironProvisioner(c) 495 defer stop(c, p) 496 s.checkStartInstanceCustom(c, m, "pork", cons, nil, nil, nil, false, nil, true) 497 } 498 499 func (s *ProvisionerSuite) TestPossibleTools(c *gc.C) { 500 501 storageDir := c.MkDir() 502 s.PatchValue(&tools.DefaultBaseURL, storageDir) 503 stor, err := filestorage.NewFileStorageWriter(storageDir) 504 c.Assert(err, jc.ErrorIsNil) 505 506 // Set a current version that does not match the 507 // agent-version in the environ config. 508 currentVersion := version.MustParseBinary("1.2.3-quantal-arm64") 509 s.PatchValue(&version.Current, currentVersion) 510 511 // Upload some plausible matches, and some that should be filtered out. 512 compatibleVersion := version.MustParseBinary("1.2.3-quantal-amd64") 513 ignoreVersion1 := version.MustParseBinary("1.2.4-quantal-arm64") 514 ignoreVersion2 := version.MustParseBinary("1.2.3-precise-arm64") 515 availableVersions := []version.Binary{ 516 currentVersion, compatibleVersion, ignoreVersion1, ignoreVersion2, 517 } 518 envtesting.AssertUploadFakeToolsVersions(c, stor, s.cfg.AgentStream(), s.cfg.AgentStream(), availableVersions...) 519 520 // Extract the tools that we expect to actually match. 521 expectedList, err := tools.FindTools(s.Environ, -1, -1, coretools.Filter{ 522 Number: currentVersion.Number, 523 Series: currentVersion.Series, 524 }) 525 c.Assert(err, jc.ErrorIsNil) 526 527 // Create the machine and check the tools that get passed into StartInstance. 528 machine, err := s.BackingState.AddOneMachine(state.MachineTemplate{ 529 Series: "quantal", 530 Jobs: []state.MachineJob{state.JobHostUnits}, 531 }) 532 c.Assert(err, jc.ErrorIsNil) 533 534 provisioner := s.newEnvironProvisioner(c) 535 defer stop(c, provisioner) 536 s.checkStartInstanceCustom( 537 c, machine, "pork", constraints.Value{}, 538 nil, nil, nil, false, expectedList, true, 539 ) 540 } 541 542 func (s *ProvisionerSuite) TestProvisionerSetsErrorStatusWhenNoToolsAreAvailable(c *gc.C) { 543 p := s.newEnvironProvisioner(c) 544 defer stop(c, p) 545 546 // Check that an instance is not provisioned when the machine is created... 547 m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ 548 // We need a valid series that has no tools uploaded 549 Series: "raring", 550 Jobs: []state.MachineJob{state.JobHostUnits}, 551 Constraints: s.defaultConstraints, 552 }) 553 c.Assert(err, jc.ErrorIsNil) 554 s.checkNoOperations(c) 555 556 t0 := time.Now() 557 for time.Since(t0) < coretesting.LongWait { 558 // And check the machine status is set to error. 559 statusInfo, err := m.Status() 560 c.Assert(err, jc.ErrorIsNil) 561 if statusInfo.Status == state.StatusPending { 562 time.Sleep(coretesting.ShortWait) 563 continue 564 } 565 c.Assert(statusInfo.Status, gc.Equals, state.StatusError) 566 c.Assert(statusInfo.Message, gc.Equals, "no matching tools available") 567 break 568 } 569 570 // Restart the PA to make sure the machine is skipped again. 571 stop(c, p) 572 p = s.newEnvironProvisioner(c) 573 defer stop(c, p) 574 s.checkNoOperations(c) 575 } 576 577 func (s *ProvisionerSuite) TestProvisionerSetsErrorStatusWhenStartInstanceFailed(c *gc.C) { 578 brokenMsg := breakDummyProvider(c, s.State, "StartInstance") 579 p := s.newEnvironProvisioner(c) 580 defer stop(c, p) 581 582 // Check that an instance is not provisioned when the machine is created... 583 m, err := s.addMachine() 584 c.Assert(err, jc.ErrorIsNil) 585 s.checkNoOperations(c) 586 587 t0 := time.Now() 588 for time.Since(t0) < coretesting.LongWait { 589 // And check the machine status is set to error. 590 statusInfo, err := m.Status() 591 c.Assert(err, jc.ErrorIsNil) 592 if statusInfo.Status == state.StatusPending { 593 time.Sleep(coretesting.ShortWait) 594 continue 595 } 596 c.Assert(statusInfo.Status, gc.Equals, state.StatusError) 597 c.Assert(statusInfo.Message, gc.Equals, brokenMsg) 598 break 599 } 600 601 // Unbreak the environ config. 602 err = s.fixEnvironment(c) 603 c.Assert(err, jc.ErrorIsNil) 604 605 // Restart the PA to make sure the machine is skipped again. 606 stop(c, p) 607 p = s.newEnvironProvisioner(c) 608 defer stop(c, p) 609 s.checkNoOperations(c) 610 } 611 612 func (s *ProvisionerSuite) TestProvisionerFailedStartInstanceWithInjectedCreationError(c *gc.C) { 613 // create the error injection channel 614 errorInjectionChannel := make(chan error, 2) 615 616 p := s.newEnvironProvisioner(c) 617 defer stop(c, p) 618 619 // patch the dummy provider error injection channel 620 cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel) 621 defer cleanup() 622 623 retryableError := instance.NewRetryableCreationError("container failed to start and was destroyed") 624 destroyError := errors.New("container failed to start and failed to destroy: manual cleanup of containers needed") 625 // send the error message TWICE, because the provisioner will retry only ONCE 626 errorInjectionChannel <- retryableError 627 errorInjectionChannel <- destroyError 628 629 m, err := s.addMachine() 630 c.Assert(err, jc.ErrorIsNil) 631 s.checkNoOperations(c) 632 633 t0 := time.Now() 634 for time.Since(t0) < coretesting.LongWait { 635 // And check the machine status is set to error. 636 statusInfo, err := m.Status() 637 c.Assert(err, jc.ErrorIsNil) 638 if statusInfo.Status == state.StatusPending { 639 time.Sleep(coretesting.ShortWait) 640 continue 641 } 642 c.Assert(statusInfo.Status, gc.Equals, state.StatusError) 643 // check that the status matches the error message 644 c.Assert(statusInfo.Message, gc.Equals, destroyError.Error()) 645 break 646 } 647 648 } 649 650 func (s *ProvisionerSuite) TestProvisionerSucceedStartInstanceWithInjectedRetryableCreationError(c *gc.C) { 651 // create the error injection channel 652 errorInjectionChannel := make(chan error, 1) 653 c.Assert(errorInjectionChannel, gc.NotNil) 654 655 p := s.newEnvironProvisioner(c) 656 defer stop(c, p) 657 658 // patch the dummy provider error injection channel 659 cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel) 660 defer cleanup() 661 662 // send the error message once 663 // - instance creation should succeed 664 retryableError := instance.NewRetryableCreationError("container failed to start and was destroyed") 665 errorInjectionChannel <- retryableError 666 667 m, err := s.addMachine() 668 c.Assert(err, jc.ErrorIsNil) 669 s.checkStartInstanceNoSecureConnection(c, m) 670 } 671 672 func (s *ProvisionerSuite) TestProvisionerSucceedStartInstanceWithInjectedWrappedRetryableCreationError(c *gc.C) { 673 // create the error injection channel 674 errorInjectionChannel := make(chan error, 1) 675 c.Assert(errorInjectionChannel, gc.NotNil) 676 677 p := s.newEnvironProvisioner(c) 678 defer stop(c, p) 679 680 // patch the dummy provider error injection channel 681 cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel) 682 defer cleanup() 683 684 // send the error message once 685 // - instance creation should succeed 686 retryableError := errors.Wrap(errors.New(""), instance.NewRetryableCreationError("container failed to start and was destroyed")) 687 errorInjectionChannel <- retryableError 688 689 m, err := s.addMachine() 690 c.Assert(err, jc.ErrorIsNil) 691 s.checkStartInstanceNoSecureConnection(c, m) 692 } 693 694 func (s *ProvisionerSuite) TestProvisionerFailStartInstanceWithInjectedNonRetryableCreationError(c *gc.C) { 695 // create the error injection channel 696 errorInjectionChannel := make(chan error, 1) 697 c.Assert(errorInjectionChannel, gc.NotNil) 698 699 p := s.newEnvironProvisioner(c) 700 defer stop(c, p) 701 702 // patch the dummy provider error injection channel 703 cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel) 704 defer cleanup() 705 706 // send the error message once 707 // - instance creation should succeed 708 nonRetryableError := errors.New("some nonretryable error") 709 errorInjectionChannel <- nonRetryableError 710 711 m, err := s.addMachine() 712 c.Assert(err, jc.ErrorIsNil) 713 s.checkNoOperations(c) 714 715 t0 := time.Now() 716 for time.Since(t0) < coretesting.LongWait { 717 // And check the machine status is set to error. 718 statusInfo, err := m.Status() 719 c.Assert(err, jc.ErrorIsNil) 720 if statusInfo.Status == state.StatusPending { 721 time.Sleep(coretesting.ShortWait) 722 continue 723 } 724 c.Assert(statusInfo.Status, gc.Equals, state.StatusError) 725 // check that the status matches the error message 726 c.Assert(statusInfo.Message, gc.Equals, nonRetryableError.Error()) 727 break 728 } 729 } 730 731 func (s *ProvisionerSuite) TestProvisioningDoesNotOccurForContainers(c *gc.C) { 732 p := s.newEnvironProvisioner(c) 733 defer stop(c, p) 734 735 // create a machine to host the container. 736 m, err := s.addMachine() 737 c.Assert(err, jc.ErrorIsNil) 738 inst := s.checkStartInstanceNoSecureConnection(c, m) 739 740 // make a container on the machine we just created 741 template := state.MachineTemplate{ 742 Series: coretesting.FakeDefaultSeries, 743 Jobs: []state.MachineJob{state.JobHostUnits}, 744 } 745 container, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC) 746 c.Assert(err, jc.ErrorIsNil) 747 748 // the PA should not attempt to create it 749 s.checkNoOperations(c) 750 751 // cleanup 752 c.Assert(container.EnsureDead(), gc.IsNil) 753 c.Assert(container.Remove(), gc.IsNil) 754 c.Assert(m.EnsureDead(), gc.IsNil) 755 s.checkStopInstances(c, inst) 756 s.waitRemoved(c, m) 757 } 758 759 func (s *ProvisionerSuite) TestProvisioningMachinesWithRequestedNetworks(c *gc.C) { 760 p := s.newEnvironProvisioner(c) 761 defer p.Stop() 762 763 // Add and provision a machine with networks specified. 764 requestedNetworks := []string{"net1", "net2"} 765 cons := constraints.MustParse(s.defaultConstraints.String(), "networks=^net3,^net4") 766 expectNetworkInfo := []network.InterfaceInfo{{ 767 MACAddress: "aa:bb:cc:dd:ee:f0", 768 InterfaceName: "eth0", 769 ProviderId: "net1", 770 NetworkName: "net1", 771 VLANTag: 0, 772 CIDR: "0.1.2.0/24", 773 }, { 774 MACAddress: "aa:bb:cc:dd:ee:f1", 775 InterfaceName: "eth1", 776 ProviderId: "net2", 777 NetworkName: "net2", 778 VLANTag: 1, 779 CIDR: "0.2.2.0/24", 780 }} 781 m, err := s.addMachineWithRequestedNetworks(requestedNetworks, cons) 782 c.Assert(err, jc.ErrorIsNil) 783 inst := s.checkStartInstanceCustom( 784 c, m, "pork", cons, 785 requestedNetworks, expectNetworkInfo, nil, false, 786 nil, true, 787 ) 788 789 _, err = s.State.Network("net1") 790 c.Assert(err, jc.ErrorIsNil) 791 _, err = s.State.Network("net2") 792 c.Assert(err, jc.ErrorIsNil) 793 _, err = s.State.Network("net3") 794 c.Assert(err, jc.Satisfies, errors.IsNotFound) 795 _, err = s.State.Network("net4") 796 c.Assert(err, jc.Satisfies, errors.IsNotFound) 797 ifaces, err := m.NetworkInterfaces() 798 c.Assert(err, jc.ErrorIsNil) 799 c.Assert(ifaces, gc.HasLen, 2) 800 801 // Cleanup. 802 c.Assert(m.EnsureDead(), gc.IsNil) 803 s.checkStopInstances(c, inst) 804 s.waitRemoved(c, m) 805 } 806 807 func (s *ProvisionerSuite) TestProvisioningMachinesWithInvalidNetwork(c *gc.C) { 808 p := s.newEnvironProvisioner(c) 809 defer stop(c, p) 810 811 // "invalid-" prefix for networks causes the dummy provider to 812 // return network.InterfaceInfo with an invalid network name. 813 networks := []string{"invalid-net1"} 814 expectNetworkInfo := []network.InterfaceInfo{ 815 {ProviderId: "invalid-net1", NetworkName: "$$invalid-net1", CIDR: "0.1.2.0/24"}, 816 } 817 m, err := s.addMachineWithRequestedNetworks(networks, constraints.Value{}) 818 c.Assert(err, jc.ErrorIsNil) 819 s.checkStartInstanceCustom( 820 c, m, "pork", constraints.Value{}, 821 networks, expectNetworkInfo, nil, false, 822 nil, false, 823 ) 824 825 // Ensure machine error status was set. 826 t0 := time.Now() 827 for time.Since(t0) < coretesting.LongWait { 828 // And check the machine status is set to error. 829 statusInfo, err := m.Status() 830 c.Assert(err, jc.ErrorIsNil) 831 if statusInfo.Status == state.StatusPending { 832 time.Sleep(coretesting.ShortWait) 833 continue 834 } 835 c.Assert(statusInfo.Status, gc.Equals, state.StatusError) 836 c.Assert(statusInfo.Message, gc.Matches, `invalid network name "\$\$invalid-net1"`) 837 break 838 } 839 840 // Make sure the task didn't stop with an error 841 died := make(chan error) 842 go func() { 843 died <- p.Wait() 844 }() 845 select { 846 case <-time.After(coretesting.ShortWait): 847 case err = <-died: 848 c.Fatalf("provisioner task died unexpectedly with err: %v", err) 849 } 850 851 // Restart the PA to make sure the machine is not retried. 852 stop(c, p) 853 p = s.newEnvironProvisioner(c) 854 defer stop(c, p) 855 856 s.checkNoOperations(c) 857 } 858 859 func (s *CommonProvisionerSuite) addMachineWithRequestedVolumes(volumes []state.MachineVolumeParams, cons constraints.Value) (*state.Machine, error) { 860 return s.BackingState.AddOneMachine(state.MachineTemplate{ 861 Series: coretesting.FakeDefaultSeries, 862 Jobs: []state.MachineJob{state.JobHostUnits}, 863 Constraints: cons, 864 Volumes: volumes, 865 }) 866 } 867 868 func (s *ProvisionerSuite) TestProvisioningMachinesWithRequestedVolumes(c *gc.C) { 869 // Set up a persistent pool. 870 registry.RegisterProvider("static", &dummystorage.StorageProvider{IsDynamic: false}) 871 registry.RegisterEnvironStorageProviders("dummy", "static") 872 defer registry.RegisterProvider("static", nil) 873 poolManager := poolmanager.New(state.NewStateSettings(s.State)) 874 _, err := poolManager.Create("persistent-pool", "static", map[string]interface{}{"persistent": true}) 875 c.Assert(err, jc.ErrorIsNil) 876 877 p := s.newEnvironProvisioner(c) 878 defer p.Stop() 879 880 // Add and provision a machine with volumes specified. 881 requestedVolumes := []state.MachineVolumeParams{{ 882 Volume: state.VolumeParams{Pool: "static", Size: 1024}, 883 Attachment: state.VolumeAttachmentParams{}, 884 }, { 885 Volume: state.VolumeParams{Pool: "persistent-pool", Size: 2048}, 886 Attachment: state.VolumeAttachmentParams{}, 887 }} 888 cons := constraints.MustParse(s.defaultConstraints.String(), "networks=^net3,^net4") 889 expectVolumeInfo := []storage.Volume{{ 890 names.NewVolumeTag("1"), 891 storage.VolumeInfo{ 892 Size: 1024, 893 }, 894 }, { 895 names.NewVolumeTag("2"), 896 storage.VolumeInfo{ 897 Size: 2048, 898 Persistent: true, 899 }, 900 }} 901 m, err := s.addMachineWithRequestedVolumes(requestedVolumes, cons) 902 c.Assert(err, jc.ErrorIsNil) 903 inst := s.checkStartInstanceCustom( 904 c, m, "pork", cons, 905 nil, nil, expectVolumeInfo, false, 906 nil, true, 907 ) 908 909 // Cleanup. 910 c.Assert(m.EnsureDead(), gc.IsNil) 911 s.checkStopInstances(c, inst) 912 s.waitRemoved(c, m) 913 } 914 915 func (s *ProvisionerSuite) TestSetInstanceInfoFailureSetsErrorStatusAndStopsInstanceButKeepsGoing(c *gc.C) { 916 p := s.newEnvironProvisioner(c) 917 defer stop(c, p) 918 919 // Add and provision a machine with networks specified. 920 networks := []string{"bad-net1"} 921 // "bad-" prefix for networks causes dummy provider to report 922 // invalid network.InterfaceInfo. 923 expectNetworkInfo := []network.InterfaceInfo{ 924 {ProviderId: "bad-net1", NetworkName: "bad-net1", CIDR: "invalid"}, 925 } 926 m, err := s.addMachineWithRequestedNetworks(networks, constraints.Value{}) 927 c.Assert(err, jc.ErrorIsNil) 928 inst := s.checkStartInstanceCustom( 929 c, m, "pork", constraints.Value{}, 930 networks, expectNetworkInfo, nil, false, 931 nil, false, 932 ) 933 934 // Ensure machine error status was set. 935 t0 := time.Now() 936 for time.Since(t0) < coretesting.LongWait { 937 // And check the machine status is set to error. 938 statusInfo, err := m.Status() 939 c.Assert(err, jc.ErrorIsNil) 940 if statusInfo.Status == state.StatusPending { 941 time.Sleep(coretesting.ShortWait) 942 continue 943 } 944 c.Assert(statusInfo.Status, gc.Equals, state.StatusError) 945 c.Assert(statusInfo.Message, gc.Matches, `cannot record provisioning info for "dummyenv-0": cannot add network "bad-net1": invalid CIDR address: invalid`) 946 break 947 } 948 s.checkStopInstances(c, inst) 949 950 // Make sure the task didn't stop with an error 951 died := make(chan error) 952 go func() { 953 died <- p.Wait() 954 }() 955 select { 956 case <-time.After(coretesting.LongWait): 957 case err = <-died: 958 c.Fatalf("provisioner task died unexpectedly with err: %v", err) 959 } 960 961 // Restart the PA to make sure the machine is not retried. 962 stop(c, p) 963 p = s.newEnvironProvisioner(c) 964 defer stop(c, p) 965 966 s.checkNoOperations(c) 967 } 968 969 func (s *ProvisionerSuite) TestProvisioningDoesNotOccurWithAnInvalidEnvironment(c *gc.C) { 970 s.invalidateEnvironment(c) 971 972 p := s.newEnvironProvisioner(c) 973 defer stop(c, p) 974 975 // try to create a machine 976 _, err := s.addMachine() 977 c.Assert(err, jc.ErrorIsNil) 978 979 // the PA should not create it 980 s.checkNoOperations(c) 981 } 982 983 func (s *ProvisionerSuite) TestProvisioningOccursWithFixedEnvironment(c *gc.C) { 984 s.invalidateEnvironment(c) 985 986 p := s.newEnvironProvisioner(c) 987 defer stop(c, p) 988 989 // try to create a machine 990 m, err := s.addMachine() 991 c.Assert(err, jc.ErrorIsNil) 992 993 // the PA should not create it 994 s.checkNoOperations(c) 995 996 err = s.fixEnvironment(c) 997 c.Assert(err, jc.ErrorIsNil) 998 999 s.checkStartInstanceNoSecureConnection(c, m) 1000 } 1001 1002 func (s *ProvisionerSuite) TestProvisioningDoesOccurAfterInvalidEnvironmentPublished(c *gc.C) { 1003 s.PatchValue(provisioner.GetToolsFinder, func(*apiprovisioner.State) provisioner.ToolsFinder { 1004 return mockToolsFinder{} 1005 }) 1006 p := s.newEnvironProvisioner(c) 1007 defer stop(c, p) 1008 1009 // place a new machine into the state 1010 m, err := s.addMachine() 1011 c.Assert(err, jc.ErrorIsNil) 1012 1013 s.checkStartInstanceNoSecureConnection(c, m) 1014 1015 s.invalidateEnvironment(c) 1016 1017 // create a second machine 1018 m, err = s.addMachine() 1019 c.Assert(err, jc.ErrorIsNil) 1020 1021 // the PA should create it using the old environment 1022 s.checkStartInstanceNoSecureConnection(c, m) 1023 } 1024 1025 func (s *ProvisionerSuite) TestProvisioningDoesNotProvisionTheSameMachineAfterRestart(c *gc.C) { 1026 p := s.newEnvironProvisioner(c) 1027 defer stop(c, p) 1028 1029 // create a machine 1030 m, err := s.addMachine() 1031 c.Assert(err, jc.ErrorIsNil) 1032 s.checkStartInstanceNoSecureConnection(c, m) 1033 1034 // restart the PA 1035 stop(c, p) 1036 p = s.newEnvironProvisioner(c) 1037 defer stop(c, p) 1038 1039 // check that there is only one machine provisioned. 1040 machines, err := s.State.AllMachines() 1041 c.Assert(err, jc.ErrorIsNil) 1042 c.Check(len(machines), gc.Equals, 2) 1043 c.Check(machines[0].Id(), gc.Equals, "0") 1044 c.Check(machines[1].CheckProvisioned("fake_nonce"), jc.IsFalse) 1045 1046 // the PA should not create it a second time 1047 s.checkNoOperations(c) 1048 } 1049 1050 func (s *ProvisionerSuite) TestDyingMachines(c *gc.C) { 1051 p := s.newEnvironProvisioner(c) 1052 defer stop(c, p) 1053 1054 // provision a machine 1055 m0, err := s.addMachine() 1056 c.Assert(err, jc.ErrorIsNil) 1057 s.checkStartInstanceNoSecureConnection(c, m0) 1058 1059 // stop the provisioner and make the machine dying 1060 stop(c, p) 1061 err = m0.Destroy() 1062 c.Assert(err, jc.ErrorIsNil) 1063 1064 // add a new, dying, unprovisioned machine 1065 m1, err := s.addMachine() 1066 c.Assert(err, jc.ErrorIsNil) 1067 err = m1.Destroy() 1068 c.Assert(err, jc.ErrorIsNil) 1069 1070 // start the provisioner and wait for it to reap the useless machine 1071 p = s.newEnvironProvisioner(c) 1072 defer stop(c, p) 1073 s.checkNoOperations(c) 1074 s.waitRemoved(c, m1) 1075 1076 // verify the other one's still fine 1077 err = m0.Refresh() 1078 c.Assert(err, jc.ErrorIsNil) 1079 c.Assert(m0.Life(), gc.Equals, state.Dying) 1080 } 1081 1082 func (s *ProvisionerSuite) TestProvisioningRecoversAfterInvalidEnvironmentPublished(c *gc.C) { 1083 s.PatchValue(provisioner.GetToolsFinder, func(*apiprovisioner.State) provisioner.ToolsFinder { 1084 return mockToolsFinder{} 1085 }) 1086 p := s.newEnvironProvisioner(c) 1087 defer stop(c, p) 1088 1089 // place a new machine into the state 1090 m, err := s.addMachine() 1091 c.Assert(err, jc.ErrorIsNil) 1092 s.checkStartInstanceNoSecureConnection(c, m) 1093 1094 s.invalidateEnvironment(c) 1095 s.BackingState.StartSync() 1096 1097 // create a second machine 1098 m, err = s.addMachine() 1099 c.Assert(err, jc.ErrorIsNil) 1100 1101 // the PA should create it using the old environment 1102 s.checkStartInstanceNoSecureConnection(c, m) 1103 1104 err = s.fixEnvironment(c) 1105 c.Assert(err, jc.ErrorIsNil) 1106 1107 // insert our observer 1108 cfgObserver := make(chan *config.Config, 1) 1109 provisioner.SetObserver(p, cfgObserver) 1110 1111 err = s.State.UpdateEnvironConfig(map[string]interface{}{"secret": "beef"}, nil, nil) 1112 c.Assert(err, jc.ErrorIsNil) 1113 1114 s.BackingState.StartSync() 1115 1116 // wait for the PA to load the new configuration 1117 select { 1118 case <-cfgObserver: 1119 case <-time.After(coretesting.LongWait): 1120 c.Fatalf("PA did not action config change") 1121 } 1122 1123 // create a third machine 1124 m, err = s.addMachine() 1125 c.Assert(err, jc.ErrorIsNil) 1126 1127 // the PA should create it using the new environment 1128 s.checkStartInstanceCustom(c, m, "beef", s.defaultConstraints, nil, nil, nil, false, nil, true) 1129 } 1130 1131 type mockMachineGetter struct{} 1132 1133 func (*mockMachineGetter) Machine(names.MachineTag) (*apiprovisioner.Machine, error) { 1134 return nil, fmt.Errorf("error") 1135 } 1136 1137 func (*mockMachineGetter) MachinesWithTransientErrors() ([]*apiprovisioner.Machine, []params.StatusResult, error) { 1138 return nil, nil, fmt.Errorf("error") 1139 } 1140 1141 func (s *ProvisionerSuite) TestMachineErrorsRetainInstances(c *gc.C) { 1142 task := s.newProvisionerTask(c, config.HarvestAll, s.Environ, s.provisioner, mockToolsFinder{}) 1143 defer stop(c, task) 1144 1145 // create a machine 1146 m0, err := s.addMachine() 1147 c.Assert(err, jc.ErrorIsNil) 1148 s.checkStartInstance(c, m0) 1149 1150 // create an instance out of band 1151 s.startUnknownInstance(c, "999") 1152 1153 // start the provisioner and ensure it doesn't kill any instances if there are error getting machines 1154 task = s.newProvisionerTask( 1155 c, 1156 config.HarvestAll, 1157 s.Environ, 1158 &mockMachineGetter{}, 1159 &mockToolsFinder{}, 1160 ) 1161 defer func() { 1162 err := task.Stop() 1163 c.Assert(err, gc.ErrorMatches, ".*failed to get machine.*") 1164 }() 1165 s.checkNoOperations(c) 1166 } 1167 1168 func (s *ProvisionerSuite) TestEnvironProvisionerObservesConfigChanges(c *gc.C) { 1169 p := s.newEnvironProvisioner(c) 1170 defer stop(c, p) 1171 s.assertProvisionerObservesConfigChanges(c, p) 1172 } 1173 1174 func (s *ProvisionerSuite) newProvisionerTask( 1175 c *gc.C, 1176 harvestingMethod config.HarvestMode, 1177 broker environs.InstanceBroker, 1178 machineGetter provisioner.MachineGetter, 1179 toolsFinder provisioner.ToolsFinder, 1180 ) provisioner.ProvisionerTask { 1181 1182 machineWatcher, err := s.provisioner.WatchEnvironMachines() 1183 c.Assert(err, jc.ErrorIsNil) 1184 retryWatcher, err := s.provisioner.WatchMachineErrorRetry() 1185 c.Assert(err, jc.ErrorIsNil) 1186 auth, err := authentication.NewAPIAuthenticator(s.provisioner) 1187 c.Assert(err, jc.ErrorIsNil) 1188 1189 return provisioner.NewProvisionerTask( 1190 names.NewMachineTag("0"), 1191 harvestingMethod, 1192 machineGetter, 1193 toolsFinder, 1194 machineWatcher, 1195 retryWatcher, 1196 broker, 1197 auth, 1198 imagemetadata.ReleasedStream, 1199 true, 1200 ) 1201 } 1202 1203 func (s *ProvisionerSuite) TestHarvestNoneReapsNothing(c *gc.C) { 1204 1205 task := s.newProvisionerTask(c, config.HarvestDestroyed, s.Environ, s.provisioner, mockToolsFinder{}) 1206 defer stop(c, task) 1207 task.SetHarvestMode(config.HarvestNone) 1208 1209 // Create a machine and an unknown instance. 1210 m0, err := s.addMachine() 1211 c.Assert(err, jc.ErrorIsNil) 1212 s.checkStartInstance(c, m0) 1213 s.startUnknownInstance(c, "999") 1214 1215 // Mark the first machine as dead. 1216 c.Assert(m0.EnsureDead(), gc.IsNil) 1217 1218 // Ensure we're doing nothing. 1219 s.checkNoOperations(c) 1220 } 1221 1222 func (s *ProvisionerSuite) TestHarvestUnknownReapsOnlyUnknown(c *gc.C) { 1223 1224 task := s.newProvisionerTask(c, 1225 config.HarvestDestroyed, 1226 s.Environ, 1227 s.provisioner, 1228 mockToolsFinder{}, 1229 ) 1230 defer stop(c, task) 1231 task.SetHarvestMode(config.HarvestUnknown) 1232 1233 // Create a machine and an unknown instance. 1234 m0, err := s.addMachine() 1235 c.Assert(err, jc.ErrorIsNil) 1236 i0 := s.checkStartInstance(c, m0) 1237 i1 := s.startUnknownInstance(c, "999") 1238 1239 // Mark the first machine as dead. 1240 c.Assert(m0.EnsureDead(), gc.IsNil) 1241 1242 // When only harvesting unknown machines, only one of the machines 1243 // is stopped. 1244 s.checkStopSomeInstances(c, []instance.Instance{i1}, []instance.Instance{i0}) 1245 s.waitRemoved(c, m0) 1246 } 1247 1248 func (s *ProvisionerSuite) TestHarvestDestroyedReapsOnlyDestroyed(c *gc.C) { 1249 1250 task := s.newProvisionerTask( 1251 c, 1252 config.HarvestDestroyed, 1253 s.Environ, 1254 s.provisioner, 1255 mockToolsFinder{}, 1256 ) 1257 defer stop(c, task) 1258 1259 // Create a machine and an unknown instance. 1260 m0, err := s.addMachine() 1261 c.Assert(err, jc.ErrorIsNil) 1262 i0 := s.checkStartInstance(c, m0) 1263 i1 := s.startUnknownInstance(c, "999") 1264 1265 // Mark the first machine as dead. 1266 c.Assert(m0.EnsureDead(), gc.IsNil) 1267 1268 // When only harvesting destroyed machines, only one of the 1269 // machines is stopped. 1270 s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1}) 1271 s.waitRemoved(c, m0) 1272 } 1273 1274 func (s *ProvisionerSuite) TestHarvestAllReapsAllTheThings(c *gc.C) { 1275 1276 task := s.newProvisionerTask(c, 1277 config.HarvestDestroyed, 1278 s.Environ, 1279 s.provisioner, 1280 mockToolsFinder{}, 1281 ) 1282 defer stop(c, task) 1283 task.SetHarvestMode(config.HarvestAll) 1284 1285 // Create a machine and an unknown instance. 1286 m0, err := s.addMachine() 1287 c.Assert(err, jc.ErrorIsNil) 1288 i0 := s.checkStartInstance(c, m0) 1289 i1 := s.startUnknownInstance(c, "999") 1290 1291 // Mark the first machine as dead. 1292 c.Assert(m0.EnsureDead(), gc.IsNil) 1293 1294 // Everything must die! 1295 s.checkStopSomeInstances(c, []instance.Instance{i0, i1}, []instance.Instance{}) 1296 s.waitRemoved(c, m0) 1297 } 1298 1299 func (s *ProvisionerSuite) TestProvisionerRetriesTransientErrors(c *gc.C) { 1300 s.PatchValue(&apiserverprovisioner.ErrorRetryWaitDelay, 5*time.Millisecond) 1301 e := &mockBroker{Environ: s.Environ, retryCount: make(map[string]int)} 1302 task := s.newProvisionerTask(c, config.HarvestAll, e, s.provisioner, mockToolsFinder{}) 1303 defer stop(c, task) 1304 1305 // Provision some machines, some will be started first time, 1306 // another will require retries. 1307 m1, err := s.addMachine() 1308 c.Assert(err, jc.ErrorIsNil) 1309 s.checkStartInstance(c, m1) 1310 m2, err := s.addMachine() 1311 c.Assert(err, jc.ErrorIsNil) 1312 s.checkStartInstance(c, m2) 1313 m3, err := s.addMachine() 1314 c.Assert(err, jc.ErrorIsNil) 1315 m4, err := s.addMachine() 1316 c.Assert(err, jc.ErrorIsNil) 1317 1318 // mockBroker will fail to start machine-3 several times; 1319 // keep setting the transient flag to retry until the 1320 // instance has started. 1321 thatsAllFolks := make(chan struct{}) 1322 go func() { 1323 for { 1324 select { 1325 case <-thatsAllFolks: 1326 return 1327 case <-time.After(coretesting.ShortWait): 1328 err := m3.SetStatus(state.StatusError, "info", map[string]interface{}{"transient": true}) 1329 c.Assert(err, jc.ErrorIsNil) 1330 } 1331 } 1332 }() 1333 s.checkStartInstance(c, m3) 1334 close(thatsAllFolks) 1335 1336 // Machine 4 is never provisioned. 1337 statusInfo, err := m4.Status() 1338 c.Assert(err, jc.ErrorIsNil) 1339 c.Assert(statusInfo.Status, gc.Equals, state.StatusError) 1340 _, err = m4.InstanceId() 1341 c.Assert(err, jc.Satisfies, errors.IsNotProvisioned) 1342 } 1343 1344 func (s *ProvisionerSuite) TestProvisionerObservesMachineJobs(c *gc.C) { 1345 s.PatchValue(&apiserverprovisioner.ErrorRetryWaitDelay, 5*time.Millisecond) 1346 broker := &mockBroker{Environ: s.Environ, retryCount: make(map[string]int)} 1347 task := s.newProvisionerTask(c, config.HarvestAll, broker, s.provisioner, mockToolsFinder{}) 1348 defer stop(c, task) 1349 1350 added := s.ensureAvailability(c, 3) 1351 c.Assert(added, gc.HasLen, 2) 1352 byId := make(map[string]*state.Machine) 1353 for _, m := range added { 1354 byId[m.Id()] = m 1355 } 1356 for _, id := range broker.ids { 1357 s.checkStartInstance(c, byId[id]) 1358 } 1359 } 1360 1361 type mockBroker struct { 1362 environs.Environ 1363 retryCount map[string]int 1364 ids []string 1365 } 1366 1367 func (b *mockBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { 1368 // All machines except machines 3, 4 are provisioned successfully the first time. 1369 // Machines 3 is provisioned after some attempts have been made. 1370 // Machine 4 is never provisioned. 1371 id := args.InstanceConfig.MachineId 1372 // record ids so we can call checkStartInstance in the appropriate order. 1373 b.ids = append(b.ids, id) 1374 retries := b.retryCount[id] 1375 if (id != "3" && id != "4") || retries > 2 { 1376 return b.Environ.StartInstance(args) 1377 } else { 1378 b.retryCount[id] = retries + 1 1379 } 1380 return nil, fmt.Errorf("error: some error") 1381 } 1382 1383 type mockToolsFinder struct { 1384 } 1385 1386 func (f mockToolsFinder) FindTools(number version.Number, series string, arch *string) (coretools.List, error) { 1387 v, err := version.ParseBinary(fmt.Sprintf("%s-%s-%s", number, series, version.Current.Arch)) 1388 if err != nil { 1389 return nil, err 1390 } 1391 if arch != nil { 1392 v.Arch = *arch 1393 } 1394 return coretools.List{&coretools.Tools{Version: v}}, nil 1395 }