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 }