Merge lp:~axwalk/goamz/ec2-describeavailabilityzones into lp:goamz
- ec2-describeavailabilityzones
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
goamz maintainers | Pending | ||
Review via email: mp+221508@code.launchpad.net |
Commit message
ec2: expose DescribeAvailab
Juju is growing support for manual
and automatic availability zone
distribution. Since availability zones
may differ between accounts, we need
to use DescribeAvailab
enumerate them.
Description of the change
ec2: expose DescribeAvailab
Juju is growing support for manual
and automatic availability zone
distribution. Since availability zones
may differ between accounts, we need
to use DescribeAvailab
enumerate them.
To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote : | # |
Revision history for this message
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
Revision history for this message
Ian Booth (wallyworld) wrote : | # |
Revision history for this message
Andrew Wilkins (axwalk) wrote : | # |
Please take a look.
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
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/"> |
Reviewers: mp+221508_ code.launchpad. net,
Message:
Please take a look.
Description: ilityZone
ec2: expose DescribeAvailab
Juju is growing support for manual ilityZones to
and automatic availability zone
distribution. Since availability zones
may differ between accounts, we need
to use DescribeAvailab
enumerate them.
https:/ /code.launchpad .net/~axwalk/ goamz/ec2- describeavailab ilityzones/ +merge/ 221508
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/101980044/
Affected files (+289, -11 lines): server. go test.go
A [revision details]
M ec2/ec2.go
M ec2/ec2_test.go
M ec2/ec2t_test.go
M ec2/ec2test/
M ec2/responses_