Merge lp:~allenap/juju-core/azure-open-machine-ports into lp:~go-bot/juju-core/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 1568
Proposed branch: lp:~allenap/juju-core/azure-open-machine-ports
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 403 lines (+262/-13)
4 files modified
environs/azure/environ.go (+5/-5)
environs/azure/environ_test.go (+23/-4)
environs/azure/instance.go (+41/-1)
environs/azure/instance_test.go (+193/-3)
To merge this branch: bzr merge lp:~allenap/juju-core/azure-open-machine-ports
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+177717@code.launchpad.net

Commit message

Implement azureInstance.OpenPorts()

Description of the change

Implement azureInstance.OpenPorts()

https://codereview.appspot.com/12102045/

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Reviewers: mp+177717_code.launchpad.net,

Message:
Please take a look.

Description:
Implement azureInstance.OpenPorts()

https://code.launchpad.net/~allenap/juju-core/azure-open-machine-ports/+merge/177717

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M environs/azure/environ.go
   M environs/azure/environ_test.go
   M environs/azure/instance.go
   M environs/azure/instance_test.go

Revision history for this message
Julian Edwards (julian-edwards) wrote :

LGTM

Given that we always map one role to one deployment to one service, is
it strictly necessary to have that loop around deployments in
OpenPorts()?

Also does juju's identifier for a protocol match that of Azure's? The
doc for instance.Port does not say what "Protocol" is.

https://codereview.appspot.com/12102045/

Revision history for this message
Raphaël Badin (rvb) wrote :

On 2013/07/30 23:16:12, allenap wrote:
> Please take a look.

[0]

Julian has a point, only one deployment should be present in the
service, and it has the same name as the service
('azInstance.ServiceName'). That will save one API call.

[1]

We've used a "tag" to indicate all the places in the code which rely on
the assumption that one juju instance = one service = one deployment so
that if people change that model, they will have a marker indicating all
the places where things must be changed. Would you mind adding this
"tag" in a comment somwhere: "(instance==service)"?

https://codereview.appspot.com/12102045/

Revision history for this message
Raphaël Badin (rvb) wrote :
Revision history for this message
Raphaël Badin (rvb) wrote :

On 2013/07/31 09:00:46, rvb wrote:
> On 2013/07/30 23:16:12, allenap wrote:
> > Please take a look.

> [0]

> Julian has a point, only one deployment should be present in the
service, and it
> has the same name as the service ('azInstance.ServiceName'). That
will save one
> API call.

Actually, since you need to get all the roles for the deployment, I
don't think relying on the fact that we know there is only one
deployment named 'serviceName' will save us an API call…

https://codereview.appspot.com/12102045/

Revision history for this message
Gavin Panella (allenap) wrote :

On 2013/07/31 09:04:02, rvb wrote:
> Actually, since you need to get all the roles for the deployment, I
don't think
> relying on the fact that we know there is only one deployment named
> 'serviceName' will save us an API call…

The code as I've written it will work now and if the
service:deployment:role ratio changes so I think it's better to leave
it.

Thank you both for the reviews!

https://codereview.appspot.com/12102045/

Revision history for this message
Gavin Panella (allenap) wrote :

On 2013/07/30 23:53:07, julian.edwards wrote:
> Also does juju's identifier for a protocol match that of Azure's? The
doc for
> instance.Port does not say what "Protocol" is.

It seems to be either "tcp" or "udp". Azure's endpoint name seems to be
mainly for human consumption right now, albeit with undocumented
limitations on what form it may take.

https://codereview.appspot.com/12102045/

Revision history for this message
Raphaël Badin (rvb) wrote :

> The code as I've written it will work now and if the
service:deployment:role
> ratio changes so I think it's better to leave it.

I agree this is the way to go.

