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

Proposed by Dimiter Naydenov
Status: Merged
Merged at revision: 47
Proposed branch: lp:~dimitern/goamz/subnets-api-calls
Merge into: lp:goamz
Prerequisite: lp:~dimitern/goamz/vpc-api-calls
Diff against target: 593 lines (+513/-0)
5 files modified
ec2/ec2test/server.go (+105/-0)
ec2/responses_test.go (+55/-0)
ec2/subnets.go (+111/-0)
ec2/subnets_test.go (+212/-0)
ec2/vpc_test.go (+30/-0)
To merge this branch: bzr merge lp:~dimitern/goamz/subnets-api-calls
Reviewer Review Type Date Requested Status
Dimiter Naydenov (community) Approve
Review via email: mp+204640@code.launchpad.net

Description of the change

ec2: Add support for VPC subnets

Added the following new API calls:
- CreateSubnet
- DeleteSubnet
- Subnets
(and related types/responses)

This is the second step on the path to
support VPC networking in goamz, next
APIs for network interfaces will be
added.

Tested live on EC2, and extended the
ec2test package as needed.

Added a deleteVPCs test helpers which
waits until a VPC is no longer in use
and can be deleted, retrying as needed
when running against live EC2 servers.
This is needed to ensure live tests do
no leave stuff behind (I've run all live
tests several times in a row to make sure
it works).

https://codereview.appspot.com/54690048/

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

Reviewers: mp+204640_code.launchpad.net,

Message:
Please take a look.

Description:
ec2: Add support for VPC subnets

Added the following new API calls:
- CreateSubnet
- DeleteSubnet
- DescribeSubnets
(and related types/responses)

This is the second step on the path to
support VPC networking in goamz, next
APIs for network interfaces will be
added.

Using AWS API version 2011-12-15.

Tested live on EC2, and extended the
ec2test package as needed.

https://code.launchpad.net/~dimitern/goamz/subnets-api-calls/+merge/204640

Requires:
https://code.launchpad.net/~dimitern/goamz/vpc-api-calls/+merge/204514

(do not edit description out of merge proposal)

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

Affected files (+466, -2 lines):
   A [revision details]
   M ec2/ec2test/server.go
   M ec2/responses_test.go
   A ec2/subnets.go
   A ec2/subnets_test.go

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

Minor fixes

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

Merged conflicts; simplified & refactored a bit; naming fixed, API version updated

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

LGTM with some suggestions below.

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

https://codereview.appspot.com/54690048/diff/40001/ec2/ec2test/server.go#newcode1105
ec2/ec2test/server.go:1105: availIPs :=
int(math.Exp2(float64(maskBits-maskOnes))) - 5
availIPs := 1<<uint(maskBits - maskOnes) - 5
?

i suppose we might consider making sure that there
are enough bits in the mask too, otherwise
we can get a negative AvailableIPCount.

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go
File ec2/subnets.go (right):

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go#newcode42
ec2/subnets.go:42: // When you create each subnet, you provide the VPC
ID and the CIDR
// The vpcId and cidrBlock parameters specify the
// VPC id and CIDR block respectively - these cannot
// be changed after creation. The subnet's CIDR block can be the same as
// the VPC's CIDR block (assuming you want only a single subnet in the
// VPC), or a subset of the VPC's CIDR block. If more than one
// subnet is created in a VPC, their CIDR blocks must not overlap. The
// smallest subnet (and VPC) that can be created uses a /28 netmask (16
IP
// addresses), and the largest uses a /16 netmask (65,536 IP
// addresses).

?

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go#newcode52
ec2/subnets.go:52: // availZone can be empty, in which case Amazon EC2
selects one for
s/can/may/

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go#newcode71
ec2/subnets.go:71: // DeleteSubnet deletes the specified subnet. You
must terminate all
// All running instances in the subnet must have been terminated.

?

https://codereview.appspot.com/54690048/

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

Changes after reivew

54. By Dimiter Naydenov

Merged vpc-api-calls into subnets-api-calls.

55. By Dimiter Naydenov

Improved tests not to leave stuff around live

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

Please take a look.

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

https://codereview.appspot.com/54690048/diff/40001/ec2/ec2test/server.go#newcode1105
ec2/ec2test/server.go:1105: availIPs :=
int(math.Exp2(float64(maskBits-maskOnes))) - 5
On 2014/02/05 14:46:53, rog wrote:
> availIPs := 1<<uint(maskBits - maskOnes) - 5
> ?

> i suppose we might consider making sure that there
> are enough bits in the mask too, otherwise
> we can get a negative AvailableIPCount.

