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