Merge lp:~axwalk/goamz/ec2-describeavailabilityzones into lp:goamz

Proposed by Andrew Wilkins
Status: Merged
Merged at revision: 48
Proposed branch: lp:~axwalk/goamz/ec2-describeavailabilityzones
Merge into: lp:goamz
Diff against target: 434 lines (+284/-8)
5 files modified
ec2/ec2.go (+41/-0)
ec2/ec2_test.go (+62/-0)
ec2/ec2t_test.go (+45/-0)
ec2/ec2test/server.go (+80/-8)
ec2/responses_test.go (+56/-0)
To merge this branch: bzr merge lp:~axwalk/goamz/ec2-describeavailabilityzones
Reviewer Review Type Date Requested Status
goamz maintainers Pending
Review via email: mp+221508@code.launchpad.net

Commit message

ec2: expose DescribeAvailabilityZone

Juju is growing support for manual
and automatic availability zone
distribution. Since availability zones
may differ between accounts, we need
to use DescribeAvailabilityZones to
enumerate them.

https://codereview.appspot.com/101980044/

Description of the change

ec2: expose DescribeAvailabilityZone

Juju is growing support for manual
and automatic availability zone
distribution. Since availability zones
may differ between accounts, we need
to use DescribeAvailabilityZones to
enumerate them.

https://codereview.appspot.com/101980044/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

Reviewers: mp+221508_code.launchpad.net,

Message:
Please take a look.

Description:
ec2: expose DescribeAvailabilityZone

Juju is growing support for manual
and automatic availability zone
distribution. Since availability zones
may differ between accounts, we need
to use DescribeAvailabilityZones to
enumerate them.

https://code.launchpad.net/~axwalk/goamz/ec2-describeavailabilityzones/+merge/221508

(do not edit description out of merge proposal)

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

