github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/http/httptest"
     9  	"net/http/httputil"
    10  	"net/url"
    11  	"regexp"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/juju/clock"
    17  	"github.com/juju/collections/set"
    18  	"github.com/juju/errors"
    19  	"github.com/juju/os/series"
    20  	jc "github.com/juju/testing/checkers"
    21  	"github.com/juju/utils"
    22  	"github.com/juju/utils/arch"
    23  	"github.com/juju/version"
    24  	"gopkg.in/amz.v3/aws"
    25  	amzec2 "gopkg.in/amz.v3/ec2"
    26  	"gopkg.in/amz.v3/ec2/ec2test"
    27  	gc "gopkg.in/check.v1"
    28  	"gopkg.in/juju/names.v2"
    29  	goyaml "gopkg.in/yaml.v2"
    30  
    31  	"github.com/juju/juju/cloud"
    32  	"github.com/juju/juju/core/constraints"
    33  	"github.com/juju/juju/core/instance"
    34  	"github.com/juju/juju/core/status"
    35  	"github.com/juju/juju/environs"
    36  	"github.com/juju/juju/environs/bootstrap"
    37  	"github.com/juju/juju/environs/context"
    38  	"github.com/juju/juju/environs/imagemetadata"
    39  	imagetesting "github.com/juju/juju/environs/imagemetadata/testing"
    40  	"github.com/juju/juju/environs/instances"
    41  	"github.com/juju/juju/environs/jujutest"
    42  	"github.com/juju/juju/environs/simplestreams"
    43  	sstesting "github.com/juju/juju/environs/simplestreams/testing"
    44  	"github.com/juju/juju/environs/tags"
    45  	envtesting "github.com/juju/juju/environs/testing"
    46  	"github.com/juju/juju/environs/tools"
    47  	"github.com/juju/juju/juju/keys"
    48  	"github.com/juju/juju/juju/testing"
    49  	supportedversion "github.com/juju/juju/juju/version"
    50  	"github.com/juju/juju/jujuclient"
    51  	"github.com/juju/juju/network"
    52  	"github.com/juju/juju/provider/common"
    53  	"github.com/juju/juju/provider/ec2"
    54  	"github.com/juju/juju/storage"
    55  	coretesting "github.com/juju/juju/testing"
    56  	jujuversion "github.com/juju/juju/version"
    57  )
    58  
    59  var localConfigAttrs = coretesting.FakeConfig().Merge(coretesting.Attrs{
    60  	"name":          "sample",
    61  	"type":          "ec2",
    62  	"agent-version": coretesting.FakeVersionNumber.String(),
    63  })
    64  
    65  func fakeCallback(_ status.Status, _ string, _ map[string]interface{}) error {
    66  	return nil
    67  }
    68  
    69  func registerLocalTests() {
    70  	// N.B. Make sure the region we use here
    71  	// has entries in the images/query txt files.
    72  	aws.Regions["test"] = aws.Region{
    73  		Name: "test",
    74  	}
    75  
    76  	gc.Suite(&localServerSuite{})
    77  	gc.Suite(&localLiveSuite{})
    78  	gc.Suite(&localNonUSEastSuite{})
    79  }
    80  
    81  // localLiveSuite runs tests from LiveTests using a fake
    82  // EC2 server that runs within the test process itself.
    83  type localLiveSuite struct {
    84  	LiveTests
    85  	srv localServer
    86  }
    87  
    88  func (t *localLiveSuite) SetUpSuite(c *gc.C) {
    89  	t.LiveTests.SetUpSuite(c)
    90  	t.Credential = cloud.NewCredential(
    91  		cloud.AccessKeyAuthType,
    92  		map[string]string{
    93  			"access-key": "x",
    94  			"secret-key": "x",
    95  		},
    96  	)
    97  
    98  	// Upload arches that ec2 supports; add to this
    99  	// as ec2 coverage expands.
   100  	t.UploadArches = []string{arch.AMD64, arch.I386}
   101  	t.TestConfig = localConfigAttrs
   102  	imagetesting.PatchOfficialDataSources(&t.BaseSuite.CleanupSuite, "test:")
   103  	t.BaseSuite.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey)
   104  	t.BaseSuite.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
   105  	t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc)
   106  	t.srv.createRootDisks = true
   107  	t.srv.startServer(c)
   108  
   109  	region := t.srv.region
   110  	t.CloudRegion = region.Name
   111  	t.CloudEndpoint = region.EC2Endpoint
   112  	restoreEC2Patching := patchEC2ForTesting(c, region)
   113  	t.BaseSuite.AddCleanup(func(c *gc.C) { restoreEC2Patching() })
   114  }
   115  
   116  func (t *localLiveSuite) TearDownSuite(c *gc.C) {
   117  	t.LiveTests.TearDownSuite(c)
   118  	t.srv.stopServer(c)
   119  }
   120  
   121  // localServer represents a fake EC2 server running within
   122  // the test process itself.
   123  type localServer struct {
   124  	// createRootDisks is used to decide whether or not
   125  	// the ec2test server will create root disks for
   126  	// instances.
   127  	createRootDisks bool
   128  
   129  	ec2srv      *ec2test.Server
   130  	proxy       *httputil.ReverseProxy
   131  	proxyServer *httptest.Server
   132  	client      *amzec2.EC2
   133  	region      aws.Region
   134  
   135  	defaultVPC *amzec2.VPC
   136  	zones      []amzec2.AvailabilityZoneInfo
   137  	subnets    []amzec2.Subnet
   138  }
   139  
   140  func (srv *localServer) startServer(c *gc.C) {
   141  	var err error
   142  	srv.ec2srv, err = ec2test.NewServer()
   143  	if err != nil {
   144  		c.Fatalf("cannot start ec2 test server: %v", err)
   145  	}
   146  	srv.ec2srv.SetCreateRootDisks(srv.createRootDisks)
   147  	srv.addSpice(c)
   148  
   149  	// Create a reverse proxy, so we can override responses.
   150  	endpointURL, err := url.Parse(srv.ec2srv.URL())
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	backendURL := &url.URL{
   153  		Scheme: endpointURL.Scheme,
   154  		Host:   endpointURL.Host,
   155  	}
   156  	srv.proxy = httputil.NewSingleHostReverseProxy(backendURL)
   157  	srv.proxyServer = httptest.NewServer(srv.proxy)
   158  	endpointURL, err = url.Parse(srv.proxyServer.URL)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	srv.region = aws.Region{
   161  		Name:        "test",
   162  		EC2Endpoint: endpointURL.String(),
   163  	}
   164  	srv.client = amzec2.New(aws.Auth{}, srv.region, aws.SignV4Factory(srv.region.Name, "ec2"))
   165  
   166  	zones := make([]amzec2.AvailabilityZoneInfo, 4)
   167  	zones[0].Region = srv.region.Name
   168  	zones[0].Name = srv.region.Name + "-available"
   169  	zones[0].State = "available"
   170  	zones[1].Region = srv.region.Name
   171  	zones[1].Name = srv.region.Name + "-impaired"
   172  	zones[1].State = "impaired"
   173  	zones[2].Region = srv.region.Name
   174  	zones[2].Name = srv.region.Name + "-unavailable"
   175  	zones[2].State = "unavailable"
   176  	zones[3].Region = srv.region.Name
   177  	zones[3].Name = srv.region.Name + "-available2"
   178  	zones[3].State = "available"
   179  	srv.ec2srv.SetAvailabilityZones(zones)
   180  	srv.ec2srv.SetInitialInstanceState(ec2test.Pending)
   181  	srv.zones = zones
   182  
   183  	defaultVPC, err := srv.ec2srv.AddDefaultVPCAndSubnets()
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	srv.defaultVPC = &defaultVPC
   186  }
   187  
   188  // addSpice adds some "spice" to the local server
   189  // by adding state that may cause tests to fail.
   190  func (srv *localServer) addSpice(c *gc.C) {
   191  	states := []amzec2.InstanceState{
   192  		ec2test.ShuttingDown,
   193  		ec2test.Terminated,
   194  		ec2test.Stopped,
   195  	}
   196  	for _, state := range states {
   197  		srv.ec2srv.NewInstances(1, "m1.small", "ami-a7f539ce", state, nil)
   198  	}
   199  }
   200  
   201  func (srv *localServer) stopServer(c *gc.C) {
   202  	srv.proxyServer.Close()
   203  	srv.ec2srv.Reset(false)
   204  	srv.ec2srv.Quit()
   205  	srv.defaultVPC = nil
   206  }
   207  
   208  // localServerSuite contains tests that run against a fake EC2 server
   209  // running within the test process itself.  These tests can test things that
   210  // would be unreasonably slow or expensive to test on a live Amazon server.
   211  // It starts a new local ec2test server for each test.  The server is
   212  // accessed by using the "test" region, which is changed to point to the
   213  // network address of the local server.
   214  type localServerSuite struct {
   215  	coretesting.BaseSuite
   216  	jujutest.Tests
   217  	srv    localServer
   218  	client *amzec2.EC2
   219  
   220  	callCtx context.ProviderCallContext
   221  }
   222  
   223  func (t *localServerSuite) SetUpSuite(c *gc.C) {
   224  	t.BaseSuite.SetUpSuite(c)
   225  	t.Credential = cloud.NewCredential(
   226  		cloud.AccessKeyAuthType,
   227  		map[string]string{
   228  			"access-key": "x",
   229  			"secret-key": "x",
   230  		},
   231  	)
   232  
   233  	// Upload arches that ec2 supports; add to this
   234  	// as ec2 coverage expands.
   235  	t.UploadArches = []string{arch.AMD64, arch.I386}
   236  	t.TestConfig = localConfigAttrs
   237  	imagetesting.PatchOfficialDataSources(&t.BaseSuite.CleanupSuite, "test:")
   238  	t.BaseSuite.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey)
   239  	t.BaseSuite.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
   240  	t.BaseSuite.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
   241  	t.BaseSuite.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
   242  	t.BaseSuite.PatchValue(&series.MustHostSeries, func() string { return supportedversion.SupportedLTS() })
   243  	t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc)
   244  	t.srv.createRootDisks = true
   245  	t.srv.startServer(c)
   246  	// TODO(jam) I don't understand why we shouldn't do this.
   247  	// t.Tests embeds the sstesting.TestDataSuite, but if we call this
   248  	// SetUpSuite, then all of the tests fail because they go to access
   249  	// "test:/streams/..." and it isn't found
   250  	// t.Tests.SetUpSuite(c)
   251  }
   252  
   253  func (t *localServerSuite) TearDownSuite(c *gc.C) {
   254  	t.Tests.TearDownSuite(c)
   255  	t.BaseSuite.TearDownSuite(c)
   256  }
   257  
   258  func (t *localServerSuite) SetUpTest(c *gc.C) {
   259  	t.BaseSuite.SetUpTest(c)
   260  	t.srv.startServer(c)
   261  	region := t.srv.region
   262  	t.CloudRegion = region.Name
   263  	t.CloudEndpoint = region.EC2Endpoint
   264  	t.client = t.srv.client
   265  	restoreEC2Patching := patchEC2ForTesting(c, region)
   266  	t.AddCleanup(func(c *gc.C) { restoreEC2Patching() })
   267  	t.Tests.SetUpTest(c)
   268  
   269  	t.callCtx = context.NewCloudCallContext()
   270  }
   271  
   272  func (t *localServerSuite) TearDownTest(c *gc.C) {
   273  	t.Tests.TearDownTest(c)
   274  	t.srv.stopServer(c)
   275  	t.BaseSuite.TearDownTest(c)
   276  }
   277  
   278  func (t *localServerSuite) prepareEnviron(c *gc.C) environs.NetworkingEnviron {
   279  	env := t.Prepare(c)
   280  	netenv, supported := environs.SupportsNetworking(env)
   281  	c.Assert(supported, jc.IsTrue)
   282  	return netenv
   283  }
   284  
   285  func (t *localServerSuite) TestPrepareForBootstrapWithInvalidVPCID(c *gc.C) {
   286  	badVPCIDConfig := coretesting.Attrs{"vpc-id": "bad"}
   287  
   288  	expectedError := `invalid EC2 provider config: vpc-id: "bad" is not a valid AWS VPC ID`
   289  	t.AssertPrepareFailsWithConfig(c, badVPCIDConfig, expectedError)
   290  }
   291  
   292  func (t *localServerSuite) TestPrepareForBootstrapWithUnknownVPCID(c *gc.C) {
   293  	unknownVPCIDConfig := coretesting.Attrs{"vpc-id": "vpc-unknown"}
   294  
   295  	expectedError := `Juju cannot use the given vpc-id for bootstrapping(.|\n)*Error details: VPC "vpc-unknown" not found`
   296  	err := t.AssertPrepareFailsWithConfig(c, unknownVPCIDConfig, expectedError)
   297  	c.Check(err, jc.Satisfies, ec2.IsVPCNotUsableError)
   298  }
   299  
   300  func (t *localServerSuite) TestPrepareForBootstrapWithNotRecommendedVPCID(c *gc.C) {
   301  	t.makeTestingDefaultVPCUnavailable(c)
   302  	notRecommendedVPCIDConfig := coretesting.Attrs{"vpc-id": t.srv.defaultVPC.Id}
   303  
   304  	expectedError := `The given vpc-id does not meet one or more(.|\n)*Error details: VPC has unexpected state "unavailable"`
   305  	err := t.AssertPrepareFailsWithConfig(c, notRecommendedVPCIDConfig, expectedError)
   306  	c.Check(err, jc.Satisfies, ec2.IsVPCNotRecommendedError)
   307  }
   308  
   309  func (t *localServerSuite) makeTestingDefaultVPCUnavailable(c *gc.C) {
   310  	// For simplicity, here the test server's default VPC is updated to change
   311  	// its state to unavailable, we just verify the behavior of a "not
   312  	// recommended VPC".
   313  	t.srv.defaultVPC.State = "unavailable"
   314  	err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC)
   315  	c.Assert(err, jc.ErrorIsNil)
   316  }
   317  
   318  func (t *localServerSuite) TestPrepareForBootstrapWithNotRecommendedButForcedVPCID(c *gc.C) {
   319  	t.makeTestingDefaultVPCUnavailable(c)
   320  	params := t.PrepareParams(c)
   321  	params.ModelConfig["vpc-id"] = t.srv.defaultVPC.Id
   322  	params.ModelConfig["vpc-id-force"] = true
   323  
   324  	t.prepareWithParamsAndBootstrapWithVPCID(c, params, t.srv.defaultVPC.Id)
   325  }
   326  
   327  func (t *localServerSuite) TestPrepareForBootstrapWithEmptyVPCID(c *gc.C) {
   328  	const emptyVPCID = ""
   329  
   330  	params := t.PrepareParams(c)
   331  	params.ModelConfig["vpc-id"] = emptyVPCID
   332  
   333  	t.prepareWithParamsAndBootstrapWithVPCID(c, params, emptyVPCID)
   334  }
   335  
   336  func (t *localServerSuite) prepareWithParamsAndBootstrapWithVPCID(c *gc.C, params bootstrap.PrepareParams, expectedVPCID string) {
   337  	env := t.PrepareWithParams(c, params)
   338  	unknownAttrs := env.Config().UnknownAttrs()
   339  	vpcID, ok := unknownAttrs["vpc-id"]
   340  	c.Check(vpcID, gc.Equals, expectedVPCID)
   341  	c.Check(ok, jc.IsTrue)
   342  
   343  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
   344  		t.callCtx, bootstrap.BootstrapParams{
   345  			ControllerConfig: coretesting.FakeControllerConfig(),
   346  			AdminSecret:      testing.AdminSecret,
   347  			CAPrivateKey:     coretesting.CAKey,
   348  			Placement:        "zone=test-available",
   349  		})
   350  	c.Assert(err, jc.ErrorIsNil)
   351  }
   352  
   353  func (t *localServerSuite) TestPrepareForBootstrapWithVPCIDNone(c *gc.C) {
   354  	params := t.PrepareParams(c)
   355  	params.ModelConfig["vpc-id"] = "none"
   356  
   357  	t.prepareWithParamsAndBootstrapWithVPCID(c, params, ec2.VPCIDNone)
   358  }
   359  
   360  func (t *localServerSuite) TestPrepareForBootstrapWithDefaultVPCID(c *gc.C) {
   361  	params := t.PrepareParams(c)
   362  	params.ModelConfig["vpc-id"] = t.srv.defaultVPC.Id
   363  
   364  	t.prepareWithParamsAndBootstrapWithVPCID(c, params, t.srv.defaultVPC.Id)
   365  }
   366  
   367  func (t *localServerSuite) TestSystemdBootstrapInstanceUserDataAndState(c *gc.C) {
   368  	env := t.Prepare(c)
   369  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
   370  		t.callCtx, bootstrap.BootstrapParams{
   371  			ControllerConfig: coretesting.FakeControllerConfig(),
   372  			// TODO(redir): BBB: When we no longer support upstart based systems this can change to series.LatestLts()
   373  			BootstrapSeries: "xenial",
   374  			AdminSecret:     testing.AdminSecret,
   375  			CAPrivateKey:    coretesting.CAKey,
   376  		})
   377  	c.Assert(err, jc.ErrorIsNil)
   378  
   379  	// check that ControllerInstances returns the id of the bootstrap machine.
   380  	instanceIds, err := env.ControllerInstances(t.callCtx, t.ControllerUUID)
   381  	c.Assert(err, jc.ErrorIsNil)
   382  	c.Assert(instanceIds, gc.HasLen, 1)
   383  
   384  	insts, err := env.AllInstances(t.callCtx)
   385  	c.Assert(err, jc.ErrorIsNil)
   386  	c.Assert(insts, gc.HasLen, 1)
   387  	c.Check(insts[0].Id(), gc.Equals, instanceIds[0])
   388  
   389  	// check that the user data is configured to and the machine and
   390  	// provisioning agents.  check that the user data is configured to only
   391  	// configure authorized SSH keys and set the log output; everything else
   392  	// happens after the machine is brought up.
   393  	inst := t.srv.ec2srv.Instance(string(insts[0].Id()))
   394  	c.Assert(inst, gc.NotNil)
   395  	addresses, err := insts[0].Addresses(t.callCtx)
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
   398  	userData, err := utils.Gunzip(inst.UserData)
   399  	c.Assert(err, jc.ErrorIsNil)
   400  
   401  	var userDataMap map[string]interface{}
   402  	err = goyaml.Unmarshal(userData, &userDataMap)
   403  	c.Assert(err, jc.ErrorIsNil)
   404  	var keys []string
   405  	for key := range userDataMap {
   406  		keys = append(keys, key)
   407  	}
   408  	c.Assert(keys, jc.SameContents, []string{"output", "users", "runcmd", "ssh_keys"})
   409  	c.Assert(userDataMap["runcmd"], jc.DeepEquals, []interface{}{
   410  		"set -xe",
   411  		"install -D -m 644 /dev/null '/etc/systemd/system/juju-clean-shutdown.service'",
   412  		"printf '%s\\n' '\n[Unit]\nDescription=Stop all network interfaces on shutdown\nDefaultDependencies=false\nAfter=final.target\n\n[Service]\nType=oneshot\nExecStart=/sbin/ifdown -a -v --force\nStandardOutput=tty\nStandardError=tty\n\n[Install]\nWantedBy=final.target\n' > '/etc/systemd/system/juju-clean-shutdown.service'", "/bin/systemctl enable '/etc/systemd/system/juju-clean-shutdown.service'",
   413  		"install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'",
   414  		"printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'",
   415  	})
   416  
   417  	// check that a new instance will be started with a machine agent
   418  	inst1, hc := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1")
   419  	c.Check(*hc.Arch, gc.Equals, "amd64")
   420  	c.Check(*hc.Mem, gc.Equals, uint64(1024))
   421  	c.Check(*hc.CpuCores, gc.Equals, uint64(2))
   422  	inst = t.srv.ec2srv.Instance(string(inst1.Id()))
   423  	c.Assert(inst, gc.NotNil)
   424  	userData, err = utils.Gunzip(inst.UserData)
   425  	c.Assert(err, jc.ErrorIsNil)
   426  	c.Logf("second instance: UserData: %q", userData)
   427  	userDataMap = nil
   428  	err = goyaml.Unmarshal(userData, &userDataMap)
   429  	c.Assert(err, jc.ErrorIsNil)
   430  	CheckPackage(c, userDataMap, "curl", true)
   431  	CheckPackage(c, userDataMap, "mongodb-server", false)
   432  	CheckScripts(c, userDataMap, "jujud bootstrap-state", false)
   433  	CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true)
   434  	// TODO check for provisioning agent
   435  
   436  	err = env.Destroy(t.callCtx)
   437  	c.Assert(err, jc.ErrorIsNil)
   438  
   439  	_, err = env.ControllerInstances(t.callCtx, t.ControllerUUID)
   440  	c.Assert(err, gc.Equals, environs.ErrNotBootstrapped)
   441  }
   442  
   443  // TestUpstartBoostrapInstanceUserDataAndState is a test for legacy systems
   444  // using upstart which will be around until trusty is no longer supported.
   445  // TODO(redir): BBB: remove when trusty is no longer supported
   446  func (t *localServerSuite) TestUpstartBootstrapInstanceUserDataAndState(c *gc.C) {
   447  	env := t.Prepare(c)
   448  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
   449  		t.callCtx, bootstrap.BootstrapParams{
   450  			ControllerConfig: coretesting.FakeControllerConfig(),
   451  			BootstrapSeries:  "trusty",
   452  			AdminSecret:      testing.AdminSecret,
   453  			CAPrivateKey:     coretesting.CAKey,
   454  		})
   455  	c.Assert(err, jc.ErrorIsNil)
   456  
   457  	// check that ControllerInstances returns the id of the bootstrap machine.
   458  	instanceIds, err := env.ControllerInstances(t.callCtx, t.ControllerUUID)
   459  	c.Assert(err, jc.ErrorIsNil)
   460  	c.Assert(instanceIds, gc.HasLen, 1)
   461  
   462  	insts, err := env.AllInstances(t.callCtx)
   463  	c.Assert(err, jc.ErrorIsNil)
   464  	c.Assert(insts, gc.HasLen, 1)
   465  	c.Check(insts[0].Id(), gc.Equals, instanceIds[0])
   466  
   467  	// check that the user data is configured to and the machine and
   468  	// provisioning agents.  check that the user data is configured to only
   469  	// configure authorized SSH keys and set the log output; everything else
   470  	// happens after the machine is brought up.
   471  	inst := t.srv.ec2srv.Instance(string(insts[0].Id()))
   472  	c.Assert(inst, gc.NotNil)
   473  	addresses, err := insts[0].Addresses(t.callCtx)
   474  	c.Assert(err, jc.ErrorIsNil)
   475  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
   476  	userData, err := utils.Gunzip(inst.UserData)
   477  	c.Assert(err, jc.ErrorIsNil)
   478  
   479  	var userDataMap map[string]interface{}
   480  	err = goyaml.Unmarshal(userData, &userDataMap)
   481  	c.Assert(err, jc.ErrorIsNil)
   482  	var keys []string
   483  	for key := range userDataMap {
   484  		keys = append(keys, key)
   485  	}
   486  	c.Assert(keys, jc.SameContents, []string{"output", "users", "runcmd", "ssh_keys"})
   487  	c.Assert(userDataMap["runcmd"], jc.DeepEquals, []interface{}{
   488  		"set -xe",
   489  		"install -D -m 644 /dev/null '/etc/init/juju-clean-shutdown.conf'",
   490  		"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'",
   491  		"install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'",
   492  		"printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'",
   493  	})
   494  
   495  	// check that a new instance will be started with a machine agent
   496  	inst1, hc := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1")
   497  	c.Check(*hc.Arch, gc.Equals, "amd64")
   498  	c.Check(*hc.Mem, gc.Equals, uint64(1024))
   499  	c.Check(*hc.CpuCores, gc.Equals, uint64(2))
   500  	inst = t.srv.ec2srv.Instance(string(inst1.Id()))
   501  	c.Assert(inst, gc.NotNil)
   502  	userData, err = utils.Gunzip(inst.UserData)
   503  	c.Assert(err, jc.ErrorIsNil)
   504  	c.Logf("second instance: UserData: %q", userData)
   505  	userDataMap = nil
   506  	err = goyaml.Unmarshal(userData, &userDataMap)
   507  	c.Assert(err, jc.ErrorIsNil)
   508  	CheckPackage(c, userDataMap, "curl", true)
   509  	CheckPackage(c, userDataMap, "mongodb-server", false)
   510  	CheckScripts(c, userDataMap, "jujud bootstrap-state", false)
   511  	CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true)
   512  	// TODO check for provisioning agent
   513  
   514  	err = env.Destroy(t.callCtx)
   515  	c.Assert(err, jc.ErrorIsNil)
   516  
   517  	_, err = env.ControllerInstances(t.callCtx, t.ControllerUUID)
   518  	c.Assert(err, gc.Equals, environs.ErrNotBootstrapped)
   519  }
   520  
   521  func (t *localServerSuite) TestTerminateInstancesIgnoresNotFound(c *gc.C) {
   522  	env := t.Prepare(c)
   523  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
   524  		t.callCtx, bootstrap.BootstrapParams{
   525  			ControllerConfig: coretesting.FakeControllerConfig(),
   526  			AdminSecret:      testing.AdminSecret,
   527  			CAPrivateKey:     coretesting.CAKey,
   528  		})
   529  	c.Assert(err, jc.ErrorIsNil)
   530  
   531  	t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc)
   532  	insts, err := env.AllInstances(t.callCtx)
   533  	c.Assert(err, jc.ErrorIsNil)
   534  	idsToStop := make([]instance.Id, len(insts)+1)
   535  	for i, one := range insts {
   536  		idsToStop[i] = one.Id()
   537  	}
   538  	idsToStop[len(insts)] = instance.Id("i-am-not-found")
   539  
   540  	err = env.StopInstances(t.callCtx, idsToStop...)
   541  	// NotFound should be ignored
   542  	c.Assert(err, jc.ErrorIsNil)
   543  }
   544  
   545  func (t *localServerSuite) TestDestroyErr(c *gc.C) {
   546  	env := t.prepareAndBootstrap(c)
   547  
   548  	msg := "terminate instances error"
   549  	t.BaseSuite.PatchValue(ec2.TerminateInstancesById, func(ec2inst *amzec2.EC2, ctx context.ProviderCallContext, ids ...instance.Id) (*amzec2.TerminateInstancesResp, error) {
   550  		return nil, errors.New(msg)
   551  	})
   552  
   553  	err := env.Destroy(t.callCtx)
   554  	c.Assert(errors.Cause(err).Error(), jc.Contains, msg)
   555  }
   556  
   557  func (t *localServerSuite) TestGetTerminatedInstances(c *gc.C) {
   558  	env := t.Prepare(c)
   559  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
   560  		t.callCtx, bootstrap.BootstrapParams{
   561  			ControllerConfig: coretesting.FakeControllerConfig(),
   562  			AdminSecret:      testing.AdminSecret,
   563  			CAPrivateKey:     coretesting.CAKey,
   564  		})
   565  	c.Assert(err, jc.ErrorIsNil)
   566  
   567  	// create another instance to terminate
   568  	inst1, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1")
   569  	inst := t.srv.ec2srv.Instance(string(inst1.Id()))
   570  	c.Assert(inst, gc.NotNil)
   571  	t.BaseSuite.PatchValue(ec2.TerminateInstancesById, func(ec2inst *amzec2.EC2, ctx context.ProviderCallContext, ids ...instance.Id) (*amzec2.TerminateInstancesResp, error) {
   572  		// Terminate the one destined for termination and
   573  		// err out to ensure that one instance will be terminated, the other - not.
   574  		_, err = ec2inst.TerminateInstances([]string{string(inst1.Id())})
   575  		c.Assert(err, jc.ErrorIsNil)
   576  		return nil, errors.New("terminate instances error")
   577  	})
   578  	err = env.Destroy(t.callCtx)
   579  	c.Assert(err, gc.NotNil)
   580  
   581  	terminated, err := ec2.TerminatedInstances(env)
   582  	c.Assert(err, jc.ErrorIsNil)
   583  	c.Assert(terminated, gc.HasLen, 1)
   584  	c.Assert(terminated[0].Id(), jc.DeepEquals, inst1.Id())
   585  }
   586  
   587  func (t *localServerSuite) TestInstanceSecurityGroupsWitheInstanceStatusFilter(c *gc.C) {
   588  	env := t.prepareAndBootstrap(c)
   589  
   590  	insts, err := env.AllInstances(t.callCtx)
   591  	c.Assert(err, jc.ErrorIsNil)
   592  	ids := make([]instance.Id, len(insts))
   593  	for i, one := range insts {
   594  		ids[i] = one.Id()
   595  	}
   596  
   597  	groupsNoInstanceFilter, err := ec2.InstanceSecurityGroups(env, t.callCtx, ids)
   598  	c.Assert(err, jc.ErrorIsNil)
   599  	// get all security groups for test instances
   600  	c.Assert(groupsNoInstanceFilter, gc.HasLen, 2)
   601  
   602  	groupsFilteredForTerminatedInstances, err := ec2.InstanceSecurityGroups(env, t.callCtx, ids, "shutting-down", "terminated")
   603  	c.Assert(err, jc.ErrorIsNil)
   604  	// get all security groups for terminated test instances
   605  	c.Assert(groupsFilteredForTerminatedInstances, gc.HasLen, 0)
   606  }
   607  
   608  func (t *localServerSuite) TestDestroyControllerModelDeleteSecurityGroupInsistentlyError(c *gc.C) {
   609  	env := t.prepareAndBootstrap(c)
   610  	msg := "destroy security group error"
   611  	t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, func(
   612  		ec2.SecurityGroupCleaner, context.ProviderCallContext, amzec2.SecurityGroup, clock.Clock,
   613  	) error {
   614  		return errors.New(msg)
   615  	})
   616  	err := env.DestroyController(t.callCtx, t.ControllerUUID)
   617  	c.Assert(err, gc.ErrorMatches, "destroying managed environs: cannot delete security group .*: "+msg)
   618  }
   619  
   620  func (t *localServerSuite) TestDestroyHostedModelDeleteSecurityGroupInsistentlyError(c *gc.C) {
   621  	env := t.prepareAndBootstrap(c)
   622  	hostedEnv, err := environs.New(environs.OpenParams{
   623  		Cloud:  t.CloudSpec(),
   624  		Config: env.Config(),
   625  	})
   626  	c.Assert(err, jc.ErrorIsNil)
   627  
   628  	msg := "destroy security group error"
   629  	t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, func(
   630  		ec2.SecurityGroupCleaner, context.ProviderCallContext, amzec2.SecurityGroup, clock.Clock,
   631  	) error {
   632  		return errors.New(msg)
   633  	})
   634  	err = hostedEnv.Destroy(t.callCtx)
   635  	c.Assert(err, gc.ErrorMatches, "cannot delete environment security groups: cannot delete default security group: "+msg)
   636  }
   637  
   638  func (t *localServerSuite) TestDestroyControllerDestroysHostedModelResources(c *gc.C) {
   639  	controllerEnv := t.prepareAndBootstrap(c)
   640  
   641  	// Create a hosted model environment with an instance and a volume.
   642  	hostedModelUUID := "7e386e08-cba7-44a4-a76e-7c1633584210"
   643  	t.srv.ec2srv.SetInitialInstanceState(ec2test.Running)
   644  	cfg, err := controllerEnv.Config().Apply(map[string]interface{}{
   645  		"uuid":          hostedModelUUID,
   646  		"firewall-mode": "global",
   647  	})
   648  	c.Assert(err, jc.ErrorIsNil)
   649  	env, err := environs.New(environs.OpenParams{
   650  		Cloud:  t.CloudSpec(),
   651  		Config: cfg,
   652  	})
   653  	c.Assert(err, jc.ErrorIsNil)
   654  	inst, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "0")
   655  	c.Assert(err, jc.ErrorIsNil)
   656  	ebsProvider, err := env.StorageProvider(ec2.EBS_ProviderType)
   657  	c.Assert(err, jc.ErrorIsNil)
   658  	vs, err := ebsProvider.VolumeSource(nil)
   659  	c.Assert(err, jc.ErrorIsNil)
   660  	volumeResults, err := vs.CreateVolumes(t.callCtx, []storage.VolumeParams{{
   661  		Tag:      names.NewVolumeTag("0"),
   662  		Size:     1024,
   663  		Provider: ec2.EBS_ProviderType,
   664  		ResourceTags: map[string]string{
   665  			tags.JujuController: t.ControllerUUID,
   666  			tags.JujuModel:      hostedModelUUID,
   667  		},
   668  		Attachment: &storage.VolumeAttachmentParams{
   669  			AttachmentParams: storage.AttachmentParams{
   670  				InstanceId: inst.Id(),
   671  			},
   672  		},
   673  	}})
   674  	c.Assert(err, jc.ErrorIsNil)
   675  	c.Assert(volumeResults, gc.HasLen, 1)
   676  	c.Assert(volumeResults[0].Error, jc.ErrorIsNil)
   677  
   678  	assertInstances := func(expect ...instance.Id) {
   679  		insts, err := env.AllInstances(t.callCtx)
   680  		c.Assert(err, jc.ErrorIsNil)
   681  		ids := make([]instance.Id, len(insts))
   682  		for i, inst := range insts {
   683  			ids[i] = inst.Id()
   684  		}
   685  		c.Assert(ids, jc.SameContents, expect)
   686  	}
   687  	assertVolumes := func(expect ...string) {
   688  		volIds, err := vs.ListVolumes(t.callCtx)
   689  		c.Assert(err, jc.ErrorIsNil)
   690  		c.Assert(volIds, jc.SameContents, expect)
   691  	}
   692  	assertGroups := func(expect ...string) {
   693  		groupsResp, err := t.client.SecurityGroups(nil, nil)
   694  		c.Assert(err, jc.ErrorIsNil)
   695  		names := make([]string, len(groupsResp.Groups))
   696  		for i, group := range groupsResp.Groups {
   697  			names[i] = group.Name
   698  		}
   699  		c.Assert(names, jc.SameContents, expect)
   700  	}
   701  
   702  	assertInstances(inst.Id())
   703  	assertVolumes(volumeResults[0].Volume.VolumeId)
   704  	assertGroups(
   705  		"default",
   706  		"juju-"+controllerEnv.Config().UUID(),
   707  		"juju-"+controllerEnv.Config().UUID()+"-0",
   708  		"juju-"+hostedModelUUID,
   709  		"juju-"+hostedModelUUID+"-global",
   710  	)
   711  
   712  	// Destroy the controller resources. This should destroy the hosted
   713  	// environment too.
   714  	err = controllerEnv.DestroyController(t.callCtx, t.ControllerUUID)
   715  	c.Assert(err, jc.ErrorIsNil)
   716  
   717  	assertInstances()
   718  	assertVolumes()
   719  	assertGroups("default")
   720  }
   721  
   722  func (t *localServerSuite) TestInstanceStatus(c *gc.C) {
   723  	env := t.Prepare(c)
   724  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
   725  		t.callCtx, bootstrap.BootstrapParams{
   726  			ControllerConfig: coretesting.FakeControllerConfig(),
   727  			AdminSecret:      testing.AdminSecret,
   728  			CAPrivateKey:     coretesting.CAKey,
   729  		})
   730  	c.Assert(err, jc.ErrorIsNil)
   731  	t.srv.ec2srv.SetInitialInstanceState(ec2test.Terminated)
   732  	inst, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1")
   733  	c.Assert(err, jc.ErrorIsNil)
   734  	c.Assert(inst.Status(t.callCtx).Message, gc.Equals, "terminated")
   735  }
   736  
   737  func (t *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
   738  	env := t.prepareAndBootstrap(c)
   739  	_, hc := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1")
   740  	c.Check(*hc.Arch, gc.Equals, "amd64")
   741  	c.Check(*hc.Mem, gc.Equals, uint64(1024))
   742  	c.Check(*hc.CpuCores, gc.Equals, uint64(2))
   743  }
   744  
   745  func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) {
   746  	inst, err := t.testStartInstanceAvailZone(c, "test-available")
   747  	c.Assert(err, jc.ErrorIsNil)
   748  	c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "test-available")
   749  }
   750  
   751  func (t *localServerSuite) TestStartInstanceAvailZoneImpaired(c *gc.C) {
   752  	_, err := t.testStartInstanceAvailZone(c, "test-impaired")
   753  	c.Assert(err, gc.ErrorMatches, `availability zone "test-impaired" is "impaired"`)
   754  }
   755  
   756  func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) {
   757  	_, err := t.testStartInstanceAvailZone(c, "test-unknown")
   758  	c.Assert(err, gc.Not(jc.Satisfies), environs.IsAvailabilityZoneIndependent)
   759  	c.Assert(errors.Details(err), gc.Matches, `.*availability zone \"\" not valid.*`)
   760  }
   761  
   762  func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instances.Instance, error) {
   763  	env := t.prepareAndBootstrap(c)
   764  
   765  	params := environs.StartInstanceParams{ControllerUUID: t.ControllerUUID, AvailabilityZone: zone, StatusCallback: fakeCallback}
   766  	result, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params)
   767  	if err != nil {
   768  		return nil, err
   769  	}
   770  	return result.Instance, nil
   771  }
   772  
   773  func (t *localServerSuite) TestStartInstanceVolumeAttachmentsAvailZone(c *gc.C) {
   774  	env := t.prepareAndBootstrap(c)
   775  	resp, err := t.client.CreateVolume(amzec2.CreateVolume{
   776  		VolumeSize: 1,
   777  		VolumeType: "gp2",
   778  		AvailZone:  "volume-zone",
   779  	})
   780  	c.Assert(err, jc.ErrorIsNil)
   781  
   782  	args := environs.StartInstanceParams{
   783  		ControllerUUID: t.ControllerUUID,
   784  		StatusCallback: fakeCallback,
   785  		VolumeAttachments: []storage.VolumeAttachmentParams{{
   786  			AttachmentParams: storage.AttachmentParams{
   787  				Provider: "ebs",
   788  				Machine:  names.NewMachineTag("1"),
   789  			},
   790  			Volume:   names.NewVolumeTag("23"),
   791  			VolumeId: resp.Id,
   792  		}},
   793  	}
   794  	result, err := testing.StartInstanceWithParams(env, t.callCtx, "1", args)
   795  	c.Assert(err, jc.ErrorIsNil)
   796  	c.Assert(ec2.InstanceEC2(result.Instance).AvailZone, gc.Equals, "volume-zone")
   797  }
   798  
   799  func (t *localServerSuite) TestStartInstanceVolumeAttachmentsAvailZonePlacementConflicts(c *gc.C) {
   800  	env := t.prepareAndBootstrap(c)
   801  	resp, err := t.client.CreateVolume(amzec2.CreateVolume{
   802  		VolumeSize: 1,
   803  		VolumeType: "gp2",
   804  		AvailZone:  "volume-zone",
   805  	})
   806  	c.Assert(err, jc.ErrorIsNil)
   807  
   808  	args := environs.StartInstanceParams{
   809  		ControllerUUID: t.ControllerUUID,
   810  		StatusCallback: fakeCallback,
   811  		Placement:      "zone=test-available",
   812  		VolumeAttachments: []storage.VolumeAttachmentParams{{
   813  			AttachmentParams: storage.AttachmentParams{
   814  				Provider: "ebs",
   815  				Machine:  names.NewMachineTag("1"),
   816  			},
   817  			Volume:   names.NewVolumeTag("23"),
   818  			VolumeId: resp.Id,
   819  		}},
   820  	}
   821  	_, err = testing.StartInstanceWithParams(env, t.callCtx, "1", args)
   822  	c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=test-available", as this will prevent attaching the requested EBS volumes in zone "volume-zone"`)
   823  }
   824  
   825  func (t *localServerSuite) TestStartInstanceZoneIndependent(c *gc.C) {
   826  	env := t.prepareAndBootstrap(c)
   827  	params := environs.StartInstanceParams{
   828  		ControllerUUID:   t.ControllerUUID,
   829  		StatusCallback:   fakeCallback,
   830  		AvailabilityZone: "test-available",
   831  		Placement:        "nonsense",
   832  	}
   833  	_, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params)
   834  	c.Assert(err, gc.ErrorMatches, "unknown placement directive: nonsense")
   835  	// The returned error should indicate that it is independent
   836  	// of the availability zone specified.
   837  	c.Assert(err, jc.Satisfies, environs.IsAvailabilityZoneIndependent)
   838  }
   839  
   840  func (t *localServerSuite) TestStartInstanceSubnet(c *gc.C) {
   841  	inst, err := t.testStartInstanceSubnet(c, "0.1.2.0/24")
   842  	c.Assert(err, jc.ErrorIsNil)
   843  	ec2Inst := ec2.InstanceEC2(inst)
   844  	c.Assert(ec2Inst.AvailZone, gc.Equals, "test-available")
   845  }
   846  
   847  func (t *localServerSuite) TestStartInstanceSubnetUnavailable(c *gc.C) {
   848  	// See addTestingSubnets, 0.1.3.0/24 is in state "unavailable", but is in
   849  	// an AZ that would otherwise be available
   850  	_, err := t.testStartInstanceSubnet(c, "0.1.3.0/24")
   851  	c.Assert(err, gc.ErrorMatches, `subnet "0.1.3.0/24" is "unavailable"`)
   852  }
   853  
   854  func (t *localServerSuite) TestStartInstanceSubnetAZUnavailable(c *gc.C) {
   855  	// See addTestingSubnets, 0.1.4.0/24 is in an AZ that is unavailable
   856  	_, err := t.testStartInstanceSubnet(c, "0.1.4.0/24")
   857  	c.Assert(err, gc.ErrorMatches, `availability zone "test-unavailable" is "unavailable"`)
   858  }
   859  
   860  func (t *localServerSuite) testStartInstanceSubnet(c *gc.C, subnet string) (instances.Instance, error) {
   861  	subIDs, vpcId := t.addTestingSubnets(c)
   862  	env := t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{"vpc-id": vpcId, "vpc-id-force": true})
   863  	params := environs.StartInstanceParams{
   864  		ControllerUUID: t.ControllerUUID,
   865  		Placement:      fmt.Sprintf("subnet=%s", subnet),
   866  		SubnetsToZones: map[network.Id][]string{
   867  			subIDs[0]: {"test-available"},
   868  			subIDs[1]: {"test-available"},
   869  			subIDs[2]: {"test-unavailable"},
   870  		},
   871  	}
   872  	zonedEnviron := env.(common.ZonedEnviron)
   873  	zones, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params)
   874  	if err != nil {
   875  		return nil, err
   876  	}
   877  	if len(zones) > 0 {
   878  		params.AvailabilityZone = zones[0]
   879  		result, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params)
   880  		if err != nil {
   881  			return nil, err
   882  		}
   883  		return result.Instance, nil
   884  	}
   885  	return nil, errors.Errorf("testStartInstanceSubnet failed")
   886  }
   887  
   888  func (t *localServerSuite) TestDeriveAvailabilityZoneSubnetWrongVPC(c *gc.C) {
   889  	subIDs, vpcId := t.addTestingSubnets(c)
   890  	c.Assert(vpcId, gc.Not(gc.Equals), "vpc-0")
   891  	env := t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{"vpc-id": "vpc-0", "vpc-id-force": true})
   892  	params := environs.StartInstanceParams{
   893  		ControllerUUID: t.ControllerUUID,
   894  		Placement:      "subnet=0.1.2.0/24",
   895  		SubnetsToZones: map[network.Id][]string{
   896  			subIDs[0]: {"test-available"},
   897  			subIDs[1]: {"test-available"},
   898  			subIDs[2]: {"test-unavailable"},
   899  		},
   900  	}
   901  	zonedEnviron := env.(common.ZonedEnviron)
   902  	_, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params)
   903  	c.Assert(err, gc.ErrorMatches, `unknown placement directive: subnet=0.1.2.0/24`)
   904  }
   905  
   906  func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) {
   907  	var resultZones []amzec2.AvailabilityZoneInfo
   908  	var resultErr error
   909  	t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) {
   910  		resp := &amzec2.AvailabilityZonesResp{
   911  			Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...),
   912  		}
   913  		return resp, resultErr
   914  	})
   915  	env := t.Prepare(c).(common.ZonedEnviron)
   916  
   917  	resultErr = fmt.Errorf("failed to get availability zones")
   918  	zones, err := env.AvailabilityZones(t.callCtx)
   919  	c.Assert(err, gc.Equals, resultErr)
   920  	c.Assert(zones, gc.IsNil)
   921  
   922  	resultErr = nil
   923  	resultZones = make([]amzec2.AvailabilityZoneInfo, 1)
   924  	resultZones[0].Name = "whatever"
   925  	zones, err = env.AvailabilityZones(t.callCtx)
   926  	c.Assert(err, jc.ErrorIsNil)
   927  	c.Assert(zones, gc.HasLen, 1)
   928  	c.Assert(zones[0].Name(), gc.Equals, "whatever")
   929  
   930  	// A successful result is cached, currently for the lifetime
   931  	// of the Environ. This will change if/when we have long-lived
   932  	// Environs to cut down repeated IaaS requests.
   933  	resultErr = fmt.Errorf("failed to get availability zones")
   934  	resultZones[0].Name = "andever"
   935  	zones, err = env.AvailabilityZones(t.callCtx)
   936  	c.Assert(err, jc.ErrorIsNil)
   937  	c.Assert(zones, gc.HasLen, 1)
   938  	c.Assert(zones[0].Name(), gc.Equals, "whatever")
   939  }
   940  
   941  func (t *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) {
   942  	var resultZones []amzec2.AvailabilityZoneInfo
   943  	t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) {
   944  		resp := &amzec2.AvailabilityZonesResp{
   945  			Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...),
   946  		}
   947  		return resp, nil
   948  	})
   949  	env := t.Prepare(c).(common.ZonedEnviron)
   950  	resultZones = make([]amzec2.AvailabilityZoneInfo, 2)
   951  	resultZones[0].Name = "az1"
   952  	resultZones[1].Name = "az2"
   953  	resultZones[0].State = "available"
   954  	resultZones[1].State = "impaired"
   955  	zones, err := env.AvailabilityZones(t.callCtx)
   956  	c.Assert(err, jc.ErrorIsNil)
   957  	c.Assert(zones, gc.HasLen, 2)
   958  	c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name)
   959  	c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name)
   960  	c.Assert(zones[0].Available(), jc.IsTrue)
   961  	c.Assert(zones[1].Available(), jc.IsFalse)
   962  }
   963  
   964  type mockAvailabilityZoneAllocations struct {
   965  	group  []instance.Id // input param
   966  	result []common.AvailabilityZoneInstances
   967  	err    error
   968  }
   969  
   970  func (t *mockAvailabilityZoneAllocations) AvailabilityZoneAllocations(
   971  	e common.ZonedEnviron, group []instance.Id,
   972  ) ([]common.AvailabilityZoneInstances, error) {
   973  	t.group = group
   974  	return t.result, t.err
   975  }
   976  
   977  func (t *localServerSuite) TestDeriveAvailabilityZones(c *gc.C) {
   978  	var resultZones []amzec2.AvailabilityZoneInfo
   979  	t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) {
   980  		resp := &amzec2.AvailabilityZonesResp{
   981  			Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...),
   982  		}
   983  		return resp, nil
   984  	})
   985  	env := t.Prepare(c).(common.ZonedEnviron)
   986  	resultZones = make([]amzec2.AvailabilityZoneInfo, 2)
   987  	resultZones[0].Name = "az1"
   988  	resultZones[1].Name = "az2"
   989  	resultZones[0].State = "available"
   990  	resultZones[1].State = "impaired"
   991  
   992  	zones, err := env.DeriveAvailabilityZones(t.callCtx, environs.StartInstanceParams{Placement: "zone=az1"})
   993  	c.Assert(err, jc.ErrorIsNil)
   994  	c.Assert(zones, gc.DeepEquals, []string{"az1"})
   995  }
   996  
   997  func (t *localServerSuite) TestDeriveAvailabilityZonesImpaired(c *gc.C) {
   998  	var resultZones []amzec2.AvailabilityZoneInfo
   999  	t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) {
  1000  		resp := &amzec2.AvailabilityZonesResp{
  1001  			Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...),
  1002  		}
  1003  		return resp, nil
  1004  	})
  1005  	env := t.Prepare(c).(common.ZonedEnviron)
  1006  	resultZones = make([]amzec2.AvailabilityZoneInfo, 2)
  1007  	resultZones[0].Name = "az1"
  1008  	resultZones[1].Name = "az2"
  1009  	resultZones[0].State = "available"
  1010  	resultZones[1].State = "impaired"
  1011  
  1012  	zones, err := env.DeriveAvailabilityZones(t.callCtx, environs.StartInstanceParams{Placement: "zone=az2"})
  1013  	c.Assert(err, gc.ErrorMatches, "availability zone \"az2\" is \"impaired\"")
  1014  	c.Assert(zones, gc.HasLen, 0)
  1015  }
  1016  
  1017  func (t *localServerSuite) TestDeriveAvailabilityZonesConflictVolume(c *gc.C) {
  1018  	resp, err := t.client.CreateVolume(amzec2.CreateVolume{
  1019  		VolumeSize: 1,
  1020  		VolumeType: "gp2",
  1021  		AvailZone:  "volume-zone",
  1022  	})
  1023  	c.Assert(err, jc.ErrorIsNil)
  1024  
  1025  	args := environs.StartInstanceParams{
  1026  		ControllerUUID: t.ControllerUUID,
  1027  		StatusCallback: fakeCallback,
  1028  		Placement:      "zone=test-available",
  1029  		VolumeAttachments: []storage.VolumeAttachmentParams{{
  1030  			AttachmentParams: storage.AttachmentParams{
  1031  				Provider: "ebs",
  1032  				Machine:  names.NewMachineTag("1"),
  1033  			},
  1034  			Volume:   names.NewVolumeTag("23"),
  1035  			VolumeId: resp.Id,
  1036  		}},
  1037  	}
  1038  	env := t.Prepare(c).(common.ZonedEnviron)
  1039  	zones, err := env.DeriveAvailabilityZones(t.callCtx, args)
  1040  	c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=test-available", as this will prevent attaching the requested EBS volumes in zone "volume-zone"`)
  1041  	c.Assert(zones, gc.HasLen, 0)
  1042  }
  1043  
  1044  func (t *localServerSuite) TestDeriveAvailabilityZonesVolumeNoPlacement(c *gc.C) {
  1045  	resp, err := t.client.CreateVolume(amzec2.CreateVolume{
  1046  		VolumeSize: 1,
  1047  		VolumeType: "gp2",
  1048  		AvailZone:  "volume-zone",
  1049  	})
  1050  	c.Assert(err, jc.ErrorIsNil)
  1051  
  1052  	args := environs.StartInstanceParams{
  1053  		ControllerUUID: t.ControllerUUID,
  1054  		StatusCallback: fakeCallback,
  1055  		VolumeAttachments: []storage.VolumeAttachmentParams{{
  1056  			AttachmentParams: storage.AttachmentParams{
  1057  				Provider: "ebs",
  1058  				Machine:  names.NewMachineTag("1"),
  1059  			},
  1060  			Volume:   names.NewVolumeTag("23"),
  1061  			VolumeId: resp.Id,
  1062  		}},
  1063  	}
  1064  	env := t.Prepare(c).(common.ZonedEnviron)
  1065  	zones, err := env.DeriveAvailabilityZones(t.callCtx, args)
  1066  	c.Assert(err, jc.ErrorIsNil)
  1067  	c.Assert(zones, gc.DeepEquals, []string{"volume-zone"})
  1068  }
  1069  
  1070  var azConstrainedErr = &amzec2.Error{
  1071  	Code:    "Unsupported",
  1072  	Message: "The requested Availability Zone is currently constrained etc.",
  1073  }
  1074  
  1075  var azVolumeTypeNotAvailableInZoneErr = &amzec2.Error{
  1076  	Code:    "VolumeTypeNotAvailableInZone",
  1077  	Message: "blah blah",
  1078  }
  1079  
  1080  var azInsufficientInstanceCapacityErr = &amzec2.Error{
  1081  	Code: "InsufficientInstanceCapacity",
  1082  	Message: "We currently do not have sufficient m1.small capacity in the " +
  1083  		"Availability Zone you requested (us-east-1d). Our system will " +
  1084  		"be working on provisioning additional capacity. You can currently get m1.small " +
  1085  		"capacity by not specifying an Availability Zone in your request or choosing " +
  1086  		"us-east-1c, us-east-1a.",
  1087  }
  1088  
  1089  var azNoDefaultSubnetErr = &amzec2.Error{
  1090  	Code:    "InvalidInput",
  1091  	Message: "No default subnet for availability zone: ''us-east-1e''.",
  1092  }
  1093  
  1094  func (t *localServerSuite) TestStartInstanceAvailZoneAllConstrained(c *gc.C) {
  1095  	t.testStartInstanceAvailZoneAllConstrained(c, azConstrainedErr)
  1096  }
  1097  
  1098  func (t *localServerSuite) TestStartInstanceVolumeTypeNotAvailable(c *gc.C) {
  1099  	t.testStartInstanceAvailZoneAllConstrained(c, azVolumeTypeNotAvailableInZoneErr)
  1100  }
  1101  
  1102  func (t *localServerSuite) TestStartInstanceAvailZoneAllInsufficientInstanceCapacity(c *gc.C) {
  1103  	t.testStartInstanceAvailZoneAllConstrained(c, azInsufficientInstanceCapacityErr)
  1104  }
  1105  
  1106  func (t *localServerSuite) TestStartInstanceAvailZoneAllNoDefaultSubnet(c *gc.C) {
  1107  	t.testStartInstanceAvailZoneAllConstrained(c, azNoDefaultSubnetErr)
  1108  }
  1109  
  1110  func (t *localServerSuite) testStartInstanceAvailZoneAllConstrained(c *gc.C, runInstancesError *amzec2.Error) {
  1111  	env := t.prepareAndBootstrap(c)
  1112  
  1113  	t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ctx context.ProviderCallContext, ri *amzec2.RunInstances, c environs.StatusCallbackFunc) (*amzec2.RunInstancesResp, error) {
  1114  		return nil, runInstancesError
  1115  	})
  1116  
  1117  	params := environs.StartInstanceParams{
  1118  		ControllerUUID:   t.ControllerUUID,
  1119  		StatusCallback:   fakeCallback,
  1120  		AvailabilityZone: "test-available",
  1121  	}
  1122  
  1123  	_, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params)
  1124  	// All AZConstrained failures should return an error that does
  1125  	// *not* satisfy environs.IsAvailabilityZoneIndependent,
  1126  	// so the caller knows to try a new zone, rather than fail.
  1127  	c.Assert(err, gc.Not(jc.Satisfies), environs.IsAvailabilityZoneIndependent)
  1128  	c.Assert(errors.Details(err), jc.Contains, runInstancesError.Message)
  1129  }
  1130  
  1131  // addTestingSubnets adds a testing default VPC with 3 subnets in the EC2 test
  1132  // server: 2 of the subnets are in the "test-available" AZ, the remaining - in
  1133  // "test-unavailable". Returns a slice with the IDs of the created subnets and
  1134  // vpc id that those were added to
  1135  func (t *localServerSuite) addTestingSubnets(c *gc.C) ([]network.Id, string) {
  1136  	vpc := t.srv.ec2srv.AddVPC(amzec2.VPC{
  1137  		CIDRBlock: "0.1.0.0/16",
  1138  		IsDefault: true,
  1139  	})
  1140  	results := make([]network.Id, 3)
  1141  	sub1, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{
  1142  		VPCId:        vpc.Id,
  1143  		CIDRBlock:    "0.1.2.0/24",
  1144  		AvailZone:    "test-available",
  1145  		State:        "available",
  1146  		DefaultForAZ: true,
  1147  	})
  1148  	c.Assert(err, jc.ErrorIsNil)
  1149  	results[0] = network.Id(sub1.Id)
  1150  	sub2, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{
  1151  		VPCId:     vpc.Id,
  1152  		CIDRBlock: "0.1.3.0/24",
  1153  		AvailZone: "test-available",
  1154  		State:     "unavailable",
  1155  	})
  1156  	c.Assert(err, jc.ErrorIsNil)
  1157  	results[1] = network.Id(sub2.Id)
  1158  	sub3, err := t.srv.ec2srv.AddSubnet(amzec2.Subnet{
  1159  		VPCId:        vpc.Id,
  1160  		CIDRBlock:    "0.1.4.0/24",
  1161  		AvailZone:    "test-unavailable",
  1162  		DefaultForAZ: true,
  1163  		State:        "unavailable",
  1164  	})
  1165  	c.Assert(err, jc.ErrorIsNil)
  1166  	results[2] = network.Id(sub3.Id)
  1167  	return results, vpc.Id
  1168  }
  1169  
  1170  func (t *localServerSuite) prepareAndBootstrap(c *gc.C) environs.Environ {
  1171  	return t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{})
  1172  }
  1173  
  1174  func (t *localServerSuite) prepareAndBootstrapWithConfig(c *gc.C, config coretesting.Attrs) environs.Environ {
  1175  	args := t.PrepareParams(c)
  1176  	args.ModelConfig = coretesting.Attrs(args.ModelConfig).Merge(config)
  1177  	env := t.PrepareWithParams(c, args)
  1178  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
  1179  		t.callCtx, bootstrap.BootstrapParams{
  1180  			ControllerConfig: coretesting.FakeControllerConfig(),
  1181  			AdminSecret:      testing.AdminSecret,
  1182  			CAPrivateKey:     coretesting.CAKey,
  1183  			Placement:        "zone=test-available",
  1184  		})
  1185  	c.Assert(err, jc.ErrorIsNil)
  1186  	return env
  1187  }
  1188  
  1189  func (t *localServerSuite) TestSpaceConstraintsSpaceNotInPlacementZone(c *gc.C) {
  1190  	env := t.prepareAndBootstrap(c)
  1191  	subIDs, _ := t.addTestingSubnets(c)
  1192  
  1193  	// Expect an error because zone test-available isn't in SubnetsToZones
  1194  	params := environs.StartInstanceParams{
  1195  		ControllerUUID: t.ControllerUUID,
  1196  		Placement:      "zone=test-available",
  1197  		Constraints:    constraints.MustParse("spaces=aaaaaaaaaa"),
  1198  		SubnetsToZones: map[network.Id][]string{
  1199  			subIDs[0]: {"zone2"},
  1200  			subIDs[1]: {"zone3"},
  1201  			subIDs[2]: {"zone4"},
  1202  		},
  1203  		StatusCallback: fakeCallback,
  1204  	}
  1205  	_, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params)
  1206  	c.Assert(err, gc.Not(jc.Satisfies), environs.IsAvailabilityZoneIndependent)
  1207  	c.Assert(errors.Details(err), gc.Matches, `.*subnets in AZ "test-available" not found.*`)
  1208  }
  1209  
  1210  func (t *localServerSuite) TestSpaceConstraintsSpaceInPlacementZone(c *gc.C) {
  1211  	env := t.prepareAndBootstrap(c)
  1212  	subIDs, _ := t.addTestingSubnets(c)
  1213  
  1214  	// Should work - test-available is in SubnetsToZones and in myspace.
  1215  	params := environs.StartInstanceParams{
  1216  		ControllerUUID: t.ControllerUUID,
  1217  		Placement:      "zone=test-available",
  1218  		Constraints:    constraints.MustParse("spaces=aaaaaaaaaa"),
  1219  		SubnetsToZones: map[network.Id][]string{
  1220  			subIDs[0]: {"test-available"},
  1221  			subIDs[1]: {"zone3"},
  1222  		},
  1223  		StatusCallback: fakeCallback,
  1224  	}
  1225  	_, err := testing.StartInstanceWithParams(env, t.callCtx, "1", params)
  1226  	c.Assert(err, jc.ErrorIsNil)
  1227  }
  1228  
  1229  func (t *localServerSuite) TestSpaceConstraintsNoPlacement(c *gc.C) {
  1230  	env := t.prepareAndBootstrap(c)
  1231  	subIDs, _ := t.addTestingSubnets(c)
  1232  
  1233  	params := environs.StartInstanceParams{
  1234  		ControllerUUID: t.ControllerUUID,
  1235  		Constraints:    constraints.MustParse("spaces=aaaaaaaaaa"),
  1236  		SubnetsToZones: map[network.Id][]string{
  1237  			subIDs[0]: {"test-available"},
  1238  			subIDs[1]: {"zone3"},
  1239  		},
  1240  		StatusCallback: fakeCallback,
  1241  	}
  1242  	t.assertStartInstanceWithParamsFindAZ(c, env, "1", params)
  1243  }
  1244  
  1245  func (t *localServerSuite) assertStartInstanceWithParamsFindAZ(
  1246  	c *gc.C,
  1247  	env environs.Environ,
  1248  	machineId string,
  1249  	params environs.StartInstanceParams,
  1250  ) {
  1251  	zonedEnviron := env.(common.ZonedEnviron)
  1252  	zones, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params)
  1253  	c.Assert(err, jc.ErrorIsNil)
  1254  	if len(zones) > 0 {
  1255  		params.AvailabilityZone = zones[0]
  1256  		_, err = testing.StartInstanceWithParams(env, t.callCtx, "1", params)
  1257  		c.Assert(err, jc.ErrorIsNil)
  1258  		return
  1259  	}
  1260  	availabilityZones, err := zonedEnviron.AvailabilityZones(t.callCtx)
  1261  	c.Assert(err, jc.ErrorIsNil)
  1262  	for _, zone := range availabilityZones {
  1263  		if !zone.Available() {
  1264  			continue
  1265  		}
  1266  		params.AvailabilityZone = zone.Name()
  1267  		_, err = testing.StartInstanceWithParams(env, t.callCtx, "1", params)
  1268  		if err == nil {
  1269  			return
  1270  		} else if !environs.IsAvailabilityZoneIndependent(err) {
  1271  			continue
  1272  		}
  1273  		c.Assert(err, jc.ErrorIsNil)
  1274  	}
  1275  }
  1276  
  1277  func (t *localServerSuite) TestSpaceConstraintsNoAvailableSubnets(c *gc.C) {
  1278  	c.Skip("temporarily disabled")
  1279  	subIDs, vpcId := t.addTestingSubnets(c)
  1280  	env := t.prepareAndBootstrapWithConfig(c, coretesting.Attrs{"vpc-id": vpcId})
  1281  
  1282  	// We requested a space, but there are no subnets in SubnetsToZones, so we can't resolve
  1283  	// the constraints
  1284  	params := environs.StartInstanceParams{
  1285  		ControllerUUID: t.ControllerUUID,
  1286  		Constraints:    constraints.MustParse("spaces=aaaaaaaaaa"),
  1287  		SubnetsToZones: map[network.Id][]string{
  1288  			subIDs[0]: {""},
  1289  		},
  1290  		StatusCallback: fakeCallback,
  1291  	}
  1292  	//_, err := testing.StartInstanceWithParams(env, "1", params)
  1293  	zonedEnviron := env.(common.ZonedEnviron)
  1294  	_, err := zonedEnviron.DeriveAvailabilityZones(t.callCtx, params)
  1295  	c.Assert(err, gc.ErrorMatches, `unable to resolve constraints: space and/or subnet unavailable in zones \[test-available\]`)
  1296  }
  1297  
  1298  func (t *localServerSuite) TestStartInstanceAvailZoneOneConstrained(c *gc.C) {
  1299  	t.testStartInstanceAvailZoneOneConstrained(c, azConstrainedErr)
  1300  }
  1301  
  1302  func (t *localServerSuite) TestStartInstanceAvailZoneOneInsufficientInstanceCapacity(c *gc.C) {
  1303  	t.testStartInstanceAvailZoneOneConstrained(c, azInsufficientInstanceCapacityErr)
  1304  }
  1305  
  1306  func (t *localServerSuite) TestStartInstanceAvailZoneOneNoDefaultSubnetErr(c *gc.C) {
  1307  	t.testStartInstanceAvailZoneOneConstrained(c, azNoDefaultSubnetErr)
  1308  }
  1309  
  1310  func (t *localServerSuite) testStartInstanceAvailZoneOneConstrained(c *gc.C, runInstancesError *amzec2.Error) {
  1311  	env := t.prepareAndBootstrap(c)
  1312  
  1313  	// The first call to RunInstances fails with an error indicating the AZ
  1314  	// is constrained. The second attempt succeeds, and so allocates to az2.
  1315  	var azArgs []string
  1316  	realRunInstances := *ec2.RunInstances
  1317  
  1318  	t.PatchValue(ec2.RunInstances, func(e *amzec2.EC2, ctx context.ProviderCallContext, ri *amzec2.RunInstances, c environs.StatusCallbackFunc) (*amzec2.RunInstancesResp, error) {
  1319  		azArgs = append(azArgs, ri.AvailZone)
  1320  		if len(azArgs) == 1 {
  1321  			return nil, runInstancesError
  1322  		}
  1323  		return realRunInstances(e, ctx, ri, fakeCallback)
  1324  	})
  1325  
  1326  	params := environs.StartInstanceParams{ControllerUUID: t.ControllerUUID}
  1327  	zonedEnviron := env.(common.ZonedEnviron)
  1328  	availabilityZones, err := zonedEnviron.AvailabilityZones(t.callCtx)
  1329  	c.Assert(err, jc.ErrorIsNil)
  1330  	for _, zone := range availabilityZones {
  1331  		if !zone.Available() {
  1332  			continue
  1333  		}
  1334  		params.AvailabilityZone = zone.Name()
  1335  		_, err = testing.StartInstanceWithParams(env, t.callCtx, "1", params)
  1336  		if err == nil {
  1337  			break
  1338  		} else if !environs.IsAvailabilityZoneIndependent(err) {
  1339  			continue
  1340  		}
  1341  		c.Assert(err, jc.ErrorIsNil)
  1342  	}
  1343  	sort.Strings(azArgs)
  1344  	c.Assert(azArgs, gc.DeepEquals, []string{"test-available", "test-available2"})
  1345  }
  1346  
  1347  func (t *localServerSuite) TestAddresses(c *gc.C) {
  1348  	env := t.prepareAndBootstrap(c)
  1349  	inst, _ := testing.AssertStartInstance(c, env, t.callCtx, t.ControllerUUID, "1")
  1350  	addrs, err := inst.Addresses(t.callCtx)
  1351  	c.Assert(err, jc.ErrorIsNil)
  1352  	// Expected values use Address type but really contain a regexp for
  1353  	// the value rather than a valid ip or hostname.
  1354  	expected := []network.Address{{
  1355  		Value: "8.0.0.*",
  1356  		Type:  network.IPv4Address,
  1357  		Scope: network.ScopePublic,
  1358  	}, {
  1359  		Value: "127.0.0.*",
  1360  		Type:  network.IPv4Address,
  1361  		Scope: network.ScopeCloudLocal,
  1362  	}}
  1363  	c.Assert(addrs, gc.HasLen, len(expected))
  1364  	for i, addr := range addrs {
  1365  		c.Check(addr.Value, gc.Matches, expected[i].Value)
  1366  		c.Check(addr.Type, gc.Equals, expected[i].Type)
  1367  		c.Check(addr.Scope, gc.Equals, expected[i].Scope)
  1368  	}
  1369  }
  1370  
  1371  func (t *localServerSuite) TestConstraintsValidatorUnsupported(c *gc.C) {
  1372  	env := t.Prepare(c)
  1373  	validator, err := env.ConstraintsValidator(t.callCtx)
  1374  	c.Assert(err, jc.ErrorIsNil)
  1375  	cons := constraints.MustParse("arch=amd64 tags=foo virt-type=kvm")
  1376  	unsupported, err := validator.Validate(cons)
  1377  	c.Assert(err, jc.ErrorIsNil)
  1378  	c.Assert(unsupported, jc.SameContents, []string{"tags", "virt-type"})
  1379  }
  1380  
  1381  func (t *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
  1382  	env := t.Prepare(c)
  1383  	validator, err := env.ConstraintsValidator(t.callCtx)
  1384  	c.Assert(err, jc.ErrorIsNil)
  1385  	cons := constraints.MustParse("instance-type=foo")
  1386  	_, err = validator.Validate(cons)
  1387  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
  1388  }
  1389  
  1390  func (t *localServerSuite) TestConstraintsValidatorVocabNoDefaultOrSpecifiedVPC(c *gc.C) {
  1391  	t.srv.defaultVPC.IsDefault = false
  1392  	err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC)
  1393  	c.Assert(err, jc.ErrorIsNil)
  1394  
  1395  	env := t.Prepare(c)
  1396  	assertVPCInstanceTypeNotAvailable(c, env, t.callCtx)
  1397  }
  1398  
  1399  func (t *localServerSuite) TestConstraintsValidatorVocabDefaultVPC(c *gc.C) {
  1400  	env := t.Prepare(c)
  1401  	assertVPCInstanceTypeAvailable(c, env, t.callCtx)
  1402  }
  1403  
  1404  func (t *localServerSuite) TestConstraintsValidatorVocabSpecifiedVPC(c *gc.C) {
  1405  	t.srv.defaultVPC.IsDefault = false
  1406  	err := t.srv.ec2srv.UpdateVPC(*t.srv.defaultVPC)
  1407  	c.Assert(err, jc.ErrorIsNil)
  1408  
  1409  	t.TestConfig["vpc-id"] = t.srv.defaultVPC.Id
  1410  	defer delete(t.TestConfig, "vpc-id")
  1411  
  1412  	env := t.Prepare(c)
  1413  	assertVPCInstanceTypeAvailable(c, env, t.callCtx)
  1414  }
  1415  
  1416  func assertVPCInstanceTypeAvailable(c *gc.C, env environs.Environ, ctx context.ProviderCallContext) {
  1417  	validator, err := env.ConstraintsValidator(ctx)
  1418  	c.Assert(err, jc.ErrorIsNil)
  1419  	_, err = validator.Validate(constraints.MustParse("instance-type=t2.medium"))
  1420  	c.Assert(err, jc.ErrorIsNil)
  1421  }
  1422  
  1423  func assertVPCInstanceTypeNotAvailable(c *gc.C, env environs.Environ, ctx context.ProviderCallContext) {
  1424  	validator, err := env.ConstraintsValidator(ctx)
  1425  	c.Assert(err, jc.ErrorIsNil)
  1426  	_, err = validator.Validate(constraints.MustParse("instance-type=t2.medium"))
  1427  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=t2.medium\n.*")
  1428  }
  1429  
  1430  func (t *localServerSuite) TestConstraintsMerge(c *gc.C) {
  1431  	env := t.Prepare(c)
  1432  	validator, err := env.ConstraintsValidator(t.callCtx)
  1433  	c.Assert(err, jc.ErrorIsNil)
  1434  	consA := constraints.MustParse("arch=amd64 mem=1G cpu-power=10 cores=2 tags=bar")
  1435  	consB := constraints.MustParse("arch=i386 instance-type=m1.small")
  1436  	cons, err := validator.Merge(consA, consB)
  1437  	c.Assert(err, jc.ErrorIsNil)
  1438  	c.Assert(cons, gc.DeepEquals, constraints.MustParse("arch=i386 instance-type=m1.small tags=bar"))
  1439  }
  1440  
  1441  func (t *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) {
  1442  	env := t.Prepare(c)
  1443  	cons := constraints.MustParse("instance-type=m1.small root-disk=1G")
  1444  	err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1445  		Series:      supportedversion.SupportedLTS(),
  1446  		Constraints: cons,
  1447  	})
  1448  	c.Assert(err, jc.ErrorIsNil)
  1449  }
  1450  
  1451  func (t *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) {
  1452  	env := t.Prepare(c)
  1453  	cons := constraints.MustParse("instance-type=m1.invalid")
  1454  	err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1455  		Series:      supportedversion.SupportedLTS(),
  1456  		Constraints: cons,
  1457  	})
  1458  	c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "m1.invalid" specified`)
  1459  }
  1460  
  1461  func (t *localServerSuite) TestPrecheckInstanceUnsupportedArch(c *gc.C) {
  1462  	env := t.Prepare(c)
  1463  	cons := constraints.MustParse("instance-type=cc1.4xlarge arch=i386")
  1464  	err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1465  		Series:      supportedversion.SupportedLTS(),
  1466  		Constraints: cons,
  1467  	})
  1468  	c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "cc1.4xlarge" and arch "i386" specified`)
  1469  }
  1470  
  1471  func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) {
  1472  	env := t.Prepare(c)
  1473  	placement := "zone=test-available"
  1474  	err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1475  		Series:    supportedversion.SupportedLTS(),
  1476  		Placement: placement,
  1477  	})
  1478  	c.Assert(err, jc.ErrorIsNil)
  1479  }
  1480  
  1481  func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) {
  1482  	env := t.Prepare(c)
  1483  	placement := "zone=test-unavailable"
  1484  	err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1485  		Series:    supportedversion.SupportedLTS(),
  1486  		Placement: placement,
  1487  	})
  1488  	c.Assert(err, gc.ErrorMatches, `availability zone "test-unavailable" is "unavailable"`)
  1489  }
  1490  
  1491  func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) {
  1492  	env := t.Prepare(c)
  1493  	placement := "zone=test-unknown"
  1494  	err := env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1495  		Series:    supportedversion.SupportedLTS(),
  1496  		Placement: placement,
  1497  	})
  1498  	c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`)
  1499  }
  1500  
  1501  func (t *localServerSuite) TestPrecheckInstanceVolumeAvailZoneNoPlacement(c *gc.C) {
  1502  	t.testPrecheckInstanceVolumeAvailZone(c, "")
  1503  }
  1504  
  1505  func (t *localServerSuite) TestPrecheckInstanceVolumeAvailZoneSameZonePlacement(c *gc.C) {
  1506  	t.testPrecheckInstanceVolumeAvailZone(c, "zone=test-available")
  1507  }
  1508  
  1509  func (t *localServerSuite) testPrecheckInstanceVolumeAvailZone(c *gc.C, placement string) {
  1510  	env := t.Prepare(c)
  1511  	resp, err := t.client.CreateVolume(amzec2.CreateVolume{
  1512  		VolumeSize: 1,
  1513  		VolumeType: "gp2",
  1514  		AvailZone:  "test-available",
  1515  	})
  1516  	c.Assert(err, jc.ErrorIsNil)
  1517  
  1518  	err = env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1519  		Series:    supportedversion.SupportedLTS(),
  1520  		Placement: placement,
  1521  		VolumeAttachments: []storage.VolumeAttachmentParams{{
  1522  			AttachmentParams: storage.AttachmentParams{
  1523  				Provider: "ebs",
  1524  			},
  1525  			Volume:   names.NewVolumeTag("23"),
  1526  			VolumeId: resp.Id,
  1527  		}},
  1528  	})
  1529  	c.Assert(err, jc.ErrorIsNil)
  1530  }
  1531  
  1532  func (t *localServerSuite) TestPrecheckInstanceAvailZoneVolumeConflict(c *gc.C) {
  1533  	env := t.Prepare(c)
  1534  	resp, err := t.client.CreateVolume(amzec2.CreateVolume{
  1535  		VolumeSize: 1,
  1536  		VolumeType: "gp2",
  1537  		AvailZone:  "volume-zone",
  1538  	})
  1539  	c.Assert(err, jc.ErrorIsNil)
  1540  
  1541  	err = env.PrecheckInstance(t.callCtx, environs.PrecheckInstanceParams{
  1542  		Series:    supportedversion.SupportedLTS(),
  1543  		Placement: "zone=test-available",
  1544  		VolumeAttachments: []storage.VolumeAttachmentParams{{
  1545  			AttachmentParams: storage.AttachmentParams{
  1546  				Provider: "ebs",
  1547  			},
  1548  			Volume:   names.NewVolumeTag("23"),
  1549  			VolumeId: resp.Id,
  1550  		}},
  1551  	})
  1552  	c.Assert(err, gc.ErrorMatches, `cannot create instance with placement "zone=test-available", as this will prevent attaching the requested EBS volumes in zone "volume-zone"`)
  1553  }
  1554  
  1555  func (t *localServerSuite) TestValidateImageMetadata(c *gc.C) {
  1556  	region := t.srv.region
  1557  	aws.Regions[region.Name] = t.srv.region
  1558  	defer delete(aws.Regions, region.Name)
  1559  
  1560  	env := t.Prepare(c)
  1561  	params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("test")
  1562  	c.Assert(err, jc.ErrorIsNil)
  1563  	params.Series = supportedversion.SupportedLTS()
  1564  	params.Endpoint = region.EC2Endpoint
  1565  	params.Sources, err = environs.ImageMetadataSources(env)
  1566  	c.Assert(err, jc.ErrorIsNil)
  1567  	image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
  1568  	c.Assert(err, jc.ErrorIsNil)
  1569  	sort.Strings(image_ids)
  1570  	c.Assert(image_ids, gc.DeepEquals, []string{"ami-00001133", "ami-00001135", "ami-00001139"})
  1571  }
  1572  
  1573  func (t *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
  1574  	t.PatchValue(&tools.DefaultBaseURL, "")
  1575  
  1576  	env := t.Prepare(c)
  1577  	sources, err := tools.GetMetadataSources(env)
  1578  	c.Assert(err, jc.ErrorIsNil)
  1579  	c.Assert(sources, gc.HasLen, 0)
  1580  }
  1581  
  1582  func (t *localServerSuite) TestSupportsNetworking(c *gc.C) {
  1583  	env := t.Prepare(c)
  1584  	_, supported := environs.SupportsNetworking(env)
  1585  	c.Assert(supported, jc.IsTrue)
  1586  }
  1587  
  1588  func (t *localServerSuite) setUpInstanceWithDefaultVpc(c *gc.C) (environs.NetworkingEnviron, instance.Id) {
  1589  	env := t.prepareEnviron(c)
  1590  	err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env,
  1591  		t.callCtx, bootstrap.BootstrapParams{
  1592  			ControllerConfig: coretesting.FakeControllerConfig(),
  1593  			AdminSecret:      testing.AdminSecret,
  1594  			CAPrivateKey:     coretesting.CAKey,
  1595  		})
  1596  	c.Assert(err, jc.ErrorIsNil)
  1597  
  1598  	instanceIds, err := env.ControllerInstances(t.callCtx, t.ControllerUUID)
  1599  	c.Assert(err, jc.ErrorIsNil)
  1600  	return env, instanceIds[0]
  1601  }
  1602  
  1603  func (t *localServerSuite) TestNetworkInterfaces(c *gc.C) {
  1604  	env, instId := t.setUpInstanceWithDefaultVpc(c)
  1605  	interfaces, err := env.NetworkInterfaces(t.callCtx, instId)
  1606  	c.Assert(err, jc.ErrorIsNil)
  1607  
  1608  	// The CIDR isn't predictable, but it is in the 10.10.x.0/24 format
  1609  	// The subnet ID is in the form "subnet-x", where x matches the same
  1610  	// number from the CIDR. The interfaces address is part of the CIDR.
  1611  	// For these reasons we check that the CIDR is in the expected format
  1612  	// and derive the expected values for ProviderSubnetId and Address.
  1613  	c.Assert(interfaces, gc.HasLen, 1)
  1614  	cidr := interfaces[0].CIDR
  1615  	re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`)
  1616  	c.Assert(re.Match([]byte(cidr)), jc.IsTrue)
  1617  	index := re.FindStringSubmatch(cidr)[1]
  1618  	addr := fmt.Sprintf("10.10.%s.5", index)
  1619  	subnetId := network.Id("subnet-" + index)
  1620  
  1621  	// AvailabilityZones will either contain "test-available",
  1622  	// "test-impaired" or "test-unavailable" depending on which subnet is
  1623  	// picked. Any of these is fine.
  1624  	zones := interfaces[0].AvailabilityZones
  1625  	c.Assert(zones, gc.HasLen, 1)
  1626  	re = regexp.MustCompile("test-available|test-unavailable|test-impaired")
  1627  	c.Assert(re.Match([]byte(zones[0])), jc.IsTrue)
  1628  
  1629  	expectedInterfaces := []network.InterfaceInfo{{
  1630  		DeviceIndex:       0,
  1631  		MACAddress:        "20:01:60:cb:27:37",
  1632  		CIDR:              cidr,
  1633  		ProviderId:        "eni-0",
  1634  		ProviderSubnetId:  subnetId,
  1635  		VLANTag:           0,
  1636  		InterfaceName:     "unsupported0",
  1637  		Disabled:          false,
  1638  		NoAutoStart:       false,
  1639  		ConfigType:        network.ConfigDHCP,
  1640  		InterfaceType:     network.EthernetInterface,
  1641  		Address:           network.NewScopedAddress(addr, network.ScopeCloudLocal),
  1642  		AvailabilityZones: zones,
  1643  	}}
  1644  	c.Assert(interfaces, jc.DeepEquals, expectedInterfaces)
  1645  }
  1646  
  1647  func (t *localServerSuite) TestSubnetsWithInstanceId(c *gc.C) {
  1648  	env, instId := t.setUpInstanceWithDefaultVpc(c)
  1649  	subnets, err := env.Subnets(t.callCtx, instId, nil)
  1650  	c.Assert(err, jc.ErrorIsNil)
  1651  	c.Assert(subnets, gc.HasLen, 1)
  1652  	validateSubnets(c, subnets, "")
  1653  
  1654  	interfaces, err := env.NetworkInterfaces(t.callCtx, instId)
  1655  	c.Assert(err, jc.ErrorIsNil)
  1656  	c.Assert(interfaces, gc.HasLen, 1)
  1657  	c.Assert(interfaces[0].ProviderSubnetId, gc.Equals, subnets[0].ProviderId)
  1658  }
  1659  
  1660  func (t *localServerSuite) TestSubnetsWithInstanceIdAndSubnetId(c *gc.C) {
  1661  	env, instId := t.setUpInstanceWithDefaultVpc(c)
  1662  	interfaces, err := env.NetworkInterfaces(t.callCtx, instId)
  1663  	c.Assert(err, jc.ErrorIsNil)
  1664  	c.Assert(interfaces, gc.HasLen, 1)
  1665  
  1666  	subnets, err := env.Subnets(t.callCtx, instId, []network.Id{interfaces[0].ProviderSubnetId})
  1667  	c.Assert(err, jc.ErrorIsNil)
  1668  	c.Assert(subnets, gc.HasLen, 1)
  1669  	c.Assert(subnets[0].ProviderId, gc.Equals, interfaces[0].ProviderSubnetId)
  1670  	validateSubnets(c, subnets, "")
  1671  }
  1672  
  1673  func (t *localServerSuite) TestSubnetsWithInstanceIdMissingSubnet(c *gc.C) {
  1674  	env, instId := t.setUpInstanceWithDefaultVpc(c)
  1675  	subnets, err := env.Subnets(t.callCtx, instId, []network.Id{"missing"})
  1676  	c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[missing\]`)
  1677  	c.Assert(subnets, gc.HasLen, 0)
  1678  }
  1679  
  1680  func (t *localServerSuite) TestInstanceInformation(c *gc.C) {
  1681  	// TODO(macgreagoir) Where do these magic length numbers come from?
  1682  	c.Skip("Hard-coded InstanceTypes counts without explanation")
  1683  	env := t.prepareEnviron(c)
  1684  	types, err := env.InstanceTypes(t.callCtx, constraints.Value{})
  1685  	c.Assert(err, jc.ErrorIsNil)
  1686  	c.Assert(types.InstanceTypes, gc.HasLen, 53)
  1687  
  1688  	cons := constraints.MustParse("mem=4G")
  1689  	types, err = env.InstanceTypes(t.callCtx, cons)
  1690  	c.Assert(err, jc.ErrorIsNil)
  1691  	c.Assert(types.InstanceTypes, gc.HasLen, 48)
  1692  }
  1693  
  1694  func validateSubnets(c *gc.C, subnets []network.SubnetInfo, vpcId network.Id) {
  1695  	// These are defined in the test server for the testing default
  1696  	// VPC.
  1697  	defaultSubnets := []network.SubnetInfo{{
  1698  		CIDR:              "10.10.0.0/24",
  1699  		ProviderId:        "subnet-0",
  1700  		ProviderNetworkId: vpcId,
  1701  		VLANTag:           0,
  1702  		AvailabilityZones: []string{"test-available2"},
  1703  	}, {
  1704  		CIDR:              "10.10.1.0/24",
  1705  		ProviderId:        "subnet-1",
  1706  		ProviderNetworkId: vpcId,
  1707  		VLANTag:           0,
  1708  		AvailabilityZones: []string{"test-available"},
  1709  	}, {
  1710  		CIDR:              "10.10.2.0/24",
  1711  		ProviderId:        "subnet-2",
  1712  		ProviderNetworkId: vpcId,
  1713  		VLANTag:           0,
  1714  		AvailabilityZones: []string{"test-impaired"},
  1715  	}, {
  1716  		CIDR:              "10.10.3.0/24",
  1717  		ProviderId:        "subnet-3",
  1718  		ProviderNetworkId: vpcId,
  1719  		VLANTag:           0,
  1720  		AvailabilityZones: []string{"test-unavailable"},
  1721  	}}
  1722  
  1723  	re := regexp.MustCompile(`10\.10\.(\d+)\.0/24`)
  1724  	for _, subnet := range subnets {
  1725  		// We can find the expected data by looking at the CIDR.
  1726  		// subnets isn't in a predictable order due to the use of maps.
  1727  		c.Assert(re.Match([]byte(subnet.CIDR)), jc.IsTrue)
  1728  		index, err := strconv.Atoi(re.FindStringSubmatch(subnet.CIDR)[1])
  1729  		c.Assert(err, jc.ErrorIsNil)
  1730  		// Don't know which AZ the subnet will end up in.
  1731  		defaultSubnets[index].AvailabilityZones = subnet.AvailabilityZones
  1732  		c.Check(subnet, jc.DeepEquals, defaultSubnets[index])
  1733  	}
  1734  }
  1735  
  1736  func (t *localServerSuite) TestSubnets(c *gc.C) {
  1737  	env, _ := t.setUpInstanceWithDefaultVpc(c)
  1738  
  1739  	subnets, err := env.Subnets(t.callCtx, instance.UnknownId, []network.Id{"subnet-0"})
  1740  	c.Assert(err, jc.ErrorIsNil)
  1741  	c.Assert(subnets, gc.HasLen, 1)
  1742  	validateSubnets(c, subnets, "vpc-0")
  1743  
  1744  	subnets, err = env.Subnets(t.callCtx, instance.UnknownId, nil)
  1745  	c.Assert(err, jc.ErrorIsNil)
  1746  	c.Assert(subnets, gc.HasLen, 4)
  1747  	validateSubnets(c, subnets, "vpc-0")
  1748  }
  1749  
  1750  func (t *localServerSuite) TestSubnetsMissingSubnet(c *gc.C) {
  1751  	env, _ := t.setUpInstanceWithDefaultVpc(c)
  1752  
  1753  	_, err := env.Subnets(t.callCtx, "", []network.Id{"subnet-0", "Missing"})
  1754  	c.Assert(err, gc.ErrorMatches, `failed to find the following subnet ids: \[Missing\]`)
  1755  }
  1756  
  1757  func (t *localServerSuite) TestInstanceTags(c *gc.C) {
  1758  	env := t.prepareAndBootstrap(c)
  1759  
  1760  	instances, err := env.AllInstances(t.callCtx)
  1761  	c.Assert(err, jc.ErrorIsNil)
  1762  	c.Assert(instances, gc.HasLen, 1)
  1763  
  1764  	ec2Inst := ec2.InstanceEC2(instances[0])
  1765  	c.Assert(ec2Inst.Tags, jc.SameContents, []amzec2.Tag{
  1766  		{"Name", "juju-sample-machine-0"},
  1767  		{"juju-model-uuid", coretesting.ModelTag.Id()},
  1768  		{"juju-controller-uuid", t.ControllerUUID},
  1769  		{"juju-is-controller", "true"},
  1770  	})
  1771  }
  1772  
  1773  func (t *localServerSuite) TestRootDiskTags(c *gc.C) {
  1774  	env := t.prepareAndBootstrap(c)
  1775  
  1776  	instances, err := env.AllInstances(t.callCtx)
  1777  	c.Assert(err, jc.ErrorIsNil)
  1778  	c.Assert(instances, gc.HasLen, 1)
  1779  
  1780  	ec2conn := ec2.EnvironEC2(env)
  1781  	resp, err := ec2conn.Volumes(nil, nil)
  1782  	c.Assert(err, jc.ErrorIsNil)
  1783  	c.Assert(resp.Volumes, gc.Not(gc.HasLen), 0)
  1784  
  1785  	var found *amzec2.Volume
  1786  	for _, vol := range resp.Volumes {
  1787  		if len(vol.Tags) != 0 {
  1788  			found = &vol
  1789  			break
  1790  		}
  1791  	}
  1792  	c.Assert(found, gc.NotNil)
  1793  	c.Assert(found.Tags, jc.SameContents, []amzec2.Tag{
  1794  		{"Name", "juju-sample-machine-0-root"},
  1795  		{"juju-model-uuid", coretesting.ModelTag.Id()},
  1796  		{"juju-controller-uuid", t.ControllerUUID},
  1797  	})
  1798  }
  1799  
  1800  func (s *localServerSuite) TestBootstrapInstanceConstraints(c *gc.C) {
  1801  	env := s.prepareAndBootstrap(c)
  1802  	inst, err := env.AllInstances(s.callCtx)
  1803  	c.Assert(err, jc.ErrorIsNil)
  1804  	c.Assert(inst, gc.HasLen, 1)
  1805  	ec2inst := ec2.InstanceEC2(inst[0])
  1806  	// Controllers should be started with a burstable
  1807  	// instance if possible, and a 32 GiB disk.
  1808  	c.Assert(ec2inst.InstanceType, gc.Equals, "t3.medium")
  1809  }
  1810  
  1811  func makeFilter(key string, values ...string) *amzec2.Filter {
  1812  	result := amzec2.NewFilter()
  1813  	result.Add(key, values...)
  1814  	return result
  1815  }
  1816  
  1817  func (s *localServerSuite) TestAdoptResources(c *gc.C) {
  1818  	controllerEnv := s.prepareAndBootstrap(c)
  1819  	controllerInsts, err := controllerEnv.AllInstances(s.callCtx)
  1820  	c.Assert(err, jc.ErrorIsNil)
  1821  	c.Assert(controllerInsts, gc.HasLen, 1)
  1822  
  1823  	controllerVolumes, err := ec2.AllModelVolumes(controllerEnv, s.callCtx)
  1824  	c.Assert(err, jc.ErrorIsNil)
  1825  
  1826  	controllerGroups, err := ec2.AllModelGroups(controllerEnv, s.callCtx)
  1827  	c.Assert(err, jc.ErrorIsNil)
  1828  
  1829  	// Create a hosted model environment with an instance and a volume.
  1830  	hostedModelUUID := "7e386e08-cba7-44a4-a76e-7c1633584210"
  1831  	s.srv.ec2srv.SetInitialInstanceState(ec2test.Running)
  1832  	cfg, err := controllerEnv.Config().Apply(map[string]interface{}{
  1833  		"uuid":          hostedModelUUID,
  1834  		"firewall-mode": "global",
  1835  	})
  1836  	c.Assert(err, jc.ErrorIsNil)
  1837  	env, err := environs.New(environs.OpenParams{
  1838  		Cloud:  s.CloudSpec(),
  1839  		Config: cfg,
  1840  	})
  1841  	c.Assert(err, jc.ErrorIsNil)
  1842  	inst, _ := testing.AssertStartInstance(c, env, s.callCtx, s.ControllerUUID, "0")
  1843  	c.Assert(err, jc.ErrorIsNil)
  1844  	ebsProvider, err := env.StorageProvider(ec2.EBS_ProviderType)
  1845  	c.Assert(err, jc.ErrorIsNil)
  1846  	vs, err := ebsProvider.VolumeSource(nil)
  1847  	c.Assert(err, jc.ErrorIsNil)
  1848  	volumeResults, err := vs.CreateVolumes(s.callCtx, []storage.VolumeParams{{
  1849  		Tag:      names.NewVolumeTag("0"),
  1850  		Size:     1024,
  1851  		Provider: ec2.EBS_ProviderType,
  1852  		ResourceTags: map[string]string{
  1853  			tags.JujuController: s.ControllerUUID,
  1854  			tags.JujuModel:      hostedModelUUID,
  1855  		},
  1856  		Attachment: &storage.VolumeAttachmentParams{
  1857  			AttachmentParams: storage.AttachmentParams{
  1858  				InstanceId: inst.Id(),
  1859  			},
  1860  		},
  1861  	}})
  1862  	c.Assert(err, jc.ErrorIsNil)
  1863  	c.Assert(volumeResults, gc.HasLen, 1)
  1864  	c.Assert(volumeResults[0].Error, jc.ErrorIsNil)
  1865  
  1866  	modelVolumes, err := ec2.AllModelVolumes(env, s.callCtx)
  1867  	c.Assert(err, jc.ErrorIsNil)
  1868  	allVolumes := append([]string{}, controllerVolumes...)
  1869  	allVolumes = append(allVolumes, modelVolumes...)
  1870  
  1871  	modelGroups, err := ec2.AllModelGroups(env, s.callCtx)
  1872  	c.Assert(err, jc.ErrorIsNil)
  1873  	allGroups := append([]string{}, controllerGroups...)
  1874  	allGroups = append(allGroups, modelGroups...)
  1875  
  1876  	ec2conn := ec2.EnvironEC2(env)
  1877  
  1878  	origController := coretesting.ControllerTag.Id()
  1879  
  1880  	checkInstanceTags := func(controllerUUID string, expectedIds ...string) {
  1881  		resp, err := ec2conn.Instances(
  1882  			nil, makeFilter("tag:"+tags.JujuController, controllerUUID))
  1883  		c.Assert(err, jc.ErrorIsNil)
  1884  		actualIds := set.NewStrings()
  1885  		for _, reservation := range resp.Reservations {
  1886  			for _, instance := range reservation.Instances {
  1887  				actualIds.Add(instance.InstanceId)
  1888  			}
  1889  		}
  1890  		c.Check(actualIds, gc.DeepEquals, set.NewStrings(expectedIds...))
  1891  	}
  1892  
  1893  	checkVolumeTags := func(controllerUUID string, expectedIds ...string) {
  1894  		resp, err := ec2conn.Volumes(
  1895  			nil, makeFilter("tag:"+tags.JujuController, controllerUUID))
  1896  		c.Assert(err, jc.ErrorIsNil)
  1897  		actualIds := set.NewStrings()
  1898  		for _, vol := range resp.Volumes {
  1899  			actualIds.Add(vol.Id)
  1900  		}
  1901  		c.Check(actualIds, gc.DeepEquals, set.NewStrings(expectedIds...))
  1902  	}
  1903  
  1904  	checkGroupTags := func(controllerUUID string, expectedIds ...string) {
  1905  		resp, err := ec2conn.SecurityGroups(
  1906  			nil, makeFilter("tag:"+tags.JujuController, controllerUUID))
  1907  		c.Assert(err, jc.ErrorIsNil)
  1908  		actualIds := set.NewStrings()
  1909  		for _, group := range resp.Groups {
  1910  			actualIds.Add(group.Id)
  1911  		}
  1912  		c.Check(actualIds, gc.DeepEquals, set.NewStrings(expectedIds...))
  1913  	}
  1914  
  1915  	checkInstanceTags(origController, string(inst.Id()), string(controllerInsts[0].Id()))
  1916  	checkVolumeTags(origController, allVolumes...)
  1917  	checkGroupTags(origController, allGroups...)
  1918  
  1919  	err = env.AdoptResources(s.callCtx, "new-controller", version.MustParse("0.0.1"))
  1920  	c.Assert(err, jc.ErrorIsNil)
  1921  
  1922  	checkInstanceTags("new-controller", string(inst.Id()))
  1923  	checkInstanceTags(origController, string(controllerInsts[0].Id()))
  1924  	checkVolumeTags("new-controller", modelVolumes...)
  1925  	checkVolumeTags(origController, controllerVolumes...)
  1926  	checkGroupTags("new-controller", modelGroups...)
  1927  	checkGroupTags(origController, controllerGroups...)
  1928  }
  1929  
  1930  // localNonUSEastSuite is similar to localServerSuite but the S3 mock server
  1931  // behaves as if it is not in the us-east region.
  1932  type localNonUSEastSuite struct {
  1933  	coretesting.BaseSuite
  1934  	sstesting.TestDataSuite
  1935  
  1936  	srv localServer
  1937  	env environs.Environ
  1938  }
  1939  
  1940  func (t *localNonUSEastSuite) SetUpSuite(c *gc.C) {
  1941  	t.BaseSuite.SetUpSuite(c)
  1942  	t.TestDataSuite.SetUpSuite(c)
  1943  
  1944  	t.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey)
  1945  	t.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
  1946  	t.BaseSuite.PatchValue(ec2.DeleteSecurityGroupInsistently, deleteSecurityGroupForTestFunc)
  1947  }
  1948  
  1949  func (t *localNonUSEastSuite) TearDownSuite(c *gc.C) {
  1950  	t.TestDataSuite.TearDownSuite(c)
  1951  	t.BaseSuite.TearDownSuite(c)
  1952  }
  1953  
  1954  func (t *localNonUSEastSuite) SetUpTest(c *gc.C) {
  1955  	t.BaseSuite.SetUpTest(c)
  1956  	t.srv.startServer(c)
  1957  
  1958  	region := t.srv.region
  1959  	credential := cloud.NewCredential(
  1960  		cloud.AccessKeyAuthType,
  1961  		map[string]string{
  1962  			"access-key": "x",
  1963  			"secret-key": "x",
  1964  		},
  1965  	)
  1966  	restoreEC2Patching := patchEC2ForTesting(c, region)
  1967  	t.AddCleanup(func(c *gc.C) { restoreEC2Patching() })
  1968  
  1969  	env, err := bootstrap.PrepareController(
  1970  		false,
  1971  		envtesting.BootstrapContext(c),
  1972  		jujuclient.NewMemStore(),
  1973  		bootstrap.PrepareParams{
  1974  			ControllerConfig: coretesting.FakeControllerConfig(),
  1975  			ModelConfig:      localConfigAttrs,
  1976  			ControllerName:   localConfigAttrs["name"].(string),
  1977  			Cloud: environs.CloudSpec{
  1978  				Type:       "ec2",
  1979  				Region:     region.Name,
  1980  				Endpoint:   region.EC2Endpoint,
  1981  				Credential: &credential,
  1982  			},
  1983  			AdminSecret: testing.AdminSecret,
  1984  		},
  1985  	)
  1986  	c.Assert(err, jc.ErrorIsNil)
  1987  	t.env = env.(environs.Environ)
  1988  }
  1989  
  1990  func (t *localNonUSEastSuite) TearDownTest(c *gc.C) {
  1991  	t.srv.stopServer(c)
  1992  	t.BaseSuite.TearDownTest(c)
  1993  }
  1994  
  1995  func patchEC2ForTesting(c *gc.C, region aws.Region) func() {
  1996  	ec2.UseTestImageData(c, ec2.MakeTestImageStreamsData(region))
  1997  	restoreTimeouts := envtesting.PatchAttemptStrategies(ec2.ShortAttempt)
  1998  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
  1999  	return func() {
  2000  		restoreFinishBootstrap()
  2001  		restoreTimeouts()
  2002  		ec2.UseTestImageData(c, nil)
  2003  	}
  2004  }
  2005  
  2006  // If match is true, CheckScripts checks that at least one script started
  2007  // by the cloudinit data matches the given regexp pattern, otherwise it
  2008  // checks that no script matches.  It's exported so it can be used by tests
  2009  // defined in ec2_test.
  2010  func CheckScripts(c *gc.C, userDataMap map[string]interface{}, pattern string, match bool) {
  2011  	scripts0 := userDataMap["runcmd"]
  2012  	if scripts0 == nil {
  2013  		c.Errorf("cloudinit has no entry for runcmd")
  2014  		return
  2015  	}
  2016  	scripts := scripts0.([]interface{})
  2017  	re := regexp.MustCompile(pattern)
  2018  	found := false
  2019  	for _, s0 := range scripts {
  2020  		s := s0.(string)
  2021  		if re.MatchString(s) {
  2022  			found = true
  2023  		}
  2024  	}
  2025  	switch {
  2026  	case match && !found:
  2027  		c.Errorf("script %q not found in %q", pattern, scripts)
  2028  	case !match && found:
  2029  		c.Errorf("script %q found but not expected in %q", pattern, scripts)
  2030  	}
  2031  }
  2032  
  2033  // CheckPackage checks that the cloudinit will or won't install the given
  2034  // package, depending on the value of match.  It's exported so it can be
  2035  // used by tests defined outside the ec2 package.
  2036  func CheckPackage(c *gc.C, userDataMap map[string]interface{}, pkg string, match bool) {
  2037  	pkgs0 := userDataMap["packages"]
  2038  	if pkgs0 == nil {
  2039  		if match {
  2040  			c.Errorf("cloudinit has no entry for packages")
  2041  		}
  2042  		return
  2043  	}
  2044  
  2045  	pkgs := pkgs0.([]interface{})
  2046  
  2047  	found := false
  2048  	for _, p0 := range pkgs {
  2049  		p := p0.(string)
  2050  		// p might be a space separate list of packages eg 'foo bar qed' so split them up
  2051  		manyPkgs := set.NewStrings(strings.Split(p, " ")...)
  2052  		hasPkg := manyPkgs.Contains(pkg)
  2053  		if p == pkg || hasPkg {
  2054  			found = true
  2055  			break
  2056  		}
  2057  	}
  2058  	switch {
  2059  	case match && !found:
  2060  		c.Errorf("package %q not found in %v", pkg, pkgs)
  2061  	case !match && found:
  2062  		c.Errorf("%q found but not expected in %v", pkg, pkgs)
  2063  	}
  2064  }