github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/ec2/local_test.go (about) 1 // Copyright 2011, 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package ec2_test 5 6 import ( 7 "fmt" 8 "regexp" 9 "sort" 10 "strconv" 11 "strings" 12 13 "github.com/juju/errors" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils" 16 "github.com/juju/utils/arch" 17 "github.com/juju/utils/clock" 18 "github.com/juju/utils/series" 19 "github.com/juju/utils/set" 20 "github.com/juju/utils/ssh" 21 "gopkg.in/amz.v3/aws" 22 amzec2 "gopkg.in/amz.v3/ec2" 23 "gopkg.in/amz.v3/ec2/ec2test" 24 gc "gopkg.in/check.v1" 25 "gopkg.in/juju/names.v2" 26 goyaml "gopkg.in/yaml.v2" 27 28 "github.com/juju/juju/cloud" 29 "github.com/juju/juju/constraints" 30 "github.com/juju/juju/environs" 31 "github.com/juju/juju/environs/bootstrap" 32 "github.com/juju/juju/environs/imagemetadata" 33 imagetesting "github.com/juju/juju/environs/imagemetadata/testing" 34 "github.com/juju/juju/environs/jujutest" 35 "github.com/juju/juju/environs/simplestreams" 36 sstesting "github.com/juju/juju/environs/simplestreams/testing" 37 "github.com/juju/juju/environs/tags" 38 envtesting "github.com/juju/juju/environs/testing" 39 "github.com/juju/juju/environs/tools" 40 "github.com/juju/juju/instance" 41 "github.com/juju/juju/juju/keys" 42 "github.com/juju/juju/juju/testing" 43 "github.com/juju/juju/jujuclient/jujuclienttesting" 44 "github.com/juju/juju/network" 45 "github.com/juju/juju/provider/common" 46 "github.com/juju/juju/provider/ec2" 47 "github.com/juju/juju/storage" 48 coretesting "github.com/juju/juju/testing" 49 jujuversion "github.com/juju/juju/version" 50 ) 51 52 var localConfigAttrs = coretesting.FakeConfig().Merge(coretesting.Attrs{ 53 "name": "sample", 54 "type": "ec2", 55 "agent-version": coretesting.FakeVersionNumber.String(), 56 }) 57 58 func registerLocalTests() { 59 // N.B. Make sure the region we use here 60 // has entries in the images/query txt files. 61 aws.Regions["test"] = aws.Region{ 62 Name: "test", 63 } 64 65 gc.Suite(&localServerSuite{}) 66 gc.Suite(&localLiveSuite{}) 67 gc.Suite(&localNonUSEastSuite{}) 68 } 69 70 // localLiveSuite runs tests from LiveTests using a fake 71 // EC2 server that runs within the test process itself. 72 type localLiveSuite struct { 73 LiveTests 74 srv localServer 75 } 76 77 func (t *localLiveSuite) SetUpSuite(c *gc.C) { 78 t.LiveTests.SetUpSuite(c) 79 t.Credential = cloud.NewCredential( 80 cloud.AccessKeyAuthType, 81 map[string]string{ 82 "access-key": "x", 83 "secret-key": "x", 84 }, 85 ) 86 87 // Upload arches that ec2 supports; add to this 88 // as ec2 coverage expands. 89 t.UploadArches = []string{arch.AMD64, arch.I386} 90 t.TestConfig = localConfigAttrs 91 imagetesting.PatchOfficialDataSources(&t.BaseSuite.CleanupSuite, "test:") 92 t.BaseSuite.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey) 93 t.BaseSuite.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 94 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 95 t.srv.createRootDisks = true 96 t.srv.startServer(c) 97 98 region := t.srv.region() 99 t.CloudRegion = region.Name 100 t.CloudEndpoint = region.EC2Endpoint 101 restoreEC2Patching := patchEC2ForTesting(c, region) 102 t.BaseSuite.AddCleanup(func(c *gc.C) { restoreEC2Patching() }) 103 } 104 105 func (t *localLiveSuite) TearDownSuite(c *gc.C) { 106 t.LiveTests.TearDownSuite(c) 107 t.srv.stopServer(c) 108 } 109 110 // localServer represents a fake EC2 server running within 111 // the test process itself. 112 type localServer struct { 113 // createRootDisks is used to decide whether or not 114 // the ec2test server will create root disks for 115 // instances. 116 createRootDisks bool 117 118 ec2srv *ec2test.Server 119 120 defaultVPC *amzec2.VPC 121 zones []amzec2.AvailabilityZoneInfo 122 } 123 124 func (srv *localServer) startServer(c *gc.C) { 125 var err error 126 srv.ec2srv, err = ec2test.NewServer() 127 if err != nil { 128 c.Fatalf("cannot start ec2 test server: %v", err) 129 } 130 srv.ec2srv.SetCreateRootDisks(srv.createRootDisks) 131 srv.addSpice(c) 132 133 region := srv.region() 134 135 zones := make([]amzec2.AvailabilityZoneInfo, 3) 136 zones[0].Region = region.Name 137 zones[0].Name = region.Name + "-available" 138 zones[0].State = "available" 139 zones[1].Region = region.Name 140 zones[1].Name = region.Name + "-impaired" 141 zones[1].State = "impaired" 142 zones[2].Region = region.Name 143 zones[2].Name = region.Name + "-unavailable" 144 zones[2].State = "unavailable" 145 srv.ec2srv.SetAvailabilityZones(zones) 146 srv.ec2srv.SetInitialInstanceState(ec2test.Pending) 147 srv.zones = zones 148 149 defaultVPC, err := srv.ec2srv.AddDefaultVPCAndSubnets() 150 c.Assert(err, jc.ErrorIsNil) 151 srv.defaultVPC = &defaultVPC 152 } 153 154 func (srv *localServer) client() *amzec2.EC2 { 155 region := srv.region() 156 return amzec2.New(aws.Auth{}, region, aws.SignV4Factory(region.Name, "ec2")) 157 } 158 159 func (srv *localServer) region() aws.Region { 160 return aws.Region{ 161 Name: "test", 162 EC2Endpoint: srv.ec2srv.URL(), 163 } 164 } 165 166 // addSpice adds some "spice" to the local server 167 // by adding state that may cause tests to fail. 168 func (srv *localServer) addSpice(c *gc.C) { 169 states := []amzec2.InstanceState{ 170 ec2test.ShuttingDown, 171 ec2test.Terminated, 172 ec2test.Stopped, 173 } 174 for _, state := range states { 175 srv.ec2srv.NewInstances(1, "m1.small", "ami-a7f539ce", state, nil) 176 } 177 } 178 179 func (srv *localServer) stopServer(c *gc.C) { 180 srv.ec2srv.Reset(false) 181 srv.ec2srv.Quit() 182 srv.defaultVPC = nil 183 } 184 185 // localServerSuite contains tests that run against a fake EC2 server 186 // running within the test process itself. These tests can test things that 187 // would be unreasonably slow or expensive to test on a live Amazon server. 188 // It starts a new local ec2test server for each test. The server is 189 // accessed by using the "test" region, which is changed to point to the 190 // network address of the local server. 191 type localServerSuite struct { 192 coretesting.BaseSuite 193 jujutest.Tests 194 srv localServer 195 client *amzec2.EC2 196 } 197 198 func (t *localServerSuite) SetUpSuite(c *gc.C) { 199 t.BaseSuite.SetUpSuite(c) 200 t.Credential = cloud.NewCredential( 201 cloud.AccessKeyAuthType, 202 map[string]string{ 203 "access-key": "x", 204 "secret-key": "x", 205 }, 206 ) 207 208 // Upload arches that ec2 supports; add to this 209 // as ec2 coverage expands. 210 t.UploadArches = []string{arch.AMD64, arch.I386} 211 t.TestConfig = localConfigAttrs 212 imagetesting.PatchOfficialDataSources(&t.BaseSuite.CleanupSuite, "test:") 213 t.BaseSuite.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey) 214 t.BaseSuite.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 215 t.BaseSuite.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber) 216 t.BaseSuite.PatchValue(&arch.HostArch, func() string { return arch.AMD64 }) 217 t.BaseSuite.PatchValue(&series.HostSeries, func() string { return series.LatestLts() }) 218 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 219 t.srv.createRootDisks = true 220 t.srv.startServer(c) 221 // TODO(jam) I don't understand why we shouldn't do this. 222 // t.Tests embeds the sstesting.TestDataSuite, but if we call this 223 // SetUpSuite, then all of the tests fail because they go to access 224 // "test:/streams/..." and it isn't found 225 // t.Tests.SetUpSuite(c) 226 } 227 228 func (t *localServerSuite) TearDownSuite(c *gc.C) { 229 t.Tests.TearDownSuite(c) 230 t.BaseSuite.TearDownSuite(c) 231 } 232 233 func (t *localServerSuite) SetUpTest(c *gc.C) { 234 t.BaseSuite.SetUpTest(c) 235 t.srv.startServer(c) 236 region := t.srv.region() 237 t.CloudRegion = region.Name 238 t.CloudEndpoint = region.EC2Endpoint 239 t.client = t.srv.client() 240 restoreEC2Patching := patchEC2ForTesting(c, region) 241 t.AddCleanup(func(c *gc.C) { restoreEC2Patching() }) 242 t.Tests.SetUpTest(c) 243 } 244 245 func (t *localServerSuite) TearDownTest(c *gc.C) { 246 t.Tests.TearDownTest(c) 247 t.srv.stopServer(c) 248 t.BaseSuite.TearDownTest(c) 249 } 250 251 func (t *localServerSuite) prepareEnviron(c *gc.C) environs.NetworkingEnviron { 252 env := t.Prepare(c) 253 netenv, supported := environs.SupportsNetworking(env) 254 c.Assert(supported, jc.IsTrue) 255 return netenv 256 } 257 258 func (t *localServerSuite) TestPrepareForBootstrapWithInvalidVPCID(c *gc.C) { 259 badVPCIDConfig := coretesting.Attrs{"vpc-id": "bad"} 260 261 expectedError := `invalid EC2 provider config: vpc-id: "bad" is not a valid AWS VPC ID` 262 t.AssertPrepareFailsWithConfig(c, badVPCIDConfig, expectedError) 263 } 264 265 func (t *localServerSuite) TestPrepareForBootstrapWithUnknownVPCID(c *gc.C) { 266 unknownVPCIDConfig := coretesting.Attrs{"vpc-id": "vpc-unknown"} 267 268 expectedError := `Juju cannot use the given vpc-id for bootstrapping(.|\n)*Error details: VPC "vpc-unknown" not found` 269 err := t.AssertPrepareFailsWithConfig(c, unknownVPCIDConfig, expectedError) 270 c.Check(err, jc.Satisfies, ec2.IsVPCNotUsableError) 271 } 272 273 func (t *localServerSuite) TestPrepareForBootstrapWithNotRecommendedVPCID(c *gc.C) { 274 t.makeTestingDefaultVPCUnavailable(c) 275 notRecommendedVPCIDConfig := coretesting.Attrs{"vpc-id": t.srv.defaultVPC.Id} 276 277 expectedError := `The given vpc-id does not meet one or more(.|\n)*Error details: VPC has unexpected state "unavailable"` 278 err := t.AssertPrepareFailsWithConfig(c, notRecommendedVPCIDConfig, expectedError) 279 c.Check(err, jc.Satisfies, ec2.IsVPCNotRecommendedError) 280 } 281 282 func (t *localServerSuite) makeTestingDefaultVPCUnavailable(c *gc.C) { 283 // For simplicity, here the test server's default VPC is updated to change 284 // its state to unavailable, we just verify the behavior of a "not 285 // recommended VPC". 286 t.srv.defaultVPC.State = "unavailable" 287 err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC) 288 c.Assert(err, jc.ErrorIsNil) 289 } 290 291 func (t *localServerSuite) TestPrepareForBootstrapWithNotRecommendedButForcedVPCID(c *gc.C) { 292 t.makeTestingDefaultVPCUnavailable(c) 293 params := t.PrepareParams(c) 294 params.ModelConfig["vpc-id"] = t.srv.defaultVPC.Id 295 params.ModelConfig["vpc-id-force"] = true 296 297 t.prepareWithParamsAndBootstrapWithVPCID(c, params, t.srv.defaultVPC.Id) 298 } 299 300 func (t *localServerSuite) TestPrepareForBootstrapWithEmptyVPCID(c *gc.C) { 301 const emptyVPCID = "" 302 303 params := t.PrepareParams(c) 304 params.ModelConfig["vpc-id"] = emptyVPCID 305 306 t.prepareWithParamsAndBootstrapWithVPCID(c, params, emptyVPCID) 307 } 308 309 func (t *localServerSuite) prepareWithParamsAndBootstrapWithVPCID(c *gc.C, params bootstrap.PrepareParams, expectedVPCID string) { 310 env := t.PrepareWithParams(c, params) 311 unknownAttrs := env.Config().UnknownAttrs() 312 vpcID, ok := unknownAttrs["vpc-id"] 313 c.Check(vpcID, gc.Equals, expectedVPCID) 314 c.Check(ok, jc.IsTrue) 315 316 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 317 ControllerConfig: coretesting.FakeControllerConfig(), 318 AdminSecret: testing.AdminSecret, 319 CAPrivateKey: coretesting.CAKey, 320 }) 321 c.Assert(err, jc.ErrorIsNil) 322 } 323 324 func (t *localServerSuite) TestPrepareForBootstrapWithVPCIDNone(c *gc.C) { 325 params := t.PrepareParams(c) 326 params.ModelConfig["vpc-id"] = "none" 327 328 t.prepareWithParamsAndBootstrapWithVPCID(c, params, ec2.VPCIDNone) 329 } 330 331 func (t *localServerSuite) TestPrepareForBootstrapWithDefaultVPCID(c *gc.C) { 332 params := t.PrepareParams(c) 333 params.ModelConfig["vpc-id"] = t.srv.defaultVPC.Id 334 335 t.prepareWithParamsAndBootstrapWithVPCID(c, params, t.srv.defaultVPC.Id) 336 } 337 338 func (t *localServerSuite) TestSystemdBootstrapInstanceUserDataAndState(c *gc.C) { 339 env := t.Prepare(c) 340 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 341 ControllerConfig: coretesting.FakeControllerConfig(), 342 // TODO(redir): BBB: When we no longer support upstart based systems this can change to series.LatestLts() 343 BootstrapSeries: "xenial", 344 AdminSecret: testing.AdminSecret, 345 CAPrivateKey: coretesting.CAKey, 346 }) 347 c.Assert(err, jc.ErrorIsNil) 348 349 // check that ControllerInstances returns the id of the bootstrap machine. 350 instanceIds, err := env.ControllerInstances(t.ControllerUUID) 351 c.Assert(err, jc.ErrorIsNil) 352 c.Assert(instanceIds, gc.HasLen, 1) 353 354 insts, err := env.AllInstances() 355 c.Assert(err, jc.ErrorIsNil) 356 c.Assert(insts, gc.HasLen, 1) 357 c.Check(insts[0].Id(), gc.Equals, instanceIds[0]) 358 359 // check that the user data is configured to and the machine and 360 // provisioning agents. check that the user data is configured to only 361 // configure authorized SSH keys and set the log output; everything else 362 // happens after the machine is brought up. 363 inst := t.srv.ec2srv.Instance(string(insts[0].Id())) 364 c.Assert(inst, gc.NotNil) 365 addresses, err := insts[0].Addresses() 366 c.Assert(err, jc.ErrorIsNil) 367 c.Assert(addresses, gc.Not(gc.HasLen), 0) 368 userData, err := utils.Gunzip(inst.UserData) 369 c.Assert(err, jc.ErrorIsNil) 370 c.Assert(string(userData), jc.YAMLEquals, map[interface{}]interface{}{ 371 "output": map[interface{}]interface{}{ 372 "all": "| tee -a /var/log/cloud-init-output.log", 373 }, 374 "users": []interface{}{ 375 map[interface{}]interface{}{ 376 "name": "ubuntu", 377 "lock_passwd": true, 378 "groups": []interface{}{"adm", "audio", 379 "cdrom", "dialout", "dip", "floppy", 380 "netdev", "plugdev", "sudo", "video"}, 381 "shell": "/bin/bash", 382 "sudo": []interface{}{"ALL=(ALL) NOPASSWD:ALL"}, 383 "ssh-authorized-keys": splitAuthKeys(env.Config().AuthorizedKeys()), 384 }, 385 }, 386 "runcmd": []interface{}{ 387 "set -xe", 388 "install -D -m 644 /dev/null '/etc/systemd/system/juju-clean-shutdown.service'", 389 "printf '%s\\n' '\n[Unit]\nDescription=Stop all network interfaces on shutdown\nDefaultDependencies=false\nAfter=final.target\n\n[Service]\nType=oneshot\nExecStart=/sbin/ifdown -a -v --force\nStandardOutput=tty\nStandardError=tty\n\n[Install]\nWantedBy=final.target\n' > '/etc/systemd/system/juju-clean-shutdown.service'", "/bin/systemctl enable '/etc/systemd/system/juju-clean-shutdown.service'", 390 "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", 391 "printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'", 392 }, 393 }) 394 395 // check that a new instance will be started with a machine agent 396 inst1, hc := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 397 c.Check(*hc.Arch, gc.Equals, "amd64") 398 c.Check(*hc.Mem, gc.Equals, uint64(3.75*1024)) 399 c.Check(*hc.CpuCores, gc.Equals, uint64(1)) 400 inst = t.srv.ec2srv.Instance(string(inst1.Id())) 401 c.Assert(inst, gc.NotNil) 402 userData, err = utils.Gunzip(inst.UserData) 403 c.Assert(err, jc.ErrorIsNil) 404 c.Logf("second instance: UserData: %q", userData) 405 var userDataMap map[interface{}]interface{} 406 err = goyaml.Unmarshal(userData, &userDataMap) 407 c.Assert(err, jc.ErrorIsNil) 408 CheckPackage(c, userDataMap, "curl", true) 409 CheckPackage(c, userDataMap, "mongodb-server", false) 410 CheckScripts(c, userDataMap, "jujud bootstrap-state", false) 411 CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true) 412 // TODO check for provisioning agent 413 414 err = env.Destroy() 415 c.Assert(err, jc.ErrorIsNil) 416 417 _, err = env.ControllerInstances(t.ControllerUUID) 418 c.Assert(err, gc.Equals, environs.ErrNotBootstrapped) 419 } 420 421 // TestUpstartBoostrapInstanceUserDataAndState is a test for legacy systems 422 // using upstart which will be around until trusty is no longer supported. 423 // TODO(redir): BBB: remove when trusty is no longer supported 424 func (t *localServerSuite) TestUpstartBootstrapInstanceUserDataAndState(c *gc.C) { 425 env := t.Prepare(c) 426 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 427 ControllerConfig: coretesting.FakeControllerConfig(), 428 BootstrapSeries: "trusty", 429 AdminSecret: testing.AdminSecret, 430 CAPrivateKey: coretesting.CAKey, 431 }) 432 c.Assert(err, jc.ErrorIsNil) 433 434 // check that ControllerInstances returns the id of the bootstrap machine. 435 instanceIds, err := env.ControllerInstances(t.ControllerUUID) 436 c.Assert(err, jc.ErrorIsNil) 437 c.Assert(instanceIds, gc.HasLen, 1) 438 439 insts, err := env.AllInstances() 440 c.Assert(err, jc.ErrorIsNil) 441 c.Assert(insts, gc.HasLen, 1) 442 c.Check(insts[0].Id(), gc.Equals, instanceIds[0]) 443 444 // check that the user data is configured to and the machine and 445 // provisioning agents. check that the user data is configured to only 446 // configure authorized SSH keys and set the log output; everything else 447 // happens after the machine is brought up. 448 inst := t.srv.ec2srv.Instance(string(insts[0].Id())) 449 c.Assert(inst, gc.NotNil) 450 addresses, err := insts[0].Addresses() 451 c.Assert(err, jc.ErrorIsNil) 452 c.Assert(addresses, gc.Not(gc.HasLen), 0) 453 userData, err := utils.Gunzip(inst.UserData) 454 c.Assert(err, jc.ErrorIsNil) 455 c.Assert(string(userData), jc.YAMLEquals, map[interface{}]interface{}{ 456 "output": map[interface{}]interface{}{ 457 "all": "| tee -a /var/log/cloud-init-output.log", 458 }, 459 "users": []interface{}{ 460 map[interface{}]interface{}{ 461 "name": "ubuntu", 462 "lock_passwd": true, 463 "groups": []interface{}{"adm", "audio", 464 "cdrom", "dialout", "dip", "floppy", 465 "netdev", "plugdev", "sudo", "video"}, 466 "shell": "/bin/bash", 467 "sudo": []interface{}{"ALL=(ALL) NOPASSWD:ALL"}, 468 "ssh-authorized-keys": splitAuthKeys(env.Config().AuthorizedKeys()), 469 }, 470 }, 471 "runcmd": []interface{}{ 472 "set -xe", 473 "install -D -m 644 /dev/null '/etc/init/juju-clean-shutdown.conf'", 474 "printf '%s\\n' '\nauthor \"Juju Team <juju@lists.ubuntu.com>\"\ndescription \"Stop all network interfaces on shutdown\"\nstart on runlevel [016]\ntask\nconsole output\n\nexec /sbin/ifdown -a -v --force\n' > '/etc/init/juju-clean-shutdown.conf'", 475 "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", 476 "printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'", 477 }, 478 }) 479 480 // check that a new instance will be started with a machine agent 481 inst1, hc := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 482 c.Check(*hc.Arch, gc.Equals, "amd64") 483 c.Check(*hc.Mem, gc.Equals, uint64(3.75*1024)) 484 c.Check(*hc.CpuCores, gc.Equals, uint64(1)) 485 inst = t.srv.ec2srv.Instance(string(inst1.Id())) 486 c.Assert(inst, gc.NotNil) 487 userData, err = utils.Gunzip(inst.UserData) 488 c.Assert(err, jc.ErrorIsNil) 489 c.Logf("second instance: UserData: %q", userData) 490 var userDataMap map[interface{}]interface{} 491 err = goyaml.Unmarshal(userData, &userDataMap) 492 c.Assert(err, jc.ErrorIsNil) 493 CheckPackage(c, userDataMap, "curl", true) 494 CheckPackage(c, userDataMap, "mongodb-server", false) 495 CheckScripts(c, userDataMap, "jujud bootstrap-state", false) 496 CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true) 497 // TODO check for provisioning agent 498 499 err = env.Destroy() 500 c.Assert(err, jc.ErrorIsNil) 501 502 _, err = env.ControllerInstances(t.ControllerUUID) 503 c.Assert(err, gc.Equals, environs.ErrNotBootstrapped) 504 } 505 506 func (t *localServerSuite) TestTerminateInstancesIgnoresNotFound(c *gc.C) { 507 env := t.Prepare(c) 508 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 509 ControllerConfig: coretesting.FakeControllerConfig(), 510 AdminSecret: testing.AdminSecret, 511 CAPrivateKey: coretesting.CAKey, 512 }) 513 c.Assert(err, jc.ErrorIsNil) 514 515 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 516 insts, err := env.AllInstances() 517 c.Assert(err, jc.ErrorIsNil) 518 idsToStop := make([]instance.Id, len(insts)+1) 519 for i, one := range insts { 520 idsToStop[i] = one.Id() 521 } 522 idsToStop[len(insts)] = instance.Id("i-am-not-found") 523 524 err = env.StopInstances(idsToStop...) 525 // NotFound should be ignored 526 c.Assert(err, jc.ErrorIsNil) 527 } 528 529 func (t *localServerSuite) TestDestroyErr(c *gc.C) { 530 env := t.prepareAndBootstrap(c) 531 532 msg := "terminate instances error" 533 t.BaseSuite.PatchValue(ec2.TerminateInstancesById, func(ec2inst *amzec2.EC2, ids ...instance.Id) (*amzec2.TerminateInstancesResp, error) { 534 return nil, errors.New(msg) 535 }) 536 537 err := env.Destroy() 538 c.Assert(errors.Cause(err).Error(), jc.Contains, msg) 539 } 540 541 func (t *localServerSuite) TestGetTerminatedInstances(c *gc.C) { 542 env := t.Prepare(c) 543 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 544 ControllerConfig: coretesting.FakeControllerConfig(), 545 AdminSecret: testing.AdminSecret, 546 CAPrivateKey: coretesting.CAKey, 547 }) 548 c.Assert(err, jc.ErrorIsNil) 549 550 // create another instance to terminate 551 inst1, _ := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 552 inst := t.srv.ec2srv.Instance(string(inst1.Id())) 553 c.Assert(inst, gc.NotNil) 554 t.BaseSuite.PatchValue(ec2.TerminateInstancesById, func(ec2inst *amzec2.EC2, ids ...instance.Id) (*amzec2.TerminateInstancesResp, error) { 555 // Terminate the one destined for termination and 556 // err out to ensure that one instance will be terminated, the other - not. 557 _, err = ec2inst.TerminateInstances([]string{string(inst1.Id())}) 558 c.Assert(err, jc.ErrorIsNil) 559 return nil, errors.New("terminate instances error") 560 }) 561 err = env.Destroy() 562 c.Assert(err, gc.NotNil) 563 564 terminated, err := ec2.TerminatedInstances(env) 565 c.Assert(err, jc.ErrorIsNil) 566 c.Assert(terminated, gc.HasLen, 1) 567 c.Assert(terminated[0].Id(), jc.DeepEquals, inst1.Id()) 568 } 569 570 func (t *localServerSuite) TestInstanceSecurityGroupsWitheInstanceStatusFilter(c *gc.C) { 571 env := t.prepareAndBootstrap(c) 572 573 insts, err := env.AllInstances() 574 c.Assert(err, jc.ErrorIsNil) 575 ids := make([]instance.Id, len(insts)) 576 for i, one := range insts { 577 ids[i] = one.Id() 578 } 579 580 groupsNoInstanceFilter, err := ec2.InstanceSecurityGroups(env, ids) 581 c.Assert(err, jc.ErrorIsNil) 582 // get all security groups for test instances 583 c.Assert(groupsNoInstanceFilter, gc.HasLen, 2) 584 585 groupsFilteredForTerminatedInstances, err := ec2.InstanceSecurityGroups(env, ids, "shutting-down", "terminated") 586 c.Assert(err, jc.ErrorIsNil) 587 // get all security groups for terminated test instances 588 c.Assert(groupsFilteredForTerminatedInstances, gc.HasLen, 0) 589 } 590 591 func (t *localServerSuite) TestDestroyControllerModelDeleteSecurityGroupInsistentlyError(c *gc.C) { 592 env := t.prepareAndBootstrap(c) 593 msg := "destroy security group error" 594 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, func( 595 ec2.SecurityGroupCleaner, amzec2.SecurityGroup, clock.Clock, 596 ) error { 597 return errors.New(msg) 598 }) 599 err := env.DestroyController(t.ControllerUUID) 600 c.Assert(err, gc.ErrorMatches, "destroying managed environs: cannot delete security group .*: "+msg) 601 } 602 603 func (t *localServerSuite) TestDestroyHostedModelDeleteSecurityGroupInsistentlyError(c *gc.C) { 604 env := t.prepareAndBootstrap(c) 605 hostedEnv, err := environs.New(environs.OpenParams{ 606 Cloud: t.CloudSpec(), 607 Config: env.Config(), 608 }) 609 c.Assert(err, jc.ErrorIsNil) 610 611 msg := "destroy security group error" 612 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, func( 613 ec2.SecurityGroupCleaner, amzec2.SecurityGroup, clock.Clock, 614 ) error { 615 return errors.New(msg) 616 }) 617 err = hostedEnv.Destroy() 618 c.Assert(err, gc.ErrorMatches, "cannot delete environment security groups: cannot delete default security group: "+msg) 619 } 620 621 func (t *localServerSuite) TestDestroyControllerDestroysHostedModelResources(c *gc.C) { 622 controllerEnv := t.prepareAndBootstrap(c) 623 624 // Create a hosted model environment with an instance and a volume. 625 hostedModelUUID := "7e386e08-cba7-44a4-a76e-7c1633584210" 626 t.srv.ec2srv.SetInitialInstanceState(ec2test.Running) 627 cfg, err := controllerEnv.Config().Apply(map[string]interface{}{ 628 "uuid": hostedModelUUID, 629 "firewall-mode": "global", 630 }) 631 c.Assert(err, jc.ErrorIsNil) 632 env, err := environs.New(environs.OpenParams{ 633 Cloud: t.CloudSpec(), 634 Config: cfg, 635 }) 636 c.Assert(err, jc.ErrorIsNil) 637 inst, _ := testing.AssertStartInstance(c, env, t.ControllerUUID, "0") 638 c.Assert(err, jc.ErrorIsNil) 639 ebsProvider, err := env.StorageProvider(ec2.EBS_ProviderType) 640 c.Assert(err, jc.ErrorIsNil) 641 vs, err := ebsProvider.VolumeSource(nil) 642 c.Assert(err, jc.ErrorIsNil) 643 volumeResults, err := vs.CreateVolumes([]storage.VolumeParams{{ 644 Tag: names.NewVolumeTag("0"), 645 Size: 1024, 646 Provider: ec2.EBS_ProviderType, 647 ResourceTags: map[string]string{ 648 tags.JujuController: t.ControllerUUID, 649 tags.JujuModel: hostedModelUUID, 650 }, 651 Attachment: &storage.VolumeAttachmentParams{ 652 AttachmentParams: storage.AttachmentParams{ 653 InstanceId: inst.Id(), 654 }, 655 }, 656 }}) 657 c.Assert(err, jc.ErrorIsNil) 658 c.Assert(volumeResults, gc.HasLen, 1) 659 c.Assert(volumeResults[0].Error, jc.ErrorIsNil) 660 661 assertInstances := func(expect ...instance.Id) { 662 insts, err := env.AllInstances() 663 c.Assert(err, jc.ErrorIsNil) 664 ids := make([]instance.Id, len(insts)) 665 for i, inst := range insts { 666 ids[i] = inst.Id() 667 } 668 c.Assert(ids, jc.SameContents, expect) 669 } 670 assertVolumes := func(expect ...string) { 671 volIds, err := vs.ListVolumes() 672 c.Assert(err, jc.ErrorIsNil) 673 c.Assert(volIds, jc.SameContents, expect) 674 } 675 assertGroups := func(expect ...string) { 676 groupsResp, err := t.client.SecurityGroups(nil, nil) 677 c.Assert(err, jc.ErrorIsNil) 678 names := make([]string, len(groupsResp.Groups)) 679 for i, group := range groupsResp.Groups { 680 names[i] = group.Name 681 } 682 c.Assert(names, jc.SameContents, expect) 683 } 684 685 assertInstances(inst.Id()) 686 assertVolumes(volumeResults[0].Volume.VolumeId) 687 assertGroups( 688 "default", 689 "juju-"+controllerEnv.Config().UUID(), 690 "juju-"+controllerEnv.Config().UUID()+"-0", 691 "juju-"+hostedModelUUID, 692 "juju-"+hostedModelUUID+"-global", 693 ) 694 695 // Destroy the controller resources. This should destroy the hosted 696 // environment too. 697 err = controllerEnv.DestroyController(t.ControllerUUID) 698 c.Assert(err, jc.ErrorIsNil) 699 700 assertInstances() 701 assertVolumes() 702 assertGroups("default") 703 } 704 705 // splitAuthKeys splits the given authorized keys 706 // into the form expected to be found in the 707 // user data. 708 func splitAuthKeys(keys string) []interface{} { 709 slines := strings.FieldsFunc(keys, func(r rune) bool { 710 return r == '\n' 711 }) 712 var lines []interface{} 713 for _, line := range slines { 714 lines = append(lines, ssh.EnsureJujuComment(strings.TrimSpace(line))) 715 } 716 return lines 717 } 718 719 func (t *localServerSuite) TestInstanceStatus(c *gc.C) { 720 env := t.Prepare(c) 721 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 722 ControllerConfig: coretesting.FakeControllerConfig(), 723 AdminSecret: testing.AdminSecret, 724 CAPrivateKey: coretesting.CAKey, 725 }) 726 c.Assert(err, jc.ErrorIsNil) 727 t.srv.ec2srv.SetInitialInstanceState(ec2test.Terminated) 728 inst, _ := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 729 c.Assert(err, jc.ErrorIsNil) 730 c.Assert(inst.Status().Message, gc.Equals, "terminated") 731 } 732 733 func (t *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { 734 env := t.prepareAndBootstrap(c) 735 _, hc := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 736 c.Check(*hc.Arch, gc.Equals, "amd64") 737 c.Check(*hc.Mem, gc.Equals, uint64(3.75*1024)) 738 c.Check(*hc.CpuCores, gc.Equals, uint64(1)) 739 } 740 741 func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) { 742 inst, err := t.testStartInstanceAvailZone(c, "test-available") 743 c.Assert(err, jc.ErrorIsNil) 744 c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "test-available") 745 } 746 747 func (t *localServerSuite) TestStartInstanceAvailZoneImpaired(c *gc.C) { 748 _, err := t.testStartInstanceAvailZone(c, "test-impaired") 749 c.Assert(err, gc.ErrorMatches, `availability zone "test-impaired" is impaired`) 750 } 751 752 func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) { 753 _, err := t.testStartInstanceAvailZone(c, "test-unknown") 754 c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`) 755 } 756 757 func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instance.Instance, error) { 758 env := t.prepareAndBootstrap(c) 759 760 params := environs.StartInstanceParams{ControllerUUID: t.ControllerUUID, Placement: "zone=" + zone} 761 result, err := testing.StartInstanceWithParams(env, "1", params) 762 if err != nil { 763 return nil, err 764 } 765 return result.Instance, nil 766 } 767 768 func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) { 769 var resultZones []amzec2.AvailabilityZoneInfo 770 var resultErr error 771 t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) { 772 resp := &amzec2.AvailabilityZonesResp{ 773 Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...), 774 } 775 return resp, resultErr 776 }) 777 env := t.Prepare(c).(common.ZonedEnviron) 778 779 resultErr = fmt.Errorf("failed to get availability zones") 780 zones, err := env.AvailabilityZones() 781 c.Assert(err, gc.Equals, resultErr) 782 c.Assert(zones, gc.IsNil) 783 784 resultErr = nil 785 resultZones = make([]amzec2.AvailabilityZoneInfo, 1) 786 resultZones[0].Name = "whatever" 787 zones, err = env.AvailabilityZones() 788 c.Assert(err, jc.ErrorIsNil) 789 c.Assert(zones, gc.HasLen, 1) 790 c.Assert(zones[0].Name(), gc.Equals, "whatever") 791 792 // A successful result is cached, currently for the lifetime 793 // of the Environ. This will change if/when we have long-lived 794 // Environs to cut down repeated IaaS requests. 795 resultErr = fmt.Errorf("failed to get availability zones") 796 resultZones[0].Name = "andever" 797 zones, err = env.AvailabilityZones() 798 c.Assert(err, jc.ErrorIsNil) 799 c.Assert(zones, gc.HasLen, 1) 800 c.Assert(zones[0].Name(), gc.Equals, "whatever") 801 } 802 803 func (t *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) { 804 var resultZones []amzec2.AvailabilityZoneInfo 805 t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) { 806 resp := &amzec2.AvailabilityZonesResp{ 807 Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...), 808 } 809 return resp, nil 810 }) 811 env := t.Prepare(c).(common.ZonedEnviron) 812 resultZones = make([]amzec2.AvailabilityZoneInfo, 2) 813 resultZones[0].Name = "az1" 814 resultZones[1].Name = "az2" 815 resultZones[0].State = "available" 816 resultZones[1].State = "impaired" 817 zones, err := env.AvailabilityZones() 818 c.Assert(err, jc.ErrorIsNil) 819 c.Assert(zones, gc.HasLen, 2) 820 c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name) 821 c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name) 822 c.Assert(zones[0].Available(), jc.IsTrue) 823 c.Assert(zones[1].Available(), jc.IsFalse) 824 } 825 826 type mockAvailabilityZoneAllocations struct { 827 group []instance.Id // input param 828 result []common.AvailabilityZoneInstances 829 err error 830 } 831 832 func (t *mockAvailabilityZoneAllocations) AvailabilityZoneAllocations( 833 e common.ZonedEnviron, group []instance.Id, 834 ) ([]common.AvailabilityZoneInstances, error) { 835 t.group = group 836 return t.result, t.err 837 } 838 839 func (t *localServerSuite) TestStartInstanceDistributionParams(c *gc.C) { 840 env := t.prepareAndBootstrap(c) 841 842 mock := mockAvailabilityZoneAllocations{ 843 result: []common.AvailabilityZoneInstances{{ZoneName: "az1"}}, 844 } 845 t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations) 846 847 // no distribution group specified 848 testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 849 c.Assert(mock.group, gc.HasLen, 0) 850 851 // distribution group specified: ensure it's passed through to AvailabilityZone. 852 expectedInstances := []instance.Id{"i-0", "i-1"} 853 params := environs.StartInstanceParams{ 854 ControllerUUID: t.ControllerUUID, 855 DistributionGroup: func() ([]instance.Id, error) { 856 return expectedInstances, nil 857 }, 858 } 859 _, err := testing.StartInstanceWithParams(env, "1", params) 860 c.Assert(err, jc.ErrorIsNil) 861 c.Assert(mock.group, gc.DeepEquals, expectedInstances) 862 } 863 864 func (t *localServerSuite) TestStartInstanceDistributionErrors(c *gc.C) { 865 env := t.prepareAndBootstrap(c) 866 867 mock := mockAvailabilityZoneAllocations{ 868 err: fmt.Errorf("AvailabilityZoneAllocations failed"), 869 } 870 t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations) 871 _, _, _, err := testing.StartInstance(env, t.ControllerUUID, "1") 872 c.Assert(errors.Cause(err), gc.Equals, mock.err) 873 874 mock.err = nil 875 dgErr := fmt.Errorf("DistributionGroup failed") 876 params := environs.StartInstanceParams{ 877 ControllerUUID: t.ControllerUUID, 878 DistributionGroup: func() ([]instance.Id, error) { 879 return nil, dgErr 880 }, 881 } 882 _, err = testing.StartInstanceWithParams(env, "1", params) 883 c.Assert(errors.Cause(err), gc.Equals, dgErr) 884 } 885 886 func (t *localServerSuite) TestStartInstanceDistribution(c *gc.C) { 887 env := t.prepareAndBootstrap(c) 888 889 // test-available is the only available AZ, so AvailabilityZoneAllocations 890 // is guaranteed to return that. 891 inst, _ := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 892 c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "test-available") 893 } 894 895 var azConstrainedErr = &amzec2.Error{ 896 Code: "Unsupported", 897 Message: "The requested Availability Zone is currently constrained etc.", 898 } 899 900 var azVolumeTypeNotAvailableInZoneErr = &amzec2.Error{ 901 Code: "VolumeTypeNotAvailableInZone", 902 Message: "blah blah", 903 } 904 905 var azInsufficientInstanceCapacityErr = &amzec2.Error{ 906 Code: "InsufficientInstanceCapacity", 907 Message: "We currently do not have sufficient m1.small capacity in the " + 908 "Availability Zone you requested (us-east-1d). Our system will " + 909 "be working on provisioning additional capacity. You can currently get m1.small " + 910 "capacity by not specifying an Availability Zone in your request or choosing " + 911 "us-east-1c, us-east-1a.", 912 } 913 914 var azNoDefaultSubnetErr = &amzec2.Error{ 915 Code: "InvalidInput", 916 Message: "No default subnet for availability zone: ''us-east-1e''.", 917 } 918 919 func (t *localServerSuite) TestStartInstanceAvailZoneAllConstrained(c *gc.C) { 920 t.testStartInstanceAvailZoneAllConstrained(c, azConstrainedErr) 921 } 922 923 func (t *localServerSuite) TestStartInstanceVolumeTypeNotAvailable(c *gc.C) { 924 t.testStartInstanceAvailZoneAllConstrained(c, azVolumeTypeNotAvailableInZoneErr) 925 } 926 927 func (t *localServerSuite) TestStartInstanceAvailZoneAllInsufficientInstanceCapacity(c *gc.C) { 928 t.testStartInstanceAvailZoneAllConstrained(c, azInsufficientInstanceCapacityErr) 929 } 930 931 func (t *localServerSuite) TestStartInstanceAvailZoneAllNoDefaultSubnet(c *gc.C) { 932 t.testStartInstanceAvailZoneAllConstrained(c, azNoDefaultSubnetErr) 933 } 934 935 func (t *localServerSuite) testStartInstanceAvailZoneAllConstrained(c *gc.C, runInstancesError *amzec2.Error) { 936 env := t.prepareAndBootstrap(c) 937 938 mock := mockAvailabilityZoneAllocations{ 939 result: []common.AvailabilityZoneInstances{ 940 {ZoneName: "az1"}, {ZoneName: "az2"}, 941 }, 942 } 943 t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations) 944 945 var azArgs []string 946 t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ri *amzec2.RunInstances) (*amzec2.RunInstancesResp, error) { 947 azArgs = append(azArgs, ri.AvailZone) 948 return nil, runInstancesError 949 }) 950 _, _, _, err := testing.StartInstance(env, t.ControllerUUID, "1") 951 c.Assert(err, gc.ErrorMatches, fmt.Sprintf( 952 "cannot run instances: %s \\(%s\\)", 953 regexp.QuoteMeta(runInstancesError.Message), 954 runInstancesError.Code, 955 )) 956 c.Assert(azArgs, gc.DeepEquals, []string{"az1", "az2"}) 957 } 958 959 // addTestingSubnets adds a testing default VPC with 3 subnets in the EC2 test 960 // server: 2 of the subnets are in the "test-available" AZ, the remaining - in 961 // "test-unavailable". Returns a slice with the IDs of the created subnets. 962 func (t *localServerSuite) addTestingSubnets(c *gc.C) []network.Id { 963 vpc := t.srv.ec2srv.AddVPC(amzec2.VPC{ 964 CIDRBlock: "0.1.0.0/16", 965 IsDefault: true, 966 }) 967 results := make([]network.Id, 3) 968 sub1, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{ 969 VPCId: vpc.Id, 970 CIDRBlock: "0.1.2.0/24", 971 AvailZone: "test-available", 972 DefaultForAZ: true, 973 }) 974 c.Assert(err, jc.ErrorIsNil) 975 results[0] = network.Id(sub1.Id) 976 sub2, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{ 977 VPCId: vpc.Id, 978 CIDRBlock: "0.1.3.0/24", 979 AvailZone: "test-available", 980 }) 981 c.Assert(err, jc.ErrorIsNil) 982 results[1] = network.Id(sub2.Id) 983 sub3, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{ 984 VPCId: vpc.Id, 985 CIDRBlock: "0.1.4.0/24", 986 AvailZone: "test-unavailable", 987 DefaultForAZ: true, 988 }) 989 c.Assert(err, jc.ErrorIsNil) 990 results[2] = network.Id(sub3.Id) 991 return results 992 } 993 994 func (t *localServerSuite) prepareAndBootstrap(c *gc.C) environs.Environ { 995 env := t.Prepare(c) 996 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 997 ControllerConfig: coretesting.FakeControllerConfig(), 998 AdminSecret: testing.AdminSecret, 999 CAPrivateKey: coretesting.CAKey, 1000 }) 1001 c.Assert(err, jc.ErrorIsNil) 1002 return env 1003 } 1004 1005 func (t *localServerSuite) TestSpaceConstraintsSpaceNotInPlacementZone(c *gc.C) { 1006 c.Skip("temporarily disabled") 1007 env := t.prepareAndBootstrap(c) 1008 subIDs := t.addTestingSubnets(c) 1009 1010 // Expect an error because zone test-available isn't in SubnetsToZones 1011 params := environs.StartInstanceParams{ 1012 ControllerUUID: t.ControllerUUID, 1013 Placement: "zone=test-available", 1014 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1015 SubnetsToZones: map[network.Id][]string{ 1016 subIDs[0]: []string{"zone2"}, 1017 subIDs[1]: []string{"zone3"}, 1018 subIDs[2]: []string{"zone4"}, 1019 }, 1020 } 1021 _, err := testing.StartInstanceWithParams(env, "1", params) 1022 c.Assert(err, gc.ErrorMatches, `unable to resolve constraints: space and/or subnet unavailable in zones \[test-available\]`) 1023 } 1024 1025 func (t *localServerSuite) TestSpaceConstraintsSpaceInPlacementZone(c *gc.C) { 1026 env := t.prepareAndBootstrap(c) 1027 subIDs := t.addTestingSubnets(c) 1028 1029 // Should work - test-available is in SubnetsToZones and in myspace. 1030 params := environs.StartInstanceParams{ 1031 ControllerUUID: t.ControllerUUID, 1032 Placement: "zone=test-available", 1033 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1034 SubnetsToZones: map[network.Id][]string{ 1035 subIDs[0]: []string{"test-available"}, 1036 subIDs[1]: []string{"zone3"}, 1037 }, 1038 } 1039 _, err := testing.StartInstanceWithParams(env, "1", params) 1040 c.Assert(err, jc.ErrorIsNil) 1041 } 1042 1043 func (t *localServerSuite) TestSpaceConstraintsNoPlacement(c *gc.C) { 1044 env := t.prepareAndBootstrap(c) 1045 subIDs := t.addTestingSubnets(c) 1046 1047 // Shoule work because zone is not specified so we can resolve the constraints 1048 params := environs.StartInstanceParams{ 1049 ControllerUUID: t.ControllerUUID, 1050 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1051 SubnetsToZones: map[network.Id][]string{ 1052 subIDs[0]: []string{"test-available"}, 1053 subIDs[1]: []string{"zone3"}, 1054 }, 1055 } 1056 _, err := testing.StartInstanceWithParams(env, "1", params) 1057 c.Assert(err, jc.ErrorIsNil) 1058 } 1059 1060 func (t *localServerSuite) TestSpaceConstraintsNoAvailableSubnets(c *gc.C) { 1061 c.Skip("temporarily disabled") 1062 1063 env := t.prepareAndBootstrap(c) 1064 subIDs := t.addTestingSubnets(c) 1065 1066 // We requested a space, but there are no subnets in SubnetsToZones, so we can't resolve 1067 // the constraints 1068 params := environs.StartInstanceParams{ 1069 ControllerUUID: t.ControllerUUID, 1070 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1071 SubnetsToZones: map[network.Id][]string{ 1072 subIDs[0]: []string{""}, 1073 }, 1074 } 1075 _, err := testing.StartInstanceWithParams(env, "1", params) 1076 c.Assert(err, gc.ErrorMatches, `unable to resolve constraints: space and/or subnet unavailable in zones \[test-available\]`) 1077 } 1078 1079 func (t *localServerSuite) TestStartInstanceAvailZoneOneConstrained(c *gc.C) { 1080 t.testStartInstanceAvailZoneOneConstrained(c, azConstrainedErr) 1081 } 1082 1083 func (t *localServerSuite) TestStartInstanceAvailZoneOneInsufficientInstanceCapacity(c *gc.C) { 1084 t.testStartInstanceAvailZoneOneConstrained(c, azInsufficientInstanceCapacityErr) 1085 } 1086 1087 func (t *localServerSuite) TestStartInstanceAvailZoneOneNoDefaultSubnetErr(c *gc.C) { 1088 t.testStartInstanceAvailZoneOneConstrained(c, azNoDefaultSubnetErr) 1089 } 1090 1091 func (t *localServerSuite) testStartInstanceAvailZoneOneConstrained(c *gc.C, runInstancesError *amzec2.Error) { 1092 env := t.prepareAndBootstrap(c) 1093 1094 mock := mockAvailabilityZoneAllocations{ 1095 result: []common.AvailabilityZoneInstances{ 1096 {ZoneName: "az1"}, {ZoneName: "az2"}, 1097 }, 1098 } 1099 t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations) 1100 1101 // The first call to RunInstances fails with an error indicating the AZ 1102 // is constrained. The second attempt succeeds, and so allocates to az2. 1103 var azArgs []string 1104 realRunInstances := *ec2.RunInstances 1105 t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ri *amzec2.RunInstances) (*amzec2.RunInstancesResp, error) { 1106 azArgs = append(azArgs, ri.AvailZone) 1107 if len(azArgs) == 1 { 1108 return nil, runInstancesError 1109 } 1110 return realRunInstances(e, ri) 1111 }) 1112 inst, hwc := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 1113 c.Assert(azArgs, gc.DeepEquals, []string{"az1", "az2"}) 1114 c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "az2") 1115 c.Check(*hwc.AvailabilityZone, gc.Equals, "az2") 1116 } 1117 1118 func (t *localServerSuite) TestAddresses(c *gc.C) { 1119 env := t.prepareAndBootstrap(c) 1120 inst, _ := testing.AssertStartInstance(c, env, t.ControllerUUID, "1") 1121 addrs, err := inst.Addresses() 1122 c.Assert(err, jc.ErrorIsNil) 1123 // Expected values use Address type but really contain a regexp for 1124 // the value rather than a valid ip or hostname. 1125 expected := []network.Address{{ 1126 Value: "8.0.0.*", 1127 Type: network.IPv4Address, 1128 Scope: network.ScopePublic, 1129 }, { 1130 Value: "127.0.0.*", 1131 Type: network.IPv4Address, 1132 Scope: network.ScopeCloudLocal, 1133 }} 1134 c.Assert(addrs, gc.HasLen, len(expected)) 1135 for i, addr := range addrs { 1136 c.Check(addr.Value, gc.Matches, expected[i].Value) 1137 c.Check(addr.Type, gc.Equals, expected[i].Type) 1138 c.Check(addr.Scope, gc.Equals, expected[i].Scope) 1139 } 1140 } 1141 1142 func (t *localServerSuite) TestConstraintsValidatorUnsupported(c *gc.C) { 1143 env := t.Prepare(c) 1144 validator, err := env.ConstraintsValidator() 1145 c.Assert(err, jc.ErrorIsNil) 1146 cons := constraints.MustParse("arch=amd64 tags=foo virt-type=kvm") 1147 unsupported, err := validator.Validate(cons) 1148 c.Assert(err, jc.ErrorIsNil) 1149 c.Assert(unsupported, jc.SameContents, []string{"tags", "virt-type"}) 1150 } 1151 1152 func (t *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) { 1153 env := t.Prepare(c) 1154 validator, err := env.ConstraintsValidator() 1155 c.Assert(err, jc.ErrorIsNil) 1156 cons := constraints.MustParse("instance-type=foo") 1157 _, err = validator.Validate(cons) 1158 c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*") 1159 } 1160 1161 func (t *localServerSuite) TestConstraintsValidatorVocabNoDefaultOrSpecifiedVPC(c *gc.C) { 1162 t.srv.defaultVPC.IsDefault = false 1163 err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC) 1164 c.Assert(err, jc.ErrorIsNil) 1165 1166 env := t.Prepare(c) 1167 assertVPCInstanceTypeNotAvailable(c, env) 1168 } 1169 1170 func (t *localServerSuite) TestConstraintsValidatorVocabDefaultVPC(c *gc.C) { 1171 env := t.Prepare(c) 1172 assertVPCInstanceTypeAvailable(c, env) 1173 } 1174 1175 func (t *localServerSuite) TestConstraintsValidatorVocabSpecifiedVPC(c *gc.C) { 1176 t.srv.defaultVPC.IsDefault = false 1177 err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC) 1178 c.Assert(err, jc.ErrorIsNil) 1179 1180 t.TestConfig["vpc-id"] = t.srv.defaultVPC.Id 1181 defer delete(t.TestConfig, "vpc-id") 1182 1183 env := t.Prepare(c) 1184 assertVPCInstanceTypeAvailable(c, env) 1185 } 1186 1187 func assertVPCInstanceTypeAvailable(c *gc.C, env environs.Environ) { 1188 validator, err := env.ConstraintsValidator() 1189 c.Assert(err, jc.ErrorIsNil) 1190 _, err = validator.Validate(constraints.MustParse("instance-type=t2.medium")) 1191 c.Assert(err, jc.ErrorIsNil) 1192 } 1193 1194 func assertVPCInstanceTypeNotAvailable(c *gc.C, env environs.Environ) { 1195 validator, err := env.ConstraintsValidator() 1196 c.Assert(err, jc.ErrorIsNil) 1197 _, err = validator.Validate(constraints.MustParse("instance-type=t2.medium")) 1198 c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=t2.medium\n.*") 1199 } 1200 1201 func (t *localServerSuite) TestConstraintsMerge(c *gc.C) { 1202 env := t.Prepare(c) 1203 validator, err := env.ConstraintsValidator() 1204 c.Assert(err, jc.ErrorIsNil) 1205 consA := constraints.MustParse("arch=amd64 mem=1G cpu-power=10 cores=2 tags=bar") 1206 consB := constraints.MustParse("arch=i386 instance-type=m1.small") 1207 cons, err := validator.Merge(consA, consB) 1208 c.Assert(err, jc.ErrorIsNil) 1209 c.Assert(cons, gc.DeepEquals, constraints.MustParse("arch=i386 instance-type=m1.small tags=bar")) 1210 } 1211 1212 func (t *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) { 1213 env := t.Prepare(c) 1214 cons := constraints.MustParse("instance-type=m1.small root-disk=1G") 1215 placement := "" 1216 err := env.PrecheckInstance(series.LatestLts(), cons, placement) 1217 c.Assert(err, jc.ErrorIsNil) 1218 } 1219 1220 func (t *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) { 1221 env := t.Prepare(c) 1222 cons := constraints.MustParse("instance-type=m1.invalid") 1223 placement := "" 1224 err := env.PrecheckInstance(series.LatestLts(), cons, placement) 1225 c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "m1.invalid" specified`) 1226 } 1227 1228 func (t *localServerSuite) TestPrecheckInstanceUnsupportedArch(c *gc.C) { 1229 env := t.Prepare(c) 1230 cons := constraints.MustParse("instance-type=cc1.4xlarge arch=i386") 1231 placement := "" 1232 err := env.PrecheckInstance(series.LatestLts(), cons, placement) 1233 c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "cc1.4xlarge" and arch "i386" specified`) 1234 } 1235 1236 func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) { 1237 env := t.Prepare(c) 1238 placement := "zone=test-available" 1239 err := env.PrecheckInstance(series.LatestLts(), constraints.Value{}, placement) 1240 c.Assert(err, jc.ErrorIsNil) 1241 } 1242 1243 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) { 1244 env := t.Prepare(c) 1245 placement := "zone=test-unavailable" 1246 err := env.PrecheckInstance(series.LatestLts(), constraints.Value{}, placement) 1247 c.Assert(err, jc.ErrorIsNil) 1248 } 1249 1250 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) { 1251 env := t.Prepare(c) 1252 placement := "zone=test-unknown" 1253 err := env.PrecheckInstance(series.LatestLts(), constraints.Value{}, placement) 1254 c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`) 1255 } 1256 1257 func (t *localServerSuite) TestValidateImageMetadata(c *gc.C) { 1258 region := t.srv.region() 1259 aws.Regions[region.Name] = t.srv.region() 1260 defer delete(aws.Regions, region.Name) 1261 1262 env := t.Prepare(c) 1263 params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("test") 1264 c.Assert(err, jc.ErrorIsNil) 1265 params.Series = series.LatestLts() 1266 params.Endpoint = region.EC2Endpoint 1267 params.Sources, err = environs.ImageMetadataSources(env) 1268 c.Assert(err, jc.ErrorIsNil) 1269 image_ids, _, err := imagemetadata.ValidateImageMetadata(params) 1270 c.Assert(err, jc.ErrorIsNil) 1271 sort.Strings(image_ids) 1272 c.Assert(image_ids, gc.DeepEquals, []string{"ami-00000133", "ami-00000135", "ami-00000139"}) 1273 } 1274 1275 func (t *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { 1276 t.PatchValue(&tools.DefaultBaseURL, "") 1277 1278 env := t.Prepare(c) 1279 sources, err := tools.GetMetadataSources(env) 1280 c.Assert(err, jc.ErrorIsNil) 1281 c.Assert(sources, gc.HasLen, 0) 1282 } 1283 1284 func (t *localServerSuite) TestSupportsNetworking(c *gc.C) { 1285 env := t.Prepare(c) 1286 _, supported := environs.SupportsNetworking(env) 1287 c.Assert(supported, jc.IsTrue) 1288 } 1289 1290 func (t *localServerSuite) setUpInstanceWithDefaultVpc(c *gc.C) (environs.NetworkingEnviron, instance.Id) { 1291 env := t.prepareEnviron(c) 1292 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{ 1293 ControllerConfig: coretesting.FakeControllerConfig(), 1294 AdminSecret: testing.AdminSecret, 1295 CAPrivateKey: coretesting.CAKey, 1296 }) 1297 c.Assert(err, jc.ErrorIsNil) 1298 1299 instanceIds, err := env.ControllerInstances(t.ControllerUUID) 1300 c.Assert(err, jc.ErrorIsNil) 1301 return env, instanceIds[0] 1302 } 1303 1304 func (t *localServerSuite) TestNetworkInterfaces(c *gc.C) { 1305 env, instId := t.setUpInstanceWithDefaultVpc(c) 1306 interfaces, err := env.NetworkInterfaces(instId) 1307 c.Assert(err, jc.ErrorIsNil) 1308 1309 // The CIDR isn't predictable, but it is in the 10.10.x.0/24 format 1310 // The subnet ID is in the form "subnet-x", where x matches the same 1311 // number from the CIDR. The interfaces address is part of the CIDR. 1312 // For these reasons we check that the CIDR is in the expected format 1313 // and derive the expected values for ProviderSubnetId and Address. 1314 c.Assert(interfaces, gc.HasLen, 1) 1315 cidr := interfaces[0].CIDR 1316 re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`) 1317 c.Assert(re.Match([]byte(cidr)), jc.IsTrue) 1318 index := re.FindStringSubmatch(cidr)[1] 1319 addr := fmt.Sprintf("10.10.%s.5", index) 1320 subnetId := network.Id("subnet-" + index) 1321 1322 // AvailabilityZones will either contain "test-available", 1323 // "test-impaired" or "test-unavailable" depending on which subnet is 1324 // picked. Any of these is fine. 1325 zones := interfaces[0].AvailabilityZones 1326 c.Assert(zones, gc.HasLen, 1) 1327 re = regexp.MustCompile("test-available|test-unavailable|test-impaired") 1328 c.Assert(re.Match([]byte(zones[0])), jc.IsTrue) 1329 1330 expectedInterfaces := []network.InterfaceInfo{{ 1331 DeviceIndex: 0, 1332 MACAddress: "20:01:60:cb:27:37", 1333 CIDR: cidr, 1334 ProviderId: "eni-0", 1335 ProviderSubnetId: subnetId, 1336 VLANTag: 0, 1337 InterfaceName: "unsupported0", 1338 Disabled: false, 1339 NoAutoStart: false, 1340 ConfigType: network.ConfigDHCP, 1341 InterfaceType: network.EthernetInterface, 1342 Address: network.NewScopedAddress(addr, network.ScopeCloudLocal), 1343 AvailabilityZones: zones, 1344 }} 1345 c.Assert(interfaces, jc.DeepEquals, expectedInterfaces) 1346 } 1347 1348 func (t *localServerSuite) TestSubnetsWithInstanceId(c *gc.C) { 1349 env, instId := t.setUpInstanceWithDefaultVpc(c) 1350 subnets, err := env.Subnets(instId, nil) 1351 c.Assert(err, jc.ErrorIsNil) 1352 c.Assert(subnets, gc.HasLen, 1) 1353 validateSubnets(c, subnets) 1354 1355 interfaces, err := env.NetworkInterfaces(instId) 1356 c.Assert(err, jc.ErrorIsNil) 1357 c.Assert(interfaces, gc.HasLen, 1) 1358 c.Assert(interfaces[0].ProviderSubnetId, gc.Equals, subnets[0].ProviderId) 1359 } 1360 1361 func (t *localServerSuite) TestSubnetsWithInstanceIdAndSubnetId(c *gc.C) { 1362 env, instId := t.setUpInstanceWithDefaultVpc(c) 1363 interfaces, err := env.NetworkInterfaces(instId) 1364 c.Assert(err, jc.ErrorIsNil) 1365 c.Assert(interfaces, gc.HasLen, 1) 1366 1367 subnets, err := env.Subnets(instId, []network.Id{interfaces[0].ProviderSubnetId}) 1368 c.Assert(err, jc.ErrorIsNil) 1369 c.Assert(subnets, gc.HasLen, 1) 1370 c.Assert(subnets[0].ProviderId, gc.Equals, interfaces[0].ProviderSubnetId) 1371 validateSubnets(c, subnets) 1372 } 1373 1374 func (t *localServerSuite) TestSubnetsWithInstanceIdMissingSubnet(c *gc.C) { 1375 env, instId := t.setUpInstanceWithDefaultVpc(c) 1376 subnets, err := env.Subnets(instId, []network.Id{"missing"}) 1377 c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[missing\]`) 1378 c.Assert(subnets, gc.HasLen, 0) 1379 } 1380 1381 func validateSubnets(c *gc.C, subnets []network.SubnetInfo) { 1382 // These are defined in the test server for the testing default 1383 // VPC. 1384 defaultSubnets := []network.SubnetInfo{{ 1385 CIDR: "10.10.0.0/24", 1386 ProviderId: "subnet-0", 1387 VLANTag: 0, 1388 AvailabilityZones: []string{"test-available"}, 1389 }, { 1390 CIDR: "10.10.1.0/24", 1391 ProviderId: "subnet-1", 1392 VLANTag: 0, 1393 AvailabilityZones: []string{"test-impaired"}, 1394 }, { 1395 CIDR: "10.10.2.0/24", 1396 ProviderId: "subnet-2", 1397 VLANTag: 0, 1398 AvailabilityZones: []string{"test-unavailable"}, 1399 }} 1400 1401 re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`) 1402 for _, subnet := range subnets { 1403 // We can find the expected data by looking at the CIDR. 1404 // subnets isn't in a predictable order due to the use of maps. 1405 c.Assert(re.Match([]byte(subnet.CIDR)), jc.IsTrue) 1406 index, err := strconv.Atoi(re.FindStringSubmatch(subnet.CIDR)[1]) 1407 c.Assert(err, jc.ErrorIsNil) 1408 // Don't know which AZ the subnet will end up in. 1409 defaultSubnets[index].AvailabilityZones = subnet.AvailabilityZones 1410 c.Assert(subnet, jc.DeepEquals, defaultSubnets[index]) 1411 } 1412 } 1413 1414 func (t *localServerSuite) TestSubnets(c *gc.C) { 1415 env, _ := t.setUpInstanceWithDefaultVpc(c) 1416 1417 subnets, err := env.Subnets(instance.UnknownId, []network.Id{"subnet-0"}) 1418 c.Assert(err, jc.ErrorIsNil) 1419 c.Assert(subnets, gc.HasLen, 1) 1420 validateSubnets(c, subnets) 1421 1422 subnets, err = env.Subnets(instance.UnknownId, nil) 1423 c.Assert(err, jc.ErrorIsNil) 1424 c.Assert(subnets, gc.HasLen, 3) 1425 validateSubnets(c, subnets) 1426 } 1427 1428 func (t *localServerSuite) TestSubnetsMissingSubnet(c *gc.C) { 1429 env, _ := t.setUpInstanceWithDefaultVpc(c) 1430 1431 _, err := env.Subnets("", []network.Id{"subnet-0", "Missing"}) 1432 c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[Missing\]`) 1433 } 1434 1435 func (t *localServerSuite) TestInstanceTags(c *gc.C) { 1436 env := t.prepareAndBootstrap(c) 1437 1438 instances, err := env.AllInstances() 1439 c.Assert(err, jc.ErrorIsNil) 1440 c.Assert(instances, gc.HasLen, 1) 1441 1442 ec2Inst := ec2.InstanceEC2(instances[0]) 1443 c.Assert(ec2Inst.Tags, jc.SameContents, []amzec2.Tag{ 1444 {"Name", "juju-sample-machine-0"}, 1445 {"juju-model-uuid", coretesting.ModelTag.Id()}, 1446 {"juju-controller-uuid", t.ControllerUUID}, 1447 {"juju-is-controller", "true"}, 1448 }) 1449 } 1450 1451 func (t *localServerSuite) TestRootDiskTags(c *gc.C) { 1452 env := t.prepareAndBootstrap(c) 1453 1454 instances, err := env.AllInstances() 1455 c.Assert(err, jc.ErrorIsNil) 1456 c.Assert(instances, gc.HasLen, 1) 1457 1458 ec2conn := ec2.EnvironEC2(env) 1459 resp, err := ec2conn.Volumes(nil, nil) 1460 c.Assert(err, jc.ErrorIsNil) 1461 c.Assert(resp.Volumes, gc.Not(gc.HasLen), 0) 1462 1463 var found *amzec2.Volume 1464 for _, vol := range resp.Volumes { 1465 if len(vol.Tags) != 0 { 1466 found = &vol 1467 break 1468 } 1469 } 1470 c.Assert(found, gc.NotNil) 1471 c.Assert(found.Tags, jc.SameContents, []amzec2.Tag{ 1472 {"Name", "juju-sample-machine-0-root"}, 1473 {"juju-model-uuid", coretesting.ModelTag.Id()}, 1474 {"juju-controller-uuid", t.ControllerUUID}, 1475 }) 1476 } 1477 1478 func (s *localServerSuite) TestBootstrapInstanceConstraints(c *gc.C) { 1479 env := s.prepareAndBootstrap(c) 1480 inst, hc := testing.AssertStartControllerInstance(c, env, s.ControllerUUID, "1") 1481 ec2inst := ec2.InstanceEC2(inst) 1482 1483 // Controllers should be started with a burstable 1484 // instance if possible, and a 32 GiB disk. 1485 c.Assert(ec2inst.InstanceType, gc.Equals, "t2.medium") 1486 c.Assert(*hc.Arch, gc.Equals, "amd64") 1487 c.Assert(*hc.Mem, gc.Equals, uint64(4*1024)) 1488 c.Assert(*hc.RootDisk, gc.Equals, uint64(32*1024)) 1489 c.Assert(*hc.CpuCores, gc.Equals, uint64(2)) 1490 } 1491 1492 // localNonUSEastSuite is similar to localServerSuite but the S3 mock server 1493 // behaves as if it is not in the us-east region. 1494 type localNonUSEastSuite struct { 1495 coretesting.BaseSuite 1496 sstesting.TestDataSuite 1497 1498 srv localServer 1499 env environs.Environ 1500 } 1501 1502 func (t *localNonUSEastSuite) SetUpSuite(c *gc.C) { 1503 t.BaseSuite.SetUpSuite(c) 1504 t.TestDataSuite.SetUpSuite(c) 1505 1506 t.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey) 1507 t.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 1508 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 1509 } 1510 1511 func (t *localNonUSEastSuite) TearDownSuite(c *gc.C) { 1512 t.TestDataSuite.TearDownSuite(c) 1513 t.BaseSuite.TearDownSuite(c) 1514 } 1515 1516 func (t *localNonUSEastSuite) SetUpTest(c *gc.C) { 1517 t.BaseSuite.SetUpTest(c) 1518 t.srv.startServer(c) 1519 1520 region := t.srv.region() 1521 credential := cloud.NewCredential( 1522 cloud.AccessKeyAuthType, 1523 map[string]string{ 1524 "access-key": "x", 1525 "secret-key": "x", 1526 }, 1527 ) 1528 restoreEC2Patching := patchEC2ForTesting(c, region) 1529 t.AddCleanup(func(c *gc.C) { restoreEC2Patching() }) 1530 1531 env, err := bootstrap.Prepare( 1532 envtesting.BootstrapContext(c), 1533 jujuclienttesting.NewMemStore(), 1534 bootstrap.PrepareParams{ 1535 ControllerConfig: coretesting.FakeControllerConfig(), 1536 ModelConfig: localConfigAttrs, 1537 ControllerName: localConfigAttrs["name"].(string), 1538 Cloud: environs.CloudSpec{ 1539 Type: "ec2", 1540 Region: region.Name, 1541 Endpoint: region.EC2Endpoint, 1542 Credential: &credential, 1543 }, 1544 AdminSecret: testing.AdminSecret, 1545 }, 1546 ) 1547 c.Assert(err, jc.ErrorIsNil) 1548 t.env = env 1549 } 1550 1551 func (t *localNonUSEastSuite) TearDownTest(c *gc.C) { 1552 t.srv.stopServer(c) 1553 t.BaseSuite.TearDownTest(c) 1554 } 1555 1556 func patchEC2ForTesting(c *gc.C, region aws.Region) func() { 1557 ec2.UseTestImageData(c, ec2.MakeTestImageStreamsData(region)) 1558 restoreTimeouts := envtesting.PatchAttemptStrategies(ec2.ShortAttempt) 1559 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 1560 return func() { 1561 restoreFinishBootstrap() 1562 restoreTimeouts() 1563 ec2.UseTestImageData(c, nil) 1564 } 1565 } 1566 1567 // If match is true, CheckScripts checks that at least one script started 1568 // by the cloudinit data matches the given regexp pattern, otherwise it 1569 // checks that no script matches. It's exported so it can be used by tests 1570 // defined in ec2_test. 1571 func CheckScripts(c *gc.C, userDataMap map[interface{}]interface{}, pattern string, match bool) { 1572 scripts0 := userDataMap["runcmd"] 1573 if scripts0 == nil { 1574 c.Errorf("cloudinit has no entry for runcmd") 1575 return 1576 } 1577 scripts := scripts0.([]interface{}) 1578 re := regexp.MustCompile(pattern) 1579 found := false 1580 for _, s0 := range scripts { 1581 s := s0.(string) 1582 if re.MatchString(s) { 1583 found = true 1584 } 1585 } 1586 switch { 1587 case match && !found: 1588 c.Errorf("script %q not found in %q", pattern, scripts) 1589 case !match && found: 1590 c.Errorf("script %q found but not expected in %q", pattern, scripts) 1591 } 1592 } 1593 1594 // CheckPackage checks that the cloudinit will or won't install the given 1595 // package, depending on the value of match. It's exported so it can be 1596 // used by tests defined outside the ec2 package. 1597 func CheckPackage(c *gc.C, userDataMap map[interface{}]interface{}, pkg string, match bool) { 1598 pkgs0 := userDataMap["packages"] 1599 if pkgs0 == nil { 1600 if match { 1601 c.Errorf("cloudinit has no entry for packages") 1602 } 1603 return 1604 } 1605 1606 pkgs := pkgs0.([]interface{}) 1607 1608 found := false 1609 for _, p0 := range pkgs { 1610 p := p0.(string) 1611 // p might be a space separate list of packages eg 'foo bar qed' so split them up 1612 manyPkgs := set.NewStrings(strings.Split(p, " ")...) 1613 hasPkg := manyPkgs.Contains(pkg) 1614 if p == pkg || hasPkg { 1615 found = true 1616 break 1617 } 1618 } 1619 switch { 1620 case match && !found: 1621 c.Errorf("package %q not found in %v", pkg, pkgs) 1622 case !match && found: 1623 c.Errorf("%q found but not expected in %v", pkg, pkgs) 1624 } 1625 }