github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/ec2/local_test.go (about)

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