github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/provider/ec2/local_test.go (about)

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