github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils"
    17  	"github.com/juju/utils/arch"
    18  	"github.com/juju/utils/set"
    19  	"gopkg.in/amz.v3/aws"
    20  	amzec2 "gopkg.in/amz.v3/ec2"
    21  	"gopkg.in/amz.v3/ec2/ec2test"
    22  	"gopkg.in/amz.v3/s3/s3test"
    23  	gc "gopkg.in/check.v1"
    24  	goyaml "gopkg.in/yaml.v1"
    25  
    26  	"github.com/juju/juju/constraints"
    27  	"github.com/juju/juju/environs"
    28  	"github.com/juju/juju/environs/bootstrap"
    29  	"github.com/juju/juju/environs/config"
    30  	"github.com/juju/juju/environs/configstore"
    31  	"github.com/juju/juju/environs/imagemetadata"
    32  	"github.com/juju/juju/environs/jujutest"
    33  	"github.com/juju/juju/environs/simplestreams"
    34  	envtesting "github.com/juju/juju/environs/testing"
    35  	"github.com/juju/juju/environs/tools"
    36  	"github.com/juju/juju/feature"
    37  	"github.com/juju/juju/instance"
    38  	"github.com/juju/juju/juju/testing"
    39  	"github.com/juju/juju/network"
    40  	"github.com/juju/juju/provider/common"
    41  	"github.com/juju/juju/provider/ec2"
    42  	coretesting "github.com/juju/juju/testing"
    43  	"github.com/juju/juju/utils/ssh"
    44  	"github.com/juju/juju/version"
    45  )
    46  
    47  type ProviderSuite struct {
    48  	coretesting.BaseSuite
    49  }
    50  
    51  var _ = gc.Suite(&ProviderSuite{})
    52  
    53  var localConfigAttrs = coretesting.FakeConfig().Merge(coretesting.Attrs{
    54  	"name":           "sample",
    55  	"type":           "ec2",
    56  	"region":         "test",
    57  	"control-bucket": "test-bucket",
    58  	"access-key":     "x",
    59  	"secret-key":     "x",
    60  	"agent-version":  coretesting.FakeVersionNumber.String(),
    61  })
    62  
    63  func registerLocalTests() {
    64  	// N.B. Make sure the region we use here
    65  	// has entries in the images/query txt files.
    66  	aws.Regions["test"] = aws.Region{
    67  		Name: "test",
    68  	}
    69  
    70  	gc.Suite(&localServerSuite{})
    71  	gc.Suite(&localLiveSuite{})
    72  	gc.Suite(&localNonUSEastSuite{})
    73  }
    74  
    75  // localLiveSuite runs tests from LiveTests using a fake
    76  // EC2 server that runs within the test process itself.
    77  type localLiveSuite struct {
    78  	LiveTests
    79  	srv                localServer
    80  	restoreEC2Patching func()
    81  }
    82  
    83  func (t *localLiveSuite) SetUpSuite(c *gc.C) {
    84  	// Upload arches that ec2 supports; add to this
    85  	// as ec2 coverage expands.
    86  	t.UploadArches = []string{arch.AMD64, arch.I386}
    87  	t.TestConfig = localConfigAttrs
    88  	t.restoreEC2Patching = patchEC2ForTesting()
    89  	t.srv.createRootDisks = true
    90  	t.srv.startServer(c)
    91  	t.LiveTests.SetUpSuite(c)
    92  }
    93  
    94  func (t *localLiveSuite) TearDownSuite(c *gc.C) {
    95  	t.LiveTests.TearDownSuite(c)
    96  	t.srv.stopServer(c)
    97  	t.restoreEC2Patching()
    98  }
    99  
   100  // localServer represents a fake EC2 server running within
   101  // the test process itself.
   102  type localServer struct {
   103  	// createRootDisks is used to decide whether or not
   104  	// the ec2test server will create root disks for
   105  	// instances.
   106  	createRootDisks bool
   107  
   108  	ec2srv *ec2test.Server
   109  	s3srv  *s3test.Server
   110  	config *s3test.Config
   111  }
   112  
   113  func (srv *localServer) startServer(c *gc.C) {
   114  	var err error
   115  	srv.ec2srv, err = ec2test.NewServer()
   116  	if err != nil {
   117  		c.Fatalf("cannot start ec2 test server: %v", err)
   118  	}
   119  	srv.ec2srv.SetCreateRootDisks(srv.createRootDisks)
   120  	srv.s3srv, err = s3test.NewServer(srv.config)
   121  	if err != nil {
   122  		c.Fatalf("cannot start s3 test server: %v", err)
   123  	}
   124  	aws.Regions["test"] = aws.Region{
   125  		Name:                 "test",
   126  		EC2Endpoint:          srv.ec2srv.URL(),
   127  		S3Endpoint:           srv.s3srv.URL(),
   128  		S3LocationConstraint: true,
   129  	}
   130  	srv.addSpice(c)
   131  
   132  	zones := make([]amzec2.AvailabilityZoneInfo, 3)
   133  	zones[0].Region = "test"
   134  	zones[0].Name = "test-available"
   135  	zones[0].State = "available"
   136  	zones[1].Region = "test"
   137  	zones[1].Name = "test-impaired"
   138  	zones[1].State = "impaired"
   139  	zones[2].Region = "test"
   140  	zones[2].Name = "test-unavailable"
   141  	zones[2].State = "unavailable"
   142  	srv.ec2srv.SetAvailabilityZones(zones)
   143  	srv.ec2srv.SetInitialInstanceState(ec2test.Pending)
   144  }
   145  
   146  // addSpice adds some "spice" to the local server
   147  // by adding state that may cause tests to fail.
   148  func (srv *localServer) addSpice(c *gc.C) {
   149  	states := []amzec2.InstanceState{
   150  		ec2test.ShuttingDown,
   151  		ec2test.Terminated,
   152  		ec2test.Stopped,
   153  	}
   154  	for _, state := range states {
   155  		srv.ec2srv.NewInstances(1, "m1.small", "ami-a7f539ce", state, nil)
   156  	}
   157  }
   158  
   159  func (srv *localServer) stopServer(c *gc.C) {
   160  	srv.ec2srv.Quit()
   161  	srv.s3srv.Quit()
   162  	// Clear out the region because the server address is
   163  	// no longer valid.
   164  	delete(aws.Regions, "test")
   165  }
   166  
   167  // localServerSuite contains tests that run against a fake EC2 server
   168  // running within the test process itself.  These tests can test things that
   169  // would be unreasonably slow or expensive to test on a live Amazon server.
   170  // It starts a new local ec2test server for each test.  The server is
   171  // accessed by using the "test" region, which is changed to point to the
   172  // network address of the local server.
   173  type localServerSuite struct {
   174  	coretesting.BaseSuite
   175  	jujutest.Tests
   176  	srv                localServer
   177  	restoreEC2Patching func()
   178  }
   179  
   180  func (t *localServerSuite) SetUpSuite(c *gc.C) {
   181  	// Upload arches that ec2 supports; add to this
   182  	// as ec2 coverage expands.
   183  	t.UploadArches = []string{arch.AMD64, arch.I386}
   184  	t.TestConfig = localConfigAttrs
   185  	t.restoreEC2Patching = patchEC2ForTesting()
   186  	t.BaseSuite.SetUpSuite(c)
   187  	t.srv.createRootDisks = true
   188  }
   189  
   190  func (t *localServerSuite) TearDownSuite(c *gc.C) {
   191  	t.BaseSuite.TearDownSuite(c)
   192  	t.restoreEC2Patching()
   193  }
   194  
   195  func (t *localServerSuite) SetUpTest(c *gc.C) {
   196  	t.PatchValue(&version.Current, version.Binary{
   197  		Number: coretesting.FakeVersionNumber,
   198  		Series: coretesting.FakeDefaultSeries,
   199  		Arch:   arch.AMD64,
   200  	})
   201  	t.BaseSuite.SetUpTest(c)
   202  	t.SetFeatureFlags(feature.AddressAllocation)
   203  	t.srv.startServer(c)
   204  	t.Tests.SetUpTest(c)
   205  }
   206  
   207  func (t *localServerSuite) TearDownTest(c *gc.C) {
   208  	t.Tests.TearDownTest(c)
   209  	t.srv.stopServer(c)
   210  	t.BaseSuite.TearDownTest(c)
   211  }
   212  
   213  func (t *localServerSuite) prepareEnviron(c *gc.C) environs.NetworkingEnviron {
   214  	env := t.Prepare(c)
   215  	netenv, supported := environs.SupportsNetworking(env)
   216  	c.Assert(supported, jc.IsTrue)
   217  	return netenv
   218  }
   219  
   220  func (t *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) {
   221  	env := t.Prepare(c)
   222  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   223  	c.Assert(err, jc.ErrorIsNil)
   224  
   225  	// check that StateServerInstances returns the id of the bootstrap machine.
   226  	instanceIds, err := env.StateServerInstances()
   227  	c.Assert(err, jc.ErrorIsNil)
   228  	c.Assert(instanceIds, gc.HasLen, 1)
   229  
   230  	insts, err := env.AllInstances()
   231  	c.Assert(err, jc.ErrorIsNil)
   232  	c.Assert(insts, gc.HasLen, 1)
   233  	c.Check(insts[0].Id(), gc.Equals, instanceIds[0])
   234  
   235  	// check that the user data is configured to start zookeeper
   236  	// and the machine and provisioning agents.
   237  	// check that the user data is configured to only configure
   238  	// authorized SSH keys and set the log output; everything
   239  	// else happens after the machine is brought up.
   240  	inst := t.srv.ec2srv.Instance(string(insts[0].Id()))
   241  	c.Assert(inst, gc.NotNil)
   242  	addresses, err := insts[0].Addresses()
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
   245  	userData, err := utils.Gunzip(inst.UserData)
   246  	c.Assert(err, jc.ErrorIsNil)
   247  	c.Logf("first instance: UserData: %q", userData)
   248  	var userDataMap map[interface{}]interface{}
   249  	err = goyaml.Unmarshal(userData, &userDataMap)
   250  	c.Assert(err, jc.ErrorIsNil)
   251  	c.Assert(userDataMap, jc.DeepEquals, map[interface{}]interface{}{
   252  		"output": map[interface{}]interface{}{
   253  			"all": "| tee -a /var/log/cloud-init-output.log",
   254  		},
   255  		"ssh_authorized_keys": splitAuthKeys(env.Config().AuthorizedKeys()),
   256  		"runcmd": []interface{}{
   257  			"set -xe",
   258  			"install -D -m 644 /dev/null '/etc/init/juju-clean-shutdown.conf'",
   259  			"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'",
   260  			"install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'",
   261  			"printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'",
   262  		},
   263  	})
   264  
   265  	// check that a new instance will be started with a machine agent
   266  	inst1, hc := testing.AssertStartInstance(c, env, "1")
   267  	c.Check(*hc.Arch, gc.Equals, "amd64")
   268  	c.Check(*hc.Mem, gc.Equals, uint64(1740))
   269  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   270  	c.Assert(*hc.CpuPower, gc.Equals, uint64(100))
   271  	inst = t.srv.ec2srv.Instance(string(inst1.Id()))
   272  	c.Assert(inst, gc.NotNil)
   273  	userData, err = utils.Gunzip(inst.UserData)
   274  	c.Assert(err, jc.ErrorIsNil)
   275  	c.Logf("second instance: UserData: %q", userData)
   276  	userDataMap = nil
   277  	err = goyaml.Unmarshal(userData, &userDataMap)
   278  	c.Assert(err, jc.ErrorIsNil)
   279  	CheckPackage(c, userDataMap, "curl", true)
   280  	CheckPackage(c, userDataMap, "mongodb-server", false)
   281  	CheckScripts(c, userDataMap, "jujud bootstrap-state", false)
   282  	CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true)
   283  	// TODO check for provisioning agent
   284  
   285  	err = env.Destroy()
   286  	c.Assert(err, jc.ErrorIsNil)
   287  
   288  	_, err = env.StateServerInstances()
   289  	c.Assert(err, gc.Equals, environs.ErrNotBootstrapped)
   290  }
   291  
   292  // splitAuthKeys splits the given authorized keys
   293  // into the form expected to be found in the
   294  // user data.
   295  func splitAuthKeys(keys string) []interface{} {
   296  	slines := strings.FieldsFunc(keys, func(r rune) bool {
   297  		return r == '\n'
   298  	})
   299  	var lines []interface{}
   300  	for _, line := range slines {
   301  		lines = append(lines, ssh.EnsureJujuComment(strings.TrimSpace(line)))
   302  	}
   303  	return lines
   304  }
   305  
   306  func (t *localServerSuite) TestInstanceStatus(c *gc.C) {
   307  	env := t.Prepare(c)
   308  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	t.srv.ec2srv.SetInitialInstanceState(ec2test.Terminated)
   311  	inst, _ := testing.AssertStartInstance(c, env, "1")
   312  	c.Assert(err, jc.ErrorIsNil)
   313  	c.Assert(inst.Status(), gc.Equals, "terminated")
   314  }
   315  
   316  func (t *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
   317  	env := t.Prepare(c)
   318  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   319  	c.Assert(err, jc.ErrorIsNil)
   320  	_, hc := testing.AssertStartInstance(c, env, "1")
   321  	c.Check(*hc.Arch, gc.Equals, "amd64")
   322  	c.Check(*hc.Mem, gc.Equals, uint64(1740))
   323  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   324  	c.Assert(*hc.CpuPower, gc.Equals, uint64(100))
   325  }
   326  
   327  func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) {
   328  	inst, err := t.testStartInstanceAvailZone(c, "test-available")
   329  	c.Assert(err, jc.ErrorIsNil)
   330  	c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "test-available")
   331  }
   332  
   333  func (t *localServerSuite) TestStartInstanceAvailZoneImpaired(c *gc.C) {
   334  	_, err := t.testStartInstanceAvailZone(c, "test-impaired")
   335  	c.Assert(err, gc.ErrorMatches, `availability zone "test-impaired" is impaired`)
   336  }
   337  
   338  func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) {
   339  	_, err := t.testStartInstanceAvailZone(c, "test-unknown")
   340  	c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`)
   341  }
   342  
   343  func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instance.Instance, error) {
   344  	env := t.Prepare(c)
   345  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   346  	c.Assert(err, jc.ErrorIsNil)
   347  
   348  	params := environs.StartInstanceParams{Placement: "zone=" + zone}
   349  	result, err := testing.StartInstanceWithParams(env, "1", params, nil)
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  	return result.Instance, nil
   354  }
   355  
   356  func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) {
   357  	var resultZones []amzec2.AvailabilityZoneInfo
   358  	var resultErr error
   359  	t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) {
   360  		resp := &amzec2.AvailabilityZonesResp{
   361  			Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...),
   362  		}
   363  		return resp, resultErr
   364  	})
   365  	env := t.Prepare(c).(common.ZonedEnviron)
   366  
   367  	resultErr = fmt.Errorf("failed to get availability zones")
   368  	zones, err := env.AvailabilityZones()
   369  	c.Assert(err, gc.Equals, resultErr)
   370  	c.Assert(zones, gc.IsNil)
   371  
   372  	resultErr = nil
   373  	resultZones = make([]amzec2.AvailabilityZoneInfo, 1)
   374  	resultZones[0].Name = "whatever"
   375  	zones, err = env.AvailabilityZones()
   376  	c.Assert(err, jc.ErrorIsNil)
   377  	c.Assert(zones, gc.HasLen, 1)
   378  	c.Assert(zones[0].Name(), gc.Equals, "whatever")
   379  
   380  	// A successful result is cached, currently for the lifetime
   381  	// of the Environ. This will change if/when we have long-lived
   382  	// Environs to cut down repeated IaaS requests.
   383  	resultErr = fmt.Errorf("failed to get availability zones")
   384  	resultZones[0].Name = "andever"
   385  	zones, err = env.AvailabilityZones()
   386  	c.Assert(err, jc.ErrorIsNil)
   387  	c.Assert(zones, gc.HasLen, 1)
   388  	c.Assert(zones[0].Name(), gc.Equals, "whatever")
   389  }
   390  
   391  func (t *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) {
   392  	var resultZones []amzec2.AvailabilityZoneInfo
   393  	t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) {
   394  		resp := &amzec2.AvailabilityZonesResp{
   395  			Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...),
   396  		}
   397  		return resp, nil
   398  	})
   399  	env := t.Prepare(c).(common.ZonedEnviron)
   400  	resultZones = make([]amzec2.AvailabilityZoneInfo, 2)
   401  	resultZones[0].Name = "az1"
   402  	resultZones[1].Name = "az2"
   403  	resultZones[0].State = "available"
   404  	resultZones[1].State = "impaired"
   405  	zones, err := env.AvailabilityZones()
   406  	c.Assert(err, jc.ErrorIsNil)
   407  	c.Assert(zones, gc.HasLen, 2)
   408  	c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name)
   409  	c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name)
   410  	c.Assert(zones[0].Available(), jc.IsTrue)
   411  	c.Assert(zones[1].Available(), jc.IsFalse)
   412  }
   413  
   414  type mockAvailabilityZoneAllocations struct {
   415  	group  []instance.Id // input param
   416  	result []common.AvailabilityZoneInstances
   417  	err    error
   418  }
   419  
   420  func (t *mockAvailabilityZoneAllocations) AvailabilityZoneAllocations(
   421  	e common.ZonedEnviron, group []instance.Id,
   422  ) ([]common.AvailabilityZoneInstances, error) {
   423  	t.group = group
   424  	return t.result, t.err
   425  }
   426  
   427  func (t *localServerSuite) TestStartInstanceDistributionParams(c *gc.C) {
   428  	env := t.Prepare(c)
   429  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   430  	c.Assert(err, jc.ErrorIsNil)
   431  
   432  	mock := mockAvailabilityZoneAllocations{
   433  		result: []common.AvailabilityZoneInstances{{ZoneName: "az1"}},
   434  	}
   435  	t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations)
   436  
   437  	// no distribution group specified
   438  	testing.AssertStartInstance(c, env, "1")
   439  	c.Assert(mock.group, gc.HasLen, 0)
   440  
   441  	// distribution group specified: ensure it's passed through to AvailabilityZone.
   442  	expectedInstances := []instance.Id{"i-0", "i-1"}
   443  	params := environs.StartInstanceParams{
   444  		DistributionGroup: func() ([]instance.Id, error) {
   445  			return expectedInstances, nil
   446  		},
   447  	}
   448  	_, err = testing.StartInstanceWithParams(env, "1", params, nil)
   449  	c.Assert(err, jc.ErrorIsNil)
   450  	c.Assert(mock.group, gc.DeepEquals, expectedInstances)
   451  }
   452  
   453  func (t *localServerSuite) TestStartInstanceDistributionErrors(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  	mock := mockAvailabilityZoneAllocations{
   459  		err: fmt.Errorf("AvailabilityZoneAllocations failed"),
   460  	}
   461  	t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations)
   462  	_, _, _, err = testing.StartInstance(env, "1")
   463  	c.Assert(errors.Cause(err), gc.Equals, mock.err)
   464  
   465  	mock.err = nil
   466  	dgErr := fmt.Errorf("DistributionGroup failed")
   467  	params := environs.StartInstanceParams{
   468  		DistributionGroup: func() ([]instance.Id, error) {
   469  			return nil, dgErr
   470  		},
   471  	}
   472  	_, err = testing.StartInstanceWithParams(env, "1", params, nil)
   473  	c.Assert(errors.Cause(err), gc.Equals, dgErr)
   474  }
   475  
   476  func (t *localServerSuite) TestStartInstanceDistribution(c *gc.C) {
   477  	env := t.Prepare(c)
   478  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   479  	c.Assert(err, jc.ErrorIsNil)
   480  
   481  	// test-available is the only available AZ, so AvailabilityZoneAllocations
   482  	// is guaranteed to return that.
   483  	inst, _ := testing.AssertStartInstance(c, env, "1")
   484  	c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "test-available")
   485  }
   486  
   487  var azConstrainedErr = &amzec2.Error{
   488  	Code:    "Unsupported",
   489  	Message: "The requested Availability Zone is currently constrained etc.",
   490  }
   491  
   492  var azVolumeTypeNotAvailableInZoneErr = &amzec2.Error{
   493  	Code:    "VolumeTypeNotAvailableInZone",
   494  	Message: "blah blah",
   495  }
   496  
   497  var azInsufficientInstanceCapacityErr = &amzec2.Error{
   498  	Code: "InsufficientInstanceCapacity",
   499  	Message: "We currently do not have sufficient m1.small capacity in the " +
   500  		"Availability Zone you requested (us-east-1d). Our system will " +
   501  		"be working on provisioning additional capacity. You can currently get m1.small " +
   502  		"capacity by not specifying an Availability Zone in your request or choosing " +
   503  		"us-east-1c, us-east-1a.",
   504  }
   505  
   506  var azNoDefaultSubnetErr = &amzec2.Error{
   507  	Code:    "InvalidInput",
   508  	Message: "No default subnet for availability zone: ''us-east-1e''.",
   509  }
   510  
   511  func (t *localServerSuite) TestStartInstanceAvailZoneAllConstrained(c *gc.C) {
   512  	t.testStartInstanceAvailZoneAllConstrained(c, azConstrainedErr)
   513  }
   514  
   515  func (t *localServerSuite) TestStartInstanceVolumeTypeNotAvailable(c *gc.C) {
   516  	t.testStartInstanceAvailZoneAllConstrained(c, azVolumeTypeNotAvailableInZoneErr)
   517  }
   518  
   519  func (t *localServerSuite) TestStartInstanceAvailZoneAllInsufficientInstanceCapacity(c *gc.C) {
   520  	t.testStartInstanceAvailZoneAllConstrained(c, azInsufficientInstanceCapacityErr)
   521  }
   522  
   523  func (t *localServerSuite) TestStartInstanceAvailZoneAllNoDefaultSubnet(c *gc.C) {
   524  	t.testStartInstanceAvailZoneAllConstrained(c, azNoDefaultSubnetErr)
   525  }
   526  
   527  func (t *localServerSuite) testStartInstanceAvailZoneAllConstrained(c *gc.C, runInstancesError *amzec2.Error) {
   528  	env := t.Prepare(c)
   529  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   530  	c.Assert(err, jc.ErrorIsNil)
   531  
   532  	mock := mockAvailabilityZoneAllocations{
   533  		result: []common.AvailabilityZoneInstances{
   534  			{ZoneName: "az1"}, {ZoneName: "az2"},
   535  		},
   536  	}
   537  	t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations)
   538  
   539  	var azArgs []string
   540  	t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ri *amzec2.RunInstances) (*amzec2.RunInstancesResp, error) {
   541  		azArgs = append(azArgs, ri.AvailZone)
   542  		return nil, runInstancesError
   543  	})
   544  	_, _, _, err = testing.StartInstance(env, "1")
   545  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf(
   546  		"cannot run instances: %s \\(%s\\)",
   547  		regexp.QuoteMeta(runInstancesError.Message),
   548  		runInstancesError.Code,
   549  	))
   550  	c.Assert(azArgs, gc.DeepEquals, []string{"az1", "az2"})
   551  }
   552  
   553  func (t *localServerSuite) bootstrapAndStartWithParams(c *gc.C, params environs.StartInstanceParams) error {
   554  	env := t.Prepare(c)
   555  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   556  	c.Assert(err, jc.ErrorIsNil)
   557  	_, err = testing.StartInstanceWithParams(env, "1", params, nil)
   558  	return err
   559  }
   560  
   561  func (t *localServerSuite) TestSpaceConstraintsSpaceNotInPlacementZone(c *gc.C) {
   562  	err := t.bootstrapAndStartWithParams(c, environs.StartInstanceParams{
   563  		Placement:   "zone=test-available",
   564  		Constraints: constraints.MustParse("spaces=aaaaaaaaaa"),
   565  		SubnetsToZones: map[network.Id][]string{
   566  			"subnet-2": []string{"zone2"},
   567  			"subnet-3": []string{"zone3"},
   568  		},
   569  	})
   570  
   571  	// Expect an error because zone test-available isn't in SubnetsToZones
   572  	c.Assert(err, gc.ErrorMatches, `unable to resolve constraints: space and/or subnet unavailable in zones \[test-available\]`)
   573  }
   574  
   575  func (t *localServerSuite) TestSpaceConstraintsSpaceInPlacementZone(c *gc.C) {
   576  	err := t.bootstrapAndStartWithParams(c, environs.StartInstanceParams{
   577  		Placement:   "zone=test-available",
   578  		Constraints: constraints.MustParse("spaces=aaaaaaaaaa"),
   579  		SubnetsToZones: map[network.Id][]string{
   580  			"subnet-2": []string{"test-available"},
   581  			"subnet-3": []string{"zone3"},
   582  		},
   583  	})
   584  
   585  	// Should work - test-available is in SubnetsToZones
   586  	c.Assert(err, jc.ErrorIsNil)
   587  }
   588  
   589  func (t *localServerSuite) TestSpaceConstraintsNoPlacement(c *gc.C) {
   590  	err := t.bootstrapAndStartWithParams(c, environs.StartInstanceParams{
   591  		Constraints: constraints.MustParse("spaces=aaaaaaaaaa"),
   592  		SubnetsToZones: map[network.Id][]string{
   593  			"subnet-2": []string{"test-available"},
   594  			"subnet-3": []string{"zone3"},
   595  		},
   596  	})
   597  
   598  	// Shoule work because zone is not specified so we can resolve the constraints
   599  	c.Assert(err, jc.ErrorIsNil)
   600  }
   601  
   602  func (t *localServerSuite) TestSpaceConstraintsNoAvailableSubnets(c *gc.C) {
   603  	err := t.bootstrapAndStartWithParams(c, environs.StartInstanceParams{
   604  		Constraints: constraints.MustParse("spaces=aaaaaaaaaa"),
   605  		SubnetsToZones: map[network.Id][]string{
   606  			"subnet-2": []string{""},
   607  		},
   608  	})
   609  
   610  	// We requested a space, but there are no subnets in SubnetsToZones, so we can't resolve
   611  	// the constraints
   612  	c.Assert(err, gc.ErrorMatches, `unable to resolve constraints: space and/or subnet unavailable in zones \[test-available\]`)
   613  }
   614  
   615  func (t *localServerSuite) TestStartInstanceAvailZoneOneConstrained(c *gc.C) {
   616  	t.testStartInstanceAvailZoneOneConstrained(c, azConstrainedErr)
   617  }
   618  
   619  func (t *localServerSuite) TestStartInstanceAvailZoneOneInsufficientInstanceCapacity(c *gc.C) {
   620  	t.testStartInstanceAvailZoneOneConstrained(c, azInsufficientInstanceCapacityErr)
   621  }
   622  
   623  func (t *localServerSuite) TestStartInstanceAvailZoneOneNoDefaultSubnetErr(c *gc.C) {
   624  	t.testStartInstanceAvailZoneOneConstrained(c, azNoDefaultSubnetErr)
   625  }
   626  
   627  func (t *localServerSuite) testStartInstanceAvailZoneOneConstrained(c *gc.C, runInstancesError *amzec2.Error) {
   628  	env := t.Prepare(c)
   629  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   630  	c.Assert(err, jc.ErrorIsNil)
   631  
   632  	mock := mockAvailabilityZoneAllocations{
   633  		result: []common.AvailabilityZoneInstances{
   634  			{ZoneName: "az1"}, {ZoneName: "az2"},
   635  		},
   636  	}
   637  	t.PatchValue(ec2.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations)
   638  
   639  	// The first call to RunInstances fails with an error indicating the AZ
   640  	// is constrained. The second attempt succeeds, and so allocates to az2.
   641  	var azArgs []string
   642  	realRunInstances := *ec2.RunInstances
   643  	t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ri *amzec2.RunInstances) (*amzec2.RunInstancesResp, error) {
   644  		azArgs = append(azArgs, ri.AvailZone)
   645  		if len(azArgs) == 1 {
   646  			return nil, runInstancesError
   647  		}
   648  		return realRunInstances(e, ri)
   649  	})
   650  	inst, hwc := testing.AssertStartInstance(c, env, "1")
   651  	c.Assert(azArgs, gc.DeepEquals, []string{"az1", "az2"})
   652  	c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "az2")
   653  	c.Check(*hwc.AvailabilityZone, gc.Equals, "az2")
   654  }
   655  
   656  func (t *localServerSuite) TestAddresses(c *gc.C) {
   657  	env := t.Prepare(c)
   658  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   659  	c.Assert(err, jc.ErrorIsNil)
   660  	inst, _ := testing.AssertStartInstance(c, env, "1")
   661  	c.Assert(err, jc.ErrorIsNil)
   662  	addrs, err := inst.Addresses()
   663  	c.Assert(err, jc.ErrorIsNil)
   664  	// Expected values use Address type but really contain a regexp for
   665  	// the value rather than a valid ip or hostname.
   666  	expected := []network.Address{{
   667  		Value: "8.0.0.*",
   668  		Type:  network.IPv4Address,
   669  		Scope: network.ScopePublic,
   670  	}, {
   671  		Value: "127.0.0.*",
   672  		Type:  network.IPv4Address,
   673  		Scope: network.ScopeCloudLocal,
   674  	}}
   675  	c.Assert(addrs, gc.HasLen, len(expected))
   676  	for i, addr := range addrs {
   677  		c.Check(addr.Value, gc.Matches, expected[i].Value)
   678  		c.Check(addr.Type, gc.Equals, expected[i].Type)
   679  		c.Check(addr.Scope, gc.Equals, expected[i].Scope)
   680  	}
   681  }
   682  
   683  func (t *localServerSuite) TestConstraintsValidatorUnsupported(c *gc.C) {
   684  	env := t.Prepare(c)
   685  	validator, err := env.ConstraintsValidator()
   686  	c.Assert(err, jc.ErrorIsNil)
   687  	cons := constraints.MustParse("arch=amd64 tags=foo")
   688  	unsupported, err := validator.Validate(cons)
   689  	c.Assert(err, jc.ErrorIsNil)
   690  	c.Assert(unsupported, gc.DeepEquals, []string{"tags"})
   691  }
   692  
   693  func (t *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
   694  	env := t.Prepare(c)
   695  	validator, err := env.ConstraintsValidator()
   696  	c.Assert(err, jc.ErrorIsNil)
   697  	cons := constraints.MustParse("arch=ppc64el")
   698  	_, err = validator.Validate(cons)
   699  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=ppc64el\nvalid values are:.*")
   700  	cons = constraints.MustParse("instance-type=foo")
   701  	_, err = validator.Validate(cons)
   702  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
   703  }
   704  
   705  func (t *localServerSuite) TestConstraintsMerge(c *gc.C) {
   706  	env := t.Prepare(c)
   707  	validator, err := env.ConstraintsValidator()
   708  	c.Assert(err, jc.ErrorIsNil)
   709  	consA := constraints.MustParse("arch=amd64 mem=1G cpu-power=10 cpu-cores=2 tags=bar")
   710  	consB := constraints.MustParse("arch=i386 instance-type=m1.small")
   711  	cons, err := validator.Merge(consA, consB)
   712  	c.Assert(err, jc.ErrorIsNil)
   713  	c.Assert(cons, gc.DeepEquals, constraints.MustParse("arch=i386 instance-type=m1.small tags=bar"))
   714  }
   715  
   716  func (t *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) {
   717  	env := t.Prepare(c)
   718  	cons := constraints.MustParse("instance-type=m1.small root-disk=1G")
   719  	placement := ""
   720  	err := env.PrecheckInstance(coretesting.FakeDefaultSeries, cons, placement)
   721  	c.Assert(err, jc.ErrorIsNil)
   722  }
   723  
   724  func (t *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) {
   725  	env := t.Prepare(c)
   726  	cons := constraints.MustParse("instance-type=m1.invalid")
   727  	placement := ""
   728  	err := env.PrecheckInstance(coretesting.FakeDefaultSeries, cons, placement)
   729  	c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "m1.invalid" specified`)
   730  }
   731  
   732  func (t *localServerSuite) TestPrecheckInstanceUnsupportedArch(c *gc.C) {
   733  	env := t.Prepare(c)
   734  	cons := constraints.MustParse("instance-type=cc1.4xlarge arch=i386")
   735  	placement := ""
   736  	err := env.PrecheckInstance(coretesting.FakeDefaultSeries, cons, placement)
   737  	c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "cc1.4xlarge" and arch "i386" specified`)
   738  }
   739  
   740  func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) {
   741  	env := t.Prepare(c)
   742  	placement := "zone=test-available"
   743  	err := env.PrecheckInstance(coretesting.FakeDefaultSeries, constraints.Value{}, placement)
   744  	c.Assert(err, jc.ErrorIsNil)
   745  }
   746  
   747  func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) {
   748  	env := t.Prepare(c)
   749  	placement := "zone=test-unavailable"
   750  	err := env.PrecheckInstance(coretesting.FakeDefaultSeries, constraints.Value{}, placement)
   751  	c.Assert(err, jc.ErrorIsNil)
   752  }
   753  
   754  func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) {
   755  	env := t.Prepare(c)
   756  	placement := "zone=test-unknown"
   757  	err := env.PrecheckInstance(coretesting.FakeDefaultSeries, constraints.Value{}, placement)
   758  	c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`)
   759  }
   760  
   761  func (t *localServerSuite) TestValidateImageMetadata(c *gc.C) {
   762  	env := t.Prepare(c)
   763  	params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("test")
   764  	c.Assert(err, jc.ErrorIsNil)
   765  	params.Series = coretesting.FakeDefaultSeries
   766  	params.Endpoint = "https://ec2.endpoint.com"
   767  	params.Sources, err = environs.ImageMetadataSources(env)
   768  	c.Assert(err, jc.ErrorIsNil)
   769  	image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
   770  	c.Assert(err, jc.ErrorIsNil)
   771  	sort.Strings(image_ids)
   772  	c.Assert(image_ids, gc.DeepEquals, []string{"ami-00000033", "ami-00000034", "ami-00000035", "ami-00000039"})
   773  }
   774  
   775  func (t *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
   776  	t.PatchValue(&tools.DefaultBaseURL, "")
   777  
   778  	env := t.Prepare(c)
   779  	sources, err := tools.GetMetadataSources(env)
   780  	c.Assert(err, jc.ErrorIsNil)
   781  	c.Assert(sources, gc.HasLen, 0)
   782  }
   783  
   784  func (t *localServerSuite) TestSupportedArchitectures(c *gc.C) {
   785  	env := t.Prepare(c)
   786  	a, err := env.SupportedArchitectures()
   787  	c.Assert(err, jc.ErrorIsNil)
   788  	c.Assert(a, jc.SameContents, []string{"amd64", "i386"})
   789  }
   790  
   791  func (t *localServerSuite) TestSupportsNetworking(c *gc.C) {
   792  	env := t.Prepare(c)
   793  	_, supported := environs.SupportsNetworking(env)
   794  	c.Assert(supported, jc.IsTrue)
   795  }
   796  
   797  func (t *localServerSuite) TestAllocateAddressFailureToFindNetworkInterface(c *gc.C) {
   798  	env := t.prepareEnviron(c)
   799  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   800  	c.Assert(err, jc.ErrorIsNil)
   801  
   802  	instanceIds, err := env.StateServerInstances()
   803  	c.Assert(err, jc.ErrorIsNil)
   804  
   805  	instId := instanceIds[0]
   806  	addr := network.Address{Value: "8.0.0.4"}
   807  
   808  	// Invalid instance found
   809  	err = env.AllocateAddress(instId+"foo", "", addr, "foo", "bar")
   810  	c.Assert(err, gc.ErrorMatches, ".*InvalidInstanceID.NotFound.*")
   811  
   812  	// No network interface
   813  	err = env.AllocateAddress(instId, "", addr, "foo", "bar")
   814  	c.Assert(errors.Cause(err), gc.ErrorMatches, "unexpected AWS response: network interface not found")
   815  }
   816  
   817  func (t *localServerSuite) setUpInstanceWithDefaultVpc(c *gc.C) (environs.NetworkingEnviron, instance.Id) {
   818  	// Simulate a default VPC exists.
   819  	t.srv.ec2srv.AddDefaultVPCAndSubnets()
   820  
   821  	env := t.prepareEnviron(c)
   822  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
   823  	c.Assert(err, jc.ErrorIsNil)
   824  
   825  	instanceIds, err := env.StateServerInstances()
   826  	c.Assert(err, jc.ErrorIsNil)
   827  	return env, instanceIds[0]
   828  }
   829  
   830  func (t *localServerSuite) TestAllocateAddress(c *gc.C) {
   831  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   832  
   833  	addr := network.Address{Value: "8.0.0.4"}
   834  	var actualAddr network.Address
   835  	mockAssign := func(ec2Inst *amzec2.EC2, netId string, addr network.Address) error {
   836  		actualAddr = addr
   837  		return nil
   838  	}
   839  	t.PatchValue(&ec2.AssignPrivateIPAddress, mockAssign)
   840  
   841  	err := env.AllocateAddress(instId, "", addr, "foo", "bar")
   842  	c.Assert(err, jc.ErrorIsNil)
   843  	c.Assert(actualAddr, gc.Equals, addr)
   844  }
   845  
   846  func (t *localServerSuite) TestAllocateAddressIPAddressInUseOrEmpty(c *gc.C) {
   847  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   848  
   849  	addr := network.Address{Value: "8.0.0.4"}
   850  	mockAssign := func(ec2Inst *amzec2.EC2, netId string, addr network.Address) error {
   851  		return &amzec2.Error{Code: "InvalidParameterValue"}
   852  	}
   853  	t.PatchValue(&ec2.AssignPrivateIPAddress, mockAssign)
   854  
   855  	err := env.AllocateAddress(instId, "", addr, "foo", "bar")
   856  	c.Assert(errors.Cause(err), gc.Equals, environs.ErrIPAddressUnavailable)
   857  
   858  	err = env.AllocateAddress(instId, "", network.Address{}, "foo", "bar")
   859  	c.Assert(errors.Cause(err), gc.Equals, environs.ErrIPAddressUnavailable)
   860  }
   861  
   862  func (t *localServerSuite) TestAllocateAddressNetworkInterfaceFull(c *gc.C) {
   863  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   864  
   865  	addr := network.Address{Value: "8.0.0.4"}
   866  	mockAssign := func(ec2Inst *amzec2.EC2, netId string, addr network.Address) error {
   867  		return &amzec2.Error{Code: "PrivateIpAddressLimitExceeded"}
   868  	}
   869  	t.PatchValue(&ec2.AssignPrivateIPAddress, mockAssign)
   870  
   871  	err := env.AllocateAddress(instId, "", addr, "foo", "bar")
   872  	c.Assert(errors.Cause(err), gc.Equals, environs.ErrIPAddressesExhausted)
   873  }
   874  
   875  func (t *localServerSuite) TestReleaseAddress(c *gc.C) {
   876  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   877  	addr := network.Address{Value: "8.0.0.4"}
   878  	// Allocate the address first so we can release it
   879  	err := env.AllocateAddress(instId, "", addr, "foo", "bar")
   880  	c.Assert(err, jc.ErrorIsNil)
   881  
   882  	err = env.ReleaseAddress(instId, "", addr, "")
   883  	c.Assert(err, jc.ErrorIsNil)
   884  
   885  	// Releasing a second time tests that the first call actually released
   886  	// it plus tests the error handling of ReleaseAddress
   887  	err = env.ReleaseAddress(instId, "", addr, "")
   888  	msg := fmt.Sprintf(`failed to release address "8\.0\.0\.4" from instance %q.*`, instId)
   889  	c.Assert(err, gc.ErrorMatches, msg)
   890  }
   891  
   892  func (t *localServerSuite) TestReleaseAddressUnknownInstance(c *gc.C) {
   893  	env, _ := t.setUpInstanceWithDefaultVpc(c)
   894  
   895  	// We should be able to release an address with an unknown instance id
   896  	// without it being allocated.
   897  	addr := network.Address{Value: "8.0.0.4"}
   898  	err := env.ReleaseAddress(instance.UnknownId, "", addr, "")
   899  	c.Assert(err, jc.ErrorIsNil)
   900  }
   901  
   902  func (t *localServerSuite) TestNetworkInterfaces(c *gc.C) {
   903  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   904  	interfaces, err := env.NetworkInterfaces(instId)
   905  	c.Assert(err, jc.ErrorIsNil)
   906  
   907  	// The CIDR isn't predictable, but it is in the 10.10.x.0/24 format
   908  	// The subnet ID is in the form "subnet-x", where x matches the same
   909  	// number from the CIDR. The interfaces address is part of the CIDR.
   910  	// For these reasons we check that the CIDR is in the expected format
   911  	// and derive the expected values for ProviderSubnetId and Address.
   912  	c.Assert(interfaces, gc.HasLen, 1)
   913  	cidr := interfaces[0].CIDR
   914  	re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`)
   915  	c.Assert(re.Match([]byte(cidr)), jc.IsTrue)
   916  	index := re.FindStringSubmatch(cidr)[1]
   917  	addr := fmt.Sprintf("10.10.%s.5", index)
   918  	subnetId := network.Id("subnet-" + index)
   919  
   920  	// AvailabilityZones will either contain "test-available",
   921  	// "test-impaired" or "test-unavailable" depending on which subnet is
   922  	// picked. Any of these is fine.
   923  	zones := interfaces[0].AvailabilityZones
   924  	c.Assert(zones, gc.HasLen, 1)
   925  	re = regexp.MustCompile("test-available|test-unavailable|test-impaired")
   926  	c.Assert(re.Match([]byte(zones[0])), jc.IsTrue)
   927  
   928  	expectedInterfaces := []network.InterfaceInfo{{
   929  		DeviceIndex:       0,
   930  		MACAddress:        "20:01:60:cb:27:37",
   931  		CIDR:              cidr,
   932  		ProviderId:        "eni-0",
   933  		ProviderSubnetId:  subnetId,
   934  		VLANTag:           0,
   935  		InterfaceName:     "unsupported0",
   936  		Disabled:          false,
   937  		NoAutoStart:       false,
   938  		ConfigType:        network.ConfigDHCP,
   939  		Address:           network.NewScopedAddress(addr, network.ScopeCloudLocal),
   940  		AvailabilityZones: zones,
   941  	}}
   942  	c.Assert(interfaces, jc.DeepEquals, expectedInterfaces)
   943  }
   944  
   945  func (t *localServerSuite) TestSubnetsWithInstanceId(c *gc.C) {
   946  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   947  	subnets, err := env.Subnets(instId, nil)
   948  	c.Assert(err, jc.ErrorIsNil)
   949  	c.Assert(subnets, gc.HasLen, 1)
   950  	validateSubnets(c, subnets)
   951  
   952  	interfaces, err := env.NetworkInterfaces(instId)
   953  	c.Assert(err, jc.ErrorIsNil)
   954  	c.Assert(interfaces, gc.HasLen, 1)
   955  	c.Assert(interfaces[0].ProviderSubnetId, gc.Equals, subnets[0].ProviderId)
   956  }
   957  
   958  func (t *localServerSuite) TestSubnetsWithInstanceIdAndSubnetId(c *gc.C) {
   959  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   960  	interfaces, err := env.NetworkInterfaces(instId)
   961  	c.Assert(err, jc.ErrorIsNil)
   962  	c.Assert(interfaces, gc.HasLen, 1)
   963  
   964  	subnets, err := env.Subnets(instId, []network.Id{interfaces[0].ProviderSubnetId})
   965  	c.Assert(err, jc.ErrorIsNil)
   966  	c.Assert(subnets, gc.HasLen, 1)
   967  	c.Assert(subnets[0].ProviderId, gc.Equals, interfaces[0].ProviderSubnetId)
   968  	validateSubnets(c, subnets)
   969  }
   970  
   971  func (t *localServerSuite) TestSubnetsWithInstanceIdMissingSubnet(c *gc.C) {
   972  	env, instId := t.setUpInstanceWithDefaultVpc(c)
   973  	subnets, err := env.Subnets(instId, []network.Id{"missing"})
   974  	c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[missing\]`)
   975  	c.Assert(subnets, gc.HasLen, 0)
   976  }
   977  
   978  func validateSubnets(c *gc.C, subnets []network.SubnetInfo) {
   979  	// These are defined in the test server for the testing default
   980  	// VPC.
   981  	defaultSubnets := []network.SubnetInfo{{
   982  		CIDR:              "10.10.0.0/24",
   983  		ProviderId:        "subnet-0",
   984  		VLANTag:           0,
   985  		AllocatableIPLow:  net.ParseIP("10.10.0.4").To4(),
   986  		AllocatableIPHigh: net.ParseIP("10.10.0.254").To4(),
   987  		AvailabilityZones: []string{"test-available"},
   988  	}, {
   989  		CIDR:              "10.10.1.0/24",
   990  		ProviderId:        "subnet-1",
   991  		VLANTag:           0,
   992  		AllocatableIPLow:  net.ParseIP("10.10.1.4").To4(),
   993  		AllocatableIPHigh: net.ParseIP("10.10.1.254").To4(),
   994  		AvailabilityZones: []string{"test-impaired"},
   995  	}, {
   996  		CIDR:              "10.10.2.0/24",
   997  		ProviderId:        "subnet-2",
   998  		VLANTag:           0,
   999  		AllocatableIPLow:  net.ParseIP("10.10.2.4").To4(),
  1000  		AllocatableIPHigh: net.ParseIP("10.10.2.254").To4(),
  1001  		AvailabilityZones: []string{"test-unavailable"},
  1002  	}}
  1003  
  1004  	re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`)
  1005  	for _, subnet := range subnets {
  1006  		// We can find the expected data by looking at the CIDR.
  1007  		// subnets isn't in a predictable order due to the use of maps.
  1008  		c.Assert(re.Match([]byte(subnet.CIDR)), jc.IsTrue)
  1009  		index, err := strconv.Atoi(re.FindStringSubmatch(subnet.CIDR)[1])
  1010  		c.Assert(err, jc.ErrorIsNil)
  1011  		// Don't know which AZ the subnet will end up in.
  1012  		defaultSubnets[index].AvailabilityZones = subnet.AvailabilityZones
  1013  		c.Assert(subnet, jc.DeepEquals, defaultSubnets[index])
  1014  	}
  1015  }
  1016  
  1017  func (t *localServerSuite) TestSubnets(c *gc.C) {
  1018  	env, _ := t.setUpInstanceWithDefaultVpc(c)
  1019  
  1020  	subnets, err := env.Subnets(instance.UnknownId, []network.Id{"subnet-0"})
  1021  	c.Assert(err, jc.ErrorIsNil)
  1022  	c.Assert(subnets, gc.HasLen, 1)
  1023  	validateSubnets(c, subnets)
  1024  
  1025  	subnets, err = env.Subnets(instance.UnknownId, nil)
  1026  	c.Assert(err, jc.ErrorIsNil)
  1027  	c.Assert(subnets, gc.HasLen, 3)
  1028  	validateSubnets(c, subnets)
  1029  }
  1030  
  1031  func (t *localServerSuite) TestSubnetsMissingSubnet(c *gc.C) {
  1032  	env, _ := t.setUpInstanceWithDefaultVpc(c)
  1033  
  1034  	_, err := env.Subnets("", []network.Id{"subnet-0", "Missing"})
  1035  	c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[Missing\]`)
  1036  }
  1037  
  1038  func (t *localServerSuite) TestSupportsAddressAllocationTrue(c *gc.C) {
  1039  	t.srv.ec2srv.AddDefaultVPCAndSubnets()
  1040  	env := t.prepareEnviron(c)
  1041  	result, err := env.SupportsAddressAllocation("")
  1042  	c.Assert(err, jc.ErrorIsNil)
  1043  	c.Assert(result, jc.IsTrue)
  1044  }
  1045  
  1046  func (t *localServerSuite) TestSupportsAddressAllocationWithNoFeatureFlag(c *gc.C) {
  1047  	t.SetFeatureFlags() // clear the flags.
  1048  	env := t.prepareEnviron(c)
  1049  	result, err := env.SupportsAddressAllocation("")
  1050  	c.Assert(err, gc.ErrorMatches, "address allocation not supported")
  1051  	c.Assert(err, jc.Satisfies, errors.IsNotSupported)
  1052  	c.Assert(result, jc.IsFalse)
  1053  }
  1054  
  1055  func (t *localServerSuite) TestAllocateAddressWithNoFeatureFlag(c *gc.C) {
  1056  	t.SetFeatureFlags() // clear the flags.
  1057  	env := t.prepareEnviron(c)
  1058  	err := env.AllocateAddress("i-foo", "net1", network.NewAddresses("1.2.3.4")[0], "foo", "bar")
  1059  	c.Assert(err, gc.ErrorMatches, "address allocation not supported")
  1060  	c.Assert(err, jc.Satisfies, errors.IsNotSupported)
  1061  }
  1062  
  1063  func (t *localServerSuite) TestReleaseAddressWithNoFeatureFlag(c *gc.C) {
  1064  	t.SetFeatureFlags() // clear the flags.
  1065  	env := t.prepareEnviron(c)
  1066  	err := env.ReleaseAddress("i-foo", "net1", network.NewAddress("1.2.3.4"), "")
  1067  	c.Assert(err, gc.ErrorMatches, "address allocation not supported")
  1068  	c.Assert(err, jc.Satisfies, errors.IsNotSupported)
  1069  }
  1070  
  1071  func (t *localServerSuite) TestSupportsAddressAllocationCaches(c *gc.C) {
  1072  	t.srv.ec2srv.SetAccountAttributes(map[string][]string{
  1073  		"default-vpc": {"none"},
  1074  	})
  1075  	env := t.prepareEnviron(c)
  1076  	result, err := env.SupportsAddressAllocation("")
  1077  	c.Assert(err, jc.ErrorIsNil)
  1078  	c.Assert(result, jc.IsFalse)
  1079  
  1080  	// this value won't change normally, the change here is to
  1081  	// ensure that subsequent calls use the cached value
  1082  	t.srv.ec2srv.SetAccountAttributes(map[string][]string{
  1083  		"default-vpc": {"vpc-xxxxxxx"},
  1084  	})
  1085  	result, err = env.SupportsAddressAllocation("")
  1086  	c.Assert(err, jc.ErrorIsNil)
  1087  	c.Assert(result, jc.IsFalse)
  1088  }
  1089  
  1090  func (t *localServerSuite) TestSupportsAddressAllocationFalse(c *gc.C) {
  1091  	t.srv.ec2srv.SetAccountAttributes(map[string][]string{
  1092  		"default-vpc": {"none"},
  1093  	})
  1094  	env := t.prepareEnviron(c)
  1095  	result, err := env.SupportsAddressAllocation("")
  1096  	c.Assert(err, jc.ErrorIsNil)
  1097  	c.Assert(result, jc.IsFalse)
  1098  }
  1099  
  1100  func (t *localServerSuite) TestInstanceTags(c *gc.C) {
  1101  	env := t.Prepare(c)
  1102  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
  1103  	c.Assert(err, jc.ErrorIsNil)
  1104  
  1105  	instances, err := env.AllInstances()
  1106  	c.Assert(err, jc.ErrorIsNil)
  1107  	c.Assert(instances, gc.HasLen, 1)
  1108  
  1109  	ec2Inst := ec2.InstanceEC2(instances[0])
  1110  	c.Assert(ec2Inst.Tags, jc.SameContents, []amzec2.Tag{
  1111  		{"Name", "juju-sample-machine-0"},
  1112  		{"juju-env-uuid", coretesting.EnvironmentTag.Id()},
  1113  		{"juju-is-state", "true"},
  1114  	})
  1115  }
  1116  
  1117  func (t *localServerSuite) TestRootDiskTags(c *gc.C) {
  1118  	env := t.Prepare(c)
  1119  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{})
  1120  	c.Assert(err, jc.ErrorIsNil)
  1121  
  1122  	instances, err := env.AllInstances()
  1123  	c.Assert(err, jc.ErrorIsNil)
  1124  	c.Assert(instances, gc.HasLen, 1)
  1125  
  1126  	ec2conn := ec2.EnvironEC2(env)
  1127  	resp, err := ec2conn.Volumes(nil, nil)
  1128  	c.Assert(err, jc.ErrorIsNil)
  1129  	c.Assert(resp.Volumes, gc.Not(gc.HasLen), 0)
  1130  
  1131  	var found *amzec2.Volume
  1132  	for _, vol := range resp.Volumes {
  1133  		if len(vol.Tags) != 0 {
  1134  			found = &vol
  1135  			break
  1136  		}
  1137  	}
  1138  	c.Assert(found, gc.NotNil)
  1139  	c.Assert(found.Tags, jc.SameContents, []amzec2.Tag{
  1140  		{"Name", "juju-sample-machine-0-root"},
  1141  		{"juju-env-uuid", coretesting.EnvironmentTag.Id()},
  1142  	})
  1143  }
  1144  
  1145  // localNonUSEastSuite is similar to localServerSuite but the S3 mock server
  1146  // behaves as if it is not in the us-east region.
  1147  type localNonUSEastSuite struct {
  1148  	coretesting.BaseSuite
  1149  	restoreEC2Patching func()
  1150  	srv                localServer
  1151  	env                environs.Environ
  1152  }
  1153  
  1154  func (t *localNonUSEastSuite) SetUpSuite(c *gc.C) {
  1155  	t.BaseSuite.SetUpSuite(c)
  1156  	t.restoreEC2Patching = patchEC2ForTesting()
  1157  }
  1158  
  1159  func (t *localNonUSEastSuite) TearDownSuite(c *gc.C) {
  1160  	t.restoreEC2Patching()
  1161  	t.BaseSuite.TearDownSuite(c)
  1162  }
  1163  
  1164  func (t *localNonUSEastSuite) SetUpTest(c *gc.C) {
  1165  	t.BaseSuite.SetUpTest(c)
  1166  	t.srv.config = &s3test.Config{
  1167  		Send409Conflict: true,
  1168  	}
  1169  	t.srv.startServer(c)
  1170  
  1171  	cfg, err := config.New(config.NoDefaults, localConfigAttrs)
  1172  	c.Assert(err, jc.ErrorIsNil)
  1173  	env, err := environs.Prepare(cfg, envtesting.BootstrapContext(c), configstore.NewMem())
  1174  	c.Assert(err, jc.ErrorIsNil)
  1175  	t.env = env
  1176  }
  1177  
  1178  func (t *localNonUSEastSuite) TearDownTest(c *gc.C) {
  1179  	t.srv.stopServer(c)
  1180  	t.BaseSuite.TearDownTest(c)
  1181  }
  1182  
  1183  func patchEC2ForTesting() func() {
  1184  	ec2.UseTestImageData(ec2.TestImagesData)
  1185  	ec2.UseTestInstanceTypeData(ec2.TestInstanceTypeCosts)
  1186  	ec2.UseTestRegionData(ec2.TestRegions)
  1187  	restoreTimeouts := envtesting.PatchAttemptStrategies(ec2.ShortAttempt, ec2.StorageAttempt)
  1188  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
  1189  	return func() {
  1190  		restoreFinishBootstrap()
  1191  		restoreTimeouts()
  1192  		ec2.UseTestImageData(nil)
  1193  		ec2.UseTestInstanceTypeData(nil)
  1194  		ec2.UseTestRegionData(nil)
  1195  	}
  1196  }
  1197  
  1198  // If match is true, CheckScripts checks that at least one script started
  1199  // by the cloudinit data matches the given regexp pattern, otherwise it
  1200  // checks that no script matches.  It's exported so it can be used by tests
  1201  // defined in ec2_test.
  1202  func CheckScripts(c *gc.C, userDataMap map[interface{}]interface{}, pattern string, match bool) {
  1203  	scripts0 := userDataMap["runcmd"]
  1204  	if scripts0 == nil {
  1205  		c.Errorf("cloudinit has no entry for runcmd")
  1206  		return
  1207  	}
  1208  	scripts := scripts0.([]interface{})
  1209  	re := regexp.MustCompile(pattern)
  1210  	found := false
  1211  	for _, s0 := range scripts {
  1212  		s := s0.(string)
  1213  		if re.MatchString(s) {
  1214  			found = true
  1215  		}
  1216  	}
  1217  	switch {
  1218  	case match && !found:
  1219  		c.Errorf("script %q not found in %q", pattern, scripts)
  1220  	case !match && found:
  1221  		c.Errorf("script %q found but not expected in %q", pattern, scripts)
  1222  	}
  1223  }
  1224  
  1225  // CheckPackage checks that the cloudinit will or won't install the given
  1226  // package, depending on the value of match.  It's exported so it can be
  1227  // used by tests defined outside the ec2 package.
  1228  func CheckPackage(c *gc.C, userDataMap map[interface{}]interface{}, pkg string, match bool) {
  1229  	pkgs0 := userDataMap["packages"]
  1230  	if pkgs0 == nil {
  1231  		if match {
  1232  			c.Errorf("cloudinit has no entry for packages")
  1233  		}
  1234  		return
  1235  	}
  1236  
  1237  	pkgs := pkgs0.([]interface{})
  1238  
  1239  	found := false
  1240  	for _, p0 := range pkgs {
  1241  		p := p0.(string)
  1242  		// p might be a space separate list of packages eg 'foo bar qed' so split them up
  1243  		manyPkgs := set.NewStrings(strings.Split(p, " ")...)
  1244  		hasPkg := manyPkgs.Contains(pkg)
  1245  		if p == pkg || hasPkg {
  1246  			found = true
  1247  			break
  1248  		}
  1249  	}
  1250  	switch {
  1251  	case match && !found:
  1252  		c.Errorf("package %q not found in %v", pkg, pkgs)
  1253  	case !match && found:
  1254  		c.Errorf("%q found but not expected in %v", pkg, pkgs)
  1255  	}
  1256  }