github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/provider/ec2/live_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  	"crypto/rand"
     8  	"fmt"
     9  	"io"
    10  	"strings"
    11  
    12  	jc "github.com/juju/testing/checkers"
    13  	amzec2 "gopkg.in/amz.v2/ec2"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/constraints"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/environs/config"
    19  	"github.com/juju/juju/environs/jujutest"
    20  	"github.com/juju/juju/environs/storage"
    21  	"github.com/juju/juju/instance"
    22  	"github.com/juju/juju/juju/arch"
    23  	"github.com/juju/juju/juju/testing"
    24  	jujutesting "github.com/juju/juju/juju/testing"
    25  	"github.com/juju/juju/provider/ec2"
    26  	coretesting "github.com/juju/juju/testing"
    27  	"github.com/juju/juju/version"
    28  )
    29  
    30  // uniqueName is generated afresh for every test run, so that
    31  // we are not polluted by previous test state.
    32  var uniqueName = randomName()
    33  
    34  func randomName() string {
    35  	buf := make([]byte, 8)
    36  	_, err := io.ReadFull(rand.Reader, buf)
    37  	if err != nil {
    38  		panic(fmt.Sprintf("error from crypto rand: %v", err))
    39  	}
    40  	return fmt.Sprintf("%x", buf)
    41  }
    42  
    43  func registerAmazonTests() {
    44  	// The following attributes hold the environment configuration
    45  	// for running the amazon EC2 integration tests.
    46  	//
    47  	// This is missing keys for security reasons; set the following
    48  	// environment variables to make the Amazon testing work:
    49  	//  access-key: $AWS_ACCESS_KEY_ID
    50  	//  secret-key: $AWS_SECRET_ACCESS_KEY
    51  	attrs := coretesting.FakeConfig().Merge(map[string]interface{}{
    52  		"name":           "sample-" + uniqueName,
    53  		"type":           "ec2",
    54  		"control-bucket": "juju-test-" + uniqueName,
    55  		"admin-secret":   "for real",
    56  		"firewall-mode":  config.FwInstance,
    57  		"agent-version":  version.Current.Number.String(),
    58  	})
    59  	gc.Suite(&LiveTests{
    60  		LiveTests: jujutest.LiveTests{
    61  			TestConfig:     attrs,
    62  			Attempt:        *ec2.ShortAttempt,
    63  			CanOpenState:   true,
    64  			HasProvisioner: true,
    65  		},
    66  	})
    67  }
    68  
    69  // LiveTests contains tests that can be run against the Amazon servers.
    70  // Each test runs using the same ec2 connection.
    71  type LiveTests struct {
    72  	coretesting.BaseSuite
    73  	jujutest.LiveTests
    74  }
    75  
    76  func (t *LiveTests) SetUpSuite(c *gc.C) {
    77  	// Upload arches that ec2 supports; add to this
    78  	// as ec2 coverage expands.
    79  	t.UploadArches = []string{arch.AMD64, arch.I386}
    80  	t.BaseSuite.SetUpSuite(c)
    81  	t.LiveTests.SetUpSuite(c)
    82  }
    83  
    84  func (t *LiveTests) TearDownSuite(c *gc.C) {
    85  	t.LiveTests.TearDownSuite(c)
    86  	t.BaseSuite.TearDownSuite(c)
    87  }
    88  
    89  func (t *LiveTests) SetUpTest(c *gc.C) {
    90  	t.BaseSuite.SetUpTest(c)
    91  	t.LiveTests.SetUpTest(c)
    92  	t.BaseSuite.PatchValue(&version.Current, version.Binary{
    93  		Number: version.Current.Number,
    94  		Series: coretesting.FakeDefaultSeries,
    95  		Arch:   arch.AMD64,
    96  	})
    97  }
    98  
    99  func (t *LiveTests) TearDownTest(c *gc.C) {
   100  	t.LiveTests.TearDownTest(c)
   101  	t.BaseSuite.TearDownTest(c)
   102  }
   103  
   104  // TODO(niemeyer): Looks like many of those tests should be moved to jujutest.LiveTests.
   105  
   106  func (t *LiveTests) TestInstanceAttributes(c *gc.C) {
   107  	t.PrepareOnce(c)
   108  	inst, hc := testing.AssertStartInstance(c, t.Env, "30")
   109  	defer t.Env.StopInstances(inst.Id())
   110  	// Sanity check for hardware characteristics.
   111  	c.Assert(hc.Arch, gc.NotNil)
   112  	c.Assert(hc.Mem, gc.NotNil)
   113  	c.Assert(hc.RootDisk, gc.NotNil)
   114  	c.Assert(hc.CpuCores, gc.NotNil)
   115  	c.Assert(hc.CpuPower, gc.NotNil)
   116  	addresses, err := jujutesting.WaitInstanceAddresses(t.Env, inst.Id())
   117  	// TODO(niemeyer): This assert sometimes fails with "no instances found"
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
   120  
   121  	insts, err := t.Env.Instances([]instance.Id{inst.Id()})
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	c.Assert(len(insts), gc.Equals, 1)
   124  
   125  	ec2inst := ec2.InstanceEC2(insts[0])
   126  	c.Assert(ec2inst.IPAddress, gc.Equals, addresses[0].Value)
   127  	c.Assert(ec2inst.InstanceType, gc.Equals, "m1.small")
   128  }
   129  
   130  func (t *LiveTests) TestStartInstanceConstraints(c *gc.C) {
   131  	t.PrepareOnce(c)
   132  	cons := constraints.MustParse("mem=2G")
   133  	inst, hc := testing.AssertStartInstanceWithConstraints(c, t.Env, "30", cons)
   134  	defer t.Env.StopInstances(inst.Id())
   135  	ec2inst := ec2.InstanceEC2(inst)
   136  	c.Assert(ec2inst.InstanceType, gc.Equals, "m1.medium")
   137  	c.Assert(*hc.Arch, gc.Equals, "amd64")
   138  	c.Assert(*hc.Mem, gc.Equals, uint64(3840))
   139  	c.Assert(*hc.RootDisk, gc.Equals, uint64(8192))
   140  	c.Assert(*hc.CpuCores, gc.Equals, uint64(1))
   141  	c.Assert(*hc.CpuPower, gc.Equals, uint64(200))
   142  }
   143  
   144  func (t *LiveTests) TestInstanceGroups(c *gc.C) {
   145  	t.BootstrapOnce(c)
   146  	allInsts, err := t.Env.AllInstances()
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	c.Assert(allInsts, gc.HasLen, 1) // bootstrap instance
   149  	bootstrapInstId := allInsts[0].Id()
   150  
   151  	ec2conn := ec2.EnvironEC2(t.Env)
   152  
   153  	groups := amzec2.SecurityGroupNames(
   154  		ec2.JujuGroupName(t.Env),
   155  		ec2.MachineGroupName(t.Env, "98"),
   156  		ec2.MachineGroupName(t.Env, "99"),
   157  	)
   158  	info := make([]amzec2.SecurityGroupInfo, len(groups))
   159  
   160  	// Create a group with the same name as the juju group
   161  	// but with different permissions, to check that it's deleted
   162  	// and recreated correctly.
   163  	oldJujuGroup := createGroup(c, ec2conn, groups[0].Name, "old juju group")
   164  
   165  	// Add two permissions: one is required and should be left alone;
   166  	// the other is not and should be deleted.
   167  	// N.B. this is unfortunately sensitive to the actual set of permissions used.
   168  	_, err = ec2conn.AuthorizeSecurityGroup(oldJujuGroup,
   169  		[]amzec2.IPPerm{
   170  			{
   171  				Protocol:  "tcp",
   172  				FromPort:  22,
   173  				ToPort:    22,
   174  				SourceIPs: []string{"0.0.0.0/0"},
   175  			},
   176  			{
   177  				Protocol:  "udp",
   178  				FromPort:  4321,
   179  				ToPort:    4322,
   180  				SourceIPs: []string{"3.4.5.6/32"},
   181  			},
   182  		})
   183  	c.Assert(err, jc.ErrorIsNil)
   184  
   185  	inst0, _ := testing.AssertStartInstance(c, t.Env, "98")
   186  	defer t.Env.StopInstances(inst0.Id())
   187  
   188  	// Create a same-named group for the second instance
   189  	// before starting it, to check that it's reused correctly.
   190  	oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group")
   191  
   192  	inst1, _ := testing.AssertStartInstance(c, t.Env, "99")
   193  	defer t.Env.StopInstances(inst1.Id())
   194  
   195  	groupsResp, err := ec2conn.SecurityGroups(groups, nil)
   196  	c.Assert(err, jc.ErrorIsNil)
   197  	c.Assert(groupsResp.Groups, gc.HasLen, len(groups))
   198  
   199  	// For each group, check that it exists and record its id.
   200  	for i, group := range groups {
   201  		found := false
   202  		for _, g := range groupsResp.Groups {
   203  			if g.Name == group.Name {
   204  				groups[i].Id = g.Id
   205  				info[i] = g
   206  				found = true
   207  				break
   208  			}
   209  		}
   210  		if !found {
   211  			c.Fatalf("group %q not found", group.Name)
   212  		}
   213  	}
   214  
   215  	// The old juju group should have been reused.
   216  	c.Check(groups[0].Id, gc.Equals, oldJujuGroup.Id)
   217  
   218  	// Check that it authorizes the correct ports and there
   219  	// are no extra permissions (in particular we are checking
   220  	// that the unneeded permission that we added earlier
   221  	// has been deleted).
   222  	perms := info[0].IPPerms
   223  	c.Assert(perms, gc.HasLen, 5)
   224  	checkPortAllowed(c, perms, 22) // SSH
   225  	checkPortAllowed(c, perms, coretesting.FakeConfig()["api-port"].(int))
   226  	checkSecurityGroupAllowed(c, perms, groups[0])
   227  
   228  	// The old machine group should have been reused also.
   229  	c.Check(groups[2].Id, gc.Equals, oldMachineGroup.Id)
   230  
   231  	// Check that each instance is part of the correct groups.
   232  	resp, err := ec2conn.Instances([]string{string(inst0.Id()), string(inst1.Id())}, nil)
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Assert(resp.Reservations, gc.HasLen, 2)
   235  	for _, r := range resp.Reservations {
   236  		c.Assert(r.Instances, gc.HasLen, 1)
   237  		// each instance must be part of the general juju group.
   238  		inst := r.Instances[0]
   239  		msg := gc.Commentf("instance %#v", inst)
   240  		c.Assert(hasSecurityGroup(inst, groups[0]), gc.Equals, true, msg)
   241  		switch instance.Id(inst.InstanceId) {
   242  		case inst0.Id():
   243  			c.Assert(hasSecurityGroup(inst, groups[1]), gc.Equals, true, msg)
   244  			c.Assert(hasSecurityGroup(inst, groups[2]), gc.Equals, false, msg)
   245  		case inst1.Id():
   246  			c.Assert(hasSecurityGroup(inst, groups[2]), gc.Equals, true, msg)
   247  			c.Assert(hasSecurityGroup(inst, groups[1]), gc.Equals, false, msg)
   248  		default:
   249  			c.Errorf("unknown instance found: %v", inst)
   250  		}
   251  	}
   252  
   253  	// Check that listing those instances finds them using the groups
   254  	instIds := []instance.Id{inst0.Id(), inst1.Id()}
   255  	idsFromInsts := func(insts []instance.Instance) (ids []instance.Id) {
   256  		for _, inst := range insts {
   257  			ids = append(ids, inst.Id())
   258  		}
   259  		return ids
   260  	}
   261  	insts, err := t.Env.Instances(instIds)
   262  	c.Assert(err, jc.ErrorIsNil)
   263  	c.Assert(instIds, jc.SameContents, idsFromInsts(insts))
   264  	allInsts, err = t.Env.AllInstances()
   265  	c.Assert(err, jc.ErrorIsNil)
   266  	// ignore the bootstrap instance
   267  	for i, inst := range allInsts {
   268  		if inst.Id() == bootstrapInstId {
   269  			if i+1 < len(allInsts) {
   270  				copy(allInsts[i:], allInsts[i+1:])
   271  			}
   272  			allInsts = allInsts[:len(allInsts)-1]
   273  			break
   274  		}
   275  	}
   276  	c.Assert(instIds, jc.SameContents, idsFromInsts(allInsts))
   277  }
   278  
   279  func (t *LiveTests) TestDestroy(c *gc.C) {
   280  	t.PrepareOnce(c)
   281  	s := t.Env.(environs.EnvironStorage).Storage()
   282  	err := s.Put("foo", strings.NewReader("foo"), 3)
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	err = s.Put("bar", strings.NewReader("bar"), 3)
   285  	c.Assert(err, jc.ErrorIsNil)
   286  
   287  	// Check that the bucket exists, so we can be sure
   288  	// we have checked correctly that it's been destroyed.
   289  	names, err := storage.List(s, "")
   290  	c.Assert(err, jc.ErrorIsNil)
   291  	c.Assert(len(names) >= 2, jc.IsTrue)
   292  
   293  	t.Destroy(c)
   294  	for a := ec2.ShortAttempt.Start(); a.Next(); {
   295  		names, err = storage.List(s, "")
   296  		if len(names) == 0 {
   297  			break
   298  		}
   299  	}
   300  	c.Assert(names, gc.HasLen, 0)
   301  }
   302  
   303  func checkPortAllowed(c *gc.C, perms []amzec2.IPPerm, port int) {
   304  	for _, perm := range perms {
   305  		if perm.FromPort == port {
   306  			c.Check(perm.Protocol, gc.Equals, "tcp")
   307  			c.Check(perm.ToPort, gc.Equals, port)
   308  			c.Check(perm.SourceIPs, gc.DeepEquals, []string{"0.0.0.0/0"})
   309  			c.Check(perm.SourceGroups, gc.HasLen, 0)
   310  			return
   311  		}
   312  	}
   313  	c.Errorf("ip port permission not found for %d in %#v", port, perms)
   314  }
   315  
   316  func checkSecurityGroupAllowed(c *gc.C, perms []amzec2.IPPerm, g amzec2.SecurityGroup) {
   317  	protos := map[string]struct {
   318  		fromPort int
   319  		toPort   int
   320  	}{
   321  		"tcp":  {0, 65535},
   322  		"udp":  {0, 65535},
   323  		"icmp": {-1, -1},
   324  	}
   325  	for _, perm := range perms {
   326  		if len(perm.SourceGroups) > 0 {
   327  			c.Check(perm.SourceGroups, gc.HasLen, 1)
   328  			c.Check(perm.SourceGroups[0].Id, gc.Equals, g.Id)
   329  			ports, ok := protos[perm.Protocol]
   330  			if !ok {
   331  				c.Errorf("unexpected protocol in security group: %q", perm.Protocol)
   332  				continue
   333  			}
   334  			delete(protos, perm.Protocol)
   335  			c.Check(perm.FromPort, gc.Equals, ports.fromPort)
   336  			c.Check(perm.ToPort, gc.Equals, ports.toPort)
   337  		}
   338  	}
   339  	if len(protos) > 0 {
   340  		c.Errorf("%d security group permission not found for %#v in %#v", len(protos), g, perms)
   341  	}
   342  }
   343  
   344  func (t *LiveTests) TestStopInstances(c *gc.C) {
   345  	t.PrepareOnce(c)
   346  	// It would be nice if this test was in jujutest, but
   347  	// there's no way for jujutest to fabricate a valid-looking
   348  	// instance id.
   349  	inst0, _ := testing.AssertStartInstance(c, t.Env, "40")
   350  	inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa")
   351  	inst2, _ := testing.AssertStartInstance(c, t.Env, "41")
   352  
   353  	err := t.Env.StopInstances(inst0.Id(), inst1.Id(), inst2.Id())
   354  	c.Check(err, jc.ErrorIsNil)
   355  
   356  	var insts []instance.Instance
   357  
   358  	// We need the retry logic here because we are waiting
   359  	// for Instances to return an error, and it will not retry
   360  	// if it succeeds.
   361  	gone := false
   362  	for a := ec2.ShortAttempt.Start(); a.Next(); {
   363  		insts, err = t.Env.Instances([]instance.Id{inst0.Id(), inst2.Id()})
   364  		if err == environs.ErrPartialInstances {
   365  			// instances not gone yet.
   366  			continue
   367  		}
   368  		if err == environs.ErrNoInstances {
   369  			gone = true
   370  			break
   371  		}
   372  		c.Fatalf("error getting instances: %v", err)
   373  	}
   374  	if !gone {
   375  		c.Errorf("after termination, instances remaining: %v", insts)
   376  	}
   377  }
   378  
   379  func (t *LiveTests) TestPutBucketOnlyOnce(c *gc.C) {
   380  	t.PrepareOnce(c)
   381  	s3inst := ec2.EnvironS3(t.Env)
   382  	b := s3inst.Bucket("test-once-" + uniqueName)
   383  	s := ec2.BucketStorage(b)
   384  
   385  	// Check that we don't do a PutBucket every time by
   386  	// getting it to create the bucket, destroying the bucket behind
   387  	// the scenes, and trying to put another object,
   388  	// which should fail because it doesn't try to do
   389  	// the PutBucket again.
   390  
   391  	err := s.Put("test-object", strings.NewReader("test"), 4)
   392  	c.Assert(err, jc.ErrorIsNil)
   393  
   394  	err = s.Remove("test-object")
   395  	c.Assert(err, jc.ErrorIsNil)
   396  
   397  	err = ec2.DeleteBucket(s)
   398  	c.Assert(err, jc.ErrorIsNil)
   399  
   400  	err = s.Put("test-object", strings.NewReader("test"), 4)
   401  	c.Assert(err, gc.ErrorMatches, ".*The specified bucket does not exist")
   402  }
   403  
   404  // createGroup creates a new EC2 group and returns it. If it already exists,
   405  // it revokes all its permissions and returns the existing group.
   406  func createGroup(c *gc.C, ec2conn *amzec2.EC2, name, descr string) amzec2.SecurityGroup {
   407  	resp, err := ec2conn.CreateSecurityGroup("", name, descr)
   408  	if err == nil {
   409  		return resp.SecurityGroup
   410  	}
   411  	if err.(*amzec2.Error).Code != "InvalidGroup.Duplicate" {
   412  		c.Fatalf("cannot make group %q: %v", name, err)
   413  	}
   414  
   415  	// Found duplicate group, so revoke its permissions and return it.
   416  	gresp, err := ec2conn.SecurityGroups(amzec2.SecurityGroupNames(name), nil)
   417  	c.Assert(err, jc.ErrorIsNil)
   418  
   419  	gi := gresp.Groups[0]
   420  	if len(gi.IPPerms) > 0 {
   421  		_, err = ec2conn.RevokeSecurityGroup(gi.SecurityGroup, gi.IPPerms)
   422  		c.Assert(err, jc.ErrorIsNil)
   423  	}
   424  	return gi.SecurityGroup
   425  }
   426  
   427  func hasSecurityGroup(inst amzec2.Instance, group amzec2.SecurityGroup) bool {
   428  	for _, instGroup := range inst.SecurityGroups {
   429  		if instGroup.Id == group.Id {
   430  			return true
   431  		}
   432  	}
   433  	return false
   434  }