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