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