Merge lp:~gz/goamz/tests_amazon_region into lp:goamz

Proposed by Martin Packman
Status: Needs review
Proposed branch: lp:~gz/goamz/tests_amazon_region
Merge into: lp:goamz
Diff against target: 501 lines (+311/-29)
7 files modified
ec2/ec2_test.go (+7/-7)
ec2/ec2i_test.go (+17/-7)
ec2/ec2t_test.go (+21/-9)
ec2/ec2test/server.go (+1/-1)
testutil/streams.go (+91/-0)
testutil/streams_test.go (+150/-0)
testutil/suite.go (+24/-5)
To merge this branch: bzr merge lp:~gz/goamz/tests_amazon_region
Reviewer Review Type Date Requested Status
goamz maintainers Pending
Review via email: mp+192269@code.launchpad.net

Description of the change

Enable running live tests against any EC2 region

There are two stages:
 * Making the test -amazon flag take a region name
 * Looking up real amis with simplestreams and using those in tests

Perhaps leaving the -amazon flag as an enabling bool, and setting a new
flag just for selecting a region that defaults to us-east-1 would be
better.

Both the simplestreams code and required test changes are pretty ugly,
a bigger rewrite would help to clean up the tests.

https://codereview.appspot.com/14930049/

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Reviewers: mp+192269_code.launchpad.net,

Message:
Please take a look.

Description:
Enable running live tests against any EC2 region

There are two stages:
  * Making the test -amazon flag take a region name
  * Looking up real amis with simplestreams and using those in tests

Perhaps leaving the -amazon flag as an enabling bool, and setting a new
flag just for selecting a region that defaults to us-east-1 would be
better.

Both the simplestreams code and required test changes are pretty ugly,
a bigger rewrite would help to clean up the tests.

https://code.launchpad.net/~gz/goamz/tests_amazon_region/+merge/192269

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/14930049/

Affected files (+304, -19 lines):
   A [revision details]
   M ec2/ec2i_test.go
   M ec2/ec2t_test.go
   A testutil/streams.go
   A testutil/streams_test.go
   M testutil/suite.go

lp:~gz/goamz/tests_amazon_region updated
46. By Martin Packman

go fmt

Revision history for this message
Martin Packman (gz) wrote :

Unmerged revisions

46. By Martin Packman

go fmt

45. By Martin Packman

Get real ami values for live tests from published simplestreams data

44. By Martin Packman

