github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/http/httptest" 9 "net/http/httputil" 10 "net/url" 11 "regexp" 12 "sort" 13 "strconv" 14 "strings" 15 16 "github.com/juju/clock" 17 "github.com/juju/collections/set" 18 "github.com/juju/errors" 19 "github.com/juju/os/series" 20 jc "github.com/juju/testing/checkers" 21 "github.com/juju/utils" 22 "github.com/juju/utils/arch" 23 "github.com/juju/version" 24 "gopkg.in/amz.v3/aws" 25 amzec2 "gopkg.in/amz.v3/ec2" 26 "gopkg.in/amz.v3/ec2/ec2test" 27 gc "gopkg.in/check.v1" 28 "gopkg.in/juju/names.v2" 29 goyaml "gopkg.in/yaml.v2" 30 31 "github.com/juju/juju/cloud" 32 "github.com/juju/juju/core/constraints" 33 "github.com/juju/juju/core/instance" 34 "github.com/juju/juju/core/status" 35 "github.com/juju/juju/environs" 36 "github.com/juju/juju/environs/bootstrap" 37 "github.com/juju/juju/environs/context" 38 "github.com/juju/juju/environs/imagemetadata" 39 imagetesting "github.com/juju/juju/environs/imagemetadata/testing" 40 "github.com/juju/juju/environs/instances" 41 "github.com/juju/juju/environs/jujutest" 42 "github.com/juju/juju/environs/simplestreams" 43 sstesting "github.com/juju/juju/environs/simplestreams/testing" 44 "github.com/juju/juju/environs/tags" 45 envtesting "github.com/juju/juju/environs/testing" 46 "github.com/juju/juju/environs/tools" 47 "github.com/juju/juju/juju/keys" 48 "github.com/juju/juju/juju/testing" 49 supportedversion "github.com/juju/juju/juju/version" 50 "github.com/juju/juju/jujuclient" 51 "github.com/juju/juju/network" 52 "github.com/juju/juju/provider/common" 53 "github.com/juju/juju/provider/ec2" 54 "github.com/juju/juju/storage" 55 coretesting "github.com/juju/juju/testing" 56 jujuversion "github.com/juju/juju/version" 57 ) 58 59 var localConfigAttrs = coretesting.FakeConfig().Merge(coretesting.Attrs{ 60 "name": "sample", 61 "type": "ec2", 62 "agent-version": coretesting.FakeVersionNumber.String(), 63 }) 64 65 func fakeCallback(_ status.Status, _ string, _ map[string]interface{}) error { 66 return nil 67 } 68 69 func registerLocalTests() { 70 // N.B. Make sure the region we use here 71 // has entries in the images/query txt files. 72 aws.Regions["test"] = aws.Region{ 73 Name: "test", 74 } 75 76 gc.Suite(&localServerSuite{}) 77 gc.Suite(&localLiveSuite{}) 78 gc.Suite(&localNonUSEastSuite{}) 79 } 80 81 // localLiveSuite runs tests from LiveTests using a fake 82 // EC2 server that runs within the test process itself. 83 type localLiveSuite struct { 84 LiveTests 85 srv localServer 86 } 87 88 func (t *localLiveSuite) SetUpSuite(c *gc.C) { 89 t.LiveTests.SetUpSuite(c) 90 t.Credential = cloud.NewCredential( 91 cloud.AccessKeyAuthType, 92 map[string]string{ 93 "access-key": "x", 94 "secret-key": "x", 95 }, 96 ) 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 imagetesting.PatchOfficialDataSources(&t.BaseSuite.CleanupSuite, "test:") 103 t.BaseSuite.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey) 104 t.BaseSuite.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 105 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 106 t.srv.createRootDisks = true 107 t.srv.startServer(c) 108 109 region := t.srv.region 110 t.CloudRegion = region.Name 111 t.CloudEndpoint = region.EC2Endpoint 112 restoreEC2Patching := patchEC2ForTesting(c, region) 113 t.BaseSuite.AddCleanup(func(c *gc.C) { restoreEC2Patching() }) 114 } 115 116 func (t *localLiveSuite) TearDownSuite(c *gc.C) { 117 t.LiveTests.TearDownSuite(c) 118 t.srv.stopServer(c) 119 } 120 121 // localServer represents a fake EC2 server running within 122 // the test process itself. 123 type localServer struct { 124 // createRootDisks is used to decide whether or not 125 // the ec2test server will create root disks for 126 // instances. 127 createRootDisks bool 128 129 ec2srv *ec2test.Server 130 proxy *httputil.ReverseProxy 131 proxyServer *httptest.Server 132 client *amzec2.EC2 133 region aws.Region 134 135 defaultVPC *amzec2.VPC 136 zones []amzec2.AvailabilityZoneInfo 137 subnets []amzec2.Subnet 138 } 139 140 func (srv *localServer) startServer(c *gc.C) { 141 var err error 142 srv.ec2srv, err = ec2test.NewServer() 143 if err != nil { 144 c.Fatalf("cannot start ec2 test server: %v", err) 145 } 146 srv.ec2srv.SetCreateRootDisks(srv.createRootDisks) 147 srv.addSpice(c) 148 149 // Create a reverse proxy, so we can override responses. 150 endpointURL, err := url.Parse(srv.ec2srv.URL()) 151 c.Assert(err, jc.ErrorIsNil) 152 backendURL := &url.URL{ 153 Scheme: endpointURL.Scheme, 154 Host: endpointURL.Host, 155 } 156 srv.proxy = httputil.NewSingleHostReverseProxy(backendURL) 157 srv.proxyServer = httptest.NewServer(srv.proxy) 158 endpointURL, err = url.Parse(srv.proxyServer.URL) 159 c.Assert(err, jc.ErrorIsNil) 160 srv.region = aws.Region{ 161 Name: "test", 162 EC2Endpoint: endpointURL.String(), 163 } 164 srv.client = amzec2.New(aws.Auth{}, srv.region, aws.SignV4Factory(srv.region.Name, "ec2")) 165 166 zones := make([]amzec2.AvailabilityZoneInfo, 4) 167 zones[0].Region = srv.region.Name 168 zones[0].Name = srv.region.Name + "-available" 169 zones[0].State = "available" 170 zones[1].Region = srv.region.Name 171 zones[1].Name = srv.region.Name + "-impaired" 172 zones[1].State = "impaired" 173 zones[2].Region = srv.region.Name 174 zones[2].Name = srv.region.Name + "-unavailable" 175 zones[2].State = "unavailable" 176 zones[3].Region = srv.region.Name 177 zones[3].Name = srv.region.Name + "-available2" 178 zones[3].State = "available" 179 srv.ec2srv.SetAvailabilityZones(zones) 180 srv.ec2srv.SetInitialInstanceState(ec2test.Pending) 181 srv.zones = zones 182 183 defaultVPC, err := srv.ec2srv.AddDefaultVPCAndSubnets() 184 c.Assert(err, jc.ErrorIsNil) 185 srv.defaultVPC = &defaultVPC 186 } 187 188 // addSpice adds some "spice" to the local server 189 // by adding state that may cause tests to fail. 190 func (srv *localServer) addSpice(c *gc.C) { 191 states := []amzec2.InstanceState{ 192 ec2test.ShuttingDown, 193 ec2test.Terminated, 194 ec2test.Stopped, 195 } 196 for _, state := range states { 197 srv.ec2srv.NewInstances(1, "m1.small", "ami-a7f539ce", state, nil) 198 } 199 } 200 201 func (srv *localServer) stopServer(c *gc.C) { 202 srv.proxyServer.Close() 203 srv.ec2srv.Reset(false) 204 srv.ec2srv.Quit() 205 srv.defaultVPC = nil 206 } 207 208 // localServerSuite contains tests that run against a fake EC2 server 209 // running within the test process itself. These tests can test things that 210 // would be unreasonably slow or expensive to test on a live Amazon server. 211 // It starts a new local ec2test server for each test. The server is 212 // accessed by using the "test" region, which is changed to point to the 213 // network address of the local server. 214 type localServerSuite struct { 215 coretesting.BaseSuite 216 jujutest.Tests 217 srv localServer 218 client *amzec2.EC2 219 220 callCtx context.ProviderCallContext 221 } 222 223 func (t *localServerSuite) SetUpSuite(c *gc.C) { 224 t.BaseSuite.SetUpSuite(c) 225 t.Credential = cloud.NewCredential( 226 cloud.AccessKeyAuthType, 227 map[string]string{ 228 "access-key": "x", 229 "secret-key": "x", 230 }, 231 ) 232 233 // Upload arches that ec2 supports; add to this 234 // as ec2 coverage expands. 235 t.UploadArches = []string{arch.AMD64, arch.I386} 236 t.TestConfig = localConfigAttrs 237 imagetesting.PatchOfficialDataSources(&t.BaseSuite.CleanupSuite, "test:") 238 t.BaseSuite.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey) 239 t.BaseSuite.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 240 t.BaseSuite.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber) 241 t.BaseSuite.PatchValue(&arch.HostArch, func() string { return arch.AMD64 }) 242 t.BaseSuite.PatchValue(&series.MustHostSeries, func() string { return supportedversion.SupportedLTS() }) 243 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 244 t.srv.createRootDisks = true 245 t.srv.startServer(c) 246 // TODO(jam) I don't understand why we shouldn't do this. 247 // t.Tests embeds the sstesting.TestDataSuite, but if we call this 248 // SetUpSuite, then all of the tests fail because they go to access 249 // "test:/streams/..." and it isn't found 250 // t.Tests.SetUpSuite(c) 251 } 252 253 func (t *localServerSuite) TearDownSuite(c *gc.C) { 254 t.Tests.TearDownSuite(c) 255 t.BaseSuite.TearDownSuite(c) 256 } 257 258 func (t *localServerSuite) SetUpTest(c *gc.C) { 259 t.BaseSuite.SetUpTest(c) 260 t.srv.startServer(c) 261 region := t.srv.region 262 t.CloudRegion = region.Name 263 t.CloudEndpoint = region.EC2Endpoint 264 t.client = t.srv.client 265 restoreEC2Patching := patchEC2ForTesting(c, region) 266 t.AddCleanup(func(c *gc.C) { restoreEC2Patching() }) 267 t.Tests.SetUpTest(c) 268 269 t.callCtx = context.NewCloudCallContext() 270 } 271 272 func (t *localServerSuite) TearDownTest(c *gc.C) { 273 t.Tests.TearDownTest(c) 274 t.srv.stopServer(c) 275 t.BaseSuite.TearDownTest(c) 276 } 277 278 func (t *localServerSuite) prepareEnviron(c *gc.C) environs.NetworkingEnviron { 279 env := t.Prepare(c) 280 netenv, supported := environs.SupportsNetworking(env) 281 c.Assert(supported, jc.IsTrue) 282 return netenv 283 } 284 285 func (t *localServerSuite) TestPrepareForBootstrapWithInvalidVPCID(c *gc.C) { 286 badVPCIDConfig := coretesting.Attrs{"vpc-id": "bad"} 287 288 expectedError := `invalid EC2 provider config: vpc-id: "bad" is not a valid AWS VPC ID` 289 t.AssertPrepareFailsWithConfig(c, badVPCIDConfig, expectedError) 290 } 291 292 func (t *localServerSuite) TestPrepareForBootstrapWithUnknownVPCID(c *gc.C) { 293 unknownVPCIDConfig := coretesting.Attrs{"vpc-id": "vpc-unknown"} 294 295 expectedError := `Juju cannot use the given vpc-id for bootstrapping(.|\n)*Error details: VPC "vpc-unknown" not found` 296 err := t.AssertPrepareFailsWithConfig(c, unknownVPCIDConfig, expectedError) 297 c.Check(err, jc.Satisfies, ec2.IsVPCNotUsableError) 298 } 299 300 func (t *localServerSuite) TestPrepareForBootstrapWithNotRecommendedVPCID(c *gc.C) { 301 t.makeTestingDefaultVPCUnavailable(c) 302 notRecommendedVPCIDConfig := coretesting.Attrs{"vpc-id": t.srv.defaultVPC.Id} 303 304 expectedError := `The given vpc-id does not meet one or more(.|\n)*Error details: VPC has unexpected state "unavailable"` 305 err := t.AssertPrepareFailsWithConfig(c, notRecommendedVPCIDConfig, expectedError) 306 c.Check(err, jc.Satisfies, ec2.IsVPCNotRecommendedError) 307 } 308 309 func (t *localServerSuite) makeTestingDefaultVPCUnavailable(c *gc.C) { 310 // For simplicity, here the test server's default VPC is updated to change 311 // its state to unavailable, we just verify the behavior of a "not 312 // recommended VPC". 313 t.srv.defaultVPC.State = "unavailable" 314 err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC) 315 c.Assert(err, jc.ErrorIsNil) 316 } 317 318 func (t *localServerSuite) TestPrepareForBootstrapWithNotRecommendedButForcedVPCID(c *gc.C) { 319 t.makeTestingDefaultVPCUnavailable(c) 320 params := t.PrepareParams(c) 321 params.ModelConfig["vpc-id"] = t.srv.defaultVPC.Id 322 params.ModelConfig["vpc-id-force"] = true 323 324 t.prepareWithParamsAndBootstrapWithVPCID(c, params, t.srv.defaultVPC.Id) 325 } 326 327 func (t *localServerSuite) TestPrepareForBootstrapWithEmptyVPCID(c *gc.C) { 328 const emptyVPCID = "" 329 330 params := t.PrepareParams(c) 331 params.ModelConfig["vpc-id"] = emptyVPCID 332 333 t.prepareWithParamsAndBootstrapWithVPCID(c, params, emptyVPCID) 334 } 335 336 func (t *localServerSuite) prepareWithParamsAndBootstrapWithVPCID(c *gc.C, params bootstrap.PrepareParams, expectedVPCID string) { 337 env := t.PrepareWithParams(c, params) 338 unknownAttrs := env.Config().UnknownAttrs() 339 vpcID, ok := unknownAttrs["vpc-id"] 340 c.Check(vpcID, gc.Equals, expectedVPCID) 341 c.Check(ok, jc.IsTrue) 342 343 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 344 t.callCtx, bootstrap.BootstrapParams{ 345 ControllerConfig: coretesting.FakeControllerConfig(), 346 AdminSecret: testing.AdminSecret, 347 CAPrivateKey: coretesting.CAKey, 348 Placement: "zone=test-available", 349 }) 350 c.Assert(err, jc.ErrorIsNil) 351 } 352 353 func (t *localServerSuite) TestPrepareForBootstrapWithVPCIDNone(c *gc.C) { 354 params := t.PrepareParams(c) 355 params.ModelConfig["vpc-id"] = "none" 356 357 t.prepareWithParamsAndBootstrapWithVPCID(c, params, ec2.VPCIDNone) 358 } 359 360 func (t *localServerSuite) TestPrepareForBootstrapWithDefaultVPCID(c *gc.C) { 361 params := t.PrepareParams(c) 362 params.ModelConfig["vpc-id"] = t.srv.defaultVPC.Id 363 364 t.prepareWithParamsAndBootstrapWithVPCID(c, params, t.srv.defaultVPC.Id) 365 } 366 367 func (t *localServerSuite) TestSystemdBootstrapInstanceUserDataAndState(c *gc.C) { 368 env := t.Prepare(c) 369 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 370 t.callCtx, bootstrap.BootstrapParams{ 371 ControllerConfig: coretesting.FakeControllerConfig(), 372 // TODO(redir): BBB: When we no longer support upstart based systems this can change to series.LatestLts() 373 BootstrapSeries: "xenial", 374 AdminSecret: testing.AdminSecret, 375 CAPrivateKey: coretesting.CAKey, 376 }) 377 c.Assert(err, jc.ErrorIsNil) 378 379 // check that ControllerInstances returns the id of the bootstrap machine. 380 instanceIds, err := env.ControllerInstances(t.callCtx, t.ControllerUUID) 381 c.Assert(err, jc.ErrorIsNil) 382 c.Assert(instanceIds, gc.HasLen, 1) 383 384 insts, err := env.AllInstances(t.callCtx) 385 c.Assert(err, jc.ErrorIsNil) 386 c.Assert(insts, gc.HasLen, 1) 387 c.Check(insts[0].Id(), gc.Equals, instanceIds[0]) 388 389 // check that the user data is configured to and the machine and 390 // provisioning agents. check that the user data is configured to only 391 // configure authorized SSH keys and set the log output; everything else 392 // happens after the machine is brought up. 393 inst := t.srv.ec2srv.Instance(string(insts[0].Id())) 394 c.Assert(inst, gc.NotNil) 395 addresses, err := insts[0].Addresses(t.callCtx) 396 c.Assert(err, jc.ErrorIsNil) 397 c.Assert(addresses, gc.Not(gc.HasLen), 0) 398 userData, err := utils.Gunzip(inst.UserData) 399 c.Assert(err, jc.ErrorIsNil) 400 401 var userDataMap map[string]interface{} 402 err = goyaml.Unmarshal(userData, &userDataMap) 403 c.Assert(err, jc.ErrorIsNil) 404 var keys []string 405 for key := range userDataMap { 406 keys = append(keys, key) 407 } 408 c.Assert(keys, jc.SameContents, []string{"output", "users", "runcmd", "ssh_keys"}) 409 c.Assert(userDataMap["runcmd"], jc.DeepEquals, []interface{}{ 410 "set -xe", 411 "install -D -m 644 /dev/null '/etc/systemd/system/juju-clean-shutdown.service'", 412 "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'", 413 "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", 414 "printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'", 415 }) 416 417 // check that a new instance will be started with a machine agent 418 inst1, hc := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1") 419 c.Check(*hc.Arch, gc.Equals, "amd64") 420 c.Check(*hc.Mem, gc.Equals, uint64(1024)) 421 c.Check(*hc.CpuCores, gc.Equals, uint64(2)) 422 inst = t.srv.ec2srv.Instance(string(inst1.Id())) 423 c.Assert(inst, gc.NotNil) 424 userData, err = utils.Gunzip(inst.UserData) 425 c.Assert(err, jc.ErrorIsNil) 426 c.Logf("second instance: UserData: %q", userData) 427 userDataMap = nil 428 err = goyaml.Unmarshal(userData, &userDataMap) 429 c.Assert(err, jc.ErrorIsNil) 430 CheckPackage(c, userDataMap, "curl", true) 431 CheckPackage(c, userDataMap, "mongodb-server", false) 432 CheckScripts(c, userDataMap, "jujud bootstrap-state", false) 433 CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true) 434 // TODO check for provisioning agent 435 436 err = env.Destroy(t.callCtx) 437 c.Assert(err, jc.ErrorIsNil) 438 439 _, err = env.ControllerInstances(t.callCtx, t.ControllerUUID) 440 c.Assert(err, gc.Equals, environs.ErrNotBootstrapped) 441 } 442 443 // TestUpstartBoostrapInstanceUserDataAndState is a test for legacy systems 444 // using upstart which will be around until trusty is no longer supported. 445 // TODO(redir): BBB: remove when trusty is no longer supported 446 func (t *localServerSuite) TestUpstartBootstrapInstanceUserDataAndState(c *gc.C) { 447 env := t.Prepare(c) 448 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 449 t.callCtx, bootstrap.BootstrapParams{ 450 ControllerConfig: coretesting.FakeControllerConfig(), 451 BootstrapSeries: "trusty", 452 AdminSecret: testing.AdminSecret, 453 CAPrivateKey: coretesting.CAKey, 454 }) 455 c.Assert(err, jc.ErrorIsNil) 456 457 // check that ControllerInstances returns the id of the bootstrap machine. 458 instanceIds, err := env.ControllerInstances(t.callCtx, t.ControllerUUID) 459 c.Assert(err, jc.ErrorIsNil) 460 c.Assert(instanceIds, gc.HasLen, 1) 461 462 insts, err := env.AllInstances(t.callCtx) 463 c.Assert(err, jc.ErrorIsNil) 464 c.Assert(insts, gc.HasLen, 1) 465 c.Check(insts[0].Id(), gc.Equals, instanceIds[0]) 466 467 // check that the user data is configured to and the machine and 468 // provisioning agents. check that the user data is configured to only 469 // configure authorized SSH keys and set the log output; everything else 470 // happens after the machine is brought up. 471 inst := t.srv.ec2srv.Instance(string(insts[0].Id())) 472 c.Assert(inst, gc.NotNil) 473 addresses, err := insts[0].Addresses(t.callCtx) 474 c.Assert(err, jc.ErrorIsNil) 475 c.Assert(addresses, gc.Not(gc.HasLen), 0) 476 userData, err := utils.Gunzip(inst.UserData) 477 c.Assert(err, jc.ErrorIsNil) 478 479 var userDataMap map[string]interface{} 480 err = goyaml.Unmarshal(userData, &userDataMap) 481 c.Assert(err, jc.ErrorIsNil) 482 var keys []string 483 for key := range userDataMap { 484 keys = append(keys, key) 485 } 486 c.Assert(keys, jc.SameContents, []string{"output", "users", "runcmd", "ssh_keys"}) 487 c.Assert(userDataMap["runcmd"], jc.DeepEquals, []interface{}{ 488 "set -xe", 489 "install -D -m 644 /dev/null '/etc/init/juju-clean-shutdown.conf'", 490 "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'", 491 "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", 492 "printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'", 493 }) 494 495 // check that a new instance will be started with a machine agent 496 inst1, hc := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1") 497 c.Check(*hc.Arch, gc.Equals, "amd64") 498 c.Check(*hc.Mem, gc.Equals, uint64(1024)) 499 c.Check(*hc.CpuCores, gc.Equals, uint64(2)) 500 inst = t.srv.ec2srv.Instance(string(inst1.Id())) 501 c.Assert(inst, gc.NotNil) 502 userData, err = utils.Gunzip(inst.UserData) 503 c.Assert(err, jc.ErrorIsNil) 504 c.Logf("second instance: UserData: %q", userData) 505 userDataMap = nil 506 err = goyaml.Unmarshal(userData, &userDataMap) 507 c.Assert(err, jc.ErrorIsNil) 508 CheckPackage(c, userDataMap, "curl", true) 509 CheckPackage(c, userDataMap, "mongodb-server", false) 510 CheckScripts(c, userDataMap, "jujud bootstrap-state", false) 511 CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true) 512 // TODO check for provisioning agent 513 514 err = env.Destroy(t.callCtx) 515 c.Assert(err, jc.ErrorIsNil) 516 517 _, err = env.ControllerInstances(t.callCtx, t.ControllerUUID) 518 c.Assert(err, gc.Equals, environs.ErrNotBootstrapped) 519 } 520 521 func (t *localServerSuite) TestTerminateInstancesIgnoresNotFound(c *gc.C) { 522 env := t.Prepare(c) 523 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 524 t.callCtx, bootstrap.BootstrapParams{ 525 ControllerConfig: coretesting.FakeControllerConfig(), 526 AdminSecret: testing.AdminSecret, 527 CAPrivateKey: coretesting.CAKey, 528 }) 529 c.Assert(err, jc.ErrorIsNil) 530 531 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 532 insts, err := env.AllInstances(t.callCtx) 533 c.Assert(err, jc.ErrorIsNil) 534 idsToStop := make([]instance.Id, len(insts)+1) 535 for i, one := range insts { 536 idsToStop[i] = one.Id() 537 } 538 idsToStop[len(insts)] = instance.Id("i-am-not-found") 539 540 err = env.StopInstances(t.callCtx, idsToStop...) 541 // NotFound should be ignored 542 c.Assert(err, jc.ErrorIsNil) 543 } 544 545 func (t *localServerSuite) TestDestroyErr(c *gc.C) { 546 env := t.prepareAndBootstrap(c) 547 548 msg := "terminate instances error" 549 t.BaseSuite.PatchValue(ec2.TerminateInstancesById, func(ec2inst *amzec2.EC2, ctx context.ProviderCallContext, ids ...instance.Id) (*amzec2.TerminateInstancesResp, error) { 550 return nil, errors.New(msg) 551 }) 552 553 err := env.Destroy(t.callCtx) 554 c.Assert(errors.Cause(err).Error(), jc.Contains, msg) 555 } 556 557 func (t *localServerSuite) TestGetTerminatedInstances(c *gc.C) { 558 env := t.Prepare(c) 559 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 560 t.callCtx, bootstrap.BootstrapParams{ 561 ControllerConfig: coretesting.FakeControllerConfig(), 562 AdminSecret: testing.AdminSecret, 563 CAPrivateKey: coretesting.CAKey, 564 }) 565 c.Assert(err, jc.ErrorIsNil) 566 567 // create another instance to terminate 568 inst1, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1") 569 inst := t.srv.ec2srv.Instance(string(inst1.Id())) 570 c.Assert(inst, gc.NotNil) 571 t.BaseSuite.PatchValue(ec2.TerminateInstancesById, func(ec2inst *amzec2.EC2, ctx context.ProviderCallContext, ids ...instance.Id) (*amzec2.TerminateInstancesResp, error) { 572 // Terminate the one destined for termination and 573 // err out to ensure that one instance will be terminated, the other - not. 574 _, err = ec2inst.TerminateInstances([]string{string(inst1.Id())}) 575 c.Assert(err, jc.ErrorIsNil) 576 return nil, errors.New("terminate instances error") 577 }) 578 err = env.Destroy(t.callCtx) 579 c.Assert(err, gc.NotNil) 580 581 terminated, err := ec2.TerminatedInstances(env) 582 c.Assert(err, jc.ErrorIsNil) 583 c.Assert(terminated, gc.HasLen, 1) 584 c.Assert(terminated[0].Id(), jc.DeepEquals, inst1.Id()) 585 } 586 587 func (t *localServerSuite) TestInstanceSecurityGroupsWitheInstanceStatusFilter(c *gc.C) { 588 env := t.prepareAndBootstrap(c) 589 590 insts, err := env.AllInstances(t.callCtx) 591 c.Assert(err, jc.ErrorIsNil) 592 ids := make([]instance.Id, len(insts)) 593 for i, one := range insts { 594 ids[i] = one.Id() 595 } 596 597 groupsNoInstanceFilter, err := ec2.InstanceSecurityGroups(env, t.callCtx, ids) 598 c.Assert(err, jc.ErrorIsNil) 599 // get all security groups for test instances 600 c.Assert(groupsNoInstanceFilter, gc.HasLen, 2) 601 602 groupsFilteredForTerminatedInstances, err := ec2.InstanceSecurityGroups(env, t.callCtx, ids, "shutting-down", "terminated") 603 c.Assert(err, jc.ErrorIsNil) 604 // get all security groups for terminated test instances 605 c.Assert(groupsFilteredForTerminatedInstances, gc.HasLen, 0) 606 } 607 608 func (t *localServerSuite) TestDestroyControllerModelDeleteSecurityGroupInsistentlyError(c *gc.C) { 609 env := t.prepareAndBootstrap(c) 610 msg := "destroy security group error" 611 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, func( 612 ec2.SecurityGroupCleaner, context.ProviderCallContext, amzec2.SecurityGroup, clock.Clock, 613 ) error { 614 return errors.New(msg) 615 }) 616 err := env.DestroyController(t.callCtx, t.ControllerUUID) 617 c.Assert(err, gc.ErrorMatches, "destroying managed environs: cannot delete security group .*: "+msg) 618 } 619 620 func (t *localServerSuite) TestDestroyHostedModelDeleteSecurityGroupInsistentlyError(c *gc.C) { 621 env := t.prepareAndBootstrap(c) 622 hostedEnv, err := environs.New(environs.OpenParams{ 623 Cloud: t.CloudSpec(), 624 Config: env.Config(), 625 }) 626 c.Assert(err, jc.ErrorIsNil) 627 628 msg := "destroy security group error" 629 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, func( 630 ec2.SecurityGroupCleaner, context.ProviderCallContext, amzec2.SecurityGroup, clock.Clock, 631 ) error { 632 return errors.New(msg) 633 }) 634 err = hostedEnv.Destroy(t.callCtx) 635 c.Assert(err, gc.ErrorMatches, "cannot delete environment security groups: cannot delete default security group: "+msg) 636 } 637 638 func (t *localServerSuite) TestDestroyControllerDestroysHostedModelResources(c *gc.C) { 639 controllerEnv := t.prepareAndBootstrap(c) 640 641 // Create a hosted model environment with an instance and a volume. 642 hostedModelUUID := "7e386e08-cba7-44a4-a76e-7c1633584210" 643 t.srv.ec2srv.SetInitialInstanceState(ec2test.Running) 644 cfg, err := controllerEnv.Config().Apply(map[string]interface{}{ 645 "uuid": hostedModelUUID, 646 "firewall-mode": "global", 647 }) 648 c.Assert(err, jc.ErrorIsNil) 649 env, err := environs.New(environs.OpenParams{ 650 Cloud: t.CloudSpec(), 651 Config: cfg, 652 }) 653 c.Assert(err, jc.ErrorIsNil) 654 inst, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "0") 655 c.Assert(err, jc.ErrorIsNil) 656 ebsProvider, err := env.StorageProvider(ec2.EBS_ProviderType) 657 c.Assert(err, jc.ErrorIsNil) 658 vs, err := ebsProvider.VolumeSource(nil) 659 c.Assert(err, jc.ErrorIsNil) 660 volumeResults, err := vs.CreateVolumes(t.callCtx, []storage.VolumeParams{{ 661 Tag: names.NewVolumeTag("0"), 662 Size: 1024, 663 Provider: ec2.EBS_ProviderType, 664 ResourceTags: map[string]string{ 665 tags.JujuController: t.ControllerUUID, 666 tags.JujuModel: hostedModelUUID, 667 }, 668 Attachment: &storage.VolumeAttachmentParams{ 669 AttachmentParams: storage.AttachmentParams{ 670 InstanceId: inst.Id(), 671 }, 672 }, 673 }}) 674 c.Assert(err, jc.ErrorIsNil) 675 c.Assert(volumeResults, gc.HasLen, 1) 676 c.Assert(volumeResults[0].Error, jc.ErrorIsNil) 677 678 assertInstances := func(expect ...instance.Id) { 679 insts, err := env.AllInstances(t.callCtx) 680 c.Assert(err, jc.ErrorIsNil) 681 ids := make([]instance.Id, len(insts)) 682 for i, inst := range insts { 683 ids[i] = inst.Id() 684 } 685 c.Assert(ids, jc.SameContents, expect) 686 } 687 assertVolumes := func(expect ...string) { 688 volIds, err := vs.ListVolumes(t.callCtx) 689 c.Assert(err, jc.ErrorIsNil) 690 c.Assert(volIds, jc.SameContents, expect) 691 } 692 assertGroups := func(expect ...string) { 693 groupsResp, err := t.client.SecurityGroups(nil, nil) 694 c.Assert(err, jc.ErrorIsNil) 695 names := make([]string, len(groupsResp.Groups)) 696 for i, group := range groupsResp.Groups { 697 names[i] = group.Name 698 } 699 c.Assert(names, jc.SameContents, expect) 700 } 701 702 assertInstances(inst.Id()) 703 assertVolumes(volumeResults[0].Volume.VolumeId) 704 assertGroups( 705 "default", 706 "juju-"+controllerEnv.Config().UUID(), 707 "juju-"+controllerEnv.Config().UUID()+"-0", 708 "juju-"+hostedModelUUID, 709 "juju-"+hostedModelUUID+"-global", 710 ) 711 712 // Destroy the controller resources. This should destroy the hosted 713 // environment too. 714 err = controllerEnv.DestroyController(t.callCtx, t.ControllerUUID) 715 c.Assert(err, jc.ErrorIsNil) 716 717 assertInstances() 718 assertVolumes() 719 assertGroups("default") 720 } 721 722 func (t *localServerSuite) TestInstanceStatus(c *gc.C) { 723 env := t.Prepare(c) 724 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 725 t.callCtx, bootstrap.BootstrapParams{ 726 ControllerConfig: coretesting.FakeControllerConfig(), 727 AdminSecret: testing.AdminSecret, 728 CAPrivateKey: coretesting.CAKey, 729 }) 730 c.Assert(err, jc.ErrorIsNil) 731 t.srv.ec2srv.SetInitialInstanceState(ec2test.Terminated) 732 inst, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1") 733 c.Assert(err, jc.ErrorIsNil) 734 c.Assert(inst.Status(t.callCtx).Message, gc.Equals, "terminated") 735 } 736 737 func (t *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { 738 env := t.prepareAndBootstrap(c) 739 _, hc := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1") 740 c.Check(*hc.Arch, gc.Equals, "amd64") 741 c.Check(*hc.Mem, gc.Equals, uint64(1024)) 742 c.Check(*hc.CpuCores, gc.Equals, uint64(2)) 743 } 744 745 func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) { 746 inst, err := t.testStartInstanceAvailZone(c, "test-available") 747 c.Assert(err, jc.ErrorIsNil) 748 c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "test-available") 749 } 750 751 func (t *localServerSuite) TestStartInstanceAvailZoneImpaired(c *gc.C) { 752 _, err := t.testStartInstanceAvailZone(c, "test-impaired") 753 c.Assert(err, gc.ErrorMatches, `availability zone "test-impaired" is "impaired"`) 754 } 755 756 func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) { 757 _, err := t.testStartInstanceAvailZone(c, "test-unknown") 758 c.Assert(err, gc.Not(jc.Satisfies), environs.IsAvailabilityZoneIndependent) 759 c.Assert(errors.Details(err), gc.Matches, `.*availability zone \"\" not valid.*`) 760 } 761 762 func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instances.Instance, error) { 763 env := t.prepareAndBootstrap(c) 764 765 params := environs.StartInstanceParams{ControllerUUID: t.ControllerUUID, AvailabilityZone: zone, StatusCallback: fakeCallback} 766 result, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params) 767 if err != nil { 768 return nil, err 769 } 770 return result.Instance, nil 771 } 772 773 func (t *localServerSuite) TestStartInstanceVolumeAttachmentsAvailZone(c *gc.C) { 774 env := t.prepareAndBootstrap(c) 775 resp, err := t.client.CreateVolume(amzec2.CreateVolume{ 776 VolumeSize: 1, 777 VolumeType: "gp2", 778 AvailZone: "volume-zone", 779 }) 780 c.Assert(err, jc.ErrorIsNil) 781 782 args := environs.StartInstanceParams{ 783 ControllerUUID: t.ControllerUUID, 784 StatusCallback: fakeCallback, 785 VolumeAttachments: []storage.VolumeAttachmentParams{{ 786 AttachmentParams: storage.AttachmentParams{ 787 Provider: "ebs", 788 Machine: names.NewMachineTag("1"), 789 }, 790 Volume: names.NewVolumeTag("23"), 791 VolumeId: resp.Id, 792 }}, 793 } 794 result, err := testing.StartInstanceWithParams(env, t.callCtx, "1", args) 795 c.Assert(err, jc.ErrorIsNil) 796 c.Assert(ec2.InstanceEC2(result.Instance).AvailZone, gc.Equals, "volume-zone") 797 } 798 799 func (t *localServerSuite) TestStartInstanceVolumeAttachmentsAvailZonePlacementConflicts(c *gc.C) { 800 env := t.prepareAndBootstrap(c) 801 resp, err := t.client.CreateVolume(amzec2.CreateVolume{ 802 VolumeSize: 1, 803 VolumeType: "gp2", 804 AvailZone: "volume-zone", 805 }) 806 c.Assert(err, jc.ErrorIsNil) 807 808 args := environs.StartInstanceParams{ 809 ControllerUUID: t.ControllerUUID, 810 StatusCallback: fakeCallback, 811 Placement: "zone=test-available", 812 VolumeAttachments: []storage.VolumeAttachmentParams{{ 813 AttachmentParams: storage.AttachmentParams{ 814 Provider: "ebs", 815 Machine: names.NewMachineTag("1"), 816 }, 817 Volume: names.NewVolumeTag("23"), 818 VolumeId: resp.Id, 819 }}, 820 } 821 _, err = testing.StartInstanceWithParams(env, t.callCtx, "1", args) 822 c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=test-available", as this will prevent attaching the requested EBS volumes in zone "volume-zone"`) 823 } 824 825 func (t *localServerSuite) TestStartInstanceZoneIndependent(c *gc.C) { 826 env := t.prepareAndBootstrap(c) 827 params := environs.StartInstanceParams{ 828 ControllerUUID: t.ControllerUUID, 829 StatusCallback: fakeCallback, 830 AvailabilityZone: "test-available", 831 Placement: "nonsense", 832 } 833 _, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params) 834 c.Assert(err, gc.ErrorMatches, "unknown placement directive: nonsense") 835 // The returned error should indicate that it is independent 836 // of the availability zone specified. 837 c.Assert(err, jc.Satisfies, environs.IsAvailabilityZoneIndependent) 838 } 839 840 func (t *localServerSuite) TestStartInstanceSubnet(c *gc.C) { 841 inst, err := t.testStartInstanceSubnet(c, "0.1.2.0/24") 842 c.Assert(err, jc.ErrorIsNil) 843 ec2Inst := ec2.InstanceEC2(inst) 844 c.Assert(ec2Inst.AvailZone, gc.Equals, "test-available") 845 } 846 847 func (t *localServerSuite) TestStartInstanceSubnetUnavailable(c *gc.C) { 848 // See addTestingSubnets, 0.1.3.0/24 is in state "unavailable", but is in 849 // an AZ that would otherwise be available 850 _, err := t.testStartInstanceSubnet(c, "0.1.3.0/24") 851 c.Assert(err, gc.ErrorMatches, `subnet "0.1.3.0/24" is "unavailable"`) 852 } 853 854 func (t *localServerSuite) TestStartInstanceSubnetAZUnavailable(c *gc.C) { 855 // See addTestingSubnets, 0.1.4.0/24 is in an AZ that is unavailable 856 _, err := t.testStartInstanceSubnet(c, "0.1.4.0/24") 857 c.Assert(err, gc.ErrorMatches, `availability zone "test-unavailable" is "unavailable"`) 858 } 859 860 func (t *localServerSuite) testStartInstanceSubnet(c *gc.C, subnet string) (instances.Instance, error) { 861 subIDs, vpcId := t.addTestingSubnets(c) 862 env := t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{"vpc-id": vpcId, "vpc-id-force": true}) 863 params := environs.StartInstanceParams{ 864 ControllerUUID: t.ControllerUUID, 865 Placement: fmt.Sprintf("subnet=%s", subnet), 866 SubnetsToZones: map[network.Id][]string{ 867 subIDs[0]: {"test-available"}, 868 subIDs[1]: {"test-available"}, 869 subIDs[2]: {"test-unavailable"}, 870 }, 871 } 872 zonedEnviron := env.(common.ZonedEnviron) 873 zones, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params) 874 if err != nil { 875 return nil, err 876 } 877 if len(zones) > 0 { 878 params.AvailabilityZone = zones[0] 879 result, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params) 880 if err != nil { 881 return nil, err 882 } 883 return result.Instance, nil 884 } 885 return nil, errors.Errorf("testStartInstanceSubnet failed") 886 } 887 888 func (t *localServerSuite) TestDeriveAvailabilityZoneSubnetWrongVPC(c *gc.C) { 889 subIDs, vpcId := t.addTestingSubnets(c) 890 c.Assert(vpcId, gc.Not(gc.Equals), "vpc-0") 891 env := t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{"vpc-id": "vpc-0", "vpc-id-force": true}) 892 params := environs.StartInstanceParams{ 893 ControllerUUID: t.ControllerUUID, 894 Placement: "subnet=0.1.2.0/24", 895 SubnetsToZones: map[network.Id][]string{ 896 subIDs[0]: {"test-available"}, 897 subIDs[1]: {"test-available"}, 898 subIDs[2]: {"test-unavailable"}, 899 }, 900 } 901 zonedEnviron := env.(common.ZonedEnviron) 902 _, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params) 903 c.Assert(err, gc.ErrorMatches, `unknown placement directive: subnet=0.1.2.0/24`) 904 } 905 906 func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) { 907 var resultZones []amzec2.AvailabilityZoneInfo 908 var resultErr error 909 t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) { 910 resp := &amzec2.AvailabilityZonesResp{ 911 Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...), 912 } 913 return resp, resultErr 914 }) 915 env := t.Prepare(c).(common.ZonedEnviron) 916 917 resultErr = fmt.Errorf("failed to get availability zones") 918 zones, err := env.AvailabilityZones(t.callCtx) 919 c.Assert(err, gc.Equals, resultErr) 920 c.Assert(zones, gc.IsNil) 921 922 resultErr = nil 923 resultZones = make([]amzec2.AvailabilityZoneInfo, 1) 924 resultZones[0].Name = "whatever" 925 zones, err = env.AvailabilityZones(t.callCtx) 926 c.Assert(err, jc.ErrorIsNil) 927 c.Assert(zones, gc.HasLen, 1) 928 c.Assert(zones[0].Name(), gc.Equals, "whatever") 929 930 // A successful result is cached, currently for the lifetime 931 // of the Environ. This will change if/when we have long-lived 932 // Environs to cut down repeated IaaS requests. 933 resultErr = fmt.Errorf("failed to get availability zones") 934 resultZones[0].Name = "andever" 935 zones, err = env.AvailabilityZones(t.callCtx) 936 c.Assert(err, jc.ErrorIsNil) 937 c.Assert(zones, gc.HasLen, 1) 938 c.Assert(zones[0].Name(), gc.Equals, "whatever") 939 } 940 941 func (t *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) { 942 var resultZones []amzec2.AvailabilityZoneInfo 943 t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) { 944 resp := &amzec2.AvailabilityZonesResp{ 945 Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...), 946 } 947 return resp, nil 948 }) 949 env := t.Prepare(c).(common.ZonedEnviron) 950 resultZones = make([]amzec2.AvailabilityZoneInfo, 2) 951 resultZones[0].Name = "az1" 952 resultZones[1].Name = "az2" 953 resultZones[0].State = "available" 954 resultZones[1].State = "impaired" 955 zones, err := env.AvailabilityZones(t.callCtx) 956 c.Assert(err, jc.ErrorIsNil) 957 c.Assert(zones, gc.HasLen, 2) 958 c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name) 959 c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name) 960 c.Assert(zones[0].Available(), jc.IsTrue) 961 c.Assert(zones[1].Available(), jc.IsFalse) 962 } 963 964 type mockAvailabilityZoneAllocations struct { 965 group []instance.Id // input param 966 result []common.AvailabilityZoneInstances 967 err error 968 } 969 970 func (t *mockAvailabilityZoneAllocations) AvailabilityZoneAllocations( 971 e common.ZonedEnviron, group []instance.Id, 972 ) ([]common.AvailabilityZoneInstances, error) { 973 t.group = group 974 return t.result, t.err 975 } 976 977 func (t *localServerSuite) TestDeriveAvailabilityZones(c *gc.C) { 978 var resultZones []amzec2.AvailabilityZoneInfo 979 t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) { 980 resp := &amzec2.AvailabilityZonesResp{ 981 Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...), 982 } 983 return resp, nil 984 }) 985 env := t.Prepare(c).(common.ZonedEnviron) 986 resultZones = make([]amzec2.AvailabilityZoneInfo, 2) 987 resultZones[0].Name = "az1" 988 resultZones[1].Name = "az2" 989 resultZones[0].State = "available" 990 resultZones[1].State = "impaired" 991 992 zones, err := env.DeriveAvailabilityZones(t.callCtx, environs.StartInstanceParams{Placement: "zone=az1"}) 993 c.Assert(err, jc.ErrorIsNil) 994 c.Assert(zones, gc.DeepEquals, []string{"az1"}) 995 } 996 997 func (t *localServerSuite) TestDeriveAvailabilityZonesImpaired(c *gc.C) { 998 var resultZones []amzec2.AvailabilityZoneInfo 999 t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) { 1000 resp := &amzec2.AvailabilityZonesResp{ 1001 Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...), 1002 } 1003 return resp, nil 1004 }) 1005 env := t.Prepare(c).(common.ZonedEnviron) 1006 resultZones = make([]amzec2.AvailabilityZoneInfo, 2) 1007 resultZones[0].Name = "az1" 1008 resultZones[1].Name = "az2" 1009 resultZones[0].State = "available" 1010 resultZones[1].State = "impaired" 1011 1012 zones, err := env.DeriveAvailabilityZones(t.callCtx, environs.StartInstanceParams{Placement: "zone=az2"}) 1013 c.Assert(err, gc.ErrorMatches, "availability zone \"az2\" is \"impaired\"") 1014 c.Assert(zones, gc.HasLen, 0) 1015 } 1016 1017 func (t *localServerSuite) TestDeriveAvailabilityZonesConflictVolume(c *gc.C) { 1018 resp, err := t.client.CreateVolume(amzec2.CreateVolume{ 1019 VolumeSize: 1, 1020 VolumeType: "gp2", 1021 AvailZone: "volume-zone", 1022 }) 1023 c.Assert(err, jc.ErrorIsNil) 1024 1025 args := environs.StartInstanceParams{ 1026 ControllerUUID: t.ControllerUUID, 1027 StatusCallback: fakeCallback, 1028 Placement: "zone=test-available", 1029 VolumeAttachments: []storage.VolumeAttachmentParams{{ 1030 AttachmentParams: storage.AttachmentParams{ 1031 Provider: "ebs", 1032 Machine: names.NewMachineTag("1"), 1033 }, 1034 Volume: names.NewVolumeTag("23"), 1035 VolumeId: resp.Id, 1036 }}, 1037 } 1038 env := t.Prepare(c).(common.ZonedEnviron) 1039 zones, err := env.DeriveAvailabilityZones(t.callCtx, args) 1040 c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=test-available", as this will prevent attaching the requested EBS volumes in zone "volume-zone"`) 1041 c.Assert(zones, gc.HasLen, 0) 1042 } 1043 1044 func (t *localServerSuite) TestDeriveAvailabilityZonesVolumeNoPlacement(c *gc.C) { 1045 resp, err := t.client.CreateVolume(amzec2.CreateVolume{ 1046 VolumeSize: 1, 1047 VolumeType: "gp2", 1048 AvailZone: "volume-zone", 1049 }) 1050 c.Assert(err, jc.ErrorIsNil) 1051 1052 args := environs.StartInstanceParams{ 1053 ControllerUUID: t.ControllerUUID, 1054 StatusCallback: fakeCallback, 1055 VolumeAttachments: []storage.VolumeAttachmentParams{{ 1056 AttachmentParams: storage.AttachmentParams{ 1057 Provider: "ebs", 1058 Machine: names.NewMachineTag("1"), 1059 }, 1060 Volume: names.NewVolumeTag("23"), 1061 VolumeId: resp.Id, 1062 }}, 1063 } 1064 env := t.Prepare(c).(common.ZonedEnviron) 1065 zones, err := env.DeriveAvailabilityZones(t.callCtx, args) 1066 c.Assert(err, jc.ErrorIsNil) 1067 c.Assert(zones, gc.DeepEquals, []string{"volume-zone"}) 1068 } 1069 1070 var azConstrainedErr = &amzec2.Error{ 1071 Code: "Unsupported", 1072 Message: "The requested Availability Zone is currently constrained etc.", 1073 } 1074 1075 var azVolumeTypeNotAvailableInZoneErr = &amzec2.Error{ 1076 Code: "VolumeTypeNotAvailableInZone", 1077 Message: "blah blah", 1078 } 1079 1080 var azInsufficientInstanceCapacityErr = &amzec2.Error{ 1081 Code: "InsufficientInstanceCapacity", 1082 Message: "We currently do not have sufficient m1.small capacity in the " + 1083 "Availability Zone you requested (us-east-1d). Our system will " + 1084 "be working on provisioning additional capacity. You can currently get m1.small " + 1085 "capacity by not specifying an Availability Zone in your request or choosing " + 1086 "us-east-1c, us-east-1a.", 1087 } 1088 1089 var azNoDefaultSubnetErr = &amzec2.Error{ 1090 Code: "InvalidInput", 1091 Message: "No default subnet for availability zone: ''us-east-1e''.", 1092 } 1093 1094 func (t *localServerSuite) TestStartInstanceAvailZoneAllConstrained(c *gc.C) { 1095 t.testStartInstanceAvailZoneAllConstrained(c, azConstrainedErr) 1096 } 1097 1098 func (t *localServerSuite) TestStartInstanceVolumeTypeNotAvailable(c *gc.C) { 1099 t.testStartInstanceAvailZoneAllConstrained(c, azVolumeTypeNotAvailableInZoneErr) 1100 } 1101 1102 func (t *localServerSuite) TestStartInstanceAvailZoneAllInsufficientInstanceCapacity(c *gc.C) { 1103 t.testStartInstanceAvailZoneAllConstrained(c, azInsufficientInstanceCapacityErr) 1104 } 1105 1106 func (t *localServerSuite) TestStartInstanceAvailZoneAllNoDefaultSubnet(c *gc.C) { 1107 t.testStartInstanceAvailZoneAllConstrained(c, azNoDefaultSubnetErr) 1108 } 1109 1110 func (t *localServerSuite) testStartInstanceAvailZoneAllConstrained(c *gc.C, runInstancesError *amzec2.Error) { 1111 env := t.prepareAndBootstrap(c) 1112 1113 t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ctx context.ProviderCallContext, ri *amzec2.RunInstances, c environs.StatusCallbackFunc) (*amzec2.RunInstancesResp, error) { 1114 return nil, runInstancesError 1115 }) 1116 1117 params := environs.StartInstanceParams{ 1118 ControllerUUID: t.ControllerUUID, 1119 StatusCallback: fakeCallback, 1120 AvailabilityZone: "test-available", 1121 } 1122 1123 _, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params) 1124 // All AZConstrained failures should return an error that does 1125 // *not* satisfy environs.IsAvailabilityZoneIndependent, 1126 // so the caller knows to try a new zone, rather than fail. 1127 c.Assert(err, gc.Not(jc.Satisfies), environs.IsAvailabilityZoneIndependent) 1128 c.Assert(errors.Details(err), jc.Contains, runInstancesError.Message) 1129 } 1130 1131 // addTestingSubnets adds a testing default VPC with 3 subnets in the EC2 test 1132 // server: 2 of the subnets are in the "test-available" AZ, the remaining - in 1133 // "test-unavailable". Returns a slice with the IDs of the created subnets and 1134 // vpc id that those were added to 1135 func (t *localServerSuite) addTestingSubnets(c *gc.C) ([]network.Id, string) { 1136 vpc := t.srv.ec2srv.AddVPC(amzec2.VPC{ 1137 CIDRBlock: "0.1.0.0/16", 1138 IsDefault: true, 1139 }) 1140 results := make([]network.Id, 3) 1141 sub1, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{ 1142 VPCId: vpc.Id, 1143 CIDRBlock: "0.1.2.0/24", 1144 AvailZone: "test-available", 1145 State: "available", 1146 DefaultForAZ: true, 1147 }) 1148 c.Assert(err, jc.ErrorIsNil) 1149 results[0] = network.Id(sub1.Id) 1150 sub2, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{ 1151 VPCId: vpc.Id, 1152 CIDRBlock: "0.1.3.0/24", 1153 AvailZone: "test-available", 1154 State: "unavailable", 1155 }) 1156 c.Assert(err, jc.ErrorIsNil) 1157 results[1] = network.Id(sub2.Id) 1158 sub3, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{ 1159 VPCId: vpc.Id, 1160 CIDRBlock: "0.1.4.0/24", 1161 AvailZone: "test-unavailable", 1162 DefaultForAZ: true, 1163 State: "unavailable", 1164 }) 1165 c.Assert(err, jc.ErrorIsNil) 1166 results[2] = network.Id(sub3.Id) 1167 return results, vpc.Id 1168 } 1169 1170 func (t *localServerSuite) prepareAndBootstrap(c *gc.C) environs.Environ { 1171 return t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{}) 1172 } 1173 1174 func (t *localServerSuite) prepareAndBootstrapWithConfig(c *gc.C, config coretesting.Attrs) environs.Environ { 1175 args := t.PrepareParams(c) 1176 args.ModelConfig = coretesting.Attrs(args.ModelConfig).Merge(config) 1177 env := t.PrepareWithParams(c, args) 1178 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 1179 t.callCtx, bootstrap.BootstrapParams{ 1180 ControllerConfig: coretesting.FakeControllerConfig(), 1181 AdminSecret: testing.AdminSecret, 1182 CAPrivateKey: coretesting.CAKey, 1183 Placement: "zone=test-available", 1184 }) 1185 c.Assert(err, jc.ErrorIsNil) 1186 return env 1187 } 1188 1189 func (t *localServerSuite) TestSpaceConstraintsSpaceNotInPlacementZone(c *gc.C) { 1190 env := t.prepareAndBootstrap(c) 1191 subIDs, _ := t.addTestingSubnets(c) 1192 1193 // Expect an error because zone test-available isn't in SubnetsToZones 1194 params := environs.StartInstanceParams{ 1195 ControllerUUID: t.ControllerUUID, 1196 Placement: "zone=test-available", 1197 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1198 SubnetsToZones: map[network.Id][]string{ 1199 subIDs[0]: {"zone2"}, 1200 subIDs[1]: {"zone3"}, 1201 subIDs[2]: {"zone4"}, 1202 }, 1203 StatusCallback: fakeCallback, 1204 } 1205 _, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params) 1206 c.Assert(err, gc.Not(jc.Satisfies), environs.IsAvailabilityZoneIndependent) 1207 c.Assert(errors.Details(err), gc.Matches, `.*subnets in AZ "test-available" not found.*`) 1208 } 1209 1210 func (t *localServerSuite) TestSpaceConstraintsSpaceInPlacementZone(c *gc.C) { 1211 env := t.prepareAndBootstrap(c) 1212 subIDs, _ := t.addTestingSubnets(c) 1213 1214 // Should work - test-available is in SubnetsToZones and in myspace. 1215 params := environs.StartInstanceParams{ 1216 ControllerUUID: t.ControllerUUID, 1217 Placement: "zone=test-available", 1218 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1219 SubnetsToZones: map[network.Id][]string{ 1220 subIDs[0]: {"test-available"}, 1221 subIDs[1]: {"zone3"}, 1222 }, 1223 StatusCallback: fakeCallback, 1224 } 1225 _, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params) 1226 c.Assert(err, jc.ErrorIsNil) 1227 } 1228 1229 func (t *localServerSuite) TestSpaceConstraintsNoPlacement(c *gc.C) { 1230 env := t.prepareAndBootstrap(c) 1231 subIDs, _ := t.addTestingSubnets(c) 1232 1233 params := environs.StartInstanceParams{ 1234 ControllerUUID: t.ControllerUUID, 1235 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1236 SubnetsToZones: map[network.Id][]string{ 1237 subIDs[0]: {"test-available"}, 1238 subIDs[1]: {"zone3"}, 1239 }, 1240 StatusCallback: fakeCallback, 1241 } 1242 t.assertStartInstanceWithParamsFindAZ(c, env, "1", params) 1243 } 1244 1245 func (t *localServerSuite) assertStartInstanceWithParamsFindAZ( 1246 c *gc.C, 1247 env environs.Environ, 1248 machineId string, 1249 params environs.StartInstanceParams, 1250 ) { 1251 zonedEnviron := env.(common.ZonedEnviron) 1252 zones, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params) 1253 c.Assert(err, jc.ErrorIsNil) 1254 if len(zones) > 0 { 1255 params.AvailabilityZone = zones[0] 1256 _, err = testing.StartInstanceWithParams(env, t.callCtx, "1", params) 1257 c.Assert(err, jc.ErrorIsNil) 1258 return 1259 } 1260 availabilityZones, err := zonedEnviron.AvailabilityZones(t.callCtx) 1261 c.Assert(err, jc.ErrorIsNil) 1262 for _, zone := range availabilityZones { 1263 if !zone.Available() { 1264 continue 1265 } 1266 params.AvailabilityZone = zone.Name() 1267 _, err = testing.StartInstanceWithParams(env, t.callCtx, "1", params) 1268 if err == nil { 1269 return 1270 } else if !environs.IsAvailabilityZoneIndependent(err) { 1271 continue 1272 } 1273 c.Assert(err, jc.ErrorIsNil) 1274 } 1275 } 1276 1277 func (t *localServerSuite) TestSpaceConstraintsNoAvailableSubnets(c *gc.C) { 1278 c.Skip("temporarily disabled") 1279 subIDs, vpcId := t.addTestingSubnets(c) 1280 env := t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{"vpc-id": vpcId}) 1281 1282 // We requested a space, but there are no subnets in SubnetsToZones, so we can't resolve 1283 // the constraints 1284 params := environs.StartInstanceParams{ 1285 ControllerUUID: t.ControllerUUID, 1286 Constraints: constraints.MustParse("spaces=aaaaaaaaaa"), 1287 SubnetsToZones: map[network.Id][]string{ 1288 subIDs[0]: {""}, 1289 }, 1290 StatusCallback: fakeCallback, 1291 } 1292 //_, err := testing.StartInstanceWithParams(env, "1", params) 1293 zonedEnviron := env.(common.ZonedEnviron) 1294 _, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params) 1295 c.Assert(err, gc.ErrorMatches, `unable to resolve constraints: space and/or subnet unavailable in zones \[test-available\]`) 1296 } 1297 1298 func (t *localServerSuite) TestStartInstanceAvailZoneOneConstrained(c *gc.C) { 1299 t.testStartInstanceAvailZoneOneConstrained(c, azConstrainedErr) 1300 } 1301 1302 func (t *localServerSuite) TestStartInstanceAvailZoneOneInsufficientInstanceCapacity(c *gc.C) { 1303 t.testStartInstanceAvailZoneOneConstrained(c, azInsufficientInstanceCapacityErr) 1304 } 1305 1306 func (t *localServerSuite) TestStartInstanceAvailZoneOneNoDefaultSubnetErr(c *gc.C) { 1307 t.testStartInstanceAvailZoneOneConstrained(c, azNoDefaultSubnetErr) 1308 } 1309 1310 func (t *localServerSuite) testStartInstanceAvailZoneOneConstrained(c *gc.C, runInstancesError *amzec2.Error) { 1311 env := t.prepareAndBootstrap(c) 1312 1313 // The first call to RunInstances fails with an error indicating the AZ 1314 // is constrained. The second attempt succeeds, and so allocates to az2. 1315 var azArgs []string 1316 realRunInstances := *ec2.RunInstances 1317 1318 t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ctx context.ProviderCallContext, ri *amzec2.RunInstances, c environs.StatusCallbackFunc) (*amzec2.RunInstancesResp, error) { 1319 azArgs = append(azArgs, ri.AvailZone) 1320 if len(azArgs) == 1 { 1321 return nil, runInstancesError 1322 } 1323 return realRunInstances(e, ctx, ri, fakeCallback) 1324 }) 1325 1326 params := environs.StartInstanceParams{ControllerUUID: t.ControllerUUID} 1327 zonedEnviron := env.(common.ZonedEnviron) 1328 availabilityZones, err := zonedEnviron.AvailabilityZones(t.callCtx) 1329 c.Assert(err, jc.ErrorIsNil) 1330 for _, zone := range availabilityZones { 1331 if !zone.Available() { 1332 continue 1333 } 1334 params.AvailabilityZone = zone.Name() 1335 _, err = testing.StartInstanceWithParams(env, t.callCtx, "1", params) 1336 if err == nil { 1337 break 1338 } else if !environs.IsAvailabilityZoneIndependent(err) { 1339 continue 1340 } 1341 c.Assert(err, jc.ErrorIsNil) 1342 } 1343 sort.Strings(azArgs) 1344 c.Assert(azArgs, gc.DeepEquals, []string{"test-available", "test-available2"}) 1345 } 1346 1347 func (t *localServerSuite) TestAddresses(c *gc.C) { 1348 env := t.prepareAndBootstrap(c) 1349 inst, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1") 1350 addrs, err := inst.Addresses(t.callCtx) 1351 c.Assert(err, jc.ErrorIsNil) 1352 // Expected values use Address type but really contain a regexp for 1353 // the value rather than a valid ip or hostname. 1354 expected := []network.Address{{ 1355 Value: "8.0.0.*", 1356 Type: network.IPv4Address, 1357 Scope: network.ScopePublic, 1358 }, { 1359 Value: "127.0.0.*", 1360 Type: network.IPv4Address, 1361 Scope: network.ScopeCloudLocal, 1362 }} 1363 c.Assert(addrs, gc.HasLen, len(expected)) 1364 for i, addr := range addrs { 1365 c.Check(addr.Value, gc.Matches, expected[i].Value) 1366 c.Check(addr.Type, gc.Equals, expected[i].Type) 1367 c.Check(addr.Scope, gc.Equals, expected[i].Scope) 1368 } 1369 } 1370 1371 func (t *localServerSuite) TestConstraintsValidatorUnsupported(c *gc.C) { 1372 env := t.Prepare(c) 1373 validator, err := env.ConstraintsValidator(t.callCtx) 1374 c.Assert(err, jc.ErrorIsNil) 1375 cons := constraints.MustParse("arch=amd64 tags=foo virt-type=kvm") 1376 unsupported, err := validator.Validate(cons) 1377 c.Assert(err, jc.ErrorIsNil) 1378 c.Assert(unsupported, jc.SameContents, []string{"tags", "virt-type"}) 1379 } 1380 1381 func (t *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) { 1382 env := t.Prepare(c) 1383 validator, err := env.ConstraintsValidator(t.callCtx) 1384 c.Assert(err, jc.ErrorIsNil) 1385 cons := constraints.MustParse("instance-type=foo") 1386 _, err = validator.Validate(cons) 1387 c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*") 1388 } 1389 1390 func (t *localServerSuite) TestConstraintsValidatorVocabNoDefaultOrSpecifiedVPC(c *gc.C) { 1391 t.srv.defaultVPC.IsDefault = false 1392 err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC) 1393 c.Assert(err, jc.ErrorIsNil) 1394 1395 env := t.Prepare(c) 1396 assertVPCInstanceTypeNotAvailable(c, env, t.callCtx) 1397 } 1398 1399 func (t *localServerSuite) TestConstraintsValidatorVocabDefaultVPC(c *gc.C) { 1400 env := t.Prepare(c) 1401 assertVPCInstanceTypeAvailable(c, env, t.callCtx) 1402 } 1403 1404 func (t *localServerSuite) TestConstraintsValidatorVocabSpecifiedVPC(c *gc.C) { 1405 t.srv.defaultVPC.IsDefault = false 1406 err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC) 1407 c.Assert(err, jc.ErrorIsNil) 1408 1409 t.TestConfig["vpc-id"] = t.srv.defaultVPC.Id 1410 defer delete(t.TestConfig, "vpc-id") 1411 1412 env := t.Prepare(c) 1413 assertVPCInstanceTypeAvailable(c, env, t.callCtx) 1414 } 1415 1416 func assertVPCInstanceTypeAvailable(c *gc.C, env environs.Environ, ctx context.ProviderCallContext) { 1417 validator, err := env.ConstraintsValidator(ctx) 1418 c.Assert(err, jc.ErrorIsNil) 1419 _, err = validator.Validate(constraints.MustParse("instance-type=t2.medium")) 1420 c.Assert(err, jc.ErrorIsNil) 1421 } 1422 1423 func assertVPCInstanceTypeNotAvailable(c *gc.C, env environs.Environ, ctx context.ProviderCallContext) { 1424 validator, err := env.ConstraintsValidator(ctx) 1425 c.Assert(err, jc.ErrorIsNil) 1426 _, err = validator.Validate(constraints.MustParse("instance-type=t2.medium")) 1427 c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=t2.medium\n.*") 1428 } 1429 1430 func (t *localServerSuite) TestConstraintsMerge(c *gc.C) { 1431 env := t.Prepare(c) 1432 validator, err := env.ConstraintsValidator(t.callCtx) 1433 c.Assert(err, jc.ErrorIsNil) 1434 consA := constraints.MustParse("arch=amd64 mem=1G cpu-power=10 cores=2 tags=bar") 1435 consB := constraints.MustParse("arch=i386 instance-type=m1.small") 1436 cons, err := validator.Merge(consA, consB) 1437 c.Assert(err, jc.ErrorIsNil) 1438 c.Assert(cons, gc.DeepEquals, constraints.MustParse("arch=i386 instance-type=m1.small tags=bar")) 1439 } 1440 1441 func (t *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) { 1442 env := t.Prepare(c) 1443 cons := constraints.MustParse("instance-type=m1.small root-disk=1G") 1444 err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1445 Series: supportedversion.SupportedLTS(), 1446 Constraints: cons, 1447 }) 1448 c.Assert(err, jc.ErrorIsNil) 1449 } 1450 1451 func (t *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) { 1452 env := t.Prepare(c) 1453 cons := constraints.MustParse("instance-type=m1.invalid") 1454 err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1455 Series: supportedversion.SupportedLTS(), 1456 Constraints: cons, 1457 }) 1458 c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "m1.invalid" specified`) 1459 } 1460 1461 func (t *localServerSuite) TestPrecheckInstanceUnsupportedArch(c *gc.C) { 1462 env := t.Prepare(c) 1463 cons := constraints.MustParse("instance-type=cc1.4xlarge arch=i386") 1464 err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1465 Series: supportedversion.SupportedLTS(), 1466 Constraints: cons, 1467 }) 1468 c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "cc1.4xlarge" and arch "i386" specified`) 1469 } 1470 1471 func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) { 1472 env := t.Prepare(c) 1473 placement := "zone=test-available" 1474 err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1475 Series: supportedversion.SupportedLTS(), 1476 Placement: placement, 1477 }) 1478 c.Assert(err, jc.ErrorIsNil) 1479 } 1480 1481 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) { 1482 env := t.Prepare(c) 1483 placement := "zone=test-unavailable" 1484 err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1485 Series: supportedversion.SupportedLTS(), 1486 Placement: placement, 1487 }) 1488 c.Assert(err, gc.ErrorMatches, `availability zone "test-unavailable" is "unavailable"`) 1489 } 1490 1491 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) { 1492 env := t.Prepare(c) 1493 placement := "zone=test-unknown" 1494 err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1495 Series: supportedversion.SupportedLTS(), 1496 Placement: placement, 1497 }) 1498 c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`) 1499 } 1500 1501 func (t *localServerSuite) TestPrecheckInstanceVolumeAvailZoneNoPlacement(c *gc.C) { 1502 t.testPrecheckInstanceVolumeAvailZone(c, "") 1503 } 1504 1505 func (t *localServerSuite) TestPrecheckInstanceVolumeAvailZoneSameZonePlacement(c *gc.C) { 1506 t.testPrecheckInstanceVolumeAvailZone(c, "zone=test-available") 1507 } 1508 1509 func (t *localServerSuite) testPrecheckInstanceVolumeAvailZone(c *gc.C, placement string) { 1510 env := t.Prepare(c) 1511 resp, err := t.client.CreateVolume(amzec2.CreateVolume{ 1512 VolumeSize: 1, 1513 VolumeType: "gp2", 1514 AvailZone: "test-available", 1515 }) 1516 c.Assert(err, jc.ErrorIsNil) 1517 1518 err = env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1519 Series: supportedversion.SupportedLTS(), 1520 Placement: placement, 1521 VolumeAttachments: []storage.VolumeAttachmentParams{{ 1522 AttachmentParams: storage.AttachmentParams{ 1523 Provider: "ebs", 1524 }, 1525 Volume: names.NewVolumeTag("23"), 1526 VolumeId: resp.Id, 1527 }}, 1528 }) 1529 c.Assert(err, jc.ErrorIsNil) 1530 } 1531 1532 func (t *localServerSuite) TestPrecheckInstanceAvailZoneVolumeConflict(c *gc.C) { 1533 env := t.Prepare(c) 1534 resp, err := t.client.CreateVolume(amzec2.CreateVolume{ 1535 VolumeSize: 1, 1536 VolumeType: "gp2", 1537 AvailZone: "volume-zone", 1538 }) 1539 c.Assert(err, jc.ErrorIsNil) 1540 1541 err = env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{ 1542 Series: supportedversion.SupportedLTS(), 1543 Placement: "zone=test-available", 1544 VolumeAttachments: []storage.VolumeAttachmentParams{{ 1545 AttachmentParams: storage.AttachmentParams{ 1546 Provider: "ebs", 1547 }, 1548 Volume: names.NewVolumeTag("23"), 1549 VolumeId: resp.Id, 1550 }}, 1551 }) 1552 c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=test-available", as this will prevent attaching the requested EBS volumes in zone "volume-zone"`) 1553 } 1554 1555 func (t *localServerSuite) TestValidateImageMetadata(c *gc.C) { 1556 region := t.srv.region 1557 aws.Regions[region.Name] = t.srv.region 1558 defer delete(aws.Regions, region.Name) 1559 1560 env := t.Prepare(c) 1561 params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("test") 1562 c.Assert(err, jc.ErrorIsNil) 1563 params.Series = supportedversion.SupportedLTS() 1564 params.Endpoint = region.EC2Endpoint 1565 params.Sources, err = environs.ImageMetadataSources(env) 1566 c.Assert(err, jc.ErrorIsNil) 1567 image_ids, _, err := imagemetadata.ValidateImageMetadata(params) 1568 c.Assert(err, jc.ErrorIsNil) 1569 sort.Strings(image_ids) 1570 c.Assert(image_ids, gc.DeepEquals, []string{"ami-00001133", "ami-00001135", "ami-00001139"}) 1571 } 1572 1573 func (t *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { 1574 t.PatchValue(&tools.DefaultBaseURL, "") 1575 1576 env := t.Prepare(c) 1577 sources, err := tools.GetMetadataSources(env) 1578 c.Assert(err, jc.ErrorIsNil) 1579 c.Assert(sources, gc.HasLen, 0) 1580 } 1581 1582 func (t *localServerSuite) TestSupportsNetworking(c *gc.C) { 1583 env := t.Prepare(c) 1584 _, supported := environs.SupportsNetworking(env) 1585 c.Assert(supported, jc.IsTrue) 1586 } 1587 1588 func (t *localServerSuite) setUpInstanceWithDefaultVpc(c *gc.C) (environs.NetworkingEnviron, instance.Id) { 1589 env := t.prepareEnviron(c) 1590 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, 1591 t.callCtx, bootstrap.BootstrapParams{ 1592 ControllerConfig: coretesting.FakeControllerConfig(), 1593 AdminSecret: testing.AdminSecret, 1594 CAPrivateKey: coretesting.CAKey, 1595 }) 1596 c.Assert(err, jc.ErrorIsNil) 1597 1598 instanceIds, err := env.ControllerInstances(t.callCtx, t.ControllerUUID) 1599 c.Assert(err, jc.ErrorIsNil) 1600 return env, instanceIds[0] 1601 } 1602 1603 func (t *localServerSuite) TestNetworkInterfaces(c *gc.C) { 1604 env, instId := t.setUpInstanceWithDefaultVpc(c) 1605 interfaces, err := env.NetworkInterfaces(t.callCtx, instId) 1606 c.Assert(err, jc.ErrorIsNil) 1607 1608 // The CIDR isn't predictable, but it is in the 10.10.x.0/24 format 1609 // The subnet ID is in the form "subnet-x", where x matches the same 1610 // number from the CIDR. The interfaces address is part of the CIDR. 1611 // For these reasons we check that the CIDR is in the expected format 1612 // and derive the expected values for ProviderSubnetId and Address. 1613 c.Assert(interfaces, gc.HasLen, 1) 1614 cidr := interfaces[0].CIDR 1615 re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`) 1616 c.Assert(re.Match([]byte(cidr)), jc.IsTrue) 1617 index := re.FindStringSubmatch(cidr)[1] 1618 addr := fmt.Sprintf("10.10.%s.5", index) 1619 subnetId := network.Id("subnet-" + index) 1620 1621 // AvailabilityZones will either contain "test-available", 1622 // "test-impaired" or "test-unavailable" depending on which subnet is 1623 // picked. Any of these is fine. 1624 zones := interfaces[0].AvailabilityZones 1625 c.Assert(zones, gc.HasLen, 1) 1626 re = regexp.MustCompile("test-available|test-unavailable|test-impaired") 1627 c.Assert(re.Match([]byte(zones[0])), jc.IsTrue) 1628 1629 expectedInterfaces := []network.InterfaceInfo{{ 1630 DeviceIndex: 0, 1631 MACAddress: "20:01:60:cb:27:37", 1632 CIDR: cidr, 1633 ProviderId: "eni-0", 1634 ProviderSubnetId: subnetId, 1635 VLANTag: 0, 1636 InterfaceName: "unsupported0", 1637 Disabled: false, 1638 NoAutoStart: false, 1639 ConfigType: network.ConfigDHCP, 1640 InterfaceType: network.EthernetInterface, 1641 Address: network.NewScopedAddress(addr, network.ScopeCloudLocal), 1642 AvailabilityZones: zones, 1643 }} 1644 c.Assert(interfaces, jc.DeepEquals, expectedInterfaces) 1645 } 1646 1647 func (t *localServerSuite) TestSubnetsWithInstanceId(c *gc.C) { 1648 env, instId := t.setUpInstanceWithDefaultVpc(c) 1649 subnets, err := env.Subnets(t.callCtx, instId, nil) 1650 c.Assert(err, jc.ErrorIsNil) 1651 c.Assert(subnets, gc.HasLen, 1) 1652 validateSubnets(c, subnets, "") 1653 1654 interfaces, err := env.NetworkInterfaces(t.callCtx, instId) 1655 c.Assert(err, jc.ErrorIsNil) 1656 c.Assert(interfaces, gc.HasLen, 1) 1657 c.Assert(interfaces[0].ProviderSubnetId, gc.Equals, subnets[0].ProviderId) 1658 } 1659 1660 func (t *localServerSuite) TestSubnetsWithInstanceIdAndSubnetId(c *gc.C) { 1661 env, instId := t.setUpInstanceWithDefaultVpc(c) 1662 interfaces, err := env.NetworkInterfaces(t.callCtx, instId) 1663 c.Assert(err, jc.ErrorIsNil) 1664 c.Assert(interfaces, gc.HasLen, 1) 1665 1666 subnets, err := env.Subnets(t.callCtx, instId, []network.Id{interfaces[0].ProviderSubnetId}) 1667 c.Assert(err, jc.ErrorIsNil) 1668 c.Assert(subnets, gc.HasLen, 1) 1669 c.Assert(subnets[0].ProviderId, gc.Equals, interfaces[0].ProviderSubnetId) 1670 validateSubnets(c, subnets, "") 1671 } 1672 1673 func (t *localServerSuite) TestSubnetsWithInstanceIdMissingSubnet(c *gc.C) { 1674 env, instId := t.setUpInstanceWithDefaultVpc(c) 1675 subnets, err := env.Subnets(t.callCtx, instId, []network.Id{"missing"}) 1676 c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[missing\]`) 1677 c.Assert(subnets, gc.HasLen, 0) 1678 } 1679 1680 func (t *localServerSuite) TestInstanceInformation(c *gc.C) { 1681 // TODO(macgreagoir) Where do these magic length numbers come from? 1682 c.Skip("Hard-coded InstanceTypes counts without explanation") 1683 env := t.prepareEnviron(c) 1684 types, err := env.InstanceTypes(t.callCtx, constraints.Value{}) 1685 c.Assert(err, jc.ErrorIsNil) 1686 c.Assert(types.InstanceTypes, gc.HasLen, 53) 1687 1688 cons := constraints.MustParse("mem=4G") 1689 types, err = env.InstanceTypes(t.callCtx, cons) 1690 c.Assert(err, jc.ErrorIsNil) 1691 c.Assert(types.InstanceTypes, gc.HasLen, 48) 1692 } 1693 1694 func validateSubnets(c *gc.C, subnets []network.SubnetInfo, vpcId network.Id) { 1695 // These are defined in the test server for the testing default 1696 // VPC. 1697 defaultSubnets := []network.SubnetInfo{{ 1698 CIDR: "10.10.0.0/24", 1699 ProviderId: "subnet-0", 1700 ProviderNetworkId: vpcId, 1701 VLANTag: 0, 1702 AvailabilityZones: []string{"test-available2"}, 1703 }, { 1704 CIDR: "10.10.1.0/24", 1705 ProviderId: "subnet-1", 1706 ProviderNetworkId: vpcId, 1707 VLANTag: 0, 1708 AvailabilityZones: []string{"test-available"}, 1709 }, { 1710 CIDR: "10.10.2.0/24", 1711 ProviderId: "subnet-2", 1712 ProviderNetworkId: vpcId, 1713 VLANTag: 0, 1714 AvailabilityZones: []string{"test-impaired"}, 1715 }, { 1716 CIDR: "10.10.3.0/24", 1717 ProviderId: "subnet-3", 1718 ProviderNetworkId: vpcId, 1719 VLANTag: 0, 1720 AvailabilityZones: []string{"test-unavailable"}, 1721 }} 1722 1723 re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`) 1724 for _, subnet := range subnets { 1725 // We can find the expected data by looking at the CIDR. 1726 // subnets isn't in a predictable order due to the use of maps. 1727 c.Assert(re.Match([]byte(subnet.CIDR)), jc.IsTrue) 1728 index, err := strconv.Atoi(re.FindStringSubmatch(subnet.CIDR)[1]) 1729 c.Assert(err, jc.ErrorIsNil) 1730 // Don't know which AZ the subnet will end up in. 1731 defaultSubnets[index].AvailabilityZones = subnet.AvailabilityZones 1732 c.Check(subnet, jc.DeepEquals, defaultSubnets[index]) 1733 } 1734 } 1735 1736 func (t *localServerSuite) TestSubnets(c *gc.C) { 1737 env, _ := t.setUpInstanceWithDefaultVpc(c) 1738 1739 subnets, err := env.Subnets(t.callCtx, instance.UnknownId, []network.Id{"subnet-0"}) 1740 c.Assert(err, jc.ErrorIsNil) 1741 c.Assert(subnets, gc.HasLen, 1) 1742 validateSubnets(c, subnets, "vpc-0") 1743 1744 subnets, err = env.Subnets(t.callCtx, instance.UnknownId, nil) 1745 c.Assert(err, jc.ErrorIsNil) 1746 c.Assert(subnets, gc.HasLen, 4) 1747 validateSubnets(c, subnets, "vpc-0") 1748 } 1749 1750 func (t *localServerSuite) TestSubnetsMissingSubnet(c *gc.C) { 1751 env, _ := t.setUpInstanceWithDefaultVpc(c) 1752 1753 _, err := env.Subnets(t.callCtx, "", []network.Id{"subnet-0", "Missing"}) 1754 c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[Missing\]`) 1755 } 1756 1757 func (t *localServerSuite) TestInstanceTags(c *gc.C) { 1758 env := t.prepareAndBootstrap(c) 1759 1760 instances, err := env.AllInstances(t.callCtx) 1761 c.Assert(err, jc.ErrorIsNil) 1762 c.Assert(instances, gc.HasLen, 1) 1763 1764 ec2Inst := ec2.InstanceEC2(instances[0]) 1765 c.Assert(ec2Inst.Tags, jc.SameContents, []amzec2.Tag{ 1766 {"Name", "juju-sample-machine-0"}, 1767 {"juju-model-uuid", coretesting.ModelTag.Id()}, 1768 {"juju-controller-uuid", t.ControllerUUID}, 1769 {"juju-is-controller", "true"}, 1770 }) 1771 } 1772 1773 func (t *localServerSuite) TestRootDiskTags(c *gc.C) { 1774 env := t.prepareAndBootstrap(c) 1775 1776 instances, err := env.AllInstances(t.callCtx) 1777 c.Assert(err, jc.ErrorIsNil) 1778 c.Assert(instances, gc.HasLen, 1) 1779 1780 ec2conn := ec2.EnvironEC2(env) 1781 resp, err := ec2conn.Volumes(nil, nil) 1782 c.Assert(err, jc.ErrorIsNil) 1783 c.Assert(resp.Volumes, gc.Not(gc.HasLen), 0) 1784 1785 var found *amzec2.Volume 1786 for _, vol := range resp.Volumes { 1787 if len(vol.Tags) != 0 { 1788 found = &vol 1789 break 1790 } 1791 } 1792 c.Assert(found, gc.NotNil) 1793 c.Assert(found.Tags, jc.SameContents, []amzec2.Tag{ 1794 {"Name", "juju-sample-machine-0-root"}, 1795 {"juju-model-uuid", coretesting.ModelTag.Id()}, 1796 {"juju-controller-uuid", t.ControllerUUID}, 1797 }) 1798 } 1799 1800 func (s *localServerSuite) TestBootstrapInstanceConstraints(c *gc.C) { 1801 env := s.prepareAndBootstrap(c) 1802 inst, err := env.AllInstances(s.callCtx) 1803 c.Assert(err, jc.ErrorIsNil) 1804 c.Assert(inst, gc.HasLen, 1) 1805 ec2inst := ec2.InstanceEC2(inst[0]) 1806 // Controllers should be started with a burstable 1807 // instance if possible, and a 32 GiB disk. 1808 c.Assert(ec2inst.InstanceType, gc.Equals, "t3.medium") 1809 } 1810 1811 func makeFilter(key string, values ...string) *amzec2.Filter { 1812 result := amzec2.NewFilter() 1813 result.Add(key, values...) 1814 return result 1815 } 1816 1817 func (s *localServerSuite) TestAdoptResources(c *gc.C) { 1818 controllerEnv := s.prepareAndBootstrap(c) 1819 controllerInsts, err := controllerEnv.AllInstances(s.callCtx) 1820 c.Assert(err, jc.ErrorIsNil) 1821 c.Assert(controllerInsts, gc.HasLen, 1) 1822 1823 controllerVolumes, err := ec2.AllModelVolumes(controllerEnv, s.callCtx) 1824 c.Assert(err, jc.ErrorIsNil) 1825 1826 controllerGroups, err := ec2.AllModelGroups(controllerEnv, s.callCtx) 1827 c.Assert(err, jc.ErrorIsNil) 1828 1829 // Create a hosted model environment with an instance and a volume. 1830 hostedModelUUID := "7e386e08-cba7-44a4-a76e-7c1633584210" 1831 s.srv.ec2srv.SetInitialInstanceState(ec2test.Running) 1832 cfg, err := controllerEnv.Config().Apply(map[string]interface{}{ 1833 "uuid": hostedModelUUID, 1834 "firewall-mode": "global", 1835 }) 1836 c.Assert(err, jc.ErrorIsNil) 1837 env, err := environs.New(environs.OpenParams{ 1838 Cloud: s.CloudSpec(), 1839 Config: cfg, 1840 }) 1841 c.Assert(err, jc.ErrorIsNil) 1842 inst, _ := testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, "0") 1843 c.Assert(err, jc.ErrorIsNil) 1844 ebsProvider, err := env.StorageProvider(ec2.EBS_ProviderType) 1845 c.Assert(err, jc.ErrorIsNil) 1846 vs, err := ebsProvider.VolumeSource(nil) 1847 c.Assert(err, jc.ErrorIsNil) 1848 volumeResults, err := vs.CreateVolumes(s.callCtx, []storage.VolumeParams{{ 1849 Tag: names.NewVolumeTag("0"), 1850 Size: 1024, 1851 Provider: ec2.EBS_ProviderType, 1852 ResourceTags: map[string]string{ 1853 tags.JujuController: s.ControllerUUID, 1854 tags.JujuModel: hostedModelUUID, 1855 }, 1856 Attachment: &storage.VolumeAttachmentParams{ 1857 AttachmentParams: storage.AttachmentParams{ 1858 InstanceId: inst.Id(), 1859 }, 1860 }, 1861 }}) 1862 c.Assert(err, jc.ErrorIsNil) 1863 c.Assert(volumeResults, gc.HasLen, 1) 1864 c.Assert(volumeResults[0].Error, jc.ErrorIsNil) 1865 1866 modelVolumes, err := ec2.AllModelVolumes(env, s.callCtx) 1867 c.Assert(err, jc.ErrorIsNil) 1868 allVolumes := append([]string{}, controllerVolumes...) 1869 allVolumes = append(allVolumes, modelVolumes...) 1870 1871 modelGroups, err := ec2.AllModelGroups(env, s.callCtx) 1872 c.Assert(err, jc.ErrorIsNil) 1873 allGroups := append([]string{}, controllerGroups...) 1874 allGroups = append(allGroups, modelGroups...) 1875 1876 ec2conn := ec2.EnvironEC2(env) 1877 1878 origController := coretesting.ControllerTag.Id() 1879 1880 checkInstanceTags := func(controllerUUID string, expectedIds ...string) { 1881 resp, err := ec2conn.Instances( 1882 nil, makeFilter("tag:"+tags.JujuController, controllerUUID)) 1883 c.Assert(err, jc.ErrorIsNil) 1884 actualIds := set.NewStrings() 1885 for _, reservation := range resp.Reservations { 1886 for _, instance := range reservation.Instances { 1887 actualIds.Add(instance.InstanceId) 1888 } 1889 } 1890 c.Check(actualIds, gc.DeepEquals, set.NewStrings(expectedIds...)) 1891 } 1892 1893 checkVolumeTags := func(controllerUUID string, expectedIds ...string) { 1894 resp, err := ec2conn.Volumes( 1895 nil, makeFilter("tag:"+tags.JujuController, controllerUUID)) 1896 c.Assert(err, jc.ErrorIsNil) 1897 actualIds := set.NewStrings() 1898 for _, vol := range resp.Volumes { 1899 actualIds.Add(vol.Id) 1900 } 1901 c.Check(actualIds, gc.DeepEquals, set.NewStrings(expectedIds...)) 1902 } 1903 1904 checkGroupTags := func(controllerUUID string, expectedIds ...string) { 1905 resp, err := ec2conn.SecurityGroups( 1906 nil, makeFilter("tag:"+tags.JujuController, controllerUUID)) 1907 c.Assert(err, jc.ErrorIsNil) 1908 actualIds := set.NewStrings() 1909 for _, group := range resp.Groups { 1910 actualIds.Add(group.Id) 1911 } 1912 c.Check(actualIds, gc.DeepEquals, set.NewStrings(expectedIds...)) 1913 } 1914 1915 checkInstanceTags(origController, string(inst.Id()), string(controllerInsts[0].Id())) 1916 checkVolumeTags(origController, allVolumes...) 1917 checkGroupTags(origController, allGroups...) 1918 1919 err = env.AdoptResources(s.callCtx, "new-controller", version.MustParse("0.0.1")) 1920 c.Assert(err, jc.ErrorIsNil) 1921 1922 checkInstanceTags("new-controller", string(inst.Id())) 1923 checkInstanceTags(origController, string(controllerInsts[0].Id())) 1924 checkVolumeTags("new-controller", modelVolumes...) 1925 checkVolumeTags(origController, controllerVolumes...) 1926 checkGroupTags("new-controller", modelGroups...) 1927 checkGroupTags(origController, controllerGroups...) 1928 } 1929 1930 // localNonUSEastSuite is similar to localServerSuite but the S3 mock server 1931 // behaves as if it is not in the us-east region. 1932 type localNonUSEastSuite struct { 1933 coretesting.BaseSuite 1934 sstesting.TestDataSuite 1935 1936 srv localServer 1937 env environs.Environ 1938 } 1939 1940 func (t *localNonUSEastSuite) SetUpSuite(c *gc.C) { 1941 t.BaseSuite.SetUpSuite(c) 1942 t.TestDataSuite.SetUpSuite(c) 1943 1944 t.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey) 1945 t.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 1946 t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc) 1947 } 1948 1949 func (t *localNonUSEastSuite) TearDownSuite(c *gc.C) { 1950 t.TestDataSuite.TearDownSuite(c) 1951 t.BaseSuite.TearDownSuite(c) 1952 } 1953 1954 func (t *localNonUSEastSuite) SetUpTest(c *gc.C) { 1955 t.BaseSuite.SetUpTest(c) 1956 t.srv.startServer(c) 1957 1958 region := t.srv.region 1959 credential := cloud.NewCredential( 1960 cloud.AccessKeyAuthType, 1961 map[string]string{ 1962 "access-key": "x", 1963 "secret-key": "x", 1964 }, 1965 ) 1966 restoreEC2Patching := patchEC2ForTesting(c, region) 1967 t.AddCleanup(func(c *gc.C) { restoreEC2Patching() }) 1968 1969 env, err := bootstrap.PrepareController( 1970 false, 1971 envtesting.BootstrapContext(c), 1972 jujuclient.NewMemStore(), 1973 bootstrap.PrepareParams{ 1974 ControllerConfig: coretesting.FakeControllerConfig(), 1975 ModelConfig: localConfigAttrs, 1976 ControllerName: localConfigAttrs["name"].(string), 1977 Cloud: environs.CloudSpec{ 1978 Type: "ec2", 1979 Region: region.Name, 1980 Endpoint: region.EC2Endpoint, 1981 Credential: &credential, 1982 }, 1983 AdminSecret: testing.AdminSecret, 1984 }, 1985 ) 1986 c.Assert(err, jc.ErrorIsNil) 1987 t.env = env.(environs.Environ) 1988 } 1989 1990 func (t *localNonUSEastSuite) TearDownTest(c *gc.C) { 1991 t.srv.stopServer(c) 1992 t.BaseSuite.TearDownTest(c) 1993 } 1994 1995 func patchEC2ForTesting(c *gc.C, region aws.Region) func() { 1996 ec2.UseTestImageData(c, ec2.MakeTestImageStreamsData(region)) 1997 restoreTimeouts := envtesting.PatchAttemptStrategies(ec2.ShortAttempt) 1998 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 1999 return func() { 2000 restoreFinishBootstrap() 2001 restoreTimeouts() 2002 ec2.UseTestImageData(c, nil) 2003 } 2004 } 2005 2006 // If match is true, CheckScripts checks that at least one script started 2007 // by the cloudinit data matches the given regexp pattern, otherwise it 2008 // checks that no script matches. It's exported so it can be used by tests 2009 // defined in ec2_test. 2010 func CheckScripts(c *gc.C, userDataMap map[string]interface{}, pattern string, match bool) { 2011 scripts0 := userDataMap["runcmd"] 2012 if scripts0 == nil { 2013 c.Errorf("cloudinit has no entry for runcmd") 2014 return 2015 } 2016 scripts := scripts0.([]interface{}) 2017 re := regexp.MustCompile(pattern) 2018 found := false 2019 for _, s0 := range scripts { 2020 s := s0.(string) 2021 if re.MatchString(s) { 2022 found = true 2023 } 2024 } 2025 switch { 2026 case match && !found: 2027 c.Errorf("script %q not found in %q", pattern, scripts) 2028 case !match && found: 2029 c.Errorf("script %q found but not expected in %q", pattern, scripts) 2030 } 2031 } 2032 2033 // CheckPackage checks that the cloudinit will or won't install the given 2034 // package, depending on the value of match. It's exported so it can be 2035 // used by tests defined outside the ec2 package. 2036 func CheckPackage(c *gc.C, userDataMap map[string]interface{}, pkg string, match bool) { 2037 pkgs0 := userDataMap["packages"] 2038 if pkgs0 == nil { 2039 if match { 2040 c.Errorf("cloudinit has no entry for packages") 2041 } 2042 return 2043 } 2044 2045 pkgs := pkgs0.([]interface{}) 2046 2047 found := false 2048 for _, p0 := range pkgs { 2049 p := p0.(string) 2050 // p might be a space separate list of packages eg 'foo bar qed' so split them up 2051 manyPkgs := set.NewStrings(strings.Split(p, " ")...) 2052 hasPkg := manyPkgs.Contains(pkg) 2053 if p == pkg || hasPkg { 2054 found = true 2055 break 2056 } 2057 } 2058 switch { 2059 case match && !found: 2060 c.Errorf("package %q not found in %v", pkg, pkgs) 2061 case !match && found: 2062 c.Errorf("%q found but not expected in %v", pkg, pkgs) 2063 } 2064 }