Merge lp:~dimitern/goamz/vpc-api-calls into lp:goamz

Proposed by Dimiter Naydenov
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
Reviewer Review Type Date Requested Status
Dimiter Naydenov (community) Approve
Review via email: mp+204514@code.launchpad.net

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.

https://codereview.appspot.com/49930045/

To post a comment you must log in.
Revision history for this message
Dimiter Naydenov (dimitern) wrote :

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:
https://code.launchpad.net/~dimitern/goamz/lp-1275406-lpcalserversuite-testinstanceinfo/+merge/204469

(do not edit description out of merge proposal)

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

Affected files (+404, -13 lines):
   A [revision details]
   M ec2/ec2.go
   M ec2/ec2_test.go
   M ec2/ec2t_test.go
   M ec2/ec2test/server.go
   M ec2/responses_test.go

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

LGTM, a couple of comments.

https://codereview.appspot.com/49930045/diff/1/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/ec2.go#newcode925
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://codereview.appspot.com/49930045/diff/1/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/ec2t_test.go#newcode631
ec2/ec2t_test.go:631: resp2, err := s.ec2.CreateVpc("1.2.0.0/18")
Is this a safe cidr to be using?

https://codereview.appspot.com/49930045/diff/1/ec2/ec2t_test.go#newcode659
ec2/ec2t_test.go:659: _, err = s.ec2.DeleteVpc(id1)
This feels race/eventual consistency prone, perhaps not an issue for
vpcs or delete calls specifically?

https://codereview.appspot.com/49930045/diff/1/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/ec2test/server.go#newcode217
ec2/ec2test/server.go:217: // Not done: tag, tag-key, tag-value.
Do we want an error not implemented type of thing rather than just not
matching?

https://codereview.appspot.com/49930045/diff/1/ec2/responses_test.go
File ec2/responses_test.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/responses_test.go#newcode594
ec2/responses_test.go:594: <CreateVpcResponse
xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
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.

https://codereview.appspot.com/49930045/

lp:~dimitern/goamz/vpc-api-calls updated
48. By Dimiter Naydenov

Changes after review

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Please take a look.

https://codereview.appspot.com/49930045/diff/1/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/ec2.go#newcode925
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://codereview.appspot.com/49930045/diff/1/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/ec2t_test.go#newcode631
ec2/ec2t_test.go:631: resp2, err := s.ec2.CreateVpc("1.2.0.0/18")
On 2014/02/03 21:40:54, gz wrote:
> Is this a safe cidr to be using?

Yes, it passes even live tests :)

https://codereview.appspot.com/49930045/diff/1/ec2/ec2t_test.go#newcode659
ec2/ec2t_test.go:659: _, err = s.ec2.DeleteVpc(id1)
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://codereview.appspot.com/49930045/diff/1/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/ec2test/server.go#newcode217
ec2/ec2test/server.go:217: // Not done: tag, tag-key, tag-value.
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://codereview.appspot.com/49930045/diff/1/ec2/responses_test.go
File ec2/responses_test.go (right):

https://codereview.appspot.com/49930045/diff/1/ec2/responses_test.go#newcode594
ec2/responses_test.go:594: <CreateVpcResponse
xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
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).

https://codereview.appspot.com/49930045/

lp:~dimitern/goamz/vpc-api-calls updated
49. By Dimiter Naydenov

Split VPC calls/tests in a separate file

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
lp:~dimitern/goamz/vpc-api-calls updated
50. By Dimiter Naydenov

Simplified & refactored a bit; naming fixed, API version updated

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Revision history for this message
Roger Peppe (rogpeppe) wrote :
Download full text (3.5 KiB)

LGTM with some suggestions, mostly documentation related.