Done.

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go
File ec2/subnets.go (right):

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go#newcode42
ec2/subnets.go:42: // When you create each subnet, you provide the VPC
ID and the CIDR
On 2014/02/05 14:46:53, rog wrote:
> // The vpcId and cidrBlock parameters specify the
> // VPC id and CIDR block respectively - these cannot
> // be changed after creation. The subnet's CIDR block can be the same
as
> // the VPC's CIDR block (assuming you want only a single subnet in the
> // VPC), or a subset of the VPC's CIDR block. If more than one
> // subnet is created in a VPC, their CIDR blocks must not overlap.
The
> // smallest subnet (and VPC) that can be created uses a /28 netmask
(16 IP
> // addresses), and the largest uses a /16 netmask (65,536 IP
> // addresses).

> ?

Done.

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go#newcode52
ec2/subnets.go:52: // availZone can be empty, in which case Amazon EC2
selects one for
On 2014/02/05 14:46:53, rog wrote:
> s/can/may/

Done.

https://codereview.appspot.com/54690048/diff/40001/ec2/subnets.go#newcode71
ec2/subnets.go:71: // DeleteSubnet deletes the specified subnet. You
must terminate all
On 2014/02/05 14:46:53, rog wrote:
> // All running instances in the subnet must have been terminated.

> ?

Done.

https://codereview.appspot.com/54690048/

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

LGTM.

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

https://codereview.appspot.com/54690048/diff/80001/ec2/subnets.go#newcode8
ec2/subnets.go:8: // Written by Gustavo Niemeyer
<email address hidden>
Er, I'm not sure what Gustavo thinks here, but should bump the copyright
year at least when adding a new file, if now the authorship bit (I'd
just omit that as policy). This probably applies to the other merge
proposals as well.

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

https://codereview.appspot.com/54690048/diff/80001/ec2/subnets_test.go#newcode141
ec2/subnets_test.go:141: for a := testAttempt.Start(); a.Next(); {
Hm, this looks familiar. :)

At third one, should figure out how to make a helper for this.

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

https://codereview.appspot.com/54690048/diff/80001/ec2/vpc_test.go#newcode155
ec2/vpc_test.go:155: func (s *ServerTests) deleteVPCs(c *C, ids
[]string) {
Somewher later in the pipe this gets picked up for the existing vpc live
test too?

https://codereview.appspot.com/54690048/

lp:~dimitern/goamz/subnets-api-calls updated
56. By Dimiter Naydenov

Merged vpc-api-calls into subnets-api-calls.

57. By Dimiter Naydenov

Changes after reivew

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

Please take a look.

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

https://codereview.appspot.com/54690048/diff/80001/ec2/subnets.go#newcode8
ec2/subnets.go:8: // Written by Gustavo Niemeyer
<email address hidden>
On 2014/02/06 16:37:42, gz wrote:
> Er, I'm not sure what Gustavo thinks here, but should bump the
copyright year at
> least when adding a new file, if now the authorship bit (I'd just omit
that as
> policy). This probably applies to the other merge proposals as well.

I'll change the year.

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

https://codereview.appspot.com/54690048/diff/80001/ec2/subnets_test.go#newcode141
ec2/subnets_test.go:141: for a := testAttempt.Start(); a.Next(); {
On 2014/02/06 16:37:42, gz wrote:
> Hm, this looks familiar. :)

> At third one, should figure out how to make a helper for this.

The problem is in all cases the code is similar, but it needs to call a
different "list" call and process a different slice of things. I'm not
sure how to make it unified (without generics, that is).

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

https://codereview.appspot.com/54690048/diff/80001/ec2/vpc_test.go#newcode155
ec2/vpc_test.go:155: func (s *ServerTests) deleteVPCs(c *C, ids
[]string) {
On 2014/02/06 16:37:42, gz wrote:
> Somewher later in the pipe this gets picked up for the existing vpc
live test
> too?

No, because there's no need - TestVPCs does not create any dependent
objects, so VPC can be removed right away, but other cases where you
need a VPC, a subnet and something else, deleteVPCs is useful.

https://codereview.appspot.com/54690048/

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

LGTM

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

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

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets.go#newcode45
ec2/subnets.go:45: // (assuming you want only a single subnet in the
VPC), or a subset of
Documentation should be impersonal:

     "assuming a single subnet is wanted"

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets.go#newcode51
ec2/subnets.go:51: // availZone may be empty, in which case Amazon EC2
selects one for
// If availZone is empty, an availability zone is automatically
// selected.

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets.go#newcode93
ec2/subnets.go:93: // Subnets describes one or more of your subnets.
Both parameters are
// Subnets returns one or more subnets. Both ...

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

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

https://codereview.appspot.com/54690048/

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

*** Submitted:

ec2: Add support for VPC subnets

Added the following new API calls:
- CreateSubnet
- DeleteSubnet
- Subnets
(and related types/responses)

This is the second step on the path to
support VPC networking in goamz, next
APIs for network interfaces will be
added.

Tested live on EC2, and extended the
ec2test package as needed.

Added a deleteVPCs test helpers which
waits until a VPC is no longer in use
and can be deleted, retrying as needed
when running against live EC2 servers.
This is needed to ensure live tests do
no leave stuff behind (I've run all live
tests several times in a row to make sure
it works).

R=rog, gz, niemeyer
CC=
https://codereview.appspot.com/54690048

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

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets.go#newcode8
ec2/subnets.go:8: // Written by Gustavo Niemeyer
<email address hidden>
On 2014/02/12 13:33:51, niemeyer wrote:
> Please drop this line.

Done.

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets.go#newcode45
ec2/subnets.go:45: // (assuming you want only a single subnet in the
VPC), or a subset of
On 2014/02/12 13:33:51, niemeyer wrote:
> Documentation should be impersonal:

> "assuming a single subnet is wanted"

Done.

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets.go#newcode51
ec2/subnets.go:51: // availZone may be empty, in which case Amazon EC2
selects one for
On 2014/02/12 13:33:51, niemeyer wrote:
> // If availZone is empty, an availability zone is automatically
> // selected.

Done.

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets.go#newcode93
ec2/subnets.go:93: // Subnets describes one or more of your subnets.
Both parameters are
On 2014/02/12 13:33:51, niemeyer wrote:
> // Subnets returns one or more subnets. Both ...

Done.

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

https://codereview.appspot.com/54690048/diff/100001/ec2/subnets_test.go#newcode8
ec2/subnets_test.go:8: // Written by Gustavo Niemeyer
<email address hidden>
On 2014/02/12 13:33:51, niemeyer wrote:
> Please drop this line.

Done.

https://codereview.appspot.com/54690048/

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
=== modified file 'ec2/ec2test/server.go'
--- ec2/ec2test/server.go 2014-02-07 12:48:07 +0000
+++ ec2/ec2test/server.go 2014-02-07 12:48:07 +0000
@@ -51,12 +51,14 @@
51 reservations map[string]*reservation // id -> reservation51 reservations map[string]*reservation // id -> reservation
52 groups map[string]*securityGroup // id -> group52 groups map[string]*securityGroup // id -> group
53 vpcs map[string]*vpc // id -> vpc53 vpcs map[string]*vpc // id -> vpc
54 subnets map[string]*subnet // id -> subnet
54 maxId counter55 maxId counter
55 reqId counter56 reqId counter
56 reservationId counter57 reservationId counter
57 groupId counter58 groupId counter
58 vpcId counter59 vpcId counter
59 dhcpOptsId counter60 dhcpOptsId counter
61 subnetId counter
60 initialInstanceState ec2.InstanceState62 initialInstanceState ec2.InstanceState
61}63}
6264
@@ -212,6 +214,28 @@
212 return false, fmt.Errorf("unknown attribute %q", attr)214 return false, fmt.Errorf("unknown attribute %q", attr)
213}215}
214216
217type subnet struct {
218 ec2.Subnet
219}
220
221func (s *subnet) matchAttr(attr, value string) (ok bool, err error) {
222 switch attr {
223 case "cidr":
224 return s.CIDRBlock == value, nil
225 case "availability-zone":
226 return s.AvailZone == value, nil
227 case "state":
228 return s.State == value, nil
229 case "subnet-id":
230 return s.Id == value, nil
231 case "vpc-id":
232 return s.VPCId == value, nil
233 case "tag", "tag-key", "tag-value", "available-ip-address-count", "defaultForAz":
234 return false, fmt.Errorf("%q filter not implemented", attr)
235 }
236 return false, fmt.Errorf("unknown attribute %q", attr)
237}
238
215var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) interface{}{239var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) interface{}{
216 "RunInstances": (*Server).runInstances,240 "RunInstances": (*Server).runInstances,
217 "TerminateInstances": (*Server).terminateInstances,241 "TerminateInstances": (*Server).terminateInstances,
@@ -224,6 +248,9 @@
224 "CreateVpc": (*Server).createVpc,248 "CreateVpc": (*Server).createVpc,
225 "DeleteVpc": (*Server).deleteVpc,249 "DeleteVpc": (*Server).deleteVpc,
226 "DescribeVpcs": (*Server).describeVpcs,250 "DescribeVpcs": (*Server).describeVpcs,
251 "CreateSubnet": (*Server).createSubnet,
252 "DeleteSubnet": (*Server).deleteSubnet,
253 "DescribeSubnets": (*Server).describeSubnets,
227}254}
228255
229const ownerId = "9876"256const ownerId = "9876"
@@ -245,6 +272,7 @@
245 instances: make(map[string]*Instance),272 instances: make(map[string]*Instance),
246 groups: make(map[string]*securityGroup),273 groups: make(map[string]*securityGroup),
247 vpcs: make(map[string]*vpc),274 vpcs: make(map[string]*vpc),
275 subnets: make(map[string]*subnet),
248 reservations: make(map[string]*reservation),276 reservations: make(map[string]*reservation),
249 initialInstanceState: Pending,277 initialInstanceState: Pending,
250 }278 }
@@ -1060,6 +1088,70 @@
1060 return &resp1088 return &resp
1061}1089}
10621090
1091func (srv *Server) createSubnet(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
1092 v := srv.vpc(req.Form.Get("VpcId"))
1093 cidrBlock := parseCidr(req.Form.Get("CidrBlock"))
1094 availZone := req.Form.Get("AvailabilityZone")
1095 if availZone == "" {
1096 // Assign one automatically as AWS does.
1097 availZone = "us-east-1b"
1098 }
1099 // calculate the available IP addresses, removing the first 4 and
1100 // the last, which are reserved by AWS. Since we already checked
1101 // the CIDR is valid, we don't check the error here.
1102 _, ipnet, _ := net.ParseCIDR(cidrBlock)
1103 maskOnes, maskBits := ipnet.Mask.Size()
1104 availIPs := 1<<uint(maskBits-maskOnes) - 5
1105
1106 srv.mu.Lock()
1107 defer srv.mu.Unlock()
1108 s := &subnet{ec2.Subnet{
1109 Id: fmt.Sprintf("subnet-%d", srv.subnetId.next()),
1110 VPCId: v.Id,
1111 State: ec2.AvailableState,
1112 CIDRBlock: cidrBlock,
1113 AvailZone: availZone,
1114 AvailableIPCount: availIPs,
1115 }}
1116 srv.subnets[s.Id] = s
1117 r := &ec2.CreateSubnetResp{
1118 RequestId: reqId,
1119 Subnet: s.Subnet,
1120 }
1121 return r
1122}
1123
1124func (srv *Server) deleteSubnet(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
1125 s := srv.subnet(req.Form.Get("SubnetId"))
1126 srv.mu.Lock()
1127 defer srv.mu.Unlock()
1128
1129 delete(srv.subnets, s.Id)
1130 return &ec2.SimpleResp{
1131 XMLName: xml.Name{"", "DeleteSubnetResponse"},
1132 RequestId: reqId,
1133 }
1134}
1135
1136func (srv *Server) describeSubnets(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
1137 srv.mu.Lock()
1138 defer srv.mu.Unlock()
1139
1140 idMap := collectIds(req.Form, "SubnetId.")
1141 f := newFilter(req.Form)
1142 var resp ec2.SubnetsResp
1143 resp.RequestId = reqId
1144 for _, s := range srv.subnets {
1145 ok, err := f.ok(s)
1146 if ok && (len(idMap) == 0 || idMap[s.Id]) {
1147 resp.Subnets = append(resp.Subnets, s.Subnet)
1148 } else if err != nil {
1149 fatalf(400, "InvalidParameterValue", "describe subnets: %v", err)
1150 }
1151 }
1152 return &resp
1153}
1154
1063func (r *reservation) hasRunningMachine() bool {1155func (r *reservation) hasRunningMachine() bool {
1064 for _, inst := range r.instances {1156 for _, inst := range r.instances {
1065 if inst.state.Code != ShuttingDown.Code && inst.state.Code != Terminated.Code {1157 if inst.state.Code != ShuttingDown.Code && inst.state.Code != Terminated.Code {
@@ -1092,6 +1184,19 @@
1092 return v1184 return v
1093}1185}
10941186
1187func (srv *Server) subnet(id string) *subnet {
1188 if id == "" {
1189 fatalf(400, "MissingParameter", "missing subnetId")
1190 }
1191 srv.mu.Lock()
1192 defer srv.mu.Unlock()
1193 s, found := srv.subnets[id]
1194 if !found {
1195 fatalf(400, "InvalidSubnetID.NotFound", "subnet %s not found", id)
1196 }
1197 return s
1198}
1199
1095// collectIds takes all values with the given prefix from form and1200// collectIds takes all values with the given prefix from form and
1096// returns a map with the ids as keys.1201// returns a map with the ids as keys.
1097func collectIds(form url.Values, prefix string) map[string]bool {1202func collectIds(form url.Values, prefix string) map[string]bool {
10981203
=== modified file 'ec2/responses_test.go'
--- ec2/responses_test.go 2014-02-07 12:48:07 +0000
+++ ec2/responses_test.go 2014-02-07 12:48:07 +0000
@@ -629,3 +629,58 @@
629 </vpcSet>629 </vpcSet>
630</DescribeVpcsResponse>630</DescribeVpcsResponse>
631`631`
632
633// http://goo.gl/wLPhf
634var CreateSubnetExample = `
635<CreateSubnetResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
636 <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
637 <subnet>
638 <subnetId>subnet-9d4a7b6c</subnetId>
639 <state>pending</state>
640 <vpcId>vpc-1a2b3c4d</vpcId>
641 <cidrBlock>10.0.1.0/24</cidrBlock>
642 <availableIpAddressCount>251</availableIpAddressCount>
643 <availabilityZone>us-east-1a</availabilityZone>
644 <tagSet/>
645 </subnet>
646</CreateSubnetResponse>
647`
648
649// http://goo.gl/KmhcBM
650var DeleteSubnetExample = `
651<DeleteSubnetResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
652 <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
653 <return>true</return>
654</DeleteSubnetResponse>
655`
656
657// http://goo.gl/NTKQVI
658var DescribeSubnetsExample = `
659<DescribeSubnetsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
660 <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
661 <subnetSet>
662 <item>
663 <subnetId>subnet-9d4a7b6c</subnetId>
664 <state>available</state>
665 <vpcId>vpc-1a2b3c4d</vpcId>
666 <cidrBlock>10.0.1.0/24</cidrBlock>
667 <availableIpAddressCount>251</availableIpAddressCount>
668 <availabilityZone>us-east-1a</availabilityZone>
669 <defaultForAz>false</defaultForAz>
670 <mapPublicIpOnLaunch>false</mapPublicIpOnLaunch>
671 <tagSet/>
672 </item>
673 <item>
674 <subnetId>subnet-6e7f829e</subnetId>
675 <state>available</state>
676 <vpcId>vpc-1a2b3c4d</vpcId>
677 <cidrBlock>10.0.0.0/24</cidrBlock>
678 <availableIpAddressCount>251</availableIpAddressCount>
679 <availabilityZone>us-east-1a</availabilityZone>
680 <defaultForAz>false</defaultForAz>
681 <mapPublicIpOnLaunch>false</mapPublicIpOnLaunch>
682 <tagSet/>
683 </item>
684 </subnetSet>
685</DescribeSubnetsResponse>
686`
632687
=== added file 'ec2/subnets.go'
--- ec2/subnets.go 1970-01-01 00:00:00 +0000
+++ ec2/subnets.go 2014-02-07 12:48:07 +0000
@@ -0,0 +1,111 @@
1//
2// goamz - Go packages to interact with the Amazon Web Services.
3//
4// https://wiki.ubuntu.com/goamz
5//
6// Copyright (c) 2014 Canonical Ltd.
7//
8// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
9//
10
11package ec2
12
13import (
14 "strconv"
15)
16
17// Subnet describes an Amazon VPC subnet.
18//
19// See http://goo.gl/CdkvO2 for more details.
20type Subnet struct {
21 Id string `xml:"subnetId"`
22 State string `xml:"state"`
23 VPCId string `xml:"vpcId"`
24 CIDRBlock string `xml:"cidrBlock"`
25 AvailableIPCount int `xml:"availableIpAddressCount"`
26 AvailZone string `xml:"availabilityZone"`
27 DefaultForAZ bool `xml:"defaultForAz"`
28 MapPublicIPOnLaunch bool `xml:"mapPublicIpOnLaunch"`
29 Tags []Tag `xml:"tagSet>item"`
30}
31
32// CreateSubnetResp is the response to a CreateSubnet request.
33//
34// See http://goo.gl/wLPhfI for more details.
35type CreateSubnetResp struct {
36 RequestId string `xml:"requestId"`
37 Subnet Subnet `xml:"subnet"`
38}
39
40// CreateSubnet creates a subnet in an existing VPC.
41//
42// The vpcId and cidrBlock parameters specify the VPC id and CIDR
43// block respectively - these cannot be changed after creation. The
44// subnet's CIDR block can be the same as the VPC's CIDR block
45// (assuming you want only a single subnet in the VPC), or a subset of
46// the VPC's CIDR block. If more than one subnet is created in a VPC,
47// their CIDR blocks must not overlap. The smallest subnet (and VPC)
48// that can be created uses a /28 netmask (16 IP addresses), and the
49// largest uses a /16 netmask (65,536 IP addresses).
50//
51// availZone may be empty, in which case Amazon EC2 selects one for
52// you (recommended).
53//
54// See http://goo.gl/wLPhfI for more details.
55func (ec2 *EC2) CreateSubnet(vpcId, cidrBlock, availZone string) (resp *CreateSubnetResp, err error) {
56 params := makeParamsVPC("CreateSubnet")
57 params["VpcId"] = vpcId
58 params["CidrBlock"] = cidrBlock
59 if availZone != "" {
60 params["AvailabilityZone"] = availZone
61 }
62 resp = &CreateSubnetResp{}
63 err = ec2.query(params, resp)
64 if err != nil {
65 return nil, err
66 }
67 return resp, nil
68}
69
70// DeleteSubnet deletes the specified subnet. All running instances in
71// the subnet must have been terminated.
72//
73// See http://goo.gl/KmhcBM for more details.
74func (ec2 *EC2) DeleteSubnet(id string) (resp *SimpleResp, err error) {
75 params := makeParamsVPC("DeleteSubnet")
76 params["SubnetId"] = id
77 resp = &SimpleResp{}
78 err = ec2.query(params, resp)
79 if err != nil {
80 return nil, err
81 }
82 return resp, nil
83}
84
85// SubnetsResp is the response to a Subnets request.
86//
87// See http://goo.gl/NTKQVI for more details.
88type SubnetsResp struct {
89 RequestId string `xml:"requestId"`
90 Subnets []Subnet `xml:"subnetSet>item"`
91}
92
93// Subnets describes one or more of your subnets. Both parameters are
94// optional, and if specified will limit the returned subnets to the
95// matching ids or filtering rules.
96//
97// See http://goo.gl/NTKQVI for more details.
98func (ec2 *EC2) Subnets(ids []string, filter *Filter) (resp *SubnetsResp, err error) {
99 params := makeParamsVPC("DescribeSubnets")
100 for i, id := range ids {
101 params["SubnetId."+strconv.Itoa(i+1)] = id
102 }
103 filter.addParams(params)
104
105 resp = &SubnetsResp{}
106 err = ec2.query(params, resp)
107 if err != nil {
108 return nil, err
109 }
110 return resp, nil
111}
0112
=== added file 'ec2/subnets_test.go'
--- ec2/subnets_test.go 1970-01-01 00:00:00 +0000
+++ ec2/subnets_test.go 2014-02-07 12:48:07 +0000
@@ -0,0 +1,212 @@
1//
2// goamz - Go packages to interact with the Amazon Web Services.
3//
4// https://wiki.ubuntu.com/goamz
5//
6// Copyright (c) 2014 Canonical Ltd.
7//
8// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
9//
10
11package ec2_test
12
13import (
14 "launchpad.net/goamz/aws"
15 "launchpad.net/goamz/ec2"
16 . "launchpad.net/gocheck"
17 "time"
18)
19
20// Subnet tests with example responses
21
22func (s *S) TestCreateSubnetExample(c *C) {
23 testServer.Response(200, nil, CreateSubnetExample)
24
25 resp, err := s.ec2.CreateSubnet("vpc-1a2b3c4d", "10.0.1.0/24", "us-east-1a")
26 req := testServer.WaitRequest()
27
28 c.Assert(req.Form["Action"], DeepEquals, []string{"CreateSubnet"})
29 c.Assert(req.Form["VpcId"], DeepEquals, []string{"vpc-1a2b3c4d"})
30 c.Assert(req.Form["CidrBlock"], DeepEquals, []string{"10.0.1.0/24"})
31 c.Assert(req.Form["AvailabilityZone"], DeepEquals, []string{"us-east-1a"})
32
33 c.Assert(err, IsNil)
34 c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE")
35 subnet := resp.Subnet
36 c.Check(subnet.Id, Equals, "subnet-9d4a7b6c")
37 c.Check(subnet.State, Equals, "pending")
38 c.Check(subnet.VPCId, Equals, "vpc-1a2b3c4d")
39 c.Check(subnet.CIDRBlock, Equals, "10.0.1.0/24")
40 c.Check(subnet.AvailableIPCount, Equals, 251)
41 c.Check(subnet.AvailZone, Equals, "us-east-1a")
42 c.Check(subnet.Tags, HasLen, 0)
43}
44
45func (s *S) TestDeleteSubnetExample(c *C) {
46 testServer.Response(200, nil, DeleteSubnetExample)
47
48 resp, err := s.ec2.DeleteSubnet("subnet-id")
49 req := testServer.WaitRequest()
50
51 c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteSubnet"})
52 c.Assert(req.Form["SubnetId"], DeepEquals, []string{"subnet-id"})
53
54 c.Assert(err, IsNil)
55 c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE")
56}
57
58func (s *S) TestSubnetsExample(c *C) {
59 testServer.Response(200, nil, DescribeSubnetsExample)
60
61 ids := []string{"subnet-9d4a7b6c", "subnet-6e7f829e"}
62 resp, err := s.ec2.Subnets(ids, nil)
63 req := testServer.WaitRequest()
64
65 c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSubnets"})
66 c.Assert(req.Form["SubnetId.1"], DeepEquals, []string{ids[0]})
67 c.Assert(req.Form["SubnetId.2"], DeepEquals, []string{ids[1]})
68
69 c.Assert(err, IsNil)
70 c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE")
71 c.Check(resp.Subnets, HasLen, 2)
72 subnet := resp.Subnets[0]
73 c.Check(subnet.Id, Equals, "subnet-9d4a7b6c")
74 c.Check(subnet.State, Equals, ec2.AvailableState)
75 c.Check(subnet.VPCId, Equals, "vpc-1a2b3c4d")
76 c.Check(subnet.CIDRBlock, Equals, "10.0.1.0/24")
77 c.Check(subnet.AvailableIPCount, Equals, 251)
78 c.Check(subnet.AvailZone, Equals, "us-east-1a")
79 c.Check(subnet.DefaultForAZ, Equals, false)
80 c.Check(subnet.MapPublicIPOnLaunch, Equals, false)
81 c.Check(subnet.Tags, HasLen, 0)
82 subnet = resp.Subnets[1]
83 c.Check(subnet.Id, Equals, "subnet-6e7f829e")
84 c.Check(subnet.State, Equals, ec2.AvailableState)
85 c.Check(subnet.VPCId, Equals, "vpc-1a2b3c4d")
86 c.Check(subnet.CIDRBlock, Equals, "10.0.0.0/24")
87 c.Check(subnet.AvailableIPCount, Equals, 251)
88 c.Check(subnet.AvailZone, Equals, "us-east-1a")
89 c.Check(subnet.DefaultForAZ, Equals, false)
90 c.Check(subnet.MapPublicIPOnLaunch, Equals, false)
91 c.Check(subnet.Tags, HasLen, 0)
92}
93
94// Subnet tests run against either a local test server or live on EC2.
95
96func (s *ServerTests) TestSubnets(c *C) {
97 resp, err := s.ec2.CreateVPC("10.2.0.0/16", "")
98 c.Assert(err, IsNil)
99 vpcId := resp.VPC.Id
100 defer s.deleteVPCs(c, []string{vpcId})
101
102 // It might take some time for the VPC to get created, so we need
103 // to retry a few times when running against the EC2 servers.
104 var resp1 *ec2.CreateSubnetResp
105 testAttempt := aws.AttemptStrategy{
106 Total: 2 * time.Minute,
107 Delay: 5 * time.Second,
108 }
109 done := false
110 for a := testAttempt.Start(); a.Next(); {
111 resp1, err = s.ec2.CreateSubnet(vpcId, "10.2.1.0/24", "")
112 if s.errorCode(err) == "InvalidVpcID.NotFound" {
113 c.Logf("VPC %v not created yet; retrying", vpcId)
114 continue
115 }
116 if err != nil {
117 c.Logf("retrying; CreateSubnet returned: %v", err)
118 continue
119 }
120 done = true
121 break
122 }
123 if !done {
124 c.Fatalf("timeout while waiting for VPC and subnet")
125 }
126 assertSubnet(c, resp1.Subnet, "", vpcId, "10.2.1.0/24")
127 id1 := resp1.Subnet.Id
128
129 resp2, err := s.ec2.CreateSubnet(vpcId, "10.2.2.0/24", "")
130 c.Assert(err, IsNil)
131 assertSubnet(c, resp2.Subnet, "", vpcId, "10.2.2.0/24")
132 id2 := resp2.Subnet.Id
133
134 // We only check for the subnets we just created, because the user
135 // might have others in his account (when testing against the EC2
136 // servers). In some cases it takes a short while until both
137 // subnets are created, so we need to retry a few times to make
138 // sure.
139 var list *ec2.SubnetsResp
140 done = false
141 for a := testAttempt.Start(); a.Next(); {
142 c.Logf("waiting for %v to be created", []string{id1, id2})
143 list, err = s.ec2.Subnets(nil, nil)
144 if err != nil {
145 c.Logf("retrying; Subnets returned: %v", err)
146 continue
147 }
148 found := 0
149 for _, subnet := range list.Subnets {
150 c.Logf("found subnet %v", subnet)
151 switch subnet.Id {
152 case id1:
153 assertSubnet(c, subnet, id1, vpcId, resp1.Subnet.CIDRBlock)
154 found++
155 case id2:
156 assertSubnet(c, subnet, id2, vpcId, resp2.Subnet.CIDRBlock)
157 found++
158 }
159 if found == 2 {
160 done = true
161 break
162 }
163 }
164 if done {
165 c.Logf("all subnets were created")
166 break
167 }
168 }
169 if !done {
170 c.Fatalf("timeout while waiting for subnets %v", []string{id1, id2})
171 }
172
173 list, err = s.ec2.Subnets([]string{id1}, nil)
174 c.Assert(err, IsNil)
175 c.Assert(list.Subnets, HasLen, 1)
176 assertSubnet(c, list.Subnets[0], id1, vpcId, resp1.Subnet.CIDRBlock)
177
178 f := ec2.NewFilter()
179 f.Add("cidr", resp2.Subnet.CIDRBlock)
180 list, err = s.ec2.Subnets(nil, f)
181 c.Assert(err, IsNil)
182 c.Assert(list.Subnets, HasLen, 1)
183 assertSubnet(c, list.Subnets[0], id2, vpcId, resp2.Subnet.CIDRBlock)
184
185 _, err = s.ec2.DeleteSubnet(id1)
186 c.Assert(err, IsNil)
187 _, err = s.ec2.DeleteSubnet(id2)
188 c.Assert(err, IsNil)
189}
190
191func assertSubnet(c *C, obtained ec2.Subnet, expectId, expectVpcId, expectCidr string) {
192 if expectId != "" {
193 c.Check(obtained.Id, Equals, expectId)
194 } else {
195 c.Check(obtained.Id, Matches, `^subnet-[0-9a-f]+$`)
196 }
197 c.Check(obtained.State, Matches, "("+ec2.AvailableState+"|"+ec2.PendingState+")")
198 if expectVpcId != "" {
199 c.Check(obtained.VPCId, Equals, expectVpcId)
200 } else {
201 c.Check(obtained.VPCId, Matches, `^vpc-[0-9a-f]+$`)
202 }
203 if expectCidr != "" {
204 c.Check(obtained.CIDRBlock, Equals, expectCidr)
205 } else {
206 c.Check(obtained.CIDRBlock, Matches, `^\d+\.\d+\.\d+\.\d+/\d+$`)
207 }
208 c.Check(obtained.AvailZone, Not(Equals), "")
209 c.Check(obtained.AvailableIPCount, Not(Equals), 0)
210 c.Check(obtained.DefaultForAZ, Equals, false)
211 c.Check(obtained.MapPublicIPOnLaunch, Equals, false)
212}
0213
=== modified file 'ec2/vpc_test.go'
--- ec2/vpc_test.go 2014-02-07 12:48:07 +0000
+++ ec2/vpc_test.go 2014-02-07 12:48:07 +0000
@@ -149,6 +149,36 @@
149 c.Assert(err, IsNil)149 c.Assert(err, IsNil)
150}150}
151151
152// deleteVPCs ensures the given VPCs are deleted, by retrying until a
153// timeout or all VPC cannot be found anymore. This should be used to
154// make sure tests leave no VPCs around.
155func (s *ServerTests) deleteVPCs(c *C, ids []string) {
156 testAttempt := aws.AttemptStrategy{
157 Total: 2 * time.Minute,
158 Delay: 5 * time.Second,
159 }
160 for a := testAttempt.Start(); a.Next(); {
161 deleted := 0
162 c.Logf("deleting VPCs %v", ids)
163 for _, id := range ids {
164 _, err := s.ec2.DeleteVPC(id)
165 if err == nil || s.errorCode(err) == "InvalidVpcID.NotFound" {
166 c.Logf("VPC %s deleted", id)
167 deleted++
168 continue
169 }
170 if err != nil {
171 c.Logf("retrying; DeleteVPC returned: %v", err)
172 }
173 }
174 if deleted == len(ids) {
175 c.Logf("all VPCs deleted")
176 return
177 }
178 }
179 c.Fatalf("timeout while waiting %v VPCs to get deleted!", ids)
180}
181
152func assertVPC(c *C, obtained ec2.VPC, expectId, expectCidr string) {182func assertVPC(c *C, obtained ec2.VPC, expectId, expectCidr string) {
153 if expectId != "" {183 if expectId != "" {
154 c.Check(obtained.Id, Equals, expectId)184 c.Check(obtained.Id, Equals, expectId)

Subscribers

People subscribed via source and target branches