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