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