https://codereview.appspot.com/49930045/diff/40001/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/40001/ec2/ec2test/server.go#newcode212
ec2/ec2test/server.go:212: return false, fmt.Errorf("tag, tag-key, and
tag-value filters not implemented")
fmt.Errorf("%s filter not implemented", attr)

?

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go#newcode122
ec2/ec2.go:122: if params["Version"] == "" {
Given that makeParams now always creates the Version
entry, why bother with this?

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go#newcode183
ec2/ec2.go:183: func makeParamsVPC(action string) map[string]string {
I'm not sure this really justifies itself over
just calling makeParamsForVersion(action, VPCAPIVersion)
when necessary.

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go#newcode187
ec2/ec2.go:187: func makeParamsForVersion(version, action string)
map[string]string {
makeParamsWithVersion(action, version string) map[string]string
?

then makeParamsWithVersion is just makeParams with an extra argument.
(i *think* that With is more appropriate than For in this case).

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2test/server.go#newcode1095
ec2/ec2test/server.go:1095: func collectIds(form url.Values, prefix
string) map[string]bool {
doc comment?

https://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go
File ec2/vpc.go (right):

https://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode26
ec2/vpc.go:26: // AWS API version used for VPC-related calls.
Do we actually want to export this?

https://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode53
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://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode57
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://goo.gl/nkwjvN for more details.

?

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://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode79
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...

Read more...

lp:~dimitern/goamz/vpc-api-calls updated
51. By Dimiter Naydenov

Changes after review; tests improvements

52. By Dimiter Naydenov

Changed CIDRs of VPC for easier tracking of leaky tests

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Download full text (4.2 KiB)

Please take a look.

https://codereview.appspot.com/49930045/diff/40001/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/40001/ec2/ec2test/server.go#newcode212
ec2/ec2test/server.go:212: return false, fmt.Errorf("tag, tag-key, and
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://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go#newcode122
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://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go#newcode183
ec2/ec2.go:183: func makeParamsVPC(action string) map[string]string {
On 2014/02/05 14:29:14, rog wrote:
> I'm not sure this really justifies itself over
> just calling makeParamsForVersion(action, VPCAPIVersion)
> when necessary.

I'd prefer to leave it, because it's shorter to write - it's just a
helper anyway.

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2.go#newcode187
ec2/ec2.go:187: func makeParamsForVersion(version, action string)
map[string]string {
On 2014/02/05 14:29:14, rog wrote:
> makeParamsWithVersion(action, version string) map[string]string
> ?

> then makeParamsWithVersion is just makeParams with an extra argument.
> (i *think* that With is more appropriate than For in this case).

Done.

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/80001/ec2/ec2test/server.go#newcode1095
ec2/ec2test/server.go:1095: func collectIds(form url.Values, prefix
string) map[string]bool {
On 2014/02/05 14:29:14, rog wrote:
> doc comment?

Done.

https://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go
File ec2/vpc.go (right):

https://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode26
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://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode53
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://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode57
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://goo.gl/nkwjvN for more details.

> ?

> 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...

Read more...

Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM with a few further suggestions below.

https://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go
File ec2/vpc.go (right):

https://codereview.appspot.com/49930045/diff/80001/ec2/vpc.go#newcode57
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://goo.gl/nkwjvN for more details.
> >
> > ?
> >
> > 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://codereview.appspot.com/49930045/diff/100001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2t_test.go#newcode656
ec2/ec2t_test.go:656: func (s *ServerTests) errorCode(err error, code
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.NotFound" {

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://codereview.appspot.com/49930045/diff/100001/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2test/server.go#newcode1096
ec2/ec2test/server.go:1096: // returns a map with the ids as keys for
easier lookup.
s/ for easier lookup././

No particular need for the rationalisation.

https://codereview.appspot.com/49930045/

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

LGTM.

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2.go#newcode177
ec2/ec2.go:177: return makeParamsWithVersion(action, "2011-12-15")
I'd stick "2011-12-15" in a const too. Nice adaptation to allow version
passing.

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2t_test.go#newcode627
ec2/ec2t_test.go:627: func (s *ServerTests) deleteGroups(c *C, groups
[]ec2.SecurityGroup) {
This is somewhat ick, but I like that there's enough logging going on so
failures that hit the timeout should be clear enough.

https://codereview.appspot.com/49930045/

lp:~dimitern/goamz/vpc-api-calls updated
53. By Dimiter Naydenov

Changes after reivew

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Please take a look.

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2.go#newcode177
ec2/ec2.go:177: return makeParamsWithVersion(action, "2011-12-15")
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://codereview.appspot.com/49930045/diff/100001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2t_test.go#newcode627
ec2/ec2t_test.go:627: func (s *ServerTests) deleteGroups(c *C, groups
[]ec2.SecurityGroup) {
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://codereview.appspot.com/49930045/diff/100001/ec2/ec2t_test.go#newcode656
ec2/ec2t_test.go:656: func (s *ServerTests) errorCode(err error, code
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.NotFound" {

> 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://codereview.appspot.com/49930045/diff/100001/ec2/ec2test/server.go
File ec2/ec2test/server.go (right):

https://codereview.appspot.com/49930045/diff/100001/ec2/ec2test/server.go#newcode1096
ec2/ec2test/server.go:1096: // returns a map with the ids as keys for
easier lookup.
On 2014/02/06 14:42:08, rog wrote:
> s/ for easier lookup././

> No particular need for the rationalisation.

Done.

https://codereview.appspot.com/49930045/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

LGTM with trivial points.

https://codereview.appspot.com/49930045/diff/120001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/120001/ec2/ec2.go#newcode31
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://codereview.appspot.com/49930045/diff/120001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/49930045/diff/120001/ec2/ec2t_test.go#newcode657
ec2/ec2t_test.go:657: func (s *ServerTests) errorCode(err error) string
{
This can be a function (s is unnecessary).

https://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go
File ec2/vpc.go (right):

https://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go#newcode8
ec2/vpc.go:8: // Written by Gustavo Niemeyer
<email address hidden>
Please drop this line. We should drop it from all the files.

https://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go#newcode19
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://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go#newcode24
ec2/vpc.go:24: DedicatedTenancy = "dedicated"
Same as above.

https://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go#newcode30
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://codereview.appspot.com/49930045/diff/120001/ec2/vpc_test.go
File ec2/vpc_test.go (right):

https://codereview.appspot.com/49930045/diff/120001/ec2/vpc_test.go#newcode8
ec2/vpc_test.go:8: // Written by Gustavo Niemeyer
<email address hidden>
Please drop this line.

https://codereview.appspot.com/49930045/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Download full text (4.2 KiB)

*** 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://codereview.appspot.com/49930045

https://codereview.appspot.com/49930045/diff/120001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/49930045/diff/120001/ec2/ec2.go#newcode31
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://codereview.appspot.com/49930045/diff/120001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/49930045/diff/120001/ec2/ec2t_test.go#newcode657
ec2/ec2t_test.go:657: func (s *ServerTests) errorCode(err error) string
{
On 2014/02/12 13:23:49, niemeyer wrote:
> This can be a function (s is unnecessary).

Done.

https://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go
File ec2/vpc.go (right):

https://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go#newcode8
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://codereview.appspot.com/49930045/diff/120001/ec2/vpc.go#newcode19
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 ...

Read more...

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Post-submit approval.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches