github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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  	"regexp"
     9  	"sort"
    10  	"strings"
    11  
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils"
    14  	"launchpad.net/goamz/aws"
    15  	amzec2 "launchpad.net/goamz/ec2"
    16  	"launchpad.net/goamz/ec2/ec2test"
    17  	"launchpad.net/goamz/s3"
    18  	"launchpad.net/goamz/s3/s3test"
    19  	gc "launchpad.net/gocheck"
    20  	"launchpad.net/goyaml"
    21  
    22  	"github.com/juju/juju/constraints"
    23  	"github.com/juju/juju/environs"
    24  	"github.com/juju/juju/environs/bootstrap"
    25  	"github.com/juju/juju/environs/config"
    26  	"github.com/juju/juju/environs/configstore"
    27  	"github.com/juju/juju/environs/imagemetadata"
    28  	"github.com/juju/juju/environs/jujutest"
    29  	"github.com/juju/juju/environs/simplestreams"
    30  	envtesting "github.com/juju/juju/environs/testing"
    31  	"github.com/juju/juju/environs/tools"
    32  	"github.com/juju/juju/instance"
    33  	"github.com/juju/juju/juju/arch"
    34  	"github.com/juju/juju/juju/testing"
    35  	"github.com/juju/juju/provider/ec2"
    36  	coretesting "github.com/juju/juju/testing"
    37  	"github.com/juju/juju/utils/ssh"
    38  	"github.com/juju/juju/version"
    39  )
    40  
    41  type ProviderSuite struct {
    42  	coretesting.BaseSuite
    43  }
    44  
    45  var _ = gc.Suite(&ProviderSuite{})
    46  
    47  func (t *ProviderSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) {
    48  	// Make an env configured with the stream.
    49  	envAttrs := localConfigAttrs
    50  	if stream != "" {
    51  		envAttrs = envAttrs.Merge(coretesting.Attrs{
    52  			"image-stream": stream,
    53  		})
    54  	}
    55  	cfg, err := config.New(config.NoDefaults, envAttrs)
    56  	c.Assert(err, gc.IsNil)
    57  	env, err := environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem())
    58  	c.Assert(err, gc.IsNil)
    59  	c.Assert(env, gc.NotNil)
    60  
    61  	sources, err := imagemetadata.GetMetadataSources(env)
    62  	c.Assert(err, gc.IsNil)
    63  	c.Assert(len(sources), gc.Equals, 2)
    64  	var urls = make([]string, len(sources))
    65  	for i, source := range sources {
    66  		url, err := source.URL("")
    67  		c.Assert(err, gc.IsNil)
    68  		urls[i] = url
    69  	}
    70  	// The control bucket URL contains the bucket name.
    71  	c.Check(strings.Contains(urls[0], ec2.ControlBucketName(env)+"/images"), jc.IsTrue)
    72  	c.Assert(urls[1], gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath))
    73  }
    74  
    75  func (t *ProviderSuite) TestGetImageMetadataSources(c *gc.C) {
    76  	t.assertGetImageMetadataSources(c, "", "releases")
    77  	t.assertGetImageMetadataSources(c, "released", "releases")
    78  	t.assertGetImageMetadataSources(c, "daily", "daily")
    79  }
    80  
    81  var localConfigAttrs = coretesting.FakeConfig().Merge(coretesting.Attrs{
    82  	"name":           "sample",
    83  	"type":           "ec2",
    84  	"region":         "test",
    85  	"control-bucket": "test-bucket",
    86  	"access-key":     "x",
    87  	"secret-key":     "x",
    88  	"agent-version":  version.Current.Number.String(),
    89  })
    90  
    91  func registerLocalTests() {
    92  	// N.B. Make sure the region we use here
    93  	// has entries in the images/query txt files.
    94  	aws.Regions["test"] = aws.Region{
    95  		Name: "test",
    96  	}
    97  
    98  	gc.Suite(&localServerSuite{})
    99  	gc.Suite(&localLiveSuite{})
   100  	gc.Suite(&localNonUSEastSuite{})
   101  }
   102  
   103  // localLiveSuite runs tests from LiveTests using a fake
   104  // EC2 server that runs within the test process itself.
   105  type localLiveSuite struct {
   106  	LiveTests
   107  	srv                localServer
   108  	restoreEC2Patching func()
   109  }
   110  
   111  func (t *localLiveSuite) SetUpSuite(c *gc.C) {
   112  	t.TestConfig = localConfigAttrs
   113  	t.restoreEC2Patching = patchEC2ForTesting()
   114  	t.srv.startServer(c)
   115  	t.LiveTests.SetUpSuite(c)
   116  }
   117  
   118  func (t *localLiveSuite) TearDownSuite(c *gc.C) {
   119  	t.LiveTests.TearDownSuite(c)
   120  	t.srv.stopServer(c)
   121  	t.restoreEC2Patching()
   122  }
   123  
   124  // localServer represents a fake EC2 server running within
   125  // the test process itself.
   126  type localServer struct {
   127  	ec2srv *ec2test.Server
   128  	s3srv  *s3test.Server
   129  	config *s3test.Config
   130  }
   131  
   132  func (srv *localServer) startServer(c *gc.C) {
   133  	var err error
   134  	srv.ec2srv, err = ec2test.NewServer()
   135  	if err != nil {
   136  		c.Fatalf("cannot start ec2 test server: %v", err)
   137  	}
   138  	srv.s3srv, err = s3test.NewServer(srv.config)
   139  	if err != nil {
   140  		c.Fatalf("cannot start s3 test server: %v", err)
   141  	}
   142  	aws.Regions["test"] = aws.Region{
   143  		Name:                 "test",
   144  		EC2Endpoint:          srv.ec2srv.URL(),
   145  		S3Endpoint:           srv.s3srv.URL(),
   146  		S3LocationConstraint: true,
   147  	}
   148  	s3inst := s3.New(aws.Auth{}, aws.Regions["test"])
   149  	storage := ec2.BucketStorage(s3inst.Bucket("juju-dist"))
   150  	envtesting.UploadFakeTools(c, storage)
   151  	srv.addSpice(c)
   152  
   153  	zones := make([]amzec2.AvailabilityZoneInfo, 3)
   154  	zones[0].Region = "test"
   155  	zones[0].Name = "test-available"
   156  	zones[0].State = "available"
   157  	zones[1].Region = "test"
   158  	zones[1].Name = "test-impaired"
   159  	zones[1].State = "impaired"
   160  	zones[2].Region = "test"
   161  	zones[2].Name = "test-unavailable"
   162  	zones[2].State = "unavailable"
   163  	srv.ec2srv.SetAvailabilityZones(zones)
   164  }
   165  
   166  // addSpice adds some "spice" to the local server
   167  // by adding state that may cause tests to fail.
   168  func (srv *localServer) addSpice(c *gc.C) {
   169  	states := []amzec2.InstanceState{
   170  		ec2test.ShuttingDown,
   171  		ec2test.Terminated,
   172  		ec2test.Stopped,
   173  	}
   174  	for _, state := range states {
   175  		srv.ec2srv.NewInstances(1, "m1.small", "ami-a7f539ce", state, nil)
   176  	}
   177  }
   178  
   179  func (srv *localServer) stopServer(c *gc.C) {
   180  	srv.ec2srv.Quit()
   181  	srv.s3srv.Quit()
   182  	// Clear out the region because the server address is
   183  	// no longer valid.
   184  	delete(aws.Regions, "test")
   185  }
   186  
   187  // localServerSuite contains tests that run against a fake EC2 server
   188  // running within the test process itself.  These tests can test things that
   189  // would be unreasonably slow or expensive to test on a live Amazon server.
   190  // It starts a new local ec2test server for each test.  The server is
   191  // accessed by using the "test" region, which is changed to point to the
   192  // network address of the local server.
   193  type localServerSuite struct {
   194  	coretesting.BaseSuite
   195  	jujutest.Tests
   196  	srv                localServer
   197  	restoreEC2Patching func()
   198  }
   199  
   200  func (t *localServerSuite) SetUpSuite(c *gc.C) {
   201  	t.TestConfig = localConfigAttrs
   202  	t.restoreEC2Patching = patchEC2ForTesting()
   203  	t.BaseSuite.SetUpSuite(c)
   204  }
   205  
   206  func (t *localServerSuite) TearDownSuite(c *gc.C) {
   207  	t.BaseSuite.TearDownSuite(c)
   208  	t.restoreEC2Patching()
   209  }
   210  
   211  func (t *localServerSuite) SetUpTest(c *gc.C) {
   212  	t.BaseSuite.SetUpTest(c)
   213  	t.srv.startServer(c)
   214  	t.Tests.SetUpTest(c)
   215  	t.PatchValue(&version.Current, version.Binary{
   216  		Number: version.Current.Number,
   217  		Series: coretesting.FakeDefaultSeries,
   218  		Arch:   arch.AMD64,
   219  	})
   220  }
   221  
   222  func (t *localServerSuite) TearDownTest(c *gc.C) {
   223  	t.Tests.TearDownTest(c)
   224  	t.srv.stopServer(c)
   225  	t.BaseSuite.TearDownTest(c)
   226  }
   227  
   228  func (t *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) {
   229  	env := t.Prepare(c)
   230  	envtesting.UploadFakeTools(c, env.Storage())
   231  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   232  	c.Assert(err, gc.IsNil)
   233  
   234  	// check that the state holds the id of the bootstrap machine.
   235  	bootstrapState, err := bootstrap.LoadState(env.Storage())
   236  	c.Assert(err, gc.IsNil)
   237  	c.Assert(bootstrapState.StateInstances, gc.HasLen, 1)
   238  
   239  	insts, err := env.AllInstances()
   240  	c.Assert(err, gc.IsNil)
   241  	c.Assert(insts, gc.HasLen, 1)
   242  	c.Check(insts[0].Id(), gc.Equals, bootstrapState.StateInstances[0])
   243  
   244  	// check that the user data is configured to start zookeeper
   245  	// and the machine and provisioning agents.
   246  	// check that the user data is configured to only configure
   247  	// authorized SSH keys and set the log output; everything
   248  	// else happens after the machine is brought up.
   249  	inst := t.srv.ec2srv.Instance(string(insts[0].Id()))
   250  	c.Assert(inst, gc.NotNil)
   251  	addresses, err := insts[0].Addresses()
   252  	c.Assert(err, gc.IsNil)
   253  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
   254  	userData, err := utils.Gunzip(inst.UserData)
   255  	c.Assert(err, gc.IsNil)
   256  	c.Logf("first instance: UserData: %q", userData)
   257  	var userDataMap map[interface{}]interface{}
   258  	err = goyaml.Unmarshal(userData, &userDataMap)
   259  	c.Assert(err, gc.IsNil)
   260  	c.Assert(userDataMap, jc.DeepEquals, map[interface{}]interface{}{
   261  		"output": map[interface{}]interface{}{
   262  			"all": "| tee -a /var/log/cloud-init-output.log",
   263  		},
   264  		"ssh_authorized_keys": splitAuthKeys(env.Config().AuthorizedKeys()),
   265  		"runcmd": []interface{}{
   266  			"set -xe",
   267  			"install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'",
   268  			"printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'",
   269  		},
   270  	})
   271  
   272  	// check that a new instance will be started with a machine agent
   273  	inst1, hc := testing.AssertStartInstance(c, env, "1")
   274  	c.Check(*hc.Arch, gc.Equals, "amd64")
   275  	c.Check(*hc.Mem, gc.Equals, uint64(1740))
   276  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   277  	c.Assert(*hc.CpuPower, gc.Equals, uint64(100))
   278  	inst = t.srv.ec2srv.Instance(string(inst1.Id()))
   279  	c.Assert(inst, gc.NotNil)
   280  	userData, err = utils.Gunzip(inst.UserData)
   281  	c.Assert(err, gc.IsNil)
   282  	c.Logf("second instance: UserData: %q", userData)
   283  	userDataMap = nil
   284  	err = goyaml.Unmarshal(userData, &userDataMap)
   285  	c.Assert(err, gc.IsNil)
   286  	CheckPackage(c, userDataMap, "git", true)
   287  	CheckPackage(c, userDataMap, "mongodb-server", false)
   288  	CheckScripts(c, userDataMap, "jujud bootstrap-state", false)
   289  	CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true)
   290  	// TODO check for provisioning agent
   291  
   292  	err = env.Destroy()
   293  	c.Assert(err, gc.IsNil)
   294  
   295  	_, err = bootstrap.LoadState(env.Storage())
   296  	c.Assert(err, gc.NotNil)
   297  }
   298  
   299  // splitAuthKeys splits the given authorized keys
   300  // into the form expected to be found in the
   301  // user data.
   302  func splitAuthKeys(keys string) []interface{} {
   303  	slines := strings.FieldsFunc(keys, func(r rune) bool {
   304  		return r == '\n'
   305  	})
   306  	var lines []interface{}
   307  	for _, line := range slines {
   308  		lines = append(lines, ssh.EnsureJujuComment(strings.TrimSpace(line)))
   309  	}
   310  	return lines
   311  }
   312  
   313  func (t *localServerSuite) TestInstanceStatus(c *gc.C) {
   314  	env := t.Prepare(c)
   315  	envtesting.UploadFakeTools(c, env.Storage())
   316  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   317  	c.Assert(err, gc.IsNil)
   318  	t.srv.ec2srv.SetInitialInstanceState(ec2test.Terminated)
   319  	inst, _ := testing.AssertStartInstance(c, env, "1")
   320  	c.Assert(err, gc.IsNil)
   321  	c.Assert(inst.Status(), gc.Equals, "terminated")
   322  }
   323  
   324  func (t *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
   325  	env := t.Prepare(c)
   326  	envtesting.UploadFakeTools(c, env.Storage())
   327  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   328  	c.Assert(err, gc.IsNil)
   329  	_, hc := testing.AssertStartInstance(c, env, "1")
   330  	c.Check(*hc.Arch, gc.Equals, "amd64")
   331  	c.Check(*hc.Mem, gc.Equals, uint64(1740))
   332  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   333  	c.Assert(*hc.CpuPower, gc.Equals, uint64(100))
   334  }
   335  
   336  func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) {
   337  	inst, err := t.testStartInstanceAvailZone(c, "test-available")
   338  	c.Assert(err, gc.IsNil)
   339  	c.Assert(ec2.InstanceEC2(inst).AvailZone, gc.Equals, "test-available")
   340  }
   341  
   342  func (t *localServerSuite) TestStartInstanceAvailZoneImpaired(c *gc.C) {
   343  	_, err := t.testStartInstanceAvailZone(c, "test-impaired")
   344  	c.Assert(err, gc.ErrorMatches, `availability zone "test-impaired" is impaired`)
   345  }
   346  
   347  func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) {
   348  	_, err := t.testStartInstanceAvailZone(c, "test-unknown")
   349  	c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`)
   350  }
   351  
   352  func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instance.Instance, error) {
   353  	env := t.Prepare(c)
   354  	envtesting.UploadFakeTools(c, env.Storage())
   355  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   356  	c.Assert(err, gc.IsNil)
   357  
   358  	params := environs.StartInstanceParams{Placement: "zone=" + zone}
   359  	inst, _, _, err := testing.StartInstanceWithParams(env, "1", params, nil)
   360  	return inst, err
   361  }
   362  
   363  func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) {
   364  	var resultZones []amzec2.AvailabilityZoneInfo
   365  	var resultErr error
   366  	t.PatchValue(ec2.EC2AvailabilityZones, func(e *amzec2.EC2, f *amzec2.Filter) (*amzec2.AvailabilityZonesResp, error) {
   367  		resp := &amzec2.AvailabilityZonesResp{
   368  			Zones: append([]amzec2.AvailabilityZoneInfo{}, resultZones...),
   369  		}
   370  		return resp, resultErr
   371  	})
   372  	env := t.Prepare(c)
   373  
   374  	resultErr = fmt.Errorf("failed to get availability zones")
   375  	zones, err := ec2.GetAvailabilityZones(env)
   376  	c.Assert(err, gc.Equals, resultErr)
   377  	c.Assert(zones, gc.IsNil)
   378  
   379  	resultErr = nil
   380  	resultZones = make([]amzec2.AvailabilityZoneInfo, 1)
   381  	resultZones[0].Name = "whatever"
   382  	zones, err = ec2.GetAvailabilityZones(env)
   383  	c.Assert(err, gc.IsNil)
   384  	c.Assert(zones, gc.HasLen, 1)
   385  	c.Assert(zones[0].Name, gc.Equals, "whatever")
   386  
   387  	// A successful result is cached, currently for the lifetime
   388  	// of the Environ. This will change if/when we have long-lived
   389  	// Environs to cut down repeated IaaS requests.
   390  	resultErr = fmt.Errorf("failed to get availability zones")
   391  	resultZones[0].Name = "andever"
   392  	zones, err = ec2.GetAvailabilityZones(env)
   393  	c.Assert(err, gc.IsNil)
   394  	c.Assert(zones, gc.HasLen, 1)
   395  	c.Assert(zones[0].Name, gc.Equals, "whatever")
   396  }
   397  
   398  func (t *localServerSuite) TestAddresses(c *gc.C) {
   399  	env := t.Prepare(c)
   400  	envtesting.UploadFakeTools(c, env.Storage())
   401  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   402  	c.Assert(err, gc.IsNil)
   403  	inst, _ := testing.AssertStartInstance(c, env, "1")
   404  	c.Assert(err, gc.IsNil)
   405  	addrs, err := inst.Addresses()
   406  	c.Assert(err, gc.IsNil)
   407  	// Expected values use Address type but really contain a regexp for
   408  	// the value rather than a valid ip or hostname.
   409  	expected := []instance.Address{{
   410  		Value:        "*.testing.invalid",
   411  		Type:         instance.HostName,
   412  		NetworkScope: instance.NetworkPublic,
   413  	}, {
   414  		Value:        "*.internal.invalid",
   415  		Type:         instance.HostName,
   416  		NetworkScope: instance.NetworkCloudLocal,
   417  	}, {
   418  		Value:        "8.0.0.*",
   419  		Type:         instance.Ipv4Address,
   420  		NetworkScope: instance.NetworkPublic,
   421  	}, {
   422  		Value:        "127.0.0.*",
   423  		Type:         instance.Ipv4Address,
   424  		NetworkScope: instance.NetworkCloudLocal,
   425  	}}
   426  	c.Assert(addrs, gc.HasLen, len(expected))
   427  	for i, addr := range addrs {
   428  		c.Check(addr.Value, gc.Matches, expected[i].Value)
   429  		c.Check(addr.Type, gc.Equals, expected[i].Type)
   430  		c.Check(addr.NetworkScope, gc.Equals, expected[i].NetworkScope)
   431  	}
   432  }
   433  
   434  func (t *localServerSuite) TestConstraintsValidatorUnsupported(c *gc.C) {
   435  	env := t.Prepare(c)
   436  	validator, err := env.ConstraintsValidator()
   437  	c.Assert(err, gc.IsNil)
   438  	cons := constraints.MustParse("arch=amd64 tags=foo")
   439  	unsupported, err := validator.Validate(cons)
   440  	c.Assert(err, gc.IsNil)
   441  	c.Assert(unsupported, gc.DeepEquals, []string{"tags"})
   442  }
   443  
   444  func (t *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
   445  	env := t.Prepare(c)
   446  	validator, err := env.ConstraintsValidator()
   447  	c.Assert(err, gc.IsNil)
   448  	cons := constraints.MustParse("arch=ppc64")
   449  	_, err = validator.Validate(cons)
   450  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=ppc64\nvalid values are:.*")
   451  	cons = constraints.MustParse("instance-type=foo")
   452  	_, err = validator.Validate(cons)
   453  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
   454  }
   455  
   456  func (t *localServerSuite) TestConstraintsMerge(c *gc.C) {
   457  	env := t.Prepare(c)
   458  	validator, err := env.ConstraintsValidator()
   459  	c.Assert(err, gc.IsNil)
   460  	consA := constraints.MustParse("arch=amd64 mem=1G cpu-power=10 cpu-cores=2 tags=bar")
   461  	consB := constraints.MustParse("arch=i386 instance-type=m1.small")
   462  	cons, err := validator.Merge(consA, consB)
   463  	c.Assert(err, gc.IsNil)
   464  	c.Assert(cons, gc.DeepEquals, constraints.MustParse("arch=i386 instance-type=m1.small tags=bar"))
   465  }
   466  
   467  func (t *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) {
   468  	env := t.Prepare(c)
   469  	cons := constraints.MustParse("instance-type=m1.small root-disk=1G")
   470  	placement := ""
   471  	err := env.PrecheckInstance("precise", cons, placement)
   472  	c.Assert(err, gc.IsNil)
   473  }
   474  
   475  func (t *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) {
   476  	env := t.Prepare(c)
   477  	cons := constraints.MustParse("instance-type=m1.invalid")
   478  	placement := ""
   479  	err := env.PrecheckInstance("precise", cons, placement)
   480  	c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "m1.invalid" specified`)
   481  }
   482  
   483  func (t *localServerSuite) TestPrecheckInstanceUnsupportedArch(c *gc.C) {
   484  	env := t.Prepare(c)
   485  	cons := constraints.MustParse("instance-type=cc1.4xlarge arch=i386")
   486  	placement := ""
   487  	err := env.PrecheckInstance("precise", cons, placement)
   488  	c.Assert(err, gc.ErrorMatches, `invalid AWS instance type "cc1.4xlarge" and arch "i386" specified`)
   489  }
   490  
   491  func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) {
   492  	env := t.Prepare(c)
   493  	placement := "zone=test-available"
   494  	err := env.PrecheckInstance("precise", constraints.Value{}, placement)
   495  	c.Assert(err, gc.IsNil)
   496  }
   497  
   498  func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) {
   499  	env := t.Prepare(c)
   500  	placement := "zone=test-unavailable"
   501  	err := env.PrecheckInstance("precise", constraints.Value{}, placement)
   502  	c.Assert(err, gc.IsNil)
   503  }
   504  
   505  func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) {
   506  	env := t.Prepare(c)
   507  	placement := "zone=test-unknown"
   508  	err := env.PrecheckInstance("precise", constraints.Value{}, placement)
   509  	c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`)
   510  }
   511  
   512  func (t *localServerSuite) TestValidateImageMetadata(c *gc.C) {
   513  	env := t.Prepare(c)
   514  	params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("test")
   515  	c.Assert(err, gc.IsNil)
   516  	params.Series = "precise"
   517  	params.Endpoint = "https://ec2.endpoint.com"
   518  	params.Sources, err = imagemetadata.GetMetadataSources(env)
   519  	c.Assert(err, gc.IsNil)
   520  	image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
   521  	c.Assert(err, gc.IsNil)
   522  	sort.Strings(image_ids)
   523  	c.Assert(image_ids, gc.DeepEquals, []string{"ami-00000033", "ami-00000034", "ami-00000035"})
   524  }
   525  
   526  func (t *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
   527  	env := t.Prepare(c)
   528  	sources, err := tools.GetMetadataSources(env)
   529  	c.Assert(err, gc.IsNil)
   530  	c.Assert(len(sources), gc.Equals, 1)
   531  	url, err := sources[0].URL("")
   532  	// The control bucket URL contains the bucket name.
   533  	c.Assert(strings.Contains(url, ec2.ControlBucketName(env)+"/tools"), jc.IsTrue)
   534  }
   535  
   536  func (t *localServerSuite) TestSupportedArchitectures(c *gc.C) {
   537  	env := t.Prepare(c)
   538  	a, err := env.SupportedArchitectures()
   539  	c.Assert(err, gc.IsNil)
   540  	c.Assert(a, jc.SameContents, []string{"amd64", "i386"})
   541  }
   542  
   543  func (t *localServerSuite) TestSupportNetworks(c *gc.C) {
   544  	env := t.Prepare(c)
   545  	c.Assert(env.SupportNetworks(), jc.IsFalse)
   546  }
   547  
   548  // localNonUSEastSuite is similar to localServerSuite but the S3 mock server
   549  // behaves as if it is not in the us-east region.
   550  type localNonUSEastSuite struct {
   551  	coretesting.BaseSuite
   552  	restoreEC2Patching func()
   553  	srv                localServer
   554  	env                environs.Environ
   555  }
   556  
   557  func (t *localNonUSEastSuite) SetUpSuite(c *gc.C) {
   558  	t.BaseSuite.SetUpSuite(c)
   559  	t.restoreEC2Patching = patchEC2ForTesting()
   560  }
   561  
   562  func (t *localNonUSEastSuite) TearDownSuite(c *gc.C) {
   563  	t.restoreEC2Patching()
   564  	t.BaseSuite.TearDownSuite(c)
   565  }
   566  
   567  func (t *localNonUSEastSuite) SetUpTest(c *gc.C) {
   568  	t.BaseSuite.SetUpTest(c)
   569  	t.srv.config = &s3test.Config{
   570  		Send409Conflict: true,
   571  	}
   572  	t.srv.startServer(c)
   573  
   574  	cfg, err := config.New(config.NoDefaults, localConfigAttrs)
   575  	c.Assert(err, gc.IsNil)
   576  	env, err := environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem())
   577  	c.Assert(err, gc.IsNil)
   578  	t.env = env
   579  }
   580  
   581  func (t *localNonUSEastSuite) TearDownTest(c *gc.C) {
   582  	t.srv.stopServer(c)
   583  	t.BaseSuite.TearDownTest(c)
   584  }
   585  
   586  func patchEC2ForTesting() func() {
   587  	ec2.UseTestImageData(ec2.TestImagesData)
   588  	ec2.UseTestInstanceTypeData(ec2.TestInstanceTypeCosts)
   589  	ec2.UseTestRegionData(ec2.TestRegions)
   590  	restoreTimeouts := envtesting.PatchAttemptStrategies(ec2.ShortAttempt, ec2.StorageAttempt)
   591  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   592  	return func() {
   593  		restoreFinishBootstrap()
   594  		restoreTimeouts()
   595  		ec2.UseTestImageData(nil)
   596  		ec2.UseTestInstanceTypeData(nil)
   597  		ec2.UseTestRegionData(nil)
   598  	}
   599  }
   600  
   601  // If match is true, CheckScripts checks that at least one script started
   602  // by the cloudinit data matches the given regexp pattern, otherwise it
   603  // checks that no script matches.  It's exported so it can be used by tests
   604  // defined in ec2_test.
   605  func CheckScripts(c *gc.C, userDataMap map[interface{}]interface{}, pattern string, match bool) {
   606  	scripts0 := userDataMap["runcmd"]
   607  	if scripts0 == nil {
   608  		c.Errorf("cloudinit has no entry for runcmd")
   609  		return
   610  	}
   611  	scripts := scripts0.([]interface{})
   612  	re := regexp.MustCompile(pattern)
   613  	found := false
   614  	for _, s0 := range scripts {
   615  		s := s0.(string)
   616  		if re.MatchString(s) {
   617  			found = true
   618  		}
   619  	}
   620  	switch {
   621  	case match && !found:
   622  		c.Errorf("script %q not found in %q", pattern, scripts)
   623  	case !match && found:
   624  		c.Errorf("script %q found but not expected in %q", pattern, scripts)
   625  	}
   626  }
   627  
   628  // CheckPackage checks that the cloudinit will or won't install the given
   629  // package, depending on the value of match.  It's exported so it can be
   630  // used by tests defined outside the ec2 package.
   631  func CheckPackage(c *gc.C, userDataMap map[interface{}]interface{}, pkg string, match bool) {
   632  	pkgs0 := userDataMap["packages"]
   633  	if pkgs0 == nil {
   634  		if match {
   635  			c.Errorf("cloudinit has no entry for packages")
   636  		}
   637  		return
   638  	}
   639  
   640  	pkgs := pkgs0.([]interface{})
   641  
   642  	found := false
   643  	for _, p0 := range pkgs {
   644  		p := p0.(string)
   645  		if p == pkg {
   646  			found = true
   647  		}
   648  	}
   649  	switch {
   650  	case match && !found:
   651  		c.Errorf("package %q not found in %v", pkg, pkgs)
   652  	case !match && found:
   653  		c.Errorf("%q found but not expected in %v", pkg, pkgs)
   654  	}
   655  }