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