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
=== modified file 'ec2/ec2.go'
--- ec2/ec2.go 2014-05-09 15:01:24 +0000
+++ ec2/ec2.go 2014-05-30 09:44:09 +0000
@@ -1012,6 +1012,47 @@
1012 return resp, nil1012 return resp, nil
1013}1013}
10141014
1015// ----------------------------------------------------------------------------
1016// Availability zone management functions and types.
1017// See http://goo.gl/ylxT4R for more details.
1018
1019// AvailabilityZonesResp represents a response to a DescribeAvailabilityZones
1020// request in EC2.
1021type AvailabilityZonesResp struct {
1022 RequestId string `xml:"requestId"`
1023 Zones []AvailabilityZoneInfo `xml:"availabilityZoneInfo>item"`
1024}
1025
1026// AvailabilityZoneInfo encapsulates details for an availability zone in EC2.
1027type AvailabilityZoneInfo struct {
1028 AvailabilityZone
1029 State string `xml:"zoneState"`
1030 MessageSet []string `xml:"messageSet>item"`
1031}
1032
1033// AvailabilityZone represents an EC2 availability zone.
1034type AvailabilityZone struct {
1035 Name string `xml:"zoneName"`
1036 Region string `xml:"regionName"`
1037}
1038
1039// AvailabilityZones returns details about availability zones in EC2.
1040// The filter parameter is optional, and if provided will limit the
1041// availability zones returned to those matching the given filtering
1042// rules.
1043//
1044// See http://goo.gl/ylxT4R for more details.
1045func (ec2 *EC2) AvailabilityZones(filter *Filter) (resp *AvailabilityZonesResp, err error) {
1046 params := makeParams("DescribeAvailabilityZones")
1047 filter.addParams(params)
1048 resp = &AvailabilityZonesResp{}
1049 err = ec2.query(params, resp)
1050 if err != nil {
1051 return nil, err
1052 }
1053 return
1054}
1055
1015// AccountAttribute holds information about an account attribute.1056// AccountAttribute holds information about an account attribute.
1016//1057//
1017// See http://goo.gl/hBc28j for more details.1058// See http://goo.gl/hBc28j for more details.
10181059
=== modified file 'ec2/ec2_test.go'
--- ec2/ec2_test.go 2014-05-09 15:01:24 +0000
+++ ec2/ec2_test.go 2014-05-30 09:44:09 +0000
@@ -807,6 +807,68 @@
807 c.Assert(req.Form["Signature"], DeepEquals, []string{"gdG/vEm+c6ehhhfkrJy3+wuVzw/rzKR42TYelMwti7M="})807 c.Assert(req.Form["Signature"], DeepEquals, []string{"gdG/vEm+c6ehhhfkrJy3+wuVzw/rzKR42TYelMwti7M="})
808}808}
809809
810func (s *S) TestAvailabilityZonesExample1(c *C) {
811 testServer.Response(200, nil, DescribeAvailabilityZonesExample1)
812
813 resp, err := s.ec2.AvailabilityZones(nil)
814
815 req := testServer.WaitRequest()
816 c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAvailabilityZones"})
817
818 c.Assert(err, IsNil)
819 c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
820 c.Assert(resp.Zones, HasLen, 4)
821
822 z0 := resp.Zones[0]
823 c.Assert(z0.Name, Equals, "us-east-1a")
824 c.Assert(z0.Region, Equals, "us-east-1")
825 c.Assert(z0.State, Equals, "available")
826 c.Assert(z0.MessageSet, HasLen, 0)
827
828 z1 := resp.Zones[1]
829 c.Assert(z1.Name, Equals, "us-east-1b")
830 c.Assert(z1.Region, Equals, "us-east-1")
831 c.Assert(z1.State, Equals, "available")
832 c.Assert(z1.MessageSet, HasLen, 0)
833
834 z2 := resp.Zones[2]
835 c.Assert(z2.Name, Equals, "us-east-1c")
836 c.Assert(z2.Region, Equals, "us-east-1")
837 c.Assert(z2.State, Equals, "available")
838 c.Assert(z2.MessageSet, HasLen, 0)
839
840 z3 := resp.Zones[3]
841 c.Assert(z3.Name, Equals, "us-east-1d")
842 c.Assert(z3.Region, Equals, "us-east-1")
843 c.Assert(z3.State, Equals, "available")
844 c.Assert(z3.MessageSet, HasLen, 0)
845}
846
847func (s *S) TestAvailabilityZonesExample2(c *C) {
848 testServer.Response(200, nil, DescribeAvailabilityZonesExample2)
849
850 resp, err := s.ec2.AvailabilityZones(nil)
851
852 req := testServer.WaitRequest()
853 c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAvailabilityZones"})
854
855 c.Assert(err, IsNil)
856 c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
857 c.Assert(resp.Zones, HasLen, 2)
858
859 z0 := resp.Zones[0]
860 c.Assert(z0.Name, Equals, "us-east-1a")
861 c.Assert(z0.Region, Equals, "us-east-1")
862 c.Assert(z0.State, Equals, "impaired")
863 c.Assert(z0.MessageSet, HasLen, 0)
864
865 z1 := resp.Zones[1]
866 c.Assert(z1.Name, Equals, "us-east-1b")
867 c.Assert(z1.Region, Equals, "us-east-1")
868 c.Assert(z1.State, Equals, "unavailable")
869 c.Assert(z1.MessageSet, DeepEquals, []string{"us-east-1b is currently down for maintenance."})
870}
871
810func (s *S) TestDescribeAccountAttributesExamples(c *C) {872func (s *S) TestDescribeAccountAttributesExamples(c *C) {
811 testServer.Response(200, nil, DescribeAccountAttributesExample)873 testServer.Response(200, nil, DescribeAccountAttributesExample)
812874
813875
=== modified file 'ec2/ec2t_test.go'
--- ec2/ec2t_test.go 2014-05-21 13:52:28 +0000
+++ ec2/ec2t_test.go 2014-05-30 09:44:09 +0000
@@ -152,6 +152,51 @@
152 return ip.String()152 return ip.String()
153}153}
154154
155func (s *LocalServerSuite) TestAvailabilityZones(c *C) {
156 s.srv.srv.SetAvailabilityZones([]ec2.AvailabilityZoneInfo{{
157 AvailabilityZone: ec2.AvailabilityZone{
158 Name: "us-east-1a",
159 Region: "us-east-1",
160 },
161 State: "available",
162 }, {
163 AvailabilityZone: ec2.AvailabilityZone{
164 Name: "us-east-1b",
165 Region: "us-east-1",
166 },
167 State: "impaired",
168 }, {
169 AvailabilityZone: ec2.AvailabilityZone{
170 Name: "us-west-1a",
171 Region: "us-west-1",
172 },
173 State: "available",
174 }, {
175 AvailabilityZone: ec2.AvailabilityZone{
176 Name: "us-west-1b",
177 Region: "us-west-1",
178 },
179 State: "unavailable",
180 MessageSet: []string{"down for maintenance"},
181 }})
182
183 resp, err := s.ec2.AvailabilityZones(nil)
184 c.Assert(err, IsNil)
185 c.Assert(resp.Zones, HasLen, 4)
186 c.Assert(resp.Zones[0].Name, Equals, "us-east-1a")
187 c.Assert(resp.Zones[1].Name, Equals, "us-east-1b")
188 c.Assert(resp.Zones[2].Name, Equals, "us-west-1a")
189 c.Assert(resp.Zones[3].Name, Equals, "us-west-1b")
190
191 filter := ec2.NewFilter()
192 filter.Add("region-name", "us-east-1")
193 resp, err = s.ec2.AvailabilityZones(filter)
194 c.Assert(err, IsNil)
195 c.Assert(resp.Zones, HasLen, 2)
196 c.Assert(resp.Zones[0].Name, Equals, "us-east-1a")
197 c.Assert(resp.Zones[1].Name, Equals, "us-east-1b")
198}
199
155// AmazonServerSuite runs the ec2test server tests against a live EC2 server.200// AmazonServerSuite runs the ec2test server tests against a live EC2 server.
156// It will only be activated if the -amazon flag is specified.201// It will only be activated if the -amazon flag is specified.
157type AmazonServerSuite struct {202type AmazonServerSuite struct {
158203
=== modified file 'ec2/ec2test/server.go'
--- ec2/ec2test/server.go 2014-05-27 13:26:01 +0000
+++ ec2/ec2test/server.go 2014-05-30 09:44:09 +0000
@@ -53,10 +53,11 @@
53 instances map[string]*Instance // id -> instance53 instances map[string]*Instance // id -> instance
54 reservations map[string]*reservation // id -> reservation54 reservations map[string]*reservation // id -> reservation
55 groups map[string]*securityGroup // id -> group55 groups map[string]*securityGroup // id -> group
56 vpcs map[string]*vpc // id -> vpc56 zones []availabilityZone
57 subnets map[string]*subnet // id -> subnet57 vpcs map[string]*vpc // id -> vpc
58 ifaces map[string]*iface // id -> iface58 subnets map[string]*subnet // id -> subnet
59 attachments map[string]*attachment // id -> attachment59 ifaces map[string]*iface // id -> iface
60 attachments map[string]*attachment // id -> attachment
60 maxId counter61 maxId counter
61 reqId counter62 reqId counter
62 reservationId counter63 reservationId counter
@@ -86,6 +87,7 @@
86 imageId string87 imageId string
87 reservation *reservation88 reservation *reservation
88 instType string89 instType string
90 availZone string
89 state ec2.InstanceState91 state ec2.InstanceState
90 subnetId string92 subnetId string
91 vpcId string93 vpcId string
@@ -343,6 +345,7 @@
343 "TerminateInstances": (*Server).terminateInstances,345 "TerminateInstances": (*Server).terminateInstances,
344 "DescribeInstances": (*Server).describeInstances,346 "DescribeInstances": (*Server).describeInstances,
345 "CreateSecurityGroup": (*Server).createSecurityGroup,347 "CreateSecurityGroup": (*Server).createSecurityGroup,
348 "DescribeAvailabilityZones": (*Server).describeAvailabilityZones,
346 "DescribeSecurityGroups": (*Server).describeSecurityGroups,349 "DescribeSecurityGroups": (*Server).describeSecurityGroups,
347 "DeleteSecurityGroup": (*Server).deleteSecurityGroup,350 "DeleteSecurityGroup": (*Server).deleteSecurityGroup,
348 "AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress,351 "AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress,
@@ -363,7 +366,10 @@
363 "UnassignPrivateIpAddresses": (*Server).unassignPrivateIP,366 "UnassignPrivateIpAddresses": (*Server).unassignPrivateIP,
364}367}
365368
366const ownerId = "9876"369const (
370 ownerId = "9876"
371 defaultAvailZone = "us-east-1a"
372)
367373
368// newAction allocates a new action and adds it to the374// newAction allocates a new action and adds it to the
369// recorded list of server actions.375// recorded list of server actions.
@@ -418,6 +424,13 @@
418 }424 }
419 srv.groups[g.id] = g425 srv.groups[g.id] = g
420426
427 // Add a default availability zone.
428 var z availabilityZone
429 z.Name = defaultAvailZone
430 z.Region = "us-east-1"
431 z.State = "available"
432 srv.zones = []availabilityZone{z}
433
421 l, err := net.Listen("tcp", "localhost:0")434 l, err := net.Listen("tcp", "localhost:0")
422 if err != nil {435 if err != nil {
423 return nil, fmt.Errorf("cannot listen on localhost: %v", err)436 return nil, fmt.Errorf("cannot listen on localhost: %v", err)
@@ -446,6 +459,15 @@
446 srv.mu.Unlock()459 srv.mu.Unlock()
447}460}
448461
462func (srv *Server) SetAvailabilityZones(zones []ec2.AvailabilityZoneInfo) {
463 srv.mu.Lock()
464 srv.zones = make([]availabilityZone, len(zones))
465 for i, z := range zones {
466 srv.zones[i] = availabilityZone{z}
467 }
468 srv.mu.Unlock()
469}
470
449// SetInitialAttributes sets the given account attributes on the server.471// SetInitialAttributes sets the given account attributes on the server.
450func (srv *Server) SetInitialAttributes(attrs map[string][]string) {472func (srv *Server) SetInitialAttributes(attrs map[string][]string) {
451 for attrName, values := range attrs {473 for attrName, values := range attrs {
@@ -855,6 +877,7 @@
855 // make sure that form fields are correct before creating the reservation.877 // make sure that form fields are correct before creating the reservation.
856 instType := req.Form.Get("InstanceType")878 instType := req.Form.Get("InstanceType")
857 imageId := req.Form.Get("ImageId")879 imageId := req.Form.Get("ImageId")
880 availZone := req.Form.Get("AvailZone")
858881
859 r := srv.newReservation(srv.formToGroups(req.Form))882 r := srv.newReservation(srv.formToGroups(req.Form))
860883
@@ -892,7 +915,7 @@
892 resp.OwnerId = ownerId915 resp.OwnerId = ownerId
893916
894 for i := 0; i < max; i++ {917 for i := 0; i < max; i++ {
895 inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState)918 inst := srv.newInstance(r, instType, imageId, availZone, srv.initialInstanceState)
896 // Create any NICs on the instance subnet (if any), and then919 // Create any NICs on the instance subnet (if any), and then
897 // save the VPC and subnet ids on the instance, as EC2 does.920 // save the VPC and subnet ids on the instance, as EC2 does.
898 inst.ifaces = srv.createNICsOnRun(inst.id(), instSubnet, ifacesToCreate)921 inst.ifaces = srv.createNICsOnRun(inst.id(), instSubnet, ifacesToCreate)
@@ -942,7 +965,7 @@
942965
943 ids := make([]string, n)966 ids := make([]string, n)
944 for i := 0; i < n; i++ {967 for i := 0; i < n; i++ {
945 inst := srv.newInstance(r, instType, imageId, state)968 inst := srv.newInstance(r, instType, imageId, defaultAvailZone, state)
946 inst.vpcId = vpcId969 inst.vpcId = vpcId
947 inst.subnetId = subnetId970 inst.subnetId = subnetId
948 ids[i] = inst.id()971 ids[i] = inst.id()
@@ -958,11 +981,12 @@
958 return srv.NewInstancesVPC("", "", n, instType, imageId, state, groups)981 return srv.NewInstancesVPC("", "", n, instType, imageId, state, groups)
959}982}
960983
961func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance {984func (srv *Server) newInstance(r *reservation, instType string, imageId string, availZone string, state ec2.InstanceState) *Instance {
962 inst := &Instance{985 inst := &Instance{
963 seq: srv.maxId.next(),986 seq: srv.maxId.next(),
964 instType: instType,987 instType: instType,
965 imageId: imageId,988 imageId: imageId,
989 availZone: availZone,
966 state: state,990 state: state,
967 reservation: r,991 reservation: r,
968 }992 }
@@ -1036,6 +1060,7 @@
1036 IPAddress: fmt.Sprintf("8.0.0.%d", inst.seq%256),1060 IPAddress: fmt.Sprintf("8.0.0.%d", inst.seq%256),
1037 PrivateIPAddress: fmt.Sprintf("127.0.0.%d", inst.seq%256),1061 PrivateIPAddress: fmt.Sprintf("127.0.0.%d", inst.seq%256),
1038 State: inst.state,1062 State: inst.state,
1063 AvailZone: inst.availZone,
1039 VPCId: inst.vpcId,1064 VPCId: inst.vpcId,
1040 SubnetId: inst.subnetId,1065 SubnetId: inst.subnetId,
1041 NetworkInterfaces: inst.ifaces,1066 NetworkInterfaces: inst.ifaces,
@@ -1047,6 +1072,8 @@
1047 switch attr {1072 switch attr {
1048 case "architecture":1073 case "architecture":
1049 return value == "i386", nil1074 return value == "i386", nil
1075 case "availability-zone":
1076 return value == inst.availZone, nil
1050 case "instance-id":1077 case "instance-id":
1051 return inst.id() == value, nil1078 return inst.id() == value, nil
1052 case "subnet-id":1079 case "subnet-id":
@@ -1467,6 +1494,51 @@
1467 }1494 }
1468}1495}
14691496
1497type availabilityZone struct {
1498 ec2.AvailabilityZoneInfo
1499}
1500
1501func (z *availabilityZone) matchAttr(attr, value string) (ok bool, err error) {
1502 switch attr {
1503 case "message":
1504 for _, m := range z.MessageSet {
1505 if m == value {
1506 return true, nil
1507 }
1508 }
1509 return false, nil
1510 case "region-name":
1511 return z.Region == value, nil
1512 case "state":
1513 switch value {
1514 case "available", "impaired", "unavailable":
1515 return z.State == value, nil
1516 }
1517 return false, fmt.Errorf("invalid state %q", value)
1518 case "zone-name":
1519 return z.Name == value, nil
1520 }
1521 return false, fmt.Errorf("unknown attribute %q", attr)
1522}
1523
1524func (srv *Server) describeAvailabilityZones(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
1525 srv.mu.Lock()
1526 defer srv.mu.Unlock()
1527
1528 f := newFilter(req.Form)
1529 var resp ec2.AvailabilityZonesResp
1530 resp.RequestId = reqId
1531 for _, zone := range srv.zones {
1532 ok, err := f.ok(&zone)
1533 if ok {
1534 resp.Zones = append(resp.Zones, zone.AvailabilityZoneInfo)
1535 } else if err != nil {
1536 fatalf(400, "InvalidParameterValue", "describe availability zones: %v", err)
1537 }
1538 }
1539 return &resp
1540}
1541
1470func (srv *Server) createVpc(w http.ResponseWriter, req *http.Request, reqId string) interface{} {1542func (srv *Server) createVpc(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
1471 cidrBlock := parseCidr(req.Form.Get("CidrBlock"))1543 cidrBlock := parseCidr(req.Form.Get("CidrBlock"))
1472 tenancy := req.Form.Get("InstanceTenancy")1544 tenancy := req.Form.Get("InstanceTenancy")
14731545
=== modified file 'ec2/responses_test.go'
--- ec2/responses_test.go 2014-05-08 16:37:32 +0000
+++ ec2/responses_test.go 2014-05-30 09:44:09 +0000
@@ -662,6 +662,62 @@
662</RebootInstancesResponse>662</RebootInstancesResponse>
663`663`
664664
665// http://goo.gl/ylxT4R
666var DescribeAvailabilityZonesExample1 = `
667<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2014-05-01/">
668 <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
669 <availabilityZoneInfo>
670 <item>
671 <zoneName>us-east-1a</zoneName>
672 <zoneState>available</zoneState>
673 <regionName>us-east-1</regionName>
674 <messageSet/>
675 </item>
676 <item>
677 <zoneName>us-east-1b</zoneName>
678 <zoneState>available</zoneState>
679 <regionName>us-east-1</regionName>
680 <messageSet/>
681 </item>
682 <item>
683 <zoneName>us-east-1c</zoneName>
684 <zoneState>available</zoneState>
685 <regionName>us-east-1</regionName>
686 <messageSet/>
687 </item>
688 <item>
689 <zoneName>us-east-1d</zoneName>
690 <zoneState>available</zoneState>
691 <regionName>us-east-1</regionName>
692 <messageSet/>
693 </item>
694 </availabilityZoneInfo>
695</DescribeAvailabilityZonesResponse>
696`
697
698// http://goo.gl/ylxT4R
699var DescribeAvailabilityZonesExample2 = `
700<DescribeAvailabilityZonesResponse xmlns="http://ec2.amazonaws.com/doc/2014-05-01/">
701 <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
702 <availabilityZoneInfo>
703 <item>
704 <zoneName>us-east-1a</zoneName>
705 <zoneState>impaired</zoneState>
706 <regionName>us-east-1</regionName>
707 <messageSet/>
708 </item>
709 <item>
710 <zoneName>us-east-1b</zoneName>
711 <zoneState>unavailable</zoneState>
712 <regionName>us-east-1</regionName>
713 <messageSet>
714 <item>us-east-1b is currently down for maintenance.</item>
715 </messageSet>
716 </item>
717 </availabilityZoneInfo>
718</DescribeAvailabilityZonesResponse>
719`
720
665// http://goo.gl/nkwjv721// http://goo.gl/nkwjv
666var CreateVpcExample = `722var CreateVpcExample = `
667<CreateVpcResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">723<CreateVpcResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">

Subscribers

People subscribed via source and target branches