github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/environs/jujutest/livetests.go (about) 1 // Copyright 2011, 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package jujutest 5 6 import ( 7 "fmt" 8 "path/filepath" 9 "time" 10 11 "github.com/juju/errors" 12 gitjujutesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 "github.com/juju/utils/arch" 16 "github.com/juju/utils/series" 17 "github.com/juju/version" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/charmrepo.v2-unstable" 20 21 "github.com/juju/juju/api" 22 "github.com/juju/juju/cloud" 23 "github.com/juju/juju/cloudconfig/instancecfg" 24 "github.com/juju/juju/constraints" 25 "github.com/juju/juju/environs" 26 "github.com/juju/juju/environs/bootstrap" 27 "github.com/juju/juju/environs/config" 28 "github.com/juju/juju/environs/filestorage" 29 sstesting "github.com/juju/juju/environs/simplestreams/testing" 30 "github.com/juju/juju/environs/storage" 31 "github.com/juju/juju/environs/sync" 32 envtesting "github.com/juju/juju/environs/testing" 33 envtools "github.com/juju/juju/environs/tools" 34 envtoolstesting "github.com/juju/juju/environs/tools/testing" 35 "github.com/juju/juju/instance" 36 "github.com/juju/juju/juju" 37 "github.com/juju/juju/juju/keys" 38 jujutesting "github.com/juju/juju/juju/testing" 39 "github.com/juju/juju/jujuclient" 40 "github.com/juju/juju/jujuclient/jujuclienttesting" 41 "github.com/juju/juju/network" 42 "github.com/juju/juju/provider/dummy" 43 "github.com/juju/juju/state" 44 statetesting "github.com/juju/juju/state/testing" 45 "github.com/juju/juju/testcharms" 46 coretesting "github.com/juju/juju/testing" 47 coretools "github.com/juju/juju/tools" 48 jujuversion "github.com/juju/juju/version" 49 ) 50 51 const ( 52 AdminSecret = "admin-secret" 53 ) 54 55 // LiveTests contains tests that are designed to run against a live server 56 // (e.g. Amazon EC2). The Environ is opened once only for all the tests 57 // in the suite, stored in Env, and Destroyed after the suite has completed. 58 type LiveTests struct { 59 gitjujutesting.CleanupSuite 60 61 envtesting.ToolsFixture 62 sstesting.TestDataSuite 63 64 // TestConfig contains the configuration attributes for opening an environment. 65 TestConfig coretesting.Attrs 66 67 // Credential contains the credential for preparing an environment for 68 // bootstrapping. If this is unset, empty credentials will be used. 69 Credential cloud.Credential 70 71 // CloudRegion contains the cloud region name to create resources in. 72 CloudRegion string 73 74 // CloudEndpoint contains the cloud API endpoint to communicate with. 75 CloudEndpoint string 76 77 // Attempt holds a strategy for waiting until the environment 78 // becomes logically consistent. 79 // 80 // TODO(katco): 2016-08-09: lp:1611427 81 Attempt utils.AttemptStrategy 82 83 // CanOpenState should be true if the testing environment allows 84 // the state to be opened after bootstrapping. 85 CanOpenState bool 86 87 // HasProvisioner should be true if the environment has 88 // a provisioning agent. 89 HasProvisioner bool 90 91 // Env holds the currently opened environment. 92 // This is set by PrepareOnce and BootstrapOnce. 93 Env environs.Environ 94 95 // ControllerStore holds the controller related informtion 96 // such as controllers, accounts, etc., used when preparing 97 // the environment. This is initialized by SetUpSuite. 98 ControllerStore jujuclient.ClientStore 99 100 // ControllerUUID is the uuid of the bootstrapped controller. 101 ControllerUUID string 102 103 prepared bool 104 bootstrapped bool 105 toolsStorage storage.Storage 106 } 107 108 func (t *LiveTests) SetUpSuite(c *gc.C) { 109 t.CleanupSuite.SetUpSuite(c) 110 t.TestDataSuite.SetUpSuite(c) 111 t.ControllerStore = jujuclienttesting.NewMemStore() 112 t.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 113 } 114 115 func (t *LiveTests) SetUpTest(c *gc.C) { 116 t.CleanupSuite.SetUpTest(c) 117 t.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber) 118 storageDir := c.MkDir() 119 baseURLPath := filepath.Join(storageDir, "tools") 120 t.DefaultBaseURL = utils.MakeFileURL(baseURLPath) 121 t.ToolsFixture.SetUpTest(c) 122 stor, err := filestorage.NewFileStorageWriter(storageDir) 123 c.Assert(err, jc.ErrorIsNil) 124 t.UploadFakeTools(c, stor, "released", "released") 125 t.toolsStorage = stor 126 t.CleanupSuite.PatchValue(&envtools.BundleTools, envtoolstesting.GetMockBundleTools(c, nil)) 127 } 128 129 func (t *LiveTests) TearDownSuite(c *gc.C) { 130 t.Destroy(c) 131 t.TestDataSuite.TearDownSuite(c) 132 t.CleanupSuite.TearDownSuite(c) 133 } 134 135 func (t *LiveTests) TearDownTest(c *gc.C) { 136 t.ToolsFixture.TearDownTest(c) 137 t.CleanupSuite.TearDownTest(c) 138 } 139 140 // PrepareOnce ensures that the environment is 141 // available and prepared. It sets t.Env appropriately. 142 func (t *LiveTests) PrepareOnce(c *gc.C) { 143 if t.prepared { 144 return 145 } 146 args := t.prepareForBootstrapParams(c) 147 e, err := bootstrap.Prepare(envtesting.BootstrapContext(c), t.ControllerStore, args) 148 c.Assert(err, gc.IsNil, gc.Commentf("preparing environ %#v", t.TestConfig)) 149 c.Assert(e, gc.NotNil) 150 t.Env = e 151 t.prepared = true 152 t.ControllerUUID = coretesting.FakeControllerConfig().ControllerUUID() 153 } 154 155 func (t *LiveTests) prepareForBootstrapParams(c *gc.C) bootstrap.PrepareParams { 156 credential := t.Credential 157 if credential.AuthType() == "" { 158 credential = cloud.NewEmptyCredential() 159 } 160 return bootstrap.PrepareParams{ 161 ControllerConfig: coretesting.FakeControllerConfig(), 162 ModelConfig: t.TestConfig, 163 Cloud: environs.CloudSpec{ 164 Type: t.TestConfig["type"].(string), 165 Name: t.TestConfig["type"].(string), 166 Region: t.CloudRegion, 167 Endpoint: t.CloudEndpoint, 168 Credential: &credential, 169 }, 170 ControllerName: t.TestConfig["name"].(string), 171 AdminSecret: AdminSecret, 172 } 173 } 174 175 func (t *LiveTests) bootstrapParams() bootstrap.BootstrapParams { 176 credential := t.Credential 177 if credential.AuthType() == "" { 178 credential = cloud.NewEmptyCredential() 179 } 180 var regions []cloud.Region 181 if t.CloudRegion != "" { 182 regions = []cloud.Region{{ 183 Name: t.CloudRegion, 184 Endpoint: t.CloudEndpoint, 185 }} 186 } 187 return bootstrap.BootstrapParams{ 188 ControllerConfig: coretesting.FakeControllerConfig(), 189 CloudName: t.TestConfig["type"].(string), 190 Cloud: cloud.Cloud{ 191 Type: t.TestConfig["type"].(string), 192 AuthTypes: []cloud.AuthType{credential.AuthType()}, 193 Regions: regions, 194 Endpoint: t.CloudEndpoint, 195 }, 196 CloudRegion: t.CloudRegion, 197 CloudCredential: &credential, 198 CloudCredentialName: "credential", 199 AdminSecret: AdminSecret, 200 CAPrivateKey: coretesting.CAKey, 201 } 202 } 203 204 func (t *LiveTests) BootstrapOnce(c *gc.C) { 205 if t.bootstrapped { 206 return 207 } 208 t.PrepareOnce(c) 209 // We only build and upload tools if there will be a state agent that 210 // we could connect to (actual live tests, rather than local-only) 211 cons := constraints.MustParse("mem=2G") 212 if t.CanOpenState { 213 _, err := sync.Upload(t.toolsStorage, "released", nil, series.LatestLts()) 214 c.Assert(err, jc.ErrorIsNil) 215 } 216 args := t.bootstrapParams() 217 args.BootstrapConstraints = cons 218 args.ModelConstraints = cons 219 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), t.Env, args) 220 c.Assert(err, jc.ErrorIsNil) 221 t.bootstrapped = true 222 } 223 224 func (t *LiveTests) Destroy(c *gc.C) { 225 if t.Env == nil { 226 return 227 } 228 err := environs.Destroy(t.Env.Config().Name(), t.Env, t.ControllerStore) 229 c.Assert(err, jc.ErrorIsNil) 230 t.bootstrapped = false 231 t.prepared = false 232 t.ControllerUUID = "" 233 t.Env = nil 234 } 235 236 func (t *LiveTests) TestPrechecker(c *gc.C) { 237 // Providers may implement Prechecker. If they do, then they should 238 // return nil for empty constraints (excluding the null provider). 239 prechecker, ok := t.Env.(state.Prechecker) 240 if !ok { 241 return 242 } 243 const placement = "" 244 err := prechecker.PrecheckInstance("precise", constraints.Value{}, placement) 245 c.Assert(err, jc.ErrorIsNil) 246 } 247 248 // TestStartStop is similar to Tests.TestStartStop except 249 // that it does not assume a pristine environment. 250 func (t *LiveTests) TestStartStop(c *gc.C) { 251 t.BootstrapOnce(c) 252 253 inst, _ := jujutesting.AssertStartInstance(c, t.Env, t.ControllerUUID, "0") 254 c.Assert(inst, gc.NotNil) 255 id0 := inst.Id() 256 257 insts, err := t.Env.Instances([]instance.Id{id0, id0}) 258 c.Assert(err, jc.ErrorIsNil) 259 c.Assert(insts, gc.HasLen, 2) 260 c.Assert(insts[0].Id(), gc.Equals, id0) 261 c.Assert(insts[1].Id(), gc.Equals, id0) 262 263 // Asserting on the return of AllInstances makes the test fragile, 264 // as even comparing the before and after start values can be thrown 265 // off if other instances have been created or destroyed in the same 266 // time frame. Instead, just check the instance we created exists. 267 insts, err = t.Env.AllInstances() 268 c.Assert(err, jc.ErrorIsNil) 269 found := false 270 for _, inst := range insts { 271 if inst.Id() == id0 { 272 c.Assert(found, gc.Equals, false, gc.Commentf("%v", insts)) 273 found = true 274 } 275 } 276 c.Assert(found, gc.Equals, true, gc.Commentf("expected %v in %v", inst, insts)) 277 278 addresses, err := jujutesting.WaitInstanceAddresses(t.Env, inst.Id()) 279 c.Assert(err, jc.ErrorIsNil) 280 c.Assert(addresses, gc.Not(gc.HasLen), 0) 281 282 insts, err = t.Env.Instances([]instance.Id{id0, ""}) 283 c.Assert(err, gc.Equals, environs.ErrPartialInstances) 284 c.Assert(insts, gc.HasLen, 2) 285 c.Check(insts[0].Id(), gc.Equals, id0) 286 c.Check(insts[1], gc.IsNil) 287 288 err = t.Env.StopInstances(inst.Id()) 289 c.Assert(err, jc.ErrorIsNil) 290 291 // The machine may not be marked as shutting down 292 // immediately. Repeat a few times to ensure we get the error. 293 for a := t.Attempt.Start(); a.Next(); { 294 insts, err = t.Env.Instances([]instance.Id{id0}) 295 if err != nil { 296 break 297 } 298 } 299 c.Assert(err, gc.Equals, environs.ErrNoInstances) 300 c.Assert(insts, gc.HasLen, 0) 301 } 302 303 func (t *LiveTests) TestPorts(c *gc.C) { 304 t.BootstrapOnce(c) 305 306 inst1, _ := jujutesting.AssertStartInstance(c, t.Env, t.ControllerUUID, "1") 307 c.Assert(inst1, gc.NotNil) 308 defer t.Env.StopInstances(inst1.Id()) 309 ports, err := inst1.Ports("1") 310 c.Assert(err, jc.ErrorIsNil) 311 c.Assert(ports, gc.HasLen, 0) 312 313 inst2, _ := jujutesting.AssertStartInstance(c, t.Env, t.ControllerUUID, "2") 314 c.Assert(inst2, gc.NotNil) 315 ports, err = inst2.Ports("2") 316 c.Assert(err, jc.ErrorIsNil) 317 c.Assert(ports, gc.HasLen, 0) 318 defer t.Env.StopInstances(inst2.Id()) 319 320 // Open some ports and check they're there. 321 err = inst1.OpenPorts("1", []network.PortRange{{67, 67, "udp"}, {45, 45, "tcp"}, {80, 100, "tcp"}}) 322 c.Assert(err, jc.ErrorIsNil) 323 ports, err = inst1.Ports("1") 324 c.Assert(err, jc.ErrorIsNil) 325 c.Assert(ports, gc.DeepEquals, []network.PortRange{{45, 45, "tcp"}, {80, 100, "tcp"}, {67, 67, "udp"}}) 326 ports, err = inst2.Ports("2") 327 c.Assert(err, jc.ErrorIsNil) 328 c.Assert(ports, gc.HasLen, 0) 329 330 err = inst2.OpenPorts("2", []network.PortRange{{89, 89, "tcp"}, {45, 45, "tcp"}, {20, 30, "tcp"}}) 331 c.Assert(err, jc.ErrorIsNil) 332 333 // Check there's no crosstalk to another machine 334 ports, err = inst2.Ports("2") 335 c.Assert(err, jc.ErrorIsNil) 336 c.Assert(ports, gc.DeepEquals, []network.PortRange{{20, 30, "tcp"}, {45, 45, "tcp"}, {89, 89, "tcp"}}) 337 ports, err = inst1.Ports("1") 338 c.Assert(err, jc.ErrorIsNil) 339 c.Assert(ports, gc.DeepEquals, []network.PortRange{{45, 45, "tcp"}, {80, 100, "tcp"}, {67, 67, "udp"}}) 340 341 // Check that opening the same port again is ok. 342 oldPorts, err := inst2.Ports("2") 343 c.Assert(err, jc.ErrorIsNil) 344 err = inst2.OpenPorts("2", []network.PortRange{{45, 45, "tcp"}}) 345 c.Assert(err, jc.ErrorIsNil) 346 err = inst2.OpenPorts("2", []network.PortRange{{20, 30, "tcp"}}) 347 c.Assert(err, jc.ErrorIsNil) 348 ports, err = inst2.Ports("2") 349 c.Assert(err, jc.ErrorIsNil) 350 c.Assert(ports, gc.DeepEquals, oldPorts) 351 352 // Check that opening the same port again and another port is ok. 353 err = inst2.OpenPorts("2", []network.PortRange{{45, 45, "tcp"}, {99, 99, "tcp"}}) 354 c.Assert(err, jc.ErrorIsNil) 355 ports, err = inst2.Ports("2") 356 c.Assert(err, jc.ErrorIsNil) 357 c.Assert(ports, gc.DeepEquals, []network.PortRange{{20, 30, "tcp"}, {45, 45, "tcp"}, {89, 89, "tcp"}, {99, 99, "tcp"}}) 358 359 err = inst2.ClosePorts("2", []network.PortRange{{45, 45, "tcp"}, {99, 99, "tcp"}, {20, 30, "tcp"}}) 360 c.Assert(err, jc.ErrorIsNil) 361 362 // Check that we can close ports and that there's no crosstalk. 363 ports, err = inst2.Ports("2") 364 c.Assert(err, jc.ErrorIsNil) 365 c.Assert(ports, gc.DeepEquals, []network.PortRange{{89, 89, "tcp"}}) 366 ports, err = inst1.Ports("1") 367 c.Assert(err, jc.ErrorIsNil) 368 c.Assert(ports, gc.DeepEquals, []network.PortRange{{45, 45, "tcp"}, {80, 100, "tcp"}, {67, 67, "udp"}}) 369 370 // Check that we can close multiple ports. 371 err = inst1.ClosePorts("1", []network.PortRange{{45, 45, "tcp"}, {67, 67, "udp"}, {80, 100, "tcp"}}) 372 c.Assert(err, jc.ErrorIsNil) 373 ports, err = inst1.Ports("1") 374 c.Assert(ports, gc.HasLen, 0) 375 376 // Check that we can close ports that aren't there. 377 err = inst2.ClosePorts("2", []network.PortRange{{111, 111, "tcp"}, {222, 222, "udp"}, {600, 700, "tcp"}}) 378 c.Assert(err, jc.ErrorIsNil) 379 ports, err = inst2.Ports("2") 380 c.Assert(ports, gc.DeepEquals, []network.PortRange{{89, 89, "tcp"}}) 381 382 // Check errors when acting on environment. 383 err = t.Env.OpenPorts([]network.PortRange{{80, 80, "tcp"}}) 384 c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for opening ports on model`) 385 386 err = t.Env.ClosePorts([]network.PortRange{{80, 80, "tcp"}}) 387 c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for closing ports on model`) 388 389 _, err = t.Env.Ports() 390 c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for retrieving ports from model`) 391 } 392 393 func (t *LiveTests) TestGlobalPorts(c *gc.C) { 394 t.BootstrapOnce(c) 395 396 // Change configuration. 397 oldConfig := t.Env.Config() 398 defer func() { 399 err := t.Env.SetConfig(oldConfig) 400 c.Assert(err, jc.ErrorIsNil) 401 }() 402 403 attrs := t.Env.Config().AllAttrs() 404 attrs["firewall-mode"] = config.FwGlobal 405 newConfig, err := t.Env.Config().Apply(attrs) 406 c.Assert(err, jc.ErrorIsNil) 407 err = t.Env.SetConfig(newConfig) 408 c.Assert(err, jc.ErrorIsNil) 409 410 // Create instances and check open ports on both instances. 411 inst1, _ := jujutesting.AssertStartInstance(c, t.Env, t.ControllerUUID, "1") 412 defer t.Env.StopInstances(inst1.Id()) 413 ports, err := t.Env.Ports() 414 c.Assert(err, jc.ErrorIsNil) 415 c.Assert(ports, gc.HasLen, 0) 416 417 inst2, _ := jujutesting.AssertStartInstance(c, t.Env, t.ControllerUUID, "2") 418 ports, err = t.Env.Ports() 419 c.Assert(err, jc.ErrorIsNil) 420 c.Assert(ports, gc.HasLen, 0) 421 defer t.Env.StopInstances(inst2.Id()) 422 423 err = t.Env.OpenPorts([]network.PortRange{{67, 67, "udp"}, {45, 45, "tcp"}, {89, 89, "tcp"}, {99, 99, "tcp"}, {100, 110, "tcp"}}) 424 c.Assert(err, jc.ErrorIsNil) 425 426 ports, err = t.Env.Ports() 427 c.Assert(err, jc.ErrorIsNil) 428 c.Assert(ports, gc.DeepEquals, []network.PortRange{{45, 45, "tcp"}, {89, 89, "tcp"}, {99, 99, "tcp"}, {100, 110, "tcp"}, {67, 67, "udp"}}) 429 430 // Check closing some ports. 431 err = t.Env.ClosePorts([]network.PortRange{{99, 99, "tcp"}, {67, 67, "udp"}}) 432 c.Assert(err, jc.ErrorIsNil) 433 434 ports, err = t.Env.Ports() 435 c.Assert(err, jc.ErrorIsNil) 436 c.Assert(ports, gc.DeepEquals, []network.PortRange{{45, 45, "tcp"}, {89, 89, "tcp"}, {100, 110, "tcp"}}) 437 438 // Check that we can close ports that aren't there. 439 err = t.Env.ClosePorts([]network.PortRange{{111, 111, "tcp"}, {222, 222, "udp"}, {2000, 2500, "tcp"}}) 440 c.Assert(err, jc.ErrorIsNil) 441 442 ports, err = t.Env.Ports() 443 c.Assert(err, jc.ErrorIsNil) 444 c.Assert(ports, gc.DeepEquals, []network.PortRange{{45, 45, "tcp"}, {89, 89, "tcp"}, {100, 110, "tcp"}}) 445 446 // Check errors when acting on instances. 447 err = inst1.OpenPorts("1", []network.PortRange{{80, 80, "tcp"}}) 448 c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for opening ports on instance`) 449 450 err = inst1.ClosePorts("1", []network.PortRange{{80, 80, "tcp"}}) 451 c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for closing ports on instance`) 452 453 _, err = inst1.Ports("1") 454 c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for retrieving ports from instance`) 455 } 456 457 func (t *LiveTests) TestBootstrapMultiple(c *gc.C) { 458 // bootstrap.Bootstrap no longer raises errors if the environment is 459 // already up, this has been moved into the bootstrap command. 460 t.BootstrapOnce(c) 461 462 c.Logf("destroy env") 463 env := t.Env 464 t.Destroy(c) 465 err := env.Destroy() // Again, should work fine and do nothing. 466 c.Assert(err, jc.ErrorIsNil) 467 468 // check that we can bootstrap after destroy 469 t.BootstrapOnce(c) 470 } 471 472 func (t *LiveTests) TestBootstrapAndDeploy(c *gc.C) { 473 if !t.CanOpenState || !t.HasProvisioner { 474 c.Skip(fmt.Sprintf("skipping provisioner test, CanOpenState: %v, HasProvisioner: %v", t.CanOpenState, t.HasProvisioner)) 475 } 476 t.BootstrapOnce(c) 477 478 // TODO(niemeyer): Stop growing this kitchen sink test and split it into proper parts. 479 480 c.Logf("opening state") 481 st := t.Env.(jujutesting.GetStater).GetStateInAPIServer() 482 483 model, err := st.Model() 484 c.Assert(err, jc.ErrorIsNil) 485 owner := model.Owner() 486 487 c.Logf("opening API connection") 488 controllerCfg, err := st.ControllerConfig() 489 c.Assert(err, jc.ErrorIsNil) 490 caCert, _ := controllerCfg.CACert() 491 apiInfo, err := environs.APIInfo(model.Tag().Id(), model.Tag().Id(), caCert, controllerCfg.APIPort(), t.Env) 492 c.Assert(err, jc.ErrorIsNil) 493 apiInfo.Tag = owner 494 apiInfo.Password = AdminSecret 495 apiState, err := api.Open(apiInfo, api.DefaultDialOpts()) 496 c.Assert(err, jc.ErrorIsNil) 497 defer apiState.Close() 498 499 // Check that the agent version has made it through the 500 // bootstrap process (it's optional in the config.Config) 501 cfg, err := st.ModelConfig() 502 c.Assert(err, jc.ErrorIsNil) 503 agentVersion, ok := cfg.AgentVersion() 504 c.Check(ok, jc.IsTrue) 505 c.Check(agentVersion, gc.Equals, jujuversion.Current) 506 507 // Check that the constraints have been set in the environment. 508 cons, err := st.ModelConstraints() 509 c.Assert(err, jc.ErrorIsNil) 510 c.Assert(cons.String(), gc.Equals, "mem=2048M") 511 512 // Wait for machine agent to come up on the bootstrap 513 // machine and find the deployed series from that. 514 m0, err := st.Machine("0") 515 c.Assert(err, jc.ErrorIsNil) 516 517 instId0, err := m0.InstanceId() 518 c.Assert(err, jc.ErrorIsNil) 519 520 // Check that the API connection is working. 521 status, err := apiState.Client().Status(nil) 522 c.Assert(err, jc.ErrorIsNil) 523 c.Assert(status.Machines["0"].InstanceId, gc.Equals, string(instId0)) 524 525 mw0 := newMachineToolWaiter(m0) 526 defer mw0.Stop() 527 528 // If the series has not been specified, we expect the most recent Ubuntu LTS release to be used. 529 expectedVersion := version.Binary{ 530 Number: jujuversion.Current, 531 Arch: arch.HostArch(), 532 Series: series.LatestLts(), 533 } 534 535 mtools0 := waitAgentTools(c, mw0, expectedVersion) 536 537 // Create a new service and deploy a unit of it. 538 c.Logf("deploying service") 539 repoDir := c.MkDir() 540 url := testcharms.Repo.ClonedURL(repoDir, mtools0.Version.Series, "dummy") 541 sch, err := jujutesting.PutCharm(st, url, &charmrepo.LocalRepository{Path: repoDir}, false) 542 c.Assert(err, jc.ErrorIsNil) 543 svc, err := st.AddApplication(state.AddApplicationArgs{Name: "dummy", Charm: sch}) 544 c.Assert(err, jc.ErrorIsNil) 545 units, err := juju.AddUnits(st, svc, "dummy", 1, nil) 546 c.Assert(err, jc.ErrorIsNil) 547 unit := units[0] 548 549 // Wait for the unit's machine and associated agent to come up 550 // and announce itself. 551 mid1, err := unit.AssignedMachineId() 552 c.Assert(err, jc.ErrorIsNil) 553 m1, err := st.Machine(mid1) 554 c.Assert(err, jc.ErrorIsNil) 555 mw1 := newMachineToolWaiter(m1) 556 defer mw1.Stop() 557 waitAgentTools(c, mw1, mtools0.Version) 558 559 err = m1.Refresh() 560 c.Assert(err, jc.ErrorIsNil) 561 instId1, err := m1.InstanceId() 562 c.Assert(err, jc.ErrorIsNil) 563 uw := newUnitToolWaiter(unit) 564 defer uw.Stop() 565 utools := waitAgentTools(c, uw, expectedVersion) 566 567 // Check that we can upgrade the environment. 568 newVersion := utools.Version 569 newVersion.Patch++ 570 t.checkUpgrade(c, st, newVersion, mw0, mw1, uw) 571 572 // BUG(niemeyer): Logic below is very much wrong. Must be: 573 // 574 // 1. EnsureDying on the unit and EnsureDying on the machine 575 // 2. Unit dies by itself 576 // 3. Machine removes dead unit 577 // 4. Machine dies by itself 578 // 5. Provisioner removes dead machine 579 // 580 581 // Now remove the unit and its assigned machine and 582 // check that the PA removes it. 583 c.Logf("removing unit") 584 err = unit.Destroy() 585 c.Assert(err, jc.ErrorIsNil) 586 587 // Wait until unit is dead 588 uwatch := unit.Watch() 589 defer uwatch.Stop() 590 for unit.Life() != state.Dead { 591 c.Logf("waiting for unit change") 592 <-uwatch.Changes() 593 err := unit.Refresh() 594 c.Logf("refreshed; err %v", err) 595 if errors.IsNotFound(err) { 596 c.Logf("unit has been removed") 597 break 598 } 599 c.Assert(err, jc.ErrorIsNil) 600 } 601 for { 602 c.Logf("destroying machine") 603 err := m1.Destroy() 604 if err == nil { 605 break 606 } 607 c.Assert(err, gc.FitsTypeOf, &state.HasAssignedUnitsError{}) 608 time.Sleep(5 * time.Second) 609 err = m1.Refresh() 610 if errors.IsNotFound(err) { 611 break 612 } 613 c.Assert(err, jc.ErrorIsNil) 614 } 615 c.Logf("waiting for instance to be removed") 616 t.assertStopInstance(c, t.Env, instId1) 617 } 618 619 type tooler interface { 620 Life() state.Life 621 AgentTools() (*coretools.Tools, error) 622 Refresh() error 623 String() string 624 } 625 626 type watcher interface { 627 Stop() error 628 Err() error 629 } 630 631 type toolsWaiter struct { 632 lastTools *coretools.Tools 633 // changes is a chan of struct{} so that it can 634 // be used with different kinds of entity watcher. 635 changes chan struct{} 636 watcher watcher 637 tooler tooler 638 } 639 640 func newMachineToolWaiter(m *state.Machine) *toolsWaiter { 641 w := m.Watch() 642 waiter := &toolsWaiter{ 643 changes: make(chan struct{}, 1), 644 watcher: w, 645 tooler: m, 646 } 647 go func() { 648 for _ = range w.Changes() { 649 waiter.changes <- struct{}{} 650 } 651 close(waiter.changes) 652 }() 653 return waiter 654 } 655 656 func newUnitToolWaiter(u *state.Unit) *toolsWaiter { 657 w := u.Watch() 658 waiter := &toolsWaiter{ 659 changes: make(chan struct{}, 1), 660 watcher: w, 661 tooler: u, 662 } 663 go func() { 664 for _ = range w.Changes() { 665 waiter.changes <- struct{}{} 666 } 667 close(waiter.changes) 668 }() 669 return waiter 670 } 671 672 func (w *toolsWaiter) Stop() error { 673 return w.watcher.Stop() 674 } 675 676 // NextTools returns the next changed tools, waiting 677 // until the tools are actually set. 678 func (w *toolsWaiter) NextTools(c *gc.C) (*coretools.Tools, error) { 679 for _ = range w.changes { 680 err := w.tooler.Refresh() 681 if err != nil { 682 return nil, fmt.Errorf("cannot refresh: %v", err) 683 } 684 if w.tooler.Life() == state.Dead { 685 return nil, fmt.Errorf("object is dead") 686 } 687 tools, err := w.tooler.AgentTools() 688 if errors.IsNotFound(err) { 689 c.Logf("tools not yet set") 690 continue 691 } 692 if err != nil { 693 return nil, err 694 } 695 changed := w.lastTools == nil || *tools != *w.lastTools 696 w.lastTools = tools 697 if changed { 698 return tools, nil 699 } 700 c.Logf("found same tools") 701 } 702 return nil, fmt.Errorf("watcher closed prematurely: %v", w.watcher.Err()) 703 } 704 705 // waitAgentTools waits for the given agent 706 // to start and returns the tools that it is running. 707 func waitAgentTools(c *gc.C, w *toolsWaiter, expect version.Binary) *coretools.Tools { 708 c.Logf("waiting for %v to signal agent version", w.tooler.String()) 709 tools, err := w.NextTools(c) 710 c.Assert(err, jc.ErrorIsNil) 711 c.Check(tools.Version, gc.Equals, expect) 712 return tools 713 } 714 715 // checkUpgrade sets the environment agent version and checks that 716 // all the provided watchers upgrade to the requested version. 717 func (t *LiveTests) checkUpgrade(c *gc.C, st *state.State, newVersion version.Binary, waiters ...*toolsWaiter) { 718 c.Logf("putting testing version of juju tools") 719 upgradeTools, err := sync.Upload(t.toolsStorage, "released", &newVersion.Number, newVersion.Series) 720 c.Assert(err, jc.ErrorIsNil) 721 // sync.Upload always returns tools for the series on which the tests are running. 722 // We are only interested in checking the version.Number below so need to fake the 723 // upgraded tools series to match that of newVersion. 724 upgradeTools.Version.Series = newVersion.Series 725 726 // Check that the put version really is the version we expect. 727 c.Assert(upgradeTools.Version, gc.Equals, newVersion) 728 err = statetesting.SetAgentVersion(st, newVersion.Number) 729 c.Assert(err, jc.ErrorIsNil) 730 731 for i, w := range waiters { 732 c.Logf("waiting for upgrade of %d: %v", i, w.tooler.String()) 733 734 waitAgentTools(c, w, newVersion) 735 c.Logf("upgrade %d successful", i) 736 } 737 } 738 739 // TODO(katco): 2016-08-09: lp:1611427 740 var waitAgent = utils.AttemptStrategy{ 741 Total: 30 * time.Second, 742 Delay: 1 * time.Second, 743 } 744 745 func (t *LiveTests) assertStopInstance(c *gc.C, env environs.Environ, instId instance.Id) { 746 var err error 747 for a := waitAgent.Start(); a.Next(); { 748 _, err = t.Env.Instances([]instance.Id{instId}) 749 if err == nil { 750 continue 751 } 752 if err == environs.ErrNoInstances { 753 return 754 } 755 c.Logf("error from Instances: %v", err) 756 } 757 c.Fatalf("provisioner failed to stop machine after %v", waitAgent.Total) 758 } 759 760 // Check that we get a consistent error when asking for an instance without 761 // a valid machine config. 762 func (t *LiveTests) TestStartInstanceWithEmptyNonceFails(c *gc.C) { 763 machineId := "4" 764 apiInfo := jujutesting.FakeAPIInfo(machineId) 765 instanceConfig, err := instancecfg.NewInstanceConfig(coretesting.ControllerTag, machineId, "", "released", "quantal", apiInfo) 766 c.Assert(err, jc.ErrorIsNil) 767 768 t.PrepareOnce(c) 769 possibleTools := coretools.List(envtesting.AssertUploadFakeToolsVersions( 770 c, t.toolsStorage, "released", "released", version.MustParseBinary("5.4.5-trusty-amd64"), 771 )) 772 params := environs.StartInstanceParams{ 773 ControllerUUID: coretesting.ControllerTag.Id(), 774 Tools: possibleTools, 775 InstanceConfig: instanceConfig, 776 } 777 err = jujutesting.SetImageMetadata( 778 t.Env, 779 possibleTools.AllSeries(), 780 possibleTools.Arches(), 781 ¶ms.ImageMetadata, 782 ) 783 c.Check(err, jc.ErrorIsNil) 784 result, err := t.Env.StartInstance(params) 785 if result != nil && result.Instance != nil { 786 err := t.Env.StopInstances(result.Instance.Id()) 787 c.Check(err, jc.ErrorIsNil) 788 } 789 c.Assert(result, gc.IsNil) 790 c.Assert(err, gc.ErrorMatches, ".*missing machine nonce") 791 } 792 793 func (t *LiveTests) TestBootstrapWithDefaultSeries(c *gc.C) { 794 if !t.HasProvisioner { 795 c.Skip("HasProvisioner is false; cannot test deployment") 796 } 797 798 current := version.Binary{ 799 Number: jujuversion.Current, 800 Arch: arch.HostArch(), 801 Series: series.HostSeries(), 802 } 803 other := current 804 other.Series = "quantal" 805 if current == other { 806 other.Series = "precise" 807 } 808 809 dummyCfg := dummy.SampleConfig().Merge(coretesting.Attrs{ 810 "controller": false, 811 "name": "dummy storage", 812 }) 813 args := t.prepareForBootstrapParams(c) 814 args.ModelConfig = dummyCfg 815 dummyenv, err := bootstrap.Prepare(envtesting.BootstrapContext(c), 816 jujuclienttesting.NewMemStore(), 817 args, 818 ) 819 c.Assert(err, jc.ErrorIsNil) 820 defer dummyenv.Destroy() 821 822 t.Destroy(c) 823 824 attrs := t.TestConfig.Merge(coretesting.Attrs{ 825 "name": "livetests", 826 "default-series": other.Series, 827 }) 828 args.ModelConfig = attrs 829 env, err := bootstrap.Prepare(envtesting.BootstrapContext(c), 830 t.ControllerStore, 831 args) 832 c.Assert(err, jc.ErrorIsNil) 833 defer environs.Destroy("livetests", env, t.ControllerStore) 834 835 err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, t.bootstrapParams()) 836 c.Assert(err, jc.ErrorIsNil) 837 838 st := t.Env.(jujutesting.GetStater).GetStateInAPIServer() 839 // Wait for machine agent to come up on the bootstrap 840 // machine and ensure it deployed the proper series. 841 m0, err := st.Machine("0") 842 c.Assert(err, jc.ErrorIsNil) 843 mw0 := newMachineToolWaiter(m0) 844 defer mw0.Stop() 845 846 waitAgentTools(c, mw0, other) 847 }