Change -amazon test flag to accept region value for testing against other regions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ec2/ec2_test.go'
--- ec2/ec2_test.go 2013-10-04 17:05:49 +0000
+++ ec2/ec2_test.go 2013-10-24 00:16:37 +0000
@@ -96,14 +96,14 @@
96 DisableAPITermination: true,96 DisableAPITermination: true,
97 ShutdownBehavior: "terminate",97 ShutdownBehavior: "terminate",
98 PrivateIPAddress: "10.0.0.25",98 PrivateIPAddress: "10.0.0.25",
99 BlockDeviceMappings: []ec2.BlockDeviceMapping{{99 BlockDeviceMappings: []ec2.BlockDeviceMapping{{
100 DeviceName: "device-name",100 DeviceName: "device-name",
101 VirtualName: "virtual-name",101 VirtualName: "virtual-name",
102 SnapshotId: "snapshot-id",102 SnapshotId: "snapshot-id",
103 VolumeType: "volume-type",103 VolumeType: "volume-type",
104 VolumeSize: 10,104 VolumeSize: 10,
105 DeleteOnTermination: true,105 DeleteOnTermination: true,
106 IOPS: 1000,106 IOPS: 1000,
107 }},107 }},
108 }108 }
109 resp, err := s.ec2.RunInstances(&options)109 resp, err := s.ec2.RunInstances(&options)
110110
=== modified file 'ec2/ec2i_test.go'
--- ec2/ec2i_test.go 2013-01-31 14:52:05 +0000
+++ ec2/ec2i_test.go 2013-10-24 00:16:37 +0000
@@ -32,27 +32,35 @@
32}32}
3333
34func (s *AmazonClientSuite) SetUpSuite(c *C) {34func (s *AmazonClientSuite) SetUpSuite(c *C) {
35 if !testutil.Amazon {35 if testutil.AmazonRegion == nil {
36 c.Skip("AmazonClientSuite tests not enabled")36 c.Skip("AmazonClientSuite tests not enabled")
37 }37 }
38 s.srv.SetUp(c)38 s.srv.SetUp(c)
39 s.ec2 = ec2.New(s.srv.auth, aws.USEast)39 s.ec2 = ec2.New(s.srv.auth, *testutil.AmazonRegion)
40 s.GetImage = func(version, rootStore string) (string, error) {
41 return testutil.GetImageForRegion(s.ec2.Region, version, rootStore)
42 }
40}43}
4144
45type imageGetter func(string, string) (string, error)
46
42// ClientTests defines integration tests designed to test the client.47// ClientTests defines integration tests designed to test the client.
43// It is not used as a test suite in itself, but embedded within48// It is not used as a test suite in itself, but embedded within
44// another type.49// another type.
45type ClientTests struct {50type ClientTests struct {
46 ec2 *ec2.EC251 ec2 *ec2.EC2
52 GetImage imageGetter
47}53}
4854
49var imageId = "ami-ccf405a5" // Ubuntu Maverick, i386, EBS store
50
51// Cost: 0.00 USD55// Cost: 0.00 USD
52func (s *ClientTests) TestRunInstancesError(c *C) {56func (s *ClientTests) TestRunInstancesError(c *C) {
57 // Get an image id that used instance store, which can't be used on
58 // t1.micro machines, results in 400 error from RunInstances.
59 imageId, err := s.GetImage("12.04", "instance")
60 c.Assert(err, IsNil)
53 options := ec2.RunInstances{61 options := ec2.RunInstances{
54 ImageId: "ami-a6f504cf", // Ubuntu Maverick, i386, instance store62 ImageId: imageId,
55 InstanceType: "t1.micro", // Doesn't work with micro, results in 400.63 InstanceType: "t1.micro",
56 }64 }
5765
58 resp, err := s.ec2.RunInstances(&options)66 resp, err := s.ec2.RunInstances(&options)
@@ -70,6 +78,8 @@
7078
71// Cost: 0.02 USD79// Cost: 0.02 USD
72func (s *ClientTests) TestRunAndTerminate(c *C) {80func (s *ClientTests) TestRunAndTerminate(c *C) {
81 imageId, err := s.GetImage("12.04", "ebs")
82 c.Assert(err, IsNil)
73 options := ec2.RunInstances{83 options := ec2.RunInstances{
74 ImageId: imageId,84 ImageId: imageId,
75 InstanceType: "t1.micro",85 InstanceType: "t1.micro",
7686
=== modified file 'ec2/ec2t_test.go'
--- ec2/ec2t_test.go 2013-10-02 21:34:26 +0000
+++ ec2/ec2t_test.go 2013-10-24 00:16:37 +0000
@@ -46,6 +46,11 @@
46 s.srv.SetUp(c)46 s.srv.SetUp(c)
47 s.ServerTests.ec2 = ec2.New(s.srv.auth, s.srv.region)47 s.ServerTests.ec2 = ec2.New(s.srv.auth, s.srv.region)
48 s.clientTests.ec2 = ec2.New(s.srv.auth, s.srv.region)48 s.clientTests.ec2 = ec2.New(s.srv.auth, s.srv.region)
49 getFakeImage := func(version, rootStore string) (string, error) {
50 return fmt.Sprintf("ami-fake-%v-%v", version, rootStore), nil
51 }
52 s.ServerTests.GetImage = getFakeImage
53 s.clientTests.GetImage = getFakeImage
49}54}
5055
51func (s *LocalServerSuite) TestRunAndTerminate(c *C) {56func (s *LocalServerSuite) TestRunAndTerminate(c *C) {
@@ -64,7 +69,7 @@
64 data[i] = byte(i)69 data[i] = byte(i)
65 }70 }
66 inst, err := s.ec2.RunInstances(&ec2.RunInstances{71 inst, err := s.ec2.RunInstances(&ec2.RunInstances{
67 ImageId: imageId,72 ImageId: "ami-fakeimage",
68 InstanceType: "t1.micro",73 InstanceType: "t1.micro",
69 UserData: data,74 UserData: data,
70 })75 })
@@ -82,7 +87,7 @@
8287
83func (s *LocalServerSuite) TestInstanceInfo(c *C) {88func (s *LocalServerSuite) TestInstanceInfo(c *C) {
84 list, err := s.ec2.RunInstances(&ec2.RunInstances{89 list, err := s.ec2.RunInstances(&ec2.RunInstances{
85 ImageId: imageId,90 ImageId: "ami-fakeimage",
86 InstanceType: "t1.micro",91 InstanceType: "t1.micro",
87 })92 })
88 c.Assert(err, IsNil)93 c.Assert(err, IsNil)
@@ -113,11 +118,14 @@
113var _ = Suite(&AmazonServerSuite{})118var _ = Suite(&AmazonServerSuite{})
114119
115func (s *AmazonServerSuite) SetUpSuite(c *C) {120func (s *AmazonServerSuite) SetUpSuite(c *C) {
116 if !testutil.Amazon {121 if testutil.AmazonRegion == nil {
117 c.Skip("AmazonServerSuite tests not enabled")122 c.Skip("AmazonServerSuite tests not enabled")
118 }123 }
119 s.srv.SetUp(c)124 s.srv.SetUp(c)
120 s.ServerTests.ec2 = ec2.New(s.srv.auth, aws.USEast)125 s.ServerTests.ec2 = ec2.New(s.srv.auth, *testutil.AmazonRegion)
126 s.GetImage = func(version string, rootStore string) (string, error) {
127 return testutil.GetImageForRegion(s.ec2.Region, version, rootStore)
128 }
121}129}
122130
123// ServerTests defines a set of tests designed to test131// ServerTests defines a set of tests designed to test
@@ -125,7 +133,8 @@
125// It is not used as a test suite in itself, but embedded within133// It is not used as a test suite in itself, but embedded within
126// another type.134// another type.
127type ServerTests struct {135type ServerTests struct {
128 ec2 *ec2.EC2136 ec2 *ec2.EC2
137 GetImage imageGetter
129}138}
130139
131func terminateInstances(c *C, e *ec2.EC2, insts []*ec2.Instance) {140func terminateInstances(c *C, e *ec2.EC2, insts []*ec2.Instance) {
@@ -313,10 +322,12 @@
313 group2 := groupResp.SecurityGroup322 group2 := groupResp.SecurityGroup
314 defer s.ec2.DeleteSecurityGroup(group2)323 defer s.ec2.DeleteSecurityGroup(group2)
315324
325 imageId1, err := s.GetImage("12.04", "ebs")
326 c.Assert(err, IsNil)
316 insts := make([]*ec2.Instance, 3)327 insts := make([]*ec2.Instance, 3)
317 inst, err := s.ec2.RunInstances(&ec2.RunInstances{328 inst, err := s.ec2.RunInstances(&ec2.RunInstances{
318 MinCount: 2,329 MinCount: 2,
319 ImageId: imageId,330 ImageId: imageId1,
320 InstanceType: "t1.micro",331 InstanceType: "t1.micro",
321 SecurityGroups: []ec2.SecurityGroup{group1},332 SecurityGroups: []ec2.SecurityGroup{group1},
322 })333 })
@@ -325,7 +336,8 @@
325 insts[1] = &inst.Instances[1]336 insts[1] = &inst.Instances[1]
326 defer terminateInstances(c, s.ec2, insts)337 defer terminateInstances(c, s.ec2, insts)
327338
328 imageId2 := "ami-e358958a" // Natty server, i386, EBS store339 imageId2, err := s.GetImage("13.10", "ebs")
340 c.Assert(err, IsNil)
329 inst, err = s.ec2.RunInstances(&ec2.RunInstances{341 inst, err = s.ec2.RunInstances(&ec2.RunInstances{
330 ImageId: imageId2,342 ImageId: imageId2,
331 InstanceType: "t1.micro",343 InstanceType: "t1.micro",
@@ -405,14 +417,14 @@
405 }, {417 }, {
406 about: "check that filtering on image id works",418 about: "check that filtering on image id works",
407 filters: []filterSpec{419 filters: []filterSpec{
408 {"image-id", []string{imageId}},420 {"image-id", []string{imageId1}},
409 },421 },
410 resultIds: ids(0, 1),422 resultIds: ids(0, 1),
411 allowExtra: true,423 allowExtra: true,
412 }, {424 }, {
413 about: "combination filters 1",425 about: "combination filters 1",
414 filters: []filterSpec{426 filters: []filterSpec{
415 {"image-id", []string{imageId, imageId2}},427 {"image-id", []string{imageId1, imageId2}},
416 {"group-name", []string{group1.Name}},428 {"group-name", []string{group1.Name}},
417 },429 },
418 resultIds: ids(0, 1),430 resultIds: ids(0, 1),
419431
=== modified file 'ec2/ec2test/server.go'
--- ec2/ec2test/server.go 2013-10-03 12:10:33 +0000
+++ ec2/ec2test/server.go 2013-10-24 00:16:37 +0000
@@ -66,7 +66,7 @@
6666
67// instance holds a simulated ec2 instance67// instance holds a simulated ec2 instance
68type Instance struct {68type Instance struct {
69 seq int69 seq int
70 // UserData holds the data that was passed to the RunInstances request70 // UserData holds the data that was passed to the RunInstances request
71 // when the instance was started.71 // when the instance was started.
72 UserData []byte72 UserData []byte
7373
=== added file 'testutil/streams.go'
--- testutil/streams.go 1970-01-01 00:00:00 +0000
+++ testutil/streams.go 2013-10-24 00:16:37 +0000
@@ -0,0 +1,91 @@
1package testutil
2
3import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "launchpad.net/goamz/aws"
8 "net/http"
9)
10
11var streamsURL = "http://cloud-images.ubuntu.com/releases/streams/v1/com.ubuntu.cloud:released:aws.json"
12
13func fetchStreamsData(result interface{}) error {
14 response, err := http.Get(streamsURL)
15 if err != nil {
16 return fmt.Errorf("failed to fetch streams data: %v", err)
17 }
18 defer response.Body.Close()
19 contents, err := ioutil.ReadAll(response.Body)
20 if err != nil {
21 return fmt.Errorf("failed to read streams data: %v", err)
22 }
23 if response.StatusCode != 200 {
24 return fmt.Errorf("http error fetching streams data: %q", contents)
25 }
26 err = json.Unmarshal(contents, &result)
27 if err != nil {
28 return fmt.Errorf("failed to parse streams data: %v", err)
29 }
30 return nil
31}
32
33// TODO(gz): These structs are based on those in lp:juju-core simplestreams
34// code at environs/simplestreams/simplestreams.go which if split out into a
35// seperate module could be reused here instead.
36
37type CloudMetadata struct {
38 Products map[string]MetadataCatalog `json:"products"`
39 DataType string `json:"datatype"`
40}
41
42type MetadataCatalog struct {
43 Series string `json:"release,omitempty"`
44 Version string `json:"version,omitempty"`
45 Arch string `json:"arch,omitempty"`
46
47 Versions map[string]ItemCollection `json:"versions"`
48}
49
50type ItemCollection struct {
51 Items map[string]Item `json:"items"`
52}
53
54type Item struct {
55 RootStore string `json:"root_store"`
56 Virt string `json:"virt"`
57 CRSN string `json:"crsn"`
58 Id string `json:"id"`
59}
60
61var streamsData *CloudMetadata
62
63// GetImageForRegion selects an ami string id from the published simplestreams
64// data matching the region, version, and root store values given.
65func GetImageForRegion(region aws.Region, version string, rootStore string) (string, error) {
66 if streamsData == nil {
67 err := fetchStreamsData(&streamsData)
68 if err != nil {
69 return "", err
70 }
71 }
72 for productKey, _ := range streamsData.Products {
73 product := streamsData.Products[productKey]
74 if version != product.Version {
75 continue
76 }
77 for versionsKey, _ := range product.Versions {
78 itemCollection := product.Versions[versionsKey]
79 for itemKey, _ := range itemCollection.Items {
80 item := itemCollection.Items[itemKey]
81 if item.Virt == "hvm" || item.RootStore != rootStore {
82 continue
83 }
84 if item.CRSN == region.Name {
85 return item.Id, nil
86 }
87 }
88 }
89 }
90 return "", fmt.Errorf("no image found for region:%v version:%v root_store:%v", region.Name, version, rootStore)
91}
092
=== added file 'testutil/streams_test.go'
--- testutil/streams_test.go 1970-01-01 00:00:00 +0000
+++ testutil/streams_test.go 2013-10-24 00:16:37 +0000
@@ -0,0 +1,150 @@
1package testutil
2
3import (
4 "encoding/json"
5 "testing"
6
7 gc "launchpad.net/gocheck"
8
9 "launchpad.net/goamz/aws"
10)
11
12func Test(t *testing.T) {
13 gc.TestingT(t)
14}
15
16func init() {
17 gc.Suite(&StreamsServerSuite{NewHTTPServer()})
18 gc.Suite(&StreamsSuite{})
19}
20
21type StreamsSuite struct{}
22
23type StreamsServerSuite struct {
24 Server *HTTPServer
25}
26
27func (s *StreamsServerSuite) SetUpSuite(c *gc.C) {
28 s.Server.Start()
29}
30
31func (s *StreamsServerSuite) TearDownTest(c *gc.C) {
32 s.Server.Flush()
33}
34
35func (s *StreamsServerSuite) TearDownSuite(c *gc.C) {
36 s.Server.Stop()
37}
38
39func (s *StreamsServerSuite) patchStreamsURL() func() {
40 origStreamsURL := streamsURL
41 streamsURL = s.Server.URL
42 return func() {
43 streamsURL = origStreamsURL
44 }
45}
46
47func (s *StreamsServerSuite) TestFetchStreams(c *gc.C) {
48 s.Server.Response(200, nil, "{}")
49
50 defer s.patchStreamsURL()()
51
52 result := make(map[string]interface{})
53 err := fetchStreamsData(&result)
54 c.Assert(err, gc.IsNil)
55 c.Check(result, gc.DeepEquals, map[string]interface{}{})
56}
57
58func (s *StreamsServerSuite) TestFetchStreamsServerError(c *gc.C) {
59 s.Server.Response(500, nil, "Server error")
60
61 defer s.patchStreamsURL()()
62
63 result := make(map[string]interface{})
64 err := fetchStreamsData(&result)
65 c.Assert(err, gc.NotNil)
66 c.Assert(err, gc.ErrorMatches, "http error.*Server error.*")
67}
68
69func (s *StreamsServerSuite) TestFetchStreamsBadJSON(c *gc.C) {
70 s.Server.Response(200, nil, "--junk-- {}")
71
72 defer s.patchStreamsURL()()
73
74 result := make(map[string]interface{})
75 err := fetchStreamsData(&result)
76 c.Assert(err, gc.NotNil)
77 c.Assert(err, gc.ErrorMatches, "failed to parse streams data: .*")
78}
79
80func (s *StreamsSuite) patchStreamsData(data string) func() {
81 cloudData := CloudMetadata{}
82 err := json.Unmarshal([]byte(data), &cloudData)
83 if err != nil {
84 panic(err)
85 }
86 streamsData = &cloudData
87 return func() {
88 streamsData = nil
89 }
90}
91
92var sampleStreamsData = `{
93 "updated": "Sun, 20 Oct 2013 13:48:35 +0000",
94 "format": "products:1.0",
95 "datatype": "image-ids",
96 "products": {
97 "com.ubuntu.cloud:server:13.10:amd64": {
98 "release": "saucy",
99 "version": "13.10",
100 "arch": "amd64",
101 "versions": {
102 "20130808": {
103 "items": {
104 "usww1pe": {
105 "root_store": "ebs",
106 "virt": "pv",
107 "crsn": "us-west-1",
108 "id": "ami-88ffd4cd"
109 },
110 "usww2he": {
111 "root_store": "ebs",
112 "virt": "hvm",
113 "crsn": "us-west-2",
114 "id": "ami-89f964b9"
115 },
116 "apne1pe": {
117 "root_store": "ebs",
118 "virt": "pv",
119 "crsn": "ap-northeast-1",
120 "id": "ami-1139a810"
121 },
122 "usee1pi": {
123 "root_store": "instance",
124 "virt": "pv",
125 "crsn": "us-east-1",
126 "id": "ami-eaafe883"
127 }
128 }
129 }
130 }
131 }
132 }
133}
134`
135
136func (s *StreamsSuite) TestFetchStreamsUSEast(c *gc.C) {
137 defer s.patchStreamsData(sampleStreamsData)()
138
139 imageId, err := GetImageForRegion(aws.USEast, "13.10", "ebs")
140 c.Assert(err, gc.IsNil)
141 c.Assert(imageId, gc.Equals, "ami-eaafe883")
142}
143
144func (s *StreamsSuite) TestFetchStreamsBadJSON(c *gc.C) {
145 defer s.patchStreamsData("{}")()
146
147 _, err := GetImageForRegion(aws.USEast, "13.10", "ebs")
148 c.Assert(err, gc.NotNil)
149 c.Assert(err, gc.ErrorMatches, "no image found for region.*")
150}
0151
=== modified file 'testutil/suite.go'
--- testutil/suite.go 2013-01-31 14:52:05 +0000
+++ testutil/suite.go 2013-10-24 00:16:37 +0000
@@ -1,17 +1,36 @@
1package testutil1package testutil
22
3import (3import (
4 "errors"
4 "flag"5 "flag"
5 "launchpad.net/goamz/aws"6 "launchpad.net/goamz/aws"
6 . "launchpad.net/gocheck"7 . "launchpad.net/gocheck"
7)8)
89
9// Amazon must be used by all tested packages to determine whether to10// AmazonRegion must be used by all tested packages to determine which region
10// run functional tests against the real AWS servers.11// to use when runnong functional tests against the real AWS servers, and
11var Amazon bool12// disable those tests if nil.
13var AmazonRegion *aws.Region
14
15type amazonRegionValue struct {
16 flag.Value
17}
18
19func (v *amazonRegionValue) String() string {
20 return "REGION"
21}
22
23func (v *amazonRegionValue) Set(val string) error {
24 region, ok := aws.Regions[val]
25 if !ok {
26 return errors.New("not an AWS region")
27 }
28 AmazonRegion = &region
29 return nil
30}
1231
13func init() {32func init() {
14 flag.BoolVar(&Amazon, "amazon", false, "Enable tests against amazon server")33 flag.Var(&amazonRegionValue{}, "amazon", "Region to enable tests against amazon server")
15}34}
1635
17type LiveSuite struct {36type LiveSuite struct {
@@ -19,7 +38,7 @@
19}38}
2039
21func (s *LiveSuite) SetUpSuite(c *C) {40func (s *LiveSuite) SetUpSuite(c *C) {
22 if !Amazon {41 if AmazonRegion == nil {
23 c.Skip("amazon tests not enabled (-amazon flag)")42 c.Skip("amazon tests not enabled (-amazon flag)")
24 }43 }
25 auth, err := aws.EnvAuth()44 auth, err := aws.EnvAuth()

Subscribers

People subscribed via source and target branches