github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 coretesting.FakeDefaultSeries }) 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, "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, "30", cons) 130 defer t.Env.StopInstances(inst.Id()) 131 ec2inst := ec2.InstanceEC2(inst) 132 c.Assert(ec2inst.InstanceType, gc.Equals, "m3.large") 133 c.Assert(*hc.Arch, gc.Equals, "amd64") 134 c.Assert(*hc.Mem, gc.Equals, uint64(7680)) 135 c.Assert(*hc.RootDisk, gc.Equals, uint64(8192)) 136 c.Assert(*hc.CpuCores, gc.Equals, uint64(2)) 137 c.Assert(*hc.CpuPower, gc.Equals, uint64(650)) 138 } 139 140 func (t *LiveTests) TestControllerInstances(c *gc.C) { 141 t.BootstrapOnce(c) 142 allInsts, err := t.Env.AllInstances() 143 c.Assert(err, jc.ErrorIsNil) 144 c.Assert(allInsts, gc.HasLen, 1) // bootstrap instance 145 bootstrapInstId := allInsts[0].Id() 146 147 inst0, _ := testing.AssertStartInstance(c, t.Env, "98") 148 defer t.Env.StopInstances(inst0.Id()) 149 150 inst1, _ := testing.AssertStartInstance(c, t.Env, "99") 151 defer t.Env.StopInstances(inst1.Id()) 152 153 insts, err := t.Env.ControllerInstances() 154 c.Assert(err, jc.ErrorIsNil) 155 c.Assert(insts, gc.DeepEquals, []instance.Id{bootstrapInstId}) 156 } 157 158 func (t *LiveTests) TestInstanceGroups(c *gc.C) { 159 t.BootstrapOnce(c) 160 allInsts, err := t.Env.AllInstances() 161 c.Assert(err, jc.ErrorIsNil) 162 c.Assert(allInsts, gc.HasLen, 1) // bootstrap instance 163 bootstrapInstId := allInsts[0].Id() 164 165 ec2conn := ec2.EnvironEC2(t.Env) 166 167 groups := amzec2.SecurityGroupNames( 168 ec2.JujuGroupName(t.Env), 169 ec2.MachineGroupName(t.Env, "98"), 170 ec2.MachineGroupName(t.Env, "99"), 171 ) 172 info := make([]amzec2.SecurityGroupInfo, len(groups)) 173 174 // Create a group with the same name as the juju group 175 // but with different permissions, to check that it's deleted 176 // and recreated correctly. 177 oldJujuGroup := createGroup(c, ec2conn, groups[0].Name, "old juju group") 178 179 // Add two permissions: one is required and should be left alone; 180 // the other is not and should be deleted. 181 // N.B. this is unfortunately sensitive to the actual set of permissions used. 182 _, err = ec2conn.AuthorizeSecurityGroup(oldJujuGroup, 183 []amzec2.IPPerm{ 184 { 185 Protocol: "tcp", 186 FromPort: 22, 187 ToPort: 22, 188 SourceIPs: []string{"0.0.0.0/0"}, 189 }, 190 { 191 Protocol: "udp", 192 FromPort: 4321, 193 ToPort: 4322, 194 SourceIPs: []string{"3.4.5.6/32"}, 195 }, 196 }) 197 c.Assert(err, jc.ErrorIsNil) 198 199 inst0, _ := testing.AssertStartInstance(c, t.Env, "98") 200 defer t.Env.StopInstances(inst0.Id()) 201 202 // Create a same-named group for the second instance 203 // before starting it, to check that it's reused correctly. 204 oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group") 205 206 inst1, _ := testing.AssertStartInstance(c, t.Env, "99") 207 defer t.Env.StopInstances(inst1.Id()) 208 209 groupsResp, err := ec2conn.SecurityGroups(groups, nil) 210 c.Assert(err, jc.ErrorIsNil) 211 c.Assert(groupsResp.Groups, gc.HasLen, len(groups)) 212 213 // For each group, check that it exists and record its id. 214 for i, group := range groups { 215 found := false 216 for _, g := range groupsResp.Groups { 217 if g.Name == group.Name { 218 groups[i].Id = g.Id 219 info[i] = g 220 found = true 221 break 222 } 223 } 224 if !found { 225 c.Fatalf("group %q not found", group.Name) 226 } 227 } 228 229 // The old juju group should have been reused. 230 c.Check(groups[0].Id, gc.Equals, oldJujuGroup.Id) 231 232 // Check that it authorizes the correct ports and there 233 // are no extra permissions (in particular we are checking 234 // that the unneeded permission that we added earlier 235 // has been deleted). 236 perms := info[0].IPPerms 237 c.Assert(perms, gc.HasLen, 5) 238 checkPortAllowed(c, perms, 22) // SSH 239 checkPortAllowed(c, perms, coretesting.FakeConfig()["api-port"].(int)) 240 checkSecurityGroupAllowed(c, perms, groups[0]) 241 242 // The old machine group should have been reused also. 243 c.Check(groups[2].Id, gc.Equals, oldMachineGroup.Id) 244 245 // Check that each instance is part of the correct groups. 246 resp, err := ec2conn.Instances([]string{string(inst0.Id()), string(inst1.Id())}, nil) 247 c.Assert(err, jc.ErrorIsNil) 248 c.Assert(resp.Reservations, gc.HasLen, 2) 249 for _, r := range resp.Reservations { 250 c.Assert(r.Instances, gc.HasLen, 1) 251 // each instance must be part of the general juju group. 252 inst := r.Instances[0] 253 msg := gc.Commentf("instance %#v", inst) 254 c.Assert(hasSecurityGroup(inst, groups[0]), gc.Equals, true, msg) 255 switch instance.Id(inst.InstanceId) { 256 case inst0.Id(): 257 c.Assert(hasSecurityGroup(inst, groups[1]), gc.Equals, true, msg) 258 c.Assert(hasSecurityGroup(inst, groups[2]), gc.Equals, false, msg) 259 case inst1.Id(): 260 c.Assert(hasSecurityGroup(inst, groups[2]), gc.Equals, true, msg) 261 c.Assert(hasSecurityGroup(inst, groups[1]), gc.Equals, false, msg) 262 default: 263 c.Errorf("unknown instance found: %v", inst) 264 } 265 } 266 267 // Check that listing those instances finds them using the groups 268 instIds := []instance.Id{inst0.Id(), inst1.Id()} 269 idsFromInsts := func(insts []instance.Instance) (ids []instance.Id) { 270 for _, inst := range insts { 271 ids = append(ids, inst.Id()) 272 } 273 return ids 274 } 275 insts, err := t.Env.Instances(instIds) 276 c.Assert(err, jc.ErrorIsNil) 277 c.Assert(instIds, jc.SameContents, idsFromInsts(insts)) 278 allInsts, err = t.Env.AllInstances() 279 c.Assert(err, jc.ErrorIsNil) 280 // ignore the bootstrap instance 281 for i, inst := range allInsts { 282 if inst.Id() == bootstrapInstId { 283 if i+1 < len(allInsts) { 284 copy(allInsts[i:], allInsts[i+1:]) 285 } 286 allInsts = allInsts[:len(allInsts)-1] 287 break 288 } 289 } 290 c.Assert(instIds, jc.SameContents, idsFromInsts(allInsts)) 291 } 292 293 func checkPortAllowed(c *gc.C, perms []amzec2.IPPerm, port int) { 294 for _, perm := range perms { 295 if perm.FromPort == port { 296 c.Check(perm.Protocol, gc.Equals, "tcp") 297 c.Check(perm.ToPort, gc.Equals, port) 298 c.Check(perm.SourceIPs, gc.DeepEquals, []string{"0.0.0.0/0"}) 299 c.Check(perm.SourceGroups, gc.HasLen, 0) 300 return 301 } 302 } 303 c.Errorf("ip port permission not found for %d in %#v", port, perms) 304 } 305 306 func checkSecurityGroupAllowed(c *gc.C, perms []amzec2.IPPerm, g amzec2.SecurityGroup) { 307 protos := map[string]struct { 308 fromPort int 309 toPort int 310 }{ 311 "tcp": {0, 65535}, 312 "udp": {0, 65535}, 313 "icmp": {-1, -1}, 314 } 315 for _, perm := range perms { 316 if len(perm.SourceGroups) > 0 { 317 c.Check(perm.SourceGroups, gc.HasLen, 1) 318 c.Check(perm.SourceGroups[0].Id, gc.Equals, g.Id) 319 ports, ok := protos[perm.Protocol] 320 if !ok { 321 c.Errorf("unexpected protocol in security group: %q", perm.Protocol) 322 continue 323 } 324 delete(protos, perm.Protocol) 325 c.Check(perm.FromPort, gc.Equals, ports.fromPort) 326 c.Check(perm.ToPort, gc.Equals, ports.toPort) 327 } 328 } 329 if len(protos) > 0 { 330 c.Errorf("%d security group permission not found for %#v in %#v", len(protos), g, perms) 331 } 332 } 333 334 func (t *LiveTests) TestStopInstances(c *gc.C) { 335 t.PrepareOnce(c) 336 // It would be nice if this test was in jujutest, but 337 // there's no way for jujutest to fabricate a valid-looking 338 // instance id. 339 inst0, _ := testing.AssertStartInstance(c, t.Env, "40") 340 inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa") 341 inst2, _ := testing.AssertStartInstance(c, t.Env, "41") 342 343 err := t.Env.StopInstances(inst0.Id(), inst1.Id(), inst2.Id()) 344 c.Check(err, jc.ErrorIsNil) 345 346 var insts []instance.Instance 347 348 // We need the retry logic here because we are waiting 349 // for Instances to return an error, and it will not retry 350 // if it succeeds. 351 gone := false 352 for a := ec2.ShortAttempt.Start(); a.Next(); { 353 insts, err = t.Env.Instances([]instance.Id{inst0.Id(), inst2.Id()}) 354 if err == environs.ErrPartialInstances { 355 // instances not gone yet. 356 continue 357 } 358 if err == environs.ErrNoInstances { 359 gone = true 360 break 361 } 362 c.Fatalf("error getting instances: %v", err) 363 } 364 if !gone { 365 c.Errorf("after termination, instances remaining: %v", insts) 366 } 367 } 368 369 // createGroup creates a new EC2 group and returns it. If it already exists, 370 // it revokes all its permissions and returns the existing group. 371 func createGroup(c *gc.C, ec2conn *amzec2.EC2, name, descr string) amzec2.SecurityGroup { 372 resp, err := ec2conn.CreateSecurityGroup("", name, descr) 373 if err == nil { 374 return resp.SecurityGroup 375 } 376 if err.(*amzec2.Error).Code != "InvalidGroup.Duplicate" { 377 c.Fatalf("cannot make group %q: %v", name, err) 378 } 379 380 // Found duplicate group, so revoke its permissions and return it. 381 gresp, err := ec2conn.SecurityGroups(amzec2.SecurityGroupNames(name), nil) 382 c.Assert(err, jc.ErrorIsNil) 383 384 gi := gresp.Groups[0] 385 if len(gi.IPPerms) > 0 { 386 _, err = ec2conn.RevokeSecurityGroup(gi.SecurityGroup, gi.IPPerms) 387 c.Assert(err, jc.ErrorIsNil) 388 } 389 return gi.SecurityGroup 390 } 391 392 func hasSecurityGroup(inst amzec2.Instance, group amzec2.SecurityGroup) bool { 393 for _, instGroup := range inst.SecurityGroups { 394 if instGroup.Id == group.Id { 395 return true 396 } 397 } 398 return false 399 }