Affected files (+289, -11 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
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Ian Booth (wallyworld) wrote :
Revision history for this message
Andrew Wilkins (axwalk) wrote :
Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/07/08 15:44:00, axw wrote:
> Please take a look.

uh oh, ignore me

https://codereview.appspot.com/101980044/

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 2014-05-09 15:01:24 +0000
3+++ ec2/ec2.go 2014-05-30 09:44:09 +0000
4@@ -1012,6 +1012,47 @@
5 return resp, nil
6 }
7
8+// ----------------------------------------------------------------------------
9+// Availability zone management functions and types.
10+// See http://goo.gl/ylxT4R for more details.
11+
12+// AvailabilityZonesResp represents a response to a DescribeAvailabilityZones
13+// request in EC2.
14+type AvailabilityZonesResp struct {
15+ RequestId string `xml:"requestId"`
16+ Zones []AvailabilityZoneInfo `xml:"availabilityZoneInfo>item"`
17+}
18+
19+// AvailabilityZoneInfo encapsulates details for an availability zone in EC2.
20+type AvailabilityZoneInfo struct {
21+ AvailabilityZone
22+ State string `xml:"zoneState"`
23+ MessageSet []string `xml:"messageSet>item"`
24+}
25+
26+// AvailabilityZone represents an EC2 availability zone.
27+type AvailabilityZone struct {
28+ Name string `xml:"zoneName"`
29+ Region string `xml:"regionName"`
30+}
31+
32+// AvailabilityZones returns details about availability zones in EC2.
33+// The filter parameter is optional, and if provided will limit the
34+// availability zones returned to those matching the given filtering
35+// rules.
36+//
37+// See http://goo.gl/ylxT4R for more details.
38+func (ec2 *EC2) AvailabilityZones(filter *Filter) (resp *AvailabilityZonesResp, err error) {
39+ params := makeParams("DescribeAvailabilityZones")
40+ filter.addParams(params)
41+ resp = &AvailabilityZonesResp{}
42+ err = ec2.query(params, resp)
43+ if err != nil {
44+ return nil, err
45+ }
46+ return
47+}
48+
49 // AccountAttribute holds information about an account attribute.
50 //
51 // See http://goo.gl/hBc28j for more details.
52
53=== modified file 'ec2/ec2_test.go'
54--- ec2/ec2_test.go 2014-05-09 15:01:24 +0000
55+++ ec2/ec2_test.go 2014-05-30 09:44:09 +0000
56@@ -807,6 +807,68 @@
57 c.Assert(req.Form["Signature"], DeepEquals, []string{"gdG/vEm+c6ehhhfkrJy3+wuVzw/rzKR42TYelMwti7M="})
58 }
59
60+func (s *S) TestAvailabilityZonesExample1(c *C) {
61+ testServer.Response(200, nil, DescribeAvailabilityZonesExample1)
62+
63+ resp, err := s.ec2.AvailabilityZones(nil)
64+
65+ req := testServer.WaitRequest()
66+ c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAvailabilityZones"})
67+
68+ c.Assert(err, IsNil)
69+ c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
70+ c.Assert(resp.Zones, HasLen, 4)
71+
72+ z0 := resp.Zones[0]
73+ c.Assert(z0.Name, Equals, "us-east-1a")
74+ c.Assert(z0.Region, Equals, "us-east-1")
75+ c.Assert(z0.State, Equals, "available")
76+ c.Assert(z0.MessageSet, HasLen, 0)
77+
78+ z1 := resp.Zones[1]
79+ c.Assert(z1.Name, Equals, "us-east-1b")
80+ c.Assert(z1.Region, Equals, "us-east-1")
81+ c.Assert(z1.State, Equals, "available")
82+ c.Assert(z1.MessageSet, HasLen, 0)
83+
84+ z2 := resp.Zones[2]
85+ c.Assert(z2.Name, Equals, "us-east-1c")
86+ c.Assert(z2.Region, Equals, "us-east-1")
87+ c.Assert(z2.State, Equals, "available")
88+ c.Assert(z2.MessageSet, HasLen, 0)
89+
90+ z3 := resp.Zones[3]
91+ c.Assert(z3.Name, Equals, "us-east-1d")
92+ c.Assert(z3.Region, Equals, "us-east-1")
93+ c.Assert(z3.State, Equals, "available")
94+ c.Assert(z3.MessageSet, HasLen, 0)
95+}
96+
97+func (s *S) TestAvailabilityZonesExample2(c *C) {
98+ testServer.Response(200, nil, DescribeAvailabilityZonesExample2)
99+
100+ resp, err := s.ec2.AvailabilityZones(nil)
101+
102+ req := testServer.WaitRequest()
103+ c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAvailabilityZones"})
104+
105+ c.Assert(err, IsNil)
106+ c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
107+ c.Assert(resp.Zones, HasLen, 2)
108+
109+ z0 := resp.Zones[0]
110+ c.Assert(z0.Name, Equals, "us-east-1a")
111+ c.Assert(z0.Region, Equals, "us-east-1")
112+ c.Assert(z0.State, Equals, "impaired")
113+ c.Assert(z0.MessageSet, HasLen, 0)
114+
115+ z1 := resp.Zones[1]
116+ c.Assert(z1.Name, Equals, "us-east-1b")
117+ c.Assert(z1.Region, Equals, "us-east-1")
118+ c.Assert(z1.State, Equals, "unavailable")
119+ c.Assert(z1.MessageSet, DeepEquals, []string{"us-east-1b is currently down for maintenance."})
120+}
121+
122 func (s *S) TestDescribeAccountAttributesExamples(c *C) {
123 testServer.Response(200, nil, DescribeAccountAttributesExample)
124
125
126=== modified file 'ec2/ec2t_test.go'
127--- ec2/ec2t_test.go 2014-05-21 13:52:28 +0000
128+++ ec2/ec2t_test.go 2014-05-30 09:44:09 +0000
129@@ -152,6 +152,51 @@
130 return ip.String()
131 }
132
133+func (s *LocalServerSuite) TestAvailabilityZones(c *C) {
134+ s.srv.srv.SetAvailabilityZones([]ec2.AvailabilityZoneInfo{{
135+ AvailabilityZone: ec2.AvailabilityZone{
136+ Name: "us-east-1a",
137+ Region: "us-east-1",
138+ },
139+ State: "available",
140+ }, {
141+ AvailabilityZone: ec2.AvailabilityZone{
142+ Name: "us-east-1b",
143+ Region: "us-east-1",
144+ },
145+ State: "impaired",
146+ }, {
147+ AvailabilityZone: ec2.AvailabilityZone{
148+ Name: "us-west-1a",
149+ Region: "us-west-1",
150+ },
151+ State: "available",
152+ }, {
153+ AvailabilityZone: ec2.AvailabilityZone{
154+ Name: "us-west-1b",
155+ Region: "us-west-1",
156+ },
157+ State: "unavailable",
158+ MessageSet: []string{"down for maintenance"},
159+ }})
160+
161+ resp, err := s.ec2.AvailabilityZones(nil)
162+ c.Assert(err, IsNil)
163+ c.Assert(resp.Zones, HasLen, 4)
164+ c.Assert(resp.Zones[0].Name, Equals, "us-east-1a")
165+ c.Assert(resp.Zones[1].Name, Equals, "us-east-1b")
166+ c.Assert(resp.Zones[2].Name, Equals, "us-west-1a")
167+ c.Assert(resp.Zones[3].Name, Equals, "us-west-1b")
168+
169+ filter := ec2.NewFilter()
170+ filter.Add("region-name", "us-east-1")
171+ resp, err = s.ec2.AvailabilityZones(filter)
172+ c.Assert(err, IsNil)
173+ c.Assert(resp.Zones, HasLen, 2)
174+ c.Assert(resp.Zones[0].Name, Equals, "us-east-1a")
175+ c.Assert(resp.Zones[1].Name, Equals, "us-east-1b")
176+}
177+
178 // AmazonServerSuite runs the ec2test server tests against a live EC2 server.
179 // It will only be activated if the -amazon flag is specified.
180 type AmazonServerSuite struct {
181
182=== modified file 'ec2/ec2test/server.go'
183--- ec2/ec2test/server.go 2014-05-27 13:26:01 +0000
184+++ ec2/ec2test/server.go 2014-05-30 09:44:09 +0000
185@@ -53,10 +53,11 @@
186 instances map[string]*Instance // id -> instance
187 reservations map[string]*reservation // id -> reservation
188 groups map[string]*securityGroup // id -> group
189- vpcs map[string]*vpc // id -> vpc
190- subnets map[string]*subnet // id -> subnet
191- ifaces map[string]*iface // id -> iface
192- attachments map[string]*attachment // id -> attachment
193+ zones []availabilityZone
194+ vpcs map[string]*vpc // id -> vpc
195+ subnets map[string]*subnet // id -> subnet
196+ ifaces map[string]*iface // id -> iface
197+ attachments map[string]*attachment // id -> attachment
198 maxId counter
199 reqId counter
200 reservationId counter
201@@ -86,6 +87,7 @@
202 imageId string
203 reservation *reservation
204 instType string
205+ availZone string
206 state ec2.InstanceState
207 subnetId string
208 vpcId string
209@@ -343,6 +345,7 @@
210 "TerminateInstances": (*Server).terminateInstances,
211 "DescribeInstances": (*Server).describeInstances,
212 "CreateSecurityGroup": (*Server).createSecurityGroup,
213+ "DescribeAvailabilityZones": (*Server).describeAvailabilityZones,
214 "DescribeSecurityGroups": (*Server).describeSecurityGroups,
215 "DeleteSecurityGroup": (*Server).deleteSecurityGroup,
216 "AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress,
217@@ -363,7 +366,10 @@
218 "UnassignPrivateIpAddresses": (*Server).unassignPrivateIP,
219 }
220
221-const ownerId = "9876"
222+const (
223+ ownerId = "9876"
224+ defaultAvailZone = "us-east-1a"
225+)
226
227 // newAction allocates a new action and adds it to the
228 // recorded list of server actions.
229@@ -418,6 +424,13 @@
230 }
231 srv.groups[g.id] = g
232
233+ // Add a default availability zone.
234+ var z availabilityZone
235+ z.Name = defaultAvailZone
236+ z.Region = "us-east-1"
237+ z.State = "available"
238+ srv.zones = []availabilityZone{z}
239+
240 l, err := net.Listen("tcp", "localhost:0")
241 if err != nil {
242 return nil, fmt.Errorf("cannot listen on localhost: %v", err)
243@@ -446,6 +459,15 @@
244 srv.mu.Unlock()
245 }
246
247+func (srv *Server) SetAvailabilityZones(zones []ec2.AvailabilityZoneInfo) {
248+ srv.mu.Lock()
249+ srv.zones = make([]availabilityZone, len(zones))
250+ for i, z := range zones {
251+ srv.zones[i] = availabilityZone{z}
252+ }
253+ srv.mu.Unlock()
254+}
255+
256 // SetInitialAttributes sets the given account attributes on the server.
257 func (srv *Server) SetInitialAttributes(attrs map[string][]string) {
258 for attrName, values := range attrs {
259@@ -855,6 +877,7 @@
260 // make sure that form fields are correct before creating the reservation.
261 instType := req.Form.Get("InstanceType")
262 imageId := req.Form.Get("ImageId")
263+ availZone := req.Form.Get("AvailZone")
264
265 r := srv.newReservation(srv.formToGroups(req.Form))
266
267@@ -892,7 +915,7 @@
268 resp.OwnerId = ownerId
269
270 for i := 0; i < max; i++ {
271- inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState)
272+ inst := srv.newInstance(r, instType, imageId, availZone, srv.initialInstanceState)
273 // Create any NICs on the instance subnet (if any), and then
274 // save the VPC and subnet ids on the instance, as EC2 does.
275 inst.ifaces = srv.createNICsOnRun(inst.id(), instSubnet, ifacesToCreate)
276@@ -942,7 +965,7 @@
277
278 ids := make([]string, n)
279 for i := 0; i < n; i++ {
280- inst := srv.newInstance(r, instType, imageId, state)
281+ inst := srv.newInstance(r, instType, imageId, defaultAvailZone, state)
282 inst.vpcId = vpcId
283 inst.subnetId = subnetId
284 ids[i] = inst.id()
285@@ -958,11 +981,12 @@
286 return srv.NewInstancesVPC("", "", n, instType, imageId, state, groups)
287 }
288
289-func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance {
290+func (srv *Server) newInstance(r *reservation, instType string, imageId string, availZone string, state ec2.InstanceState) *Instance {
291 inst := &Instance{
292 seq: srv.maxId.next(),
293 instType: instType,
294 imageId: imageId,
295+ availZone: availZone,
296 state: state,
297 reservation: r,
298 }
299@@ -1036,6 +1060,7 @@
300 IPAddress: fmt.Sprintf("8.0.0.%d", inst.seq%256),
301 PrivateIPAddress: fmt.Sprintf("127.0.0.%d", inst.seq%256),
302 State: inst.state,
303+ AvailZone: inst.availZone,
304 VPCId: inst.vpcId,
305 SubnetId: inst.subnetId,
306 NetworkInterfaces: inst.ifaces,
307@@ -1047,6 +1072,8 @@
308 switch attr {
309 case "architecture":
310 return value == "i386", nil
311+ case "availability-zone":
312+ return value == inst.availZone, nil
313 case "instance-id":
314 return inst.id() == value, nil
315 case "subnet-id":
316@@ -1467,6 +1494,51 @@
317 }
318 }
319
320+type availabilityZone struct {
321+ ec2.AvailabilityZoneInfo
322+}
323+
324+func (z *availabilityZone) matchAttr(attr, value string) (ok bool, err error) {
325+ switch attr {
326+ case "message":
327+ for _, m := range z.MessageSet {
328+ if m == value {
329+ return true, nil
330+ }
331+ }
332+ return false, nil
333+ case "region-name":
334+ return z.Region == value, nil
335+ case "state":
336+ switch value {
337+ case "available", "impaired", "unavailable":
338+ return z.State == value, nil
339+ }
340+ return false, fmt.Errorf("invalid state %q", value)
341+ case "zone-name":
342+ return z.Name == value, nil
343+ }
344+ return false, fmt.Errorf("unknown attribute %q", attr)
345+}
346+
347+func (srv *Server) describeAvailabilityZones(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
348+ srv.mu.Lock()
349+ defer srv.mu.Unlock()
350+
351+ f := newFilter(req.Form)
352+ var resp ec2.AvailabilityZonesResp
353+ resp.RequestId = reqId
354+ for _, zone := range srv.zones {
355+ ok, err := f.ok(&zone)
356+ if ok {
357+ resp.Zones = append(resp.Zones, zone.AvailabilityZoneInfo)
358+ } else if err != nil {
359+ fatalf(400, "InvalidParameterValue", "describe availability zones: %v", err)
360+ }
361+ }
362+ return &resp
363+}
364+
365 func (srv *Server) createVpc(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
366 cidrBlock := parseCidr(req.Form.Get("CidrBlock"))
367 tenancy := req.Form.Get("InstanceTenancy")
368
369=== modified file 'ec2/responses_test.go'
370--- ec2/responses_test.go 2014-05-08 16:37:32 +0000
371+++ ec2/responses_test.go 2014-05-30 09:44:09 +0000
372@@ -662,6 +662,62 @@
373 </RebootInstancesResponse>
374 `
375
376+// http://goo.gl/ylxT4R
377+var DescribeAvailabilityZonesExample1 = `
378+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2014-05-01/">
379+ <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
380+ <availabilityZoneInfo>
381+ <item>
382+ <zoneName>us-east-1a</zoneName>
383+ <zoneState>available</zoneState>
384+ <regionName>us-east-1</regionName>
385+ <messageSet/>
386+ </item>
387+ <item>
388+ <zoneName>us-east-1b</zoneName>
389+ <zoneState>available</zoneState>
390+ <regionName>us-east-1</regionName>
391+ <messageSet/>
392+ </item>
393+ <item>
394+ <zoneName>us-east-1c</zoneName>
395+ <zoneState>available</zoneState>
396+ <regionName>us-east-1</regionName>
397+ <messageSet/>
398+ </item>
399+ <item>
400+ <zoneName>us-east-1d</zoneName>
401+ <zoneState>available</zoneState>
402+ <regionName>us-east-1</regionName>
403+ <messageSet/>
404+ </item>
405+ </availabilityZoneInfo>
406+</DescribeAvailabilityZonesResponse>
407+`
408+
409+// http://goo.gl/ylxT4R
410+var DescribeAvailabilityZonesExample2 = `
411+<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2014-05-01/">
412+ <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
413+ <availabilityZoneInfo>
414+ <item>
415+ <zoneName>us-east-1a</zoneName>
416+ <zoneState>impaired</zoneState>
417+ <regionName>us-east-1</regionName>
418+ <messageSet/>
419+ </item>
420+ <item>
421+ <zoneName>us-east-1b</zoneName>
422+ <zoneState>unavailable</zoneState>
423+ <regionName>us-east-1</regionName>
424+ <messageSet>
425+ <item>us-east-1b is currently down for maintenance.</item>
426+ </messageSet>
427+ </item>
428+ </availabilityZoneInfo>
429+</DescribeAvailabilityZonesResponse>
430+`
431+
432 // http://goo.gl/nkwjv
433 var CreateVpcExample = `
434 <CreateVpcResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">

Subscribers

People subscribed via source and target branches