Merge lp:~dimitern/goamz/vpc-api-calls into lp:goamz
- vpc-api-calls
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 46 |
Proposed branch: | lp:~dimitern/goamz/vpc-api-calls |
Merge into: | lp:goamz |
Prerequisite: | lp:~dimitern/goamz/lp-1275406-lpcalserversuite-testinstanceinfo |
Diff against target: |
758 lines (+526/-25) 7 files modified
ec2/ec2.go (+14/-2) ec2/ec2_test.go (+7/-7) ec2/ec2t_test.go (+50/-11) ec2/ec2test/server.go (+121/-4) ec2/responses_test.go (+42/-1) ec2/vpc.go (+120/-0) ec2/vpc_test.go (+172/-0) |
To merge this branch: | bzr merge lp:~dimitern/goamz/vpc-api-calls |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Dimiter Naydenov (community) | Approve | ||
Review via email: mp+204514@code.launchpad.net |
Commit message
Description of the change
ec2: Add support for AWS VPCs
Added the following new API calls:
- CreateVPC
- DeleteVPC
- VPCs
(and the associated types/responses).
Most new code is in vpc.go and vpc_test.go.
These are needed in order to support VPC-related
operations on EC2, which will come in later follow-ups.
Implementation complexity in ec2test package for the
new calls is minimal.
Some changes were needed in order to support these
and the upcoming API calls. For the new ones, AWS API
version 2013-10-15 (latest) is used, while for the
existing calls use the previous API version, as before
(2011-12-15).
Added tests, updated test doubles and tested live on
EC2.
After running the EC2 live tests numerous times, I
realized some tests are leaking security groups, so
I added a deleteGroups() tests helper that retries
to ensure all groups are deleted. Also improved the
VPC tests to include retrying as well, so the live
tests are more stable and clean up after themselves.
Dimiter Naydenov (dimitern) wrote : | # |
Martin Packman (gz) wrote : | # |
LGTM, a couple of comments.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:925: func (ec2 *EC2) CreateVpc(cidrBlock string) (resp
*CreateVpcResp, err error) {
Do want to take and pass through the instanceTenancy argument here even
if we don't need it ourselves?
https:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
Is this a safe cidr to be using?
https:/
ec2/ec2t_
This feels race/eventual consistency prone, perhaps not an issue for
vpcs or delete calls specifically?
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
Do we want an error not implemented type of thing rather than just not
matching?
https:/
File ec2/responses_
https:/
ec2/responses_
xmlns="http://
We probably don't want to mix and match schema versions in our example
output unless we really don't have a feature we need in an older schema.
- 48. By Dimiter Naydenov
-
Changes after review
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:925: func (ec2 *EC2) CreateVpc(cidrBlock string) (resp
*CreateVpcResp, err error) {
On 2014/02/03 21:40:54, gz wrote:
> Do want to take and pass through the instanceTenancy argument here
even if we
> don't need it ourselves?
It's not supported as an argument in API version 2011-12-15.
https:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
On 2014/02/03 21:40:54, gz wrote:
> Is this a safe cidr to be using?
Yes, it passes even live tests :)
https:/
ec2/ec2t_
On 2014/02/03 21:40:54, gz wrote:
> This feels race/eventual consistency prone, perhaps not an issue for
vpcs or
> delete calls specifically?
I run them multiple times live without issues so far.
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
On 2014/02/03 21:40:54, gz wrote:
> Do we want an error not implemented type of thing rather than just not
matching?
It's technically not an error, but since this is just a test double, I
don't think it's a big deal to error out.
https:/
File ec2/responses_
https:/
ec2/responses_
xmlns="http://
On 2014/02/03 21:40:54, gz wrote:
> We probably don't want to mix and match schema versions in our example
output
> unless we really don't have a feature we need in an older schema.
You're right. I found the API docs for version 2011-12-15 and updated
the example responses, as well as the code to conform to that (i.e. not
IsDefault and no InstanceTenancy fields anymore).
- 49. By Dimiter Naydenov
-
Split VPC calls/tests in a separate file
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
- 50. By Dimiter Naydenov
-
Simplified & refactored a bit; naming fixed, API version updated
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
Roger Peppe (rogpeppe) wrote : | # |
LGTM with some suggestions, mostly documentation related.
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
tag-value filters not implemented")
fmt.Errorf("%s filter not implemented", attr)
?
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:122: if params["Version"] == "" {
Given that makeParams now always creates the Version
entry, why bother with this?
https:/
ec2/ec2.go:183: func makeParamsVPC(
I'm not sure this really justifies itself over
just calling makeParamsForVe
when necessary.
https:/
ec2/ec2.go:187: func makeParamsForVe
map[string]string {
makeParamsWithV
?
then makeParamsWithV
(i *think* that With is more appropriate than For in this case).
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
string) map[string]bool {
doc comment?
https:/
File ec2/vpc.go (right):
https:/
ec2/vpc.go:26: // AWS API version used for VPC-related calls.
Do we actually want to export this?
https:/
ec2/vpc.go:53: // The smallest VPC you can create uses a /28 netmask (16
IP
s/you can create/that can be created/
https:/
ec2/vpc.go:57: // The supported tenancy options for instances launched
into the
// The instanceTenancy value holds the tenancy options for
// instances launched into the VPC - either DefaultTenancy
// or DedicatedTenancy.
//
// See http://
?
I think the details of the tenancy semantics can reasonably
be left to the amazon docs.
We should also document that instanceTenancy may be empty
or (my preference) just leave the description unchanged,
and lose the special case for instanceTenancy == "".
https:/
ec2/vpc.go:79: // DeleteVPC deletes the specified VPC. You must detach
or delete all
I'm not keen on the "you" phrasing (it might not be
the reader that's responsible for doing it).
I'd prefer something like this:
// DeleteVPC deletes the VPC with the specified id.
// All gateways and resources that are associated with
// the VPC must have been previously deleted,
// including instances running in the VPC, and non-default security
// groups a...
- 51. By Dimiter Naydenov
-
Changes after review; tests improvements
- 52. By Dimiter Naydenov
-
Changed CIDRs of VPC for easier tracking of leaky tests
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
tag-value filters not implemented")
On 2014/02/05 14:29:14, rog wrote:
> fmt.Errorf("%s filter not implemented", attr)
> ?
It was changed in later patch sets.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:122: if params["Version"] == "" {
On 2014/02/05 14:29:14, rog wrote:
> Given that makeParams now always creates the Version
> entry, why bother with this?
Just to be on the safe side, but fair enough. Removed.
https:/
ec2/ec2.go:183: func makeParamsVPC(
On 2014/02/05 14:29:14, rog wrote:
> I'm not sure this really justifies itself over
> just calling makeParamsForVe
> when necessary.
I'd prefer to leave it, because it's shorter to write - it's just a
helper anyway.
https:/
ec2/ec2.go:187: func makeParamsForVe
map[string]string {
On 2014/02/05 14:29:14, rog wrote:
> makeParamsWithV
> ?
> then makeParamsWithV
> (i *think* that With is more appropriate than For in this case).
Done.
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
string) map[string]bool {
On 2014/02/05 14:29:14, rog wrote:
> doc comment?
Done.
https:/
File ec2/vpc.go (right):
https:/
ec2/vpc.go:26: // AWS API version used for VPC-related calls.
On 2014/02/05 14:29:14, rog wrote:
> Do we actually want to export this?
Not really. Done.
https:/
ec2/vpc.go:53: // The smallest VPC you can create uses a /28 netmask (16
IP
On 2014/02/05 14:29:14, rog wrote:
> s/you can create/that can be created/
Done.
https:/
ec2/vpc.go:57: // The supported tenancy options for instances launched
into the
On 2014/02/05 14:29:14, rog wrote:
> // The instanceTenancy value holds the tenancy options for
> // instances launched into the VPC - either DefaultTenancy
> // or DedicatedTenancy.
> //
> // See http://
> ?
> I think the details of the tenancy semantics can reasonably
> be left to the amazon docs.
> We should also document that instanceTenancy may be empty
> or (my preference) just leave the description unchanged,
> and lose the special case for instanceTenancy == "".
I'll change...
Roger Peppe (rogpeppe) wrote : | # |
LGTM with a few further suggestions below.
https:/
File ec2/vpc.go (right):
https:/
ec2/vpc.go:57: // The supported tenancy options for instances launched
into the
On 2014/02/06 11:32:22, dimitern wrote:
> On 2014/02/05 14:29:14, rog wrote:
> > // The instanceTenancy value holds the tenancy options for
> > // instances launched into the VPC - either DefaultTenancy
> > // or DedicatedTenancy.
> > //
> > // See http://
> >
> > ?
> >
> > I think the details of the tenancy semantics can reasonably
> > be left to the amazon docs.
> >
> > We should also document that instanceTenancy may be empty
> > or (my preference) just leave the description unchanged,
> > and lose the special case for instanceTenancy == "".
> I'll change the comment as suggested, but will change the if below to
set it to
> DefaultTenancy when empty, because we can't pass it as "" to AWS.
If the user passes "" to the function, they deserve any error they get -
the function doesn't document that that's allowed (it actually
explicitly documents that instanceTenancy should be either
DefaultTenancy or DedicatedTenancy)
ec2 doesn't accept "furble" either, but we don't check that, and I don't
think there's a need to.
https:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
string) bool {
When I've done this function before, I've implemented it as:
func errorCode(err error) string {
if err, _ := err.(*ec2.Error); err != nil {
return err.Code
}
return ""
}
then the call looks like:
if errorCode(err) == "InvalidGroup.
I think that reads a little better, and it's more
flexible.
I'd actually suggest doing that, and then a subsequent
CL could change other tests to use it (I count about 5 places).
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
easier lookup.
s/ for easier lookup././
No particular need for the rationalisation.
Martin Packman (gz) wrote : | # |
LGTM.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:177: return makeParamsWithV
I'd stick "2011-12-15" in a const too. Nice adaptation to allow version
passing.
https:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
[]ec2.SecurityG
This is somewhat ick, but I like that there's enough logging going on so
failures that hit the timeout should be clear enough.
- 53. By Dimiter Naydenov
-
Changes after reivew
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:177: return makeParamsWithV
On 2014/02/06 16:25:07, gz wrote:
> I'd stick "2011-12-15" in a const too. Nice adaptation to allow
version passing.
Done.
https:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
[]ec2.SecurityG
On 2014/02/06 16:25:07, gz wrote:
> This is somewhat ick, but I like that there's enough logging going on
so
> failures that hit the timeout should be clear enough.
Yeah, it's more resilient than just trying to delete them and not
checking the error :)
https:/
ec2/ec2t_
string) bool {
On 2014/02/06 14:42:08, rog wrote:
> When I've done this function before, I've implemented it as:
> func errorCode(err error) string {
> if err, _ := err.(*ec2.Error); err != nil {
> return err.Code
> }
> return ""
> }
> then the call looks like:
> if errorCode(err) == "InvalidGroup.
> I think that reads a little better, and it's more
> flexible.
> I'd actually suggest doing that, and then a subsequent
> CL could change other tests to use it (I count about 5 places).
Done.
https:/
File ec2/ec2test/
https:/
ec2/ec2test/
easier lookup.
On 2014/02/06 14:42:08, rog wrote:
> s/ for easier lookup././
> No particular need for the rationalisation.
Done.
Gustavo Niemeyer (niemeyer) wrote : | # |
LGTM with trivial points.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:31: // VPC-related requests.
Moving the VPC API version here as well would make it more clear. The
constant isn't even used in vpc.go either way.
Also, is there a known reason not to use the new API version across the
board?
https:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
{
This can be a function (s is unnecessary).
https:/
File ec2/vpc.go (right):
https:/
ec2/vpc.go:8: // Written by Gustavo Niemeyer
<email address hidden>
Please drop this line. We should drop it from all the files.
https:/
ec2/vpc.go:19: PendingState = "pending"
The names of these constants do not look entirely sane. Although this is
inside the vpc.go file, this is living inside the ec2 package, which
means it's ec2.PendingState, and ec2.DefaultTenancy, which is a lot more
broad than "supported values for VPC state".
I suggest either fixing them to include the fact they are about VPC
(VPCPendingState, for example), or just dropping the constants until we
have a better idea.
https:/
ec2/vpc.go:24: DedicatedTenancy = "dedicated"
Same as above.
https:/
ec2/vpc.go:30: // VPC describes an Amazon Virtual Private Cloud (VPC).
s/an Amazon Virtual/a Virtual/
We might have other pre-existent instances of that in the documentation,
but we should eventually fix them as well to not refer to Amazon. First,
because every single documentation entry refers to something coming out
of Amazon, and second, because this will work with whoever implements
this API.
https:/
File ec2/vpc_test.go (right):
https:/
ec2/vpc_test.go:8: // Written by Gustavo Niemeyer
<email address hidden>
Please drop this line.
Dimiter Naydenov (dimitern) wrote : | # |
*** Submitted:
ec2: Add support for AWS VPCs
Added the following new API calls:
- CreateVPC
- DeleteVPC
- VPCs
(and the associated types/responses).
Most new code is in vpc.go and vpc_test.go.
These are needed in order to support VPC-related
operations on EC2, which will come in later follow-ups.
Implementation complexity in ec2test package for the
new calls is minimal.
Some changes were needed in order to support these
and the upcoming API calls. For the new ones, AWS API
version 2013-10-15 (latest) is used, while for the
existing calls use the previous API version, as before
(2011-12-15).
Added tests, updated test doubles and tested live on
EC2.
After running the EC2 live tests numerous times, I
realized some tests are leaking security groups, so
I added a deleteGroups() tests helper that retries
to ensure all groups are deleted. Also improved the
VPC tests to include retrying as well, so the live
tests are more stable and clean up after themselves.
R=
CC=
https:/
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:31: // VPC-related requests.
On 2014/02/12 13:23:49, niemeyer wrote:
> Moving the VPC API version here as well would make it more clear. The
constant
> isn't even used in vpc.go either way.
> Also, is there a known reason not to use the new API version across
the board?
I'll move it here. The reason for not using the newer API version is
that some things are changed, possibly in an incompatible way (responses
include different sub sections, more filters, certain arguments have
changed). I didn't want to make a massive change to all current API
calls to make them compatible with 2013-10-15. I could do it in a
follow-up, after the current chain have landed though.
https:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
{
On 2014/02/12 13:23:49, niemeyer wrote:
> This can be a function (s is unnecessary).
Done.
https:/
File ec2/vpc.go (right):
https:/
ec2/vpc.go:8: // Written by Gustavo Niemeyer
<email address hidden>
On 2014/02/12 13:23:49, niemeyer wrote:
> Please drop this line. We should drop it from all the files.
I'll remove it from all files then.
https:/
ec2/vpc.go:19: PendingState = "pending"
On 2014/02/12 13:23:49, niemeyer wrote:
> The names of these constants do not look entirely sane. Although this
is inside
> the vpc.go file, this is living inside the ec2 package, which means
it's
> ec2.PendingState, and ec2.DefaultTenancy, which is a lot more broad
than
> "supported values for VPC state".
> I suggest either fixing them to include the fact they are about VPC
> (VPCPendingState, for example), or just dropping the constants until
we have a
> better idea.
I'll drop ...
Dimiter Naydenov (dimitern) wrote : | # |
Post-submit approval.
Preview Diff
1 | === modified file 'ec2/ec2.go' |
2 | --- ec2/ec2.go 2013-10-04 17:05:49 +0000 |
3 | +++ ec2/ec2.go 2014-02-07 12:35:18 +0000 |
4 | @@ -27,6 +27,10 @@ |
5 | |
6 | const debug = false |
7 | |
8 | +// legacyAPIVersion is the AWS API version used for all but |
9 | +// VPC-related requests. |
10 | +const legacyAPIVersion = "2011-12-15" |
11 | + |
12 | // The EC2 type encapsulates operations with a specific EC2 region. |
13 | type EC2 struct { |
14 | aws.Auth |
15 | @@ -119,7 +123,6 @@ |
16 | var timeNow = time.Now |
17 | |
18 | func (ec2 *EC2) query(params map[string]string, resp interface{}) error { |
19 | - params["Version"] = "2011-12-15" |
20 | params["Timestamp"] = timeNow().In(time.UTC).Format(time.RFC3339) |
21 | endpoint, err := url.Parse(ec2.Region.EC2Endpoint) |
22 | if err != nil { |
23 | @@ -175,8 +178,17 @@ |
24 | } |
25 | |
26 | func makeParams(action string) map[string]string { |
27 | + return makeParamsWithVersion(action, legacyAPIVersion) |
28 | +} |
29 | + |
30 | +func makeParamsVPC(action string) map[string]string { |
31 | + return makeParamsWithVersion(action, vpcAPIVersion) |
32 | +} |
33 | + |
34 | +func makeParamsWithVersion(action, version string) map[string]string { |
35 | params := make(map[string]string) |
36 | params["Action"] = action |
37 | + params["Version"] = version |
38 | return params |
39 | } |
40 | |
41 | @@ -803,7 +815,7 @@ |
42 | return resp, nil |
43 | } |
44 | |
45 | -// ResourceTag represents key-value metadata used to classify and organize |
46 | +// Tag represents key-value metadata used to classify and organize |
47 | // EC2 instances. |
48 | // |
49 | // See http://goo.gl/bncl3 for more details |
50 | |
51 | === modified file 'ec2/ec2_test.go' |
52 | --- ec2/ec2_test.go 2013-10-04 17:05:49 +0000 |
53 | +++ ec2/ec2_test.go 2014-02-07 12:35:18 +0000 |
54 | @@ -96,14 +96,14 @@ |
55 | DisableAPITermination: true, |
56 | ShutdownBehavior: "terminate", |
57 | PrivateIPAddress: "10.0.0.25", |
58 | - BlockDeviceMappings: []ec2.BlockDeviceMapping{{ |
59 | - DeviceName: "device-name", |
60 | - VirtualName: "virtual-name", |
61 | - SnapshotId: "snapshot-id", |
62 | - VolumeType: "volume-type", |
63 | - VolumeSize: 10, |
64 | + BlockDeviceMappings: []ec2.BlockDeviceMapping{{ |
65 | + DeviceName: "device-name", |
66 | + VirtualName: "virtual-name", |
67 | + SnapshotId: "snapshot-id", |
68 | + VolumeType: "volume-type", |
69 | + VolumeSize: 10, |
70 | DeleteOnTermination: true, |
71 | - IOPS: 1000, |
72 | + IOPS: 1000, |
73 | }}, |
74 | } |
75 | resp, err := s.ec2.RunInstances(&options) |
76 | |
77 | === modified file 'ec2/ec2t_test.go' |
78 | --- ec2/ec2t_test.go 2014-02-07 12:35:18 +0000 |
79 | +++ ec2/ec2t_test.go 2014-02-07 12:35:18 +0000 |
80 | @@ -5,6 +5,7 @@ |
81 | "net" |
82 | "regexp" |
83 | "sort" |
84 | + "time" |
85 | |
86 | "launchpad.net/goamz/aws" |
87 | "launchpad.net/goamz/ec2" |
88 | @@ -111,7 +112,7 @@ |
89 | } |
90 | |
91 | // AmazonServerSuite runs the ec2test server tests against a live EC2 server. |
92 | -// It will only be activated if the -all flag is specified. |
93 | +// It will only be activated if the -amazon flag is specified. |
94 | type AmazonServerSuite struct { |
95 | srv AmazonServer |
96 | ServerTests |
97 | @@ -149,7 +150,7 @@ |
98 | func (s *ServerTests) makeTestGroup(c *C, name, descr string) ec2.SecurityGroup { |
99 | // Clean it up if a previous test left it around. |
100 | _, err := s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) |
101 | - if err != nil && err.(*ec2.Error).Code != "InvalidGroup.NotFound" { |
102 | + if err != nil && s.errorCode(err) != "InvalidGroup.NotFound" { |
103 | c.Fatalf("delete security group: %v", err) |
104 | } |
105 | |
106 | @@ -161,10 +162,8 @@ |
107 | |
108 | func (s *ServerTests) TestIPPerms(c *C) { |
109 | g0 := s.makeTestGroup(c, "goamz-test0", "ec2test group 0") |
110 | - defer s.ec2.DeleteSecurityGroup(g0) |
111 | - |
112 | g1 := s.makeTestGroup(c, "goamz-test1", "ec2test group 1") |
113 | - defer s.ec2.DeleteSecurityGroup(g1) |
114 | + defer s.deleteGroups(c, []ec2.SecurityGroup{g0, g1}) |
115 | |
116 | resp, err := s.ec2.SecurityGroups([]ec2.SecurityGroup{g0, g1}, nil) |
117 | c.Assert(err, IsNil) |
118 | @@ -183,7 +182,7 @@ |
119 | SourceIPs: []string{"z127.0.0.1/24"}, |
120 | }}) |
121 | c.Assert(err, NotNil) |
122 | - c.Check(err.(*ec2.Error).Code, Equals, "InvalidPermission.Malformed") |
123 | + c.Check(s.errorCode(err), Equals, "InvalidPermission.Malformed") |
124 | |
125 | // Check that AuthorizeSecurityGroup adds the correct authorizations. |
126 | _, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{ |
127 | @@ -230,7 +229,7 @@ |
128 | // Check that we can't delete g1 (because g0 is using it) |
129 | _, err = s.ec2.DeleteSecurityGroup(g1) |
130 | c.Assert(err, NotNil) |
131 | - c.Check(err.(*ec2.Error).Code, Equals, "InvalidGroup.InUse") |
132 | + c.Check(s.errorCode(err), Equals, "InvalidGroup.InUse") |
133 | |
134 | _, err = s.ec2.RevokeSecurityGroup(g0, []ec2.IPPerm{{ |
135 | Protocol: "tcp", |
136 | @@ -301,7 +300,7 @@ |
137 | c.Assert(err, IsNil) |
138 | |
139 | _, err = s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms[0:2]) |
140 | - c.Assert(err, ErrorMatches, `.*\(InvalidPermission.Duplicate\)`) |
141 | + c.Assert(s.errorCode(err), Equals, "InvalidPermission.Duplicate") |
142 | } |
143 | |
144 | type filterSpec struct { |
145 | @@ -313,12 +312,12 @@ |
146 | groupResp, err := s.ec2.CreateSecurityGroup(sessionName("testgroup1"), "testgroup one description") |
147 | c.Assert(err, IsNil) |
148 | group1 := groupResp.SecurityGroup |
149 | - defer s.ec2.DeleteSecurityGroup(group1) |
150 | |
151 | groupResp, err = s.ec2.CreateSecurityGroup(sessionName("testgroup2"), "testgroup two description") |
152 | c.Assert(err, IsNil) |
153 | group2 := groupResp.SecurityGroup |
154 | - defer s.ec2.DeleteSecurityGroup(group2) |
155 | + |
156 | + defer s.deleteGroups(c, []ec2.SecurityGroup{group1, group2}) |
157 | |
158 | insts := make([]*ec2.Instance, 3) |
159 | inst, err := s.ec2.RunInstances(&ec2.RunInstances{ |
160 | @@ -484,8 +483,8 @@ |
161 | c.Assert(err, IsNil) |
162 | g[i] = resp.SecurityGroup |
163 | c.Logf("group %d: %v", i, g[i]) |
164 | - defer s.ec2.DeleteSecurityGroup(g[i]) |
165 | } |
166 | + defer s.deleteGroups(c, g) |
167 | |
168 | perms := [][]ec2.IPPerm{ |
169 | {{ |
170 | @@ -621,3 +620,43 @@ |
171 | } |
172 | } |
173 | } |
174 | + |
175 | +// deleteGroups ensures the given groups are deleted, by retrying |
176 | +// until a timeout or all groups cannot be found anymore. |
177 | +// This should be used to make sure tests leave no groups around. |
178 | +func (s *ServerTests) deleteGroups(c *C, groups []ec2.SecurityGroup) { |
179 | + testAttempt := aws.AttemptStrategy{ |
180 | + Total: 2 * time.Minute, |
181 | + Delay: 5 * time.Second, |
182 | + } |
183 | + for a := testAttempt.Start(); a.Next(); { |
184 | + deleted := 0 |
185 | + c.Logf("deleting groups %v", groups) |
186 | + for _, group := range groups { |
187 | + _, err := s.ec2.DeleteSecurityGroup(group) |
188 | + if err == nil || s.errorCode(err) == "InvalidGroup.NotFound" { |
189 | + c.Logf("group %v deleted", group) |
190 | + deleted++ |
191 | + continue |
192 | + } |
193 | + if err != nil { |
194 | + c.Logf("retrying; DeleteSecurityGroup returned: %v", err) |
195 | + } |
196 | + } |
197 | + if deleted == len(groups) { |
198 | + c.Logf("all groups deleted") |
199 | + return |
200 | + } |
201 | + } |
202 | + c.Fatalf("timeout while waiting %v groups to get deleted!", groups) |
203 | +} |
204 | + |
205 | +// errorCode returns the code of the given error, assuming it's not |
206 | +// nil and it's an instance of *ec2.Error. It returns an empty string |
207 | +// otherwise. |
208 | +func (s *ServerTests) errorCode(err error) string { |
209 | + if err, _ := err.(*ec2.Error); err != nil { |
210 | + return err.Code |
211 | + } |
212 | + return "" |
213 | +} |
214 | |
215 | === modified file 'ec2/ec2test/server.go' |
216 | --- ec2/ec2test/server.go 2013-12-18 14:22:35 +0000 |
217 | +++ ec2/ec2test/server.go 2014-02-07 12:35:18 +0000 |
218 | @@ -50,10 +50,13 @@ |
219 | instances map[string]*Instance // id -> instance |
220 | reservations map[string]*reservation // id -> reservation |
221 | groups map[string]*securityGroup // id -> group |
222 | + vpcs map[string]*vpc // id -> vpc |
223 | maxId counter |
224 | reqId counter |
225 | reservationId counter |
226 | groupId counter |
227 | + vpcId counter |
228 | + dhcpOptsId counter |
229 | initialInstanceState ec2.InstanceState |
230 | } |
231 | |
232 | @@ -191,6 +194,24 @@ |
233 | return |
234 | } |
235 | |
236 | +type vpc struct { |
237 | + ec2.VPC |
238 | +} |
239 | + |
240 | +func (v *vpc) matchAttr(attr, value string) (ok bool, err error) { |
241 | + switch attr { |
242 | + case "cidr": |
243 | + return v.CIDRBlock == value, nil |
244 | + case "state": |
245 | + return v.State == value, nil |
246 | + case "vpc-id": |
247 | + return v.Id == value, nil |
248 | + case "tag", "tag-key", "tag-value", "dhcp-options-id", "isDefault": |
249 | + return false, fmt.Errorf("%q filter is not implemented", attr) |
250 | + } |
251 | + return false, fmt.Errorf("unknown attribute %q", attr) |
252 | +} |
253 | + |
254 | var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) interface{}{ |
255 | "RunInstances": (*Server).runInstances, |
256 | "TerminateInstances": (*Server).terminateInstances, |
257 | @@ -200,6 +221,9 @@ |
258 | "DeleteSecurityGroup": (*Server).deleteSecurityGroup, |
259 | "AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress, |
260 | "RevokeSecurityGroupIngress": (*Server).revokeSecurityGroupIngress, |
261 | + "CreateVpc": (*Server).createVpc, |
262 | + "DeleteVpc": (*Server).deleteVpc, |
263 | + "DescribeVpcs": (*Server).describeVpcs, |
264 | } |
265 | |
266 | const ownerId = "9876" |
267 | @@ -220,6 +244,7 @@ |
268 | srv := &Server{ |
269 | instances: make(map[string]*Instance), |
270 | groups: make(map[string]*securityGroup), |
271 | + vpcs: make(map[string]*vpc), |
272 | reservations: make(map[string]*reservation), |
273 | initialInstanceState: Pending, |
274 | } |
275 | @@ -798,9 +823,11 @@ |
276 | } |
277 | } |
278 | |
279 | -var secGroupPat = regexp.MustCompile(`^sg-[a-z0-9]+$`) |
280 | -var ipPat = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$`) |
281 | -var ownerIdPat = regexp.MustCompile(`^[0-9]+$`) |
282 | +var ( |
283 | + secGroupPat = regexp.MustCompile(`^sg-[a-z0-9]+$`) |
284 | + cidrIpPat = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/([0-9]+)$`) |
285 | + ownerIdPat = regexp.MustCompile(`^[0-9]+$`) |
286 | +) |
287 | |
288 | // parsePerms returns a slice of permKey values extracted |
289 | // from the permission fields in req. |
290 | @@ -884,7 +911,7 @@ |
291 | } |
292 | switch rest { |
293 | case "CidrIp": |
294 | - if !ipPat.MatchString(val) { |
295 | + if !cidrIpPat.MatchString(val) { |
296 | fatalf(400, "InvalidPermission.Malformed", "Invalid IP range: %q", val) |
297 | } |
298 | ec2p.SourceIPs = append(ec2p.SourceIPs, val) |
299 | @@ -978,6 +1005,61 @@ |
300 | } |
301 | } |
302 | |
303 | +func (srv *Server) createVpc(w http.ResponseWriter, req *http.Request, reqId string) interface{} { |
304 | + cidrBlock := parseCidr(req.Form.Get("CidrBlock")) |
305 | + tenancy := req.Form.Get("InstanceTenancy") |
306 | + if tenancy == "" { |
307 | + tenancy = ec2.DefaultTenancy |
308 | + } |
309 | + |
310 | + srv.mu.Lock() |
311 | + defer srv.mu.Unlock() |
312 | + v := &vpc{ec2.VPC{ |
313 | + Id: fmt.Sprintf("vpc-%d", srv.vpcId.next()), |
314 | + State: ec2.AvailableState, |
315 | + CIDRBlock: cidrBlock, |
316 | + DHCPOptionsId: fmt.Sprintf("dopt-%d", srv.dhcpOptsId.next()), |
317 | + InstanceTenancy: tenancy, |
318 | + }} |
319 | + srv.vpcs[v.Id] = v |
320 | + r := &ec2.CreateVPCResp{ |
321 | + RequestId: reqId, |
322 | + VPC: v.VPC, |
323 | + } |
324 | + return r |
325 | +} |
326 | + |
327 | +func (srv *Server) deleteVpc(w http.ResponseWriter, req *http.Request, reqId string) interface{} { |
328 | + v := srv.vpc(req.Form.Get("VpcId")) |
329 | + srv.mu.Lock() |
330 | + defer srv.mu.Unlock() |
331 | + |
332 | + delete(srv.vpcs, v.Id) |
333 | + return &ec2.SimpleResp{ |
334 | + XMLName: xml.Name{"", "DeleteVpcResponse"}, |
335 | + RequestId: reqId, |
336 | + } |
337 | +} |
338 | + |
339 | +func (srv *Server) describeVpcs(w http.ResponseWriter, req *http.Request, reqId string) interface{} { |
340 | + srv.mu.Lock() |
341 | + defer srv.mu.Unlock() |
342 | + |
343 | + idMap := collectIds(req.Form, "VpcId.") |
344 | + f := newFilter(req.Form) |
345 | + var resp ec2.VPCsResp |
346 | + resp.RequestId = reqId |
347 | + for _, v := range srv.vpcs { |
348 | + ok, err := f.ok(v) |
349 | + if ok && (len(idMap) == 0 || idMap[v.Id]) { |
350 | + resp.VPCs = append(resp.VPCs, v.VPC) |
351 | + } else if err != nil { |
352 | + fatalf(400, "InvalidParameterValue", "describe VPCs: %v", err) |
353 | + } |
354 | + } |
355 | + return &resp |
356 | +} |
357 | + |
358 | func (r *reservation) hasRunningMachine() bool { |
359 | for _, inst := range r.instances { |
360 | if inst.state.Code != ShuttingDown.Code && inst.state.Code != Terminated.Code { |
361 | @@ -987,6 +1069,41 @@ |
362 | return false |
363 | } |
364 | |
365 | +func parseCidr(val string) string { |
366 | + if val == "" { |
367 | + fatalf(400, "MissingParameter", "missing cidrBlock") |
368 | + } |
369 | + if _, _, err := net.ParseCIDR(val); err != nil { |
370 | + fatalf(400, "InvalidParameterValue", "bad CIDR %q: %v", val, err) |
371 | + } |
372 | + return val |
373 | +} |
374 | + |
375 | +func (srv *Server) vpc(id string) *vpc { |
376 | + if id == "" { |
377 | + fatalf(400, "MissingParameter", "missing vpcId") |
378 | + } |
379 | + srv.mu.Lock() |
380 | + defer srv.mu.Unlock() |
381 | + v, found := srv.vpcs[id] |
382 | + if !found { |
383 | + fatalf(400, "InvalidVpcID.NotFound", "VPC %s not found", id) |
384 | + } |
385 | + return v |
386 | +} |
387 | + |
388 | +// collectIds takes all values with the given prefix from form and |
389 | +// returns a map with the ids as keys. |
390 | +func collectIds(form url.Values, prefix string) map[string]bool { |
391 | + idMap := make(map[string]bool) |
392 | + for name, vals := range form { |
393 | + if strings.HasPrefix(name, prefix) { |
394 | + idMap[vals[0]] = true |
395 | + } |
396 | + } |
397 | + return idMap |
398 | +} |
399 | + |
400 | type counter int |
401 | |
402 | func (c *counter) next() (i int) { |
403 | |
404 | === modified file 'ec2/responses_test.go' |
405 | --- ec2/responses_test.go 2013-10-02 19:10:36 +0000 |
406 | +++ ec2/responses_test.go 2014-02-07 12:35:18 +0000 |
407 | @@ -584,7 +584,48 @@ |
408 | // http://goo.gl/baoUf |
409 | var RebootInstancesExample = ` |
410 | <RebootInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> |
411 | - <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> |
412 | + <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> |
413 | <return>true</return> |
414 | </RebootInstancesResponse> |
415 | ` |
416 | + |
417 | +// http://goo.gl/nkwjv |
418 | +var CreateVpcExample = ` |
419 | +<CreateVpcResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> |
420 | + <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId> |
421 | + <vpc> |
422 | + <vpcId>vpc-1a2b3c4d</vpcId> |
423 | + <state>pending</state> |
424 | + <cidrBlock>10.0.0.0/16</cidrBlock> |
425 | + <dhcpOptionsId>dopt-1a2b3c4d2</dhcpOptionsId> |
426 | + <instanceTenancy>default</instanceTenancy> |
427 | + <tagSet/> |
428 | + </vpc> |
429 | +</CreateVpcResponse> |
430 | +` |
431 | + |
432 | +// http://goo.gl/bcxtbf |
433 | +var DeleteVpcExample = ` |
434 | +<DeleteVpcResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> |
435 | + <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId> |
436 | + <return>true</return> |
437 | +</DeleteVpcResponse> |
438 | +` |
439 | + |
440 | +// http://goo.gl/Y5kHqG |
441 | +var DescribeVpcsExample = ` |
442 | +<DescribeVpcsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/"> |
443 | + <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId> |
444 | + <vpcSet> |
445 | + <item> |
446 | + <vpcId>vpc-1a2b3c4d</vpcId> |
447 | + <state>available</state> |
448 | + <cidrBlock>10.0.0.0/23</cidrBlock> |
449 | + <dhcpOptionsId>dopt-7a8b9c2d</dhcpOptionsId> |
450 | + <instanceTenancy>default</instanceTenancy> |
451 | + <isDefault>false</isDefault> |
452 | + <tagSet/> |
453 | + </item> |
454 | + </vpcSet> |
455 | +</DescribeVpcsResponse> |
456 | +` |
457 | |
458 | === added file 'ec2/vpc.go' |
459 | --- ec2/vpc.go 1970-01-01 00:00:00 +0000 |
460 | +++ ec2/vpc.go 2014-02-07 12:35:18 +0000 |
461 | @@ -0,0 +1,120 @@ |
462 | +// |
463 | +// goamz - Go packages to interact with the Amazon Web Services. |
464 | +// |
465 | +// https://wiki.ubuntu.com/goamz |
466 | +// |
467 | +// Copyright (c) 2014 Canonical Ltd. |
468 | +// |
469 | +// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com> |
470 | +// |
471 | + |
472 | +package ec2 |
473 | + |
474 | +import ( |
475 | + "strconv" |
476 | +) |
477 | + |
478 | +const ( |
479 | + // Supported values for VPC state. |
480 | + PendingState = "pending" |
481 | + AvailableState = "available" |
482 | + |
483 | + // Supported values for InstanceTenancy. |
484 | + DefaultTenancy = "default" |
485 | + DedicatedTenancy = "dedicated" |
486 | + |
487 | + // AWS API version used for VPC-related calls. |
488 | + vpcAPIVersion = "2013-10-15" |
489 | +) |
490 | + |
491 | +// VPC describes an Amazon Virtual Private Cloud (VPC). |
492 | +// |
493 | +// See http://goo.gl/Uy6ZLL for more details. |
494 | +type VPC struct { |
495 | + Id string `xml:"vpcId"` |
496 | + State string `xml:"state"` |
497 | + CIDRBlock string `xml:"cidrBlock"` |
498 | + DHCPOptionsId string `xml:"dhcpOptionsId"` |
499 | + Tags []Tag `xml:"tagSet>item"` |
500 | + InstanceTenancy string `xml:"instanceTenancy"` |
501 | + IsDefault bool `xml:"isDefault"` |
502 | +} |
503 | + |
504 | +// CreateVPCResp is the response to a CreateVPC request. |
505 | +// |
506 | +// See http://goo.gl/nkwjvN for more details. |
507 | +type CreateVPCResp struct { |
508 | + RequestId string `xml:"requestId"` |
509 | + VPC VPC `xml:"vpc"` |
510 | +} |
511 | + |
512 | +// CreateVPC creates a VPC with the specified CIDR block. |
513 | +// |
514 | +// The smallest VPC that can be created uses a /28 netmask (16 IP |
515 | +// addresses), and the largest uses a /16 netmask (65,536 IP |
516 | +// addresses). |
517 | +// |
518 | +// The instanceTenancy value holds the tenancy options for instances |
519 | +// launched into the VPC - either DefaultTenancy or DedicatedTenancy. |
520 | +// |
521 | +// See http://goo.gl/nkwjvN for more details. |
522 | +func (ec2 *EC2) CreateVPC(CIDRBlock, instanceTenancy string) (resp *CreateVPCResp, err error) { |
523 | + params := makeParamsVPC("CreateVpc") |
524 | + params["CidrBlock"] = CIDRBlock |
525 | + if instanceTenancy == "" { |
526 | + instanceTenancy = DefaultTenancy |
527 | + } |
528 | + params["InstanceTenancy"] = instanceTenancy |
529 | + resp = &CreateVPCResp{} |
530 | + err = ec2.query(params, resp) |
531 | + if err != nil { |
532 | + return nil, err |
533 | + } |
534 | + return resp, nil |
535 | +} |
536 | + |
537 | +// DeleteVPC deletes the VPC with the specified id. All gateways and |
538 | +// resources that are associated with the VPC must have been |
539 | +// previously deleted, including instances running in the VPC, and |
540 | +// non-default security groups and route tables associated with the |
541 | +// VPC. |
542 | +// |
543 | +// See http://goo.gl/bcxtbf for more details. |
544 | +func (ec2 *EC2) DeleteVPC(id string) (resp *SimpleResp, err error) { |
545 | + params := makeParamsVPC("DeleteVpc") |
546 | + params["VpcId"] = id |
547 | + resp = &SimpleResp{} |
548 | + err = ec2.query(params, resp) |
549 | + if err != nil { |
550 | + return nil, err |
551 | + } |
552 | + return resp, nil |
553 | +} |
554 | + |
555 | +// VPCsResp is the response to a VPCs request. |
556 | +// |
557 | +// See http://goo.gl/Y5kHqG for more details. |
558 | +type VPCsResp struct { |
559 | + RequestId string `xml:"requestId"` |
560 | + VPCs []VPC `xml:"vpcSet>item"` |
561 | +} |
562 | + |
563 | +// VPCs describes one or more VPCs. Both parameters are optional, and |
564 | +// if specified will limit the returned VPCs to the matching ids or |
565 | +// filtering rules. |
566 | +// |
567 | +// See http://goo.gl/Y5kHqG for more details. |
568 | +func (ec2 *EC2) VPCs(ids []string, filter *Filter) (resp *VPCsResp, err error) { |
569 | + params := makeParamsVPC("DescribeVpcs") |
570 | + for i, id := range ids { |
571 | + params["VpcId."+strconv.Itoa(i+1)] = id |
572 | + } |
573 | + filter.addParams(params) |
574 | + |
575 | + resp = &VPCsResp{} |
576 | + err = ec2.query(params, resp) |
577 | + if err != nil { |
578 | + return nil, err |
579 | + } |
580 | + return resp, nil |
581 | +} |
582 | |
583 | === added file 'ec2/vpc_test.go' |
584 | --- ec2/vpc_test.go 1970-01-01 00:00:00 +0000 |
585 | +++ ec2/vpc_test.go 2014-02-07 12:35:18 +0000 |
586 | @@ -0,0 +1,172 @@ |
587 | +// |
588 | +// goamz - Go packages to interact with the Amazon Web Services. |
589 | +// |
590 | +// https://wiki.ubuntu.com/goamz |
591 | +// |
592 | +// Copyright (c) 2014 Canonical Ltd. |
593 | +// |
594 | +// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com> |
595 | +// |
596 | + |
597 | +package ec2_test |
598 | + |
599 | +import ( |
600 | + "launchpad.net/goamz/aws" |
601 | + "launchpad.net/goamz/ec2" |
602 | + . "launchpad.net/gocheck" |
603 | + "time" |
604 | +) |
605 | + |
606 | +// VPC tests with example responses |
607 | + |
608 | +func (s *S) TestCreateVPCExample(c *C) { |
609 | + testServer.Response(200, nil, CreateVpcExample) |
610 | + |
611 | + resp, err := s.ec2.CreateVPC("10.0.0.0/16", ec2.DefaultTenancy) |
612 | + req := testServer.WaitRequest() |
613 | + |
614 | + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateVpc"}) |
615 | + c.Assert(req.Form["CidrBlock"], DeepEquals, []string{"10.0.0.0/16"}) |
616 | + c.Assert(req.Form["InstanceTenancy"], DeepEquals, []string{ec2.DefaultTenancy}) |
617 | + |
618 | + c.Assert(err, IsNil) |
619 | + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") |
620 | + vpc := resp.VPC |
621 | + c.Check(vpc.Id, Equals, "vpc-1a2b3c4d") |
622 | + c.Check(vpc.State, Equals, ec2.PendingState) |
623 | + c.Check(vpc.CIDRBlock, Equals, "10.0.0.0/16") |
624 | + c.Check(vpc.DHCPOptionsId, Equals, "dopt-1a2b3c4d2") |
625 | + c.Check(vpc.Tags, HasLen, 0) |
626 | + c.Check(vpc.IsDefault, Equals, false) |
627 | + c.Check(vpc.InstanceTenancy, Equals, ec2.DefaultTenancy) |
628 | +} |
629 | + |
630 | +func (s *S) TestDeleteVPCExample(c *C) { |
631 | + testServer.Response(200, nil, DeleteVpcExample) |
632 | + |
633 | + resp, err := s.ec2.DeleteVPC("vpc-id") |
634 | + req := testServer.WaitRequest() |
635 | + |
636 | + c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteVpc"}) |
637 | + c.Assert(req.Form["VpcId"], DeepEquals, []string{"vpc-id"}) |
638 | + |
639 | + c.Assert(err, IsNil) |
640 | + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") |
641 | +} |
642 | + |
643 | +func (s *S) TestVPCsExample(c *C) { |
644 | + testServer.Response(200, nil, DescribeVpcsExample) |
645 | + |
646 | + resp, err := s.ec2.VPCs([]string{"vpc-1a2b3c4d"}, nil) |
647 | + req := testServer.WaitRequest() |
648 | + |
649 | + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeVpcs"}) |
650 | + c.Assert(req.Form["VpcId.1"], DeepEquals, []string{"vpc-1a2b3c4d"}) |
651 | + |
652 | + c.Assert(err, IsNil) |
653 | + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") |
654 | + c.Assert(resp.VPCs, HasLen, 1) |
655 | + vpc := resp.VPCs[0] |
656 | + c.Check(vpc.Id, Equals, "vpc-1a2b3c4d") |
657 | + c.Check(vpc.State, Equals, ec2.AvailableState) |
658 | + c.Check(vpc.CIDRBlock, Equals, "10.0.0.0/23") |
659 | + c.Check(vpc.DHCPOptionsId, Equals, "dopt-7a8b9c2d") |
660 | + c.Check(vpc.Tags, HasLen, 0) |
661 | + c.Check(vpc.IsDefault, Equals, false) |
662 | + c.Check(vpc.InstanceTenancy, Equals, ec2.DefaultTenancy) |
663 | +} |
664 | + |
665 | +// VPC tests to run against either a local test server or live on EC2. |
666 | + |
667 | +func (s *ServerTests) TestVPCs(c *C) { |
668 | + resp1, err := s.ec2.CreateVPC("10.0.0.0/16", "") |
669 | + c.Assert(err, IsNil) |
670 | + assertVPC(c, resp1.VPC, "", "10.0.0.0/16") |
671 | + id1 := resp1.VPC.Id |
672 | + |
673 | + resp2, err := s.ec2.CreateVPC("10.1.0.0/16", ec2.DefaultTenancy) |
674 | + c.Assert(err, IsNil) |
675 | + assertVPC(c, resp2.VPC, "", "10.1.0.0/16") |
676 | + id2 := resp2.VPC.Id |
677 | + |
678 | + // We only check for the VPCs we just created, because the user |
679 | + // might have others in his account (when testing against the EC2 |
680 | + // servers). In some cases it takes a short while until both VPCs |
681 | + // are created, so we need to retry a few times to make sure. |
682 | + var list *ec2.VPCsResp |
683 | + done := false |
684 | + testAttempt := aws.AttemptStrategy{ |
685 | + Total: 2 * time.Minute, |
686 | + Delay: 5 * time.Second, |
687 | + } |
688 | + for a := testAttempt.Start(); a.Next(); { |
689 | + c.Logf("waiting for %v to be created", []string{id1, id2}) |
690 | + list, err = s.ec2.VPCs(nil, nil) |
691 | + if err != nil { |
692 | + c.Logf("retrying; VPCs returned: %v", err) |
693 | + continue |
694 | + } |
695 | + found := 0 |
696 | + for _, vpc := range list.VPCs { |
697 | + c.Logf("found VPC %v", vpc) |
698 | + switch vpc.Id { |
699 | + case id1: |
700 | + assertVPC(c, vpc, id1, resp1.VPC.CIDRBlock) |
701 | + found++ |
702 | + case id2: |
703 | + assertVPC(c, vpc, id2, resp2.VPC.CIDRBlock) |
704 | + found++ |
705 | + } |
706 | + if found == 2 { |
707 | + done = true |
708 | + break |
709 | + } |
710 | + } |
711 | + if done { |
712 | + c.Logf("all VPCs were created") |
713 | + break |
714 | + } |
715 | + } |
716 | + if !done { |
717 | + c.Fatalf("timeout while waiting for VPCs %v", []string{id1, id2}) |
718 | + } |
719 | + |
720 | + list, err = s.ec2.VPCs([]string{id1}, nil) |
721 | + c.Assert(err, IsNil) |
722 | + c.Assert(list.VPCs, HasLen, 1) |
723 | + assertVPC(c, list.VPCs[0], id1, resp1.VPC.CIDRBlock) |
724 | + |
725 | + f := ec2.NewFilter() |
726 | + f.Add("cidr", resp2.VPC.CIDRBlock) |
727 | + list, err = s.ec2.VPCs(nil, f) |
728 | + c.Assert(err, IsNil) |
729 | + c.Assert(list.VPCs, HasLen, 1) |
730 | + assertVPC(c, list.VPCs[0], id2, resp2.VPC.CIDRBlock) |
731 | + |
732 | + _, err = s.ec2.DeleteVPC(id1) |
733 | + c.Assert(err, IsNil) |
734 | + _, err = s.ec2.DeleteVPC(id2) |
735 | + c.Assert(err, IsNil) |
736 | +} |
737 | + |
738 | +func assertVPC(c *C, obtained ec2.VPC, expectId, expectCidr string) { |
739 | + if expectId != "" { |
740 | + c.Check(obtained.Id, Equals, expectId) |
741 | + } else { |
742 | + c.Check(obtained.Id, Matches, `^vpc-[0-9a-f]+$`) |
743 | + } |
744 | + c.Check(obtained.State, Matches, "("+ec2.AvailableState+"|"+ec2.PendingState+")") |
745 | + if expectCidr != "" { |
746 | + c.Check(obtained.CIDRBlock, Equals, expectCidr) |
747 | + } else { |
748 | + c.Check(obtained.CIDRBlock, Matches, `^\d+\.\d+\.\d+\.\d+/\d+$`) |
749 | + } |
750 | + c.Check(obtained.DHCPOptionsId, Matches, `^dopt-[0-9a-f]+$`) |
751 | + c.Check(obtained.IsDefault, Equals, false) |
752 | + c.Check(obtained.Tags, HasLen, 0) |
753 | + c.Check( |
754 | + obtained.InstanceTenancy, |
755 | + Matches, |
756 | + "("+ec2.DefaultTenancy+"|"+ec2.DedicatedTenancy+")", |
757 | + ) |
758 | +} |
Reviewers: mp+204514_ code.launchpad. net,
Message:
Please take a look.
Description:
ec2: Add support for AWS VPCs
Added the following new API calls:
- CreateVpc
- DeleteVpc
- DescribeVpcs
(and the associated types/responses).
These are needed in order to support VPC-related
operations on EC2, which will come in later follow-ups.
Added tests, updated test doubles and tested live on EC2.
https:/ /code.launchpad .net/~dimitern/ goamz/vpc- api-calls/ +merge/ 204514
Requires: /code.launchpad .net/~dimitern/ goamz/lp- 1275406- lpcalserversuit e-testinstancei nfo/+merge/ 204469
https:/
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/49930045/
Affected files (+404, -13 lines): server. go test.go
A [revision details]
M ec2/ec2.go
M ec2/ec2_test.go
M ec2/ec2t_test.go
M ec2/ec2test/
M ec2/responses_