https://codereview.appspot.com/12102045/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'environs/azure/environ.go'
--- environs/azure/environ.go 2013-07-30 09:30:57 +0000
+++ environs/azure/environ.go 2013-07-30 23:14:25 +0000
@@ -483,7 +483,7 @@
483 if err != nil {483 if err != nil {
484 return nil, fmt.Errorf("could not get instance %q: %v", instanceName, err)484 return nil, fmt.Errorf("could not get instance %q: %v", instanceName, err)
485 }485 }
486 instance := &azureInstance{service.HostedServiceDescriptor}486 instance := &azureInstance{service.HostedServiceDescriptor, env}
487 return instance, nil487 return instance, nil
488}488}
489489
@@ -627,7 +627,7 @@
627 return nil, environs.ErrNoInstances627 return nil, environs.ErrNoInstances
628 }628 }
629629
630 instances := convertToInstances(services)630 instances := convertToInstances(services, env)
631631
632 // Check if we got a partial result.632 // Check if we got a partial result.
633 if len(ids) != len(instances) {633 if len(ids) != len(instances) {
@@ -652,7 +652,7 @@
652 if err != nil {652 if err != nil {
653 return nil, err653 return nil, err
654 }654 }
655 return convertToInstances(services), nil655 return convertToInstances(services, env), nil
656}656}
657657
658// getEnvPrefix returns the prefix used to name the objects specific to this658// getEnvPrefix returns the prefix used to name the objects specific to this
@@ -663,10 +663,10 @@
663663
664// convertToInstances converts a slice of gwacl.HostedServiceDescriptor objects664// convertToInstances converts a slice of gwacl.HostedServiceDescriptor objects
665// into a slice of instance.Instance objects.665// into a slice of instance.Instance objects.
666func convertToInstances(services []gwacl.HostedServiceDescriptor) []instance.Instance {666func convertToInstances(services []gwacl.HostedServiceDescriptor, env *azureEnviron) []instance.Instance {
667 instances := make([]instance.Instance, len(services))667 instances := make([]instance.Instance, len(services))
668 for i, service := range services {668 for i, service := range services {
669 instances[i] = &azureInstance{service}669 instances[i] = &azureInstance{service, env}
670 }670 }
671 return instances671 return instances
672}672}
673673
=== modified file 'environs/azure/environ_test.go'
--- environs/azure/environ_test.go 2013-07-30 09:20:19 +0000
+++ environs/azure/environ_test.go 2013-07-30 23:14:25 +0000
@@ -586,7 +586,9 @@
586 responses := buildDestroyAzureServiceResponses(c, services)586 responses := buildDestroyAzureServiceResponses(c, services)
587 requests := gwacl.PatchManagementAPIResponses(responses)587 requests := gwacl.PatchManagementAPIResponses(responses)
588 env := makeEnviron(c)588 env := makeEnviron(c)
589 instances := convertToInstances([]gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc})589 instances := convertToInstances(
590 []gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc},
591 env)
590592
591 err := env.StopInstances(instances)593 err := env.StopInstances(instances)
592 c.Check(err, IsNil)594 c.Check(err, IsNil)
@@ -630,7 +632,7 @@
630 cleanupResponses := getVnetAndAffinityGroupCleanupResponses(c)632 cleanupResponses := getVnetAndAffinityGroupCleanupResponses(c)
631 responses = append(responses, cleanupResponses...)633 responses = append(responses, cleanupResponses...)
632 gwacl.PatchManagementAPIResponses(responses)634 gwacl.PatchManagementAPIResponses(responses)
633 instances := convertToInstances([]gwacl.HostedServiceDescriptor{})635 instances := convertToInstances([]gwacl.HostedServiceDescriptor{}, env)
634636
635 err := env.Destroy(instances)637 err := env.Destroy(instances)
636 c.Check(err, IsNil)638 c.Check(err, IsNil)
@@ -665,7 +667,7 @@
665 }667 }
666 responses = append(responses, cleanupResponses...)668 responses = append(responses, cleanupResponses...)
667 requests := gwacl.PatchManagementAPIResponses(responses)669 requests := gwacl.PatchManagementAPIResponses(responses)
668 instances := convertToInstances([]gwacl.HostedServiceDescriptor{})670 instances := convertToInstances([]gwacl.HostedServiceDescriptor{}, env)
669671
670 err = env.Destroy(instances)672 err = env.Destroy(instances)
671 c.Check(err, IsNil)673 c.Check(err, IsNil)
@@ -718,7 +720,9 @@
718 requests := gwacl.PatchManagementAPIResponses(responses)720 requests := gwacl.PatchManagementAPIResponses(responses)
719721
720 // Call Destroy with service1 and service2.722 // Call Destroy with service1 and service2.
721 instances := convertToInstances([]gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc})723 instances := convertToInstances(
724 []gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc},
725 env)
722 err := env.Destroy(instances)726 err := env.Destroy(instances)
723 c.Check(err, IsNil)727 c.Check(err, IsNil)
724728
@@ -751,6 +755,9 @@
751 c.Check(err, IsNil)755 c.Check(err, IsNil)
752756
753 c.Check(string(instance.Id()), Equals, serviceName)757 c.Check(string(instance.Id()), Equals, serviceName)
758 c.Check(instance, FitsTypeOf, &azureInstance{})
759 azInstance := instance.(*azureInstance)
760 c.Check(azInstance.environ, Equals, env)
754}761}
755762
756func (*EnvironSuite) TestNewOSVirtualDisk(c *C) {763func (*EnvironSuite) TestNewOSVirtualDisk(c *C) {
@@ -1008,3 +1015,15 @@
1008 // may have to become configurable.1015 // may have to become configurable.
1009 c.Check(env.getImageMetadataSigningRequired(), Equals, true)1016 c.Check(env.getImageMetadataSigningRequired(), Equals, true)
1010}1017}
1018
1019func (*EnvironSuite) TestConvertToInstances(c *C) {
1020 services := []gwacl.HostedServiceDescriptor{
1021 {ServiceName: "foo"}, {ServiceName: "bar"},
1022 }
1023 env := makeEnviron(c)
1024 instances := convertToInstances(services, env)
1025 c.Check(instances, DeepEquals, []instance.Instance{
1026 &azureInstance{services[0], env},
1027 &azureInstance{services[1], env},
1028 })
1029}
10111030
=== modified file 'environs/azure/instance.go'
--- environs/azure/instance.go 2013-07-19 01:18:26 +0000
+++ environs/azure/instance.go 2013-07-30 23:14:25 +0000
@@ -15,6 +15,7 @@
15type azureInstance struct {15type azureInstance struct {
16 // An instance contains an Azure Service (instance==service).16 // An instance contains an Azure Service (instance==service).
17 gwacl.HostedServiceDescriptor17 gwacl.HostedServiceDescriptor
18 environ *azureEnviron
18}19}
1920
20// azureInstance implements Instance.21// azureInstance implements Instance.
@@ -50,7 +51,46 @@
5051
51// OpenPorts is specified in the Instance interface.52// OpenPorts is specified in the Instance interface.
52func (azInstance *azureInstance) OpenPorts(machineId string, ports []instance.Port) error {53func (azInstance *azureInstance) OpenPorts(machineId string, ports []instance.Port) error {
53 // TODO: implement this.54 env := azInstance.environ
55 context, err := env.getManagementAPI()
56 if err != nil {
57 return err
58 }
59 defer env.releaseManagementAPI(context)
60
61 env.Lock()
62 defer env.Unlock()
63
64 deployments, err := context.ListAllDeployments(&gwacl.ListAllDeploymentsRequest{
65 ServiceName: azInstance.ServiceName,
66 })
67 if err != nil {
68 return err
69 }
70
71 for _, deployment := range deployments {
72 for _, role := range deployment.RoleList {
73 request := &gwacl.AddRoleEndpointsRequest{
74 ServiceName: azInstance.ServiceName,
75 DeploymentName: deployment.Name,
76 RoleName: role.RoleName,
77 }
78 for _, port := range ports {
79 request.InputEndpoints = append(
80 request.InputEndpoints, gwacl.InputEndpoint{
81 LocalPort: port.Number,
82 Name: fmt.Sprintf("%s%d", port.Protocol, port.Number),
83 Port: port.Number,
84 Protocol: port.Protocol,
85 })
86 }
87 err := context.AddRoleEndpoints(request)
88 if err != nil {
89 return err
90 }
91 }
92 }
93
54 return nil94 return nil
55}95}
5696
5797
=== modified file 'environs/azure/instance_test.go'
--- environs/azure/instance_test.go 2013-07-16 03:10:34 +0000
+++ environs/azure/instance_test.go 2013-07-30 23:14:25 +0000
@@ -9,7 +9,9 @@
9 . "launchpad.net/gocheck"9 . "launchpad.net/gocheck"
10 "launchpad.net/gwacl"10 "launchpad.net/gwacl"
1111
12 "fmt"
12 "launchpad.net/juju-core/instance"13 "launchpad.net/juju-core/instance"
14 "net/http"
13)15)
1416
15type InstanceSuite struct{}17type InstanceSuite struct{}
@@ -26,7 +28,7 @@
26func (*StorageSuite) TestId(c *C) {28func (*StorageSuite) TestId(c *C) {
27 serviceName := "test-name"29 serviceName := "test-name"
28 testService := makeHostedServiceDescriptor(serviceName)30 testService := makeHostedServiceDescriptor(serviceName)
29 azInstance := azureInstance{*testService}31 azInstance := azureInstance{*testService, nil}
30 c.Check(azInstance.Id(), Equals, instance.Id(serviceName))32 c.Check(azInstance.Id(), Equals, instance.Id(serviceName))
31}33}
3234
@@ -34,7 +36,7 @@
34 // An instance's DNS name is computed from its hosted-service name.36 // An instance's DNS name is computed from its hosted-service name.
35 host := "hostname"37 host := "hostname"
36 testService := makeHostedServiceDescriptor(host)38 testService := makeHostedServiceDescriptor(host)
37 azInstance := azureInstance{*testService}39 azInstance := azureInstance{*testService, nil}
38 dnsName, err := azInstance.DNSName()40 dnsName, err := azInstance.DNSName()
39 c.Assert(err, IsNil)41 c.Assert(err, IsNil)
40 c.Check(dnsName, Equals, host+"."+AZURE_DOMAIN_NAME)42 c.Check(dnsName, Equals, host+"."+AZURE_DOMAIN_NAME)
@@ -45,8 +47,196 @@
45 // waiting involved.47 // waiting involved.
46 host := "hostname"48 host := "hostname"
47 testService := makeHostedServiceDescriptor(host)49 testService := makeHostedServiceDescriptor(host)
48 azInstance := azureInstance{*testService}50 azInstance := azureInstance{*testService, nil}
49 dnsName, err := azInstance.WaitDNSName()51 dnsName, err := azInstance.WaitDNSName()
50 c.Assert(err, IsNil)52 c.Assert(err, IsNil)
51 c.Check(dnsName, Equals, host+"."+AZURE_DOMAIN_NAME)53 c.Check(dnsName, Equals, host+"."+AZURE_DOMAIN_NAME)
52}54}
55
56func makeRole(name string) gwacl.Role {
57 return gwacl.Role{
58 RoleName: name,
59 ConfigurationSets: []gwacl.ConfigurationSet{
60 {ConfigurationSetType: gwacl.CONFIG_SET_NETWORK},
61 },
62 }
63}
64
65func makeDeployment(name string, roles ...gwacl.Role) gwacl.Deployment {
66 return gwacl.Deployment{
67 Name: name,
68 RoleList: roles,
69 }
70}
71
72func makeInputEndpoint(port int, protocol string) gwacl.InputEndpoint {
73 return gwacl.InputEndpoint{
74 LocalPort: port,
75 Name: fmt.Sprintf("%s%d", protocol, port),
76 Port: port,
77 Protocol: protocol,
78 }
79}
80
81func serialize(c *C, object gwacl.AzureObject) []byte {
82 xml, err := object.Serialize()
83 c.Assert(err, IsNil)
84 return []byte(xml)
85}
86
87func (*StorageSuite) TestOpenPorts(c *C) {
88 service := makeHostedServiceDescriptor("service-name")
89 deployments := []gwacl.Deployment{
90 makeDeployment("deployment-one", makeRole("role-one"), makeRole("role-two")),
91 makeDeployment("deployment-two", makeRole("role-three")),
92 }
93 responses := []gwacl.DispatcherResponse{
94 // First, GetHostedServiceProperties
95 gwacl.NewDispatcherResponse(
96 serialize(c, &gwacl.HostedService{
97 Deployments: deployments,
98 HostedServiceDescriptor: *service,
99 XMLNS: gwacl.XMLNS,
100 }),
101 http.StatusOK, nil),
102 }
103 for _, deployment := range deployments {
104 for _, role := range deployment.RoleList {
105 // GetRole returns a PersistentVMRole.
106 persistentRole := &gwacl.PersistentVMRole{
107 XMLNS: gwacl.XMLNS,
108 RoleName: role.RoleName,
109 ConfigurationSets: role.ConfigurationSets,
110 }
111 responses = append(responses, gwacl.NewDispatcherResponse(
112 serialize(c, persistentRole), http.StatusOK, nil))
113 // UpdateRole expects a 200 response, that's all.
114 responses = append(responses,
115 gwacl.NewDispatcherResponse(nil, http.StatusOK, nil))
116 }
117 }
118 record := gwacl.PatchManagementAPIResponses(responses)
119 env := makeEnviron(c)
120 azInstance := azureInstance{*service, env}
121
122 err := azInstance.OpenPorts("machine-id", []instance.Port{
123 {"tcp", 79}, {"tcp", 587}, {"udp", 9},
124 })
125
126 c.Assert(err, IsNil)
127 expected := []struct {
128 method string
129 urlpattern string
130 }{
131 {"GET", ".*/services/hostedservices/service-name[?].*"}, // GetHostedServiceProperties
132 {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole
133 {"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole
134 {"GET", ".*/deployments/deployment-one/roles/role-two"}, // GetRole
135 {"PUT", ".*/deployments/deployment-one/roles/role-two"}, // UpdateRole
136 {"GET", ".*/deployments/deployment-two/roles/role-three"}, // GetRole
137 {"PUT", ".*/deployments/deployment-two/roles/role-three"}, // UpdateRole
138 }
139 c.Assert(*record, HasLen, len(expected))
140 for index, request := range *record {
141 c.Check(request.Method, Equals, expected[index].method)
142 c.Check(request.URL, Matches, expected[index].urlpattern)
143 }
144
145 // A representative UpdateRole payload includes configuration for the
146 // ports requested.
147 role := &gwacl.PersistentVMRole{}
148 err = role.Deserialize((*record)[2].Payload)
149 c.Assert(err, IsNil)
150 c.Check(
151 *(role.ConfigurationSets[0].InputEndpoints),
152 DeepEquals, []gwacl.InputEndpoint{
153 makeInputEndpoint(79, "tcp"),
154 makeInputEndpoint(587, "tcp"),
155 makeInputEndpoint(9, "udp"),
156 })
157}
158
159func (*StorageSuite) TestOpenPortsFailsWhenUnableToGetServiceProperties(c *C) {
160 service := makeHostedServiceDescriptor("service-name")
161 responses := []gwacl.DispatcherResponse{
162 // GetHostedServiceProperties breaks.
163 gwacl.NewDispatcherResponse(nil, http.StatusInternalServerError, nil),
164 }
165 record := gwacl.PatchManagementAPIResponses(responses)
166 env := makeEnviron(c)
167 azInstance := azureInstance{*service, env}
168
169 err := azInstance.OpenPorts("machine-id", []instance.Port{
170 {"tcp", 79}, {"tcp", 587}, {"udp", 9},
171 })
172
173 c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]")
174 c.Check(*record, HasLen, 1)
175}
176
177func (*StorageSuite) TestOpenPortsFailsWhenUnableToGetRole(c *C) {
178 service := makeHostedServiceDescriptor("service-name")
179 deployments := []gwacl.Deployment{
180 makeDeployment("deployment-one", makeRole("role-one")),
181 }
182 responses := []gwacl.DispatcherResponse{
183 // First, GetHostedServiceProperties
184 gwacl.NewDispatcherResponse(
185 serialize(c, &gwacl.HostedService{
186 Deployments: deployments,
187 HostedServiceDescriptor: *service,
188 XMLNS: gwacl.XMLNS,
189 }),
190 http.StatusOK, nil),
191 // Second, GetRole fails
192 gwacl.NewDispatcherResponse(
193 nil, http.StatusInternalServerError, nil),
194 }
195 record := gwacl.PatchManagementAPIResponses(responses)
196 env := makeEnviron(c)
197 azInstance := azureInstance{*service, env}
198
199 err := azInstance.OpenPorts("machine-id", []instance.Port{
200 {"tcp", 79}, {"tcp", 587}, {"udp", 9},
201 })
202
203 c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]")
204 c.Check(*record, HasLen, 2)
205}
206
207func (*StorageSuite) TestOpenPortsFailsWhenUnableToUpdateRole(c *C) {
208 service := makeHostedServiceDescriptor("service-name")
209 deployments := []gwacl.Deployment{
210 makeDeployment("deployment-one", makeRole("role-one")),
211 }
212 responses := []gwacl.DispatcherResponse{
213 // First, GetHostedServiceProperties
214 gwacl.NewDispatcherResponse(
215 serialize(c, &gwacl.HostedService{
216 Deployments: deployments,
217 HostedServiceDescriptor: *service,
218 XMLNS: gwacl.XMLNS,
219 }),
220 http.StatusOK, nil),
221 // Seconds, GetRole
222 gwacl.NewDispatcherResponse(
223 serialize(c, &gwacl.PersistentVMRole{
224 XMLNS: gwacl.XMLNS,
225 RoleName: "role-one",
226 }),
227 http.StatusOK, nil),
228 // Third, UpdateRole fails
229 gwacl.NewDispatcherResponse(
230 nil, http.StatusInternalServerError, nil),
231 }
232 record := gwacl.PatchManagementAPIResponses(responses)
233 env := makeEnviron(c)
234 azInstance := azureInstance{*service, env}
235
236 err := azInstance.OpenPorts("machine-id", []instance.Port{
237 {"tcp", 79}, {"tcp", 587}, {"udp", 9},
238 })
239
240 c.Check(err, ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]")
241 c.Check(*record, HasLen, 3)
242}

Subscribers

People subscribed via source and target branches

to status/vote changes: