Merge lp:~allenap/juju-core/azure-open-machine-ports into lp:~go-bot/juju-core/trunk
- azure-open-machine-ports
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+177717@code.launchpad.net |
Commit message
Implement azureInstance.
Description of the change
Implement azureInstance.
Gavin Panella (allenap) wrote : | # |
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.
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.
[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=
Raphaël Badin (rvb) wrote : | # |
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.
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…
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:
it.
Thank you both for the reviews!
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.
Raphaël Badin (rvb) wrote : | # |
> The code as I've written it will work now and if the
service:
> ratio changes so I think it's better to leave it.
I agree this is the way to go.
Preview Diff
1 | === modified file 'environs/azure/environ.go' |
2 | --- environs/azure/environ.go 2013-07-30 09:30:57 +0000 |
3 | +++ environs/azure/environ.go 2013-07-30 23:14:25 +0000 |
4 | @@ -483,7 +483,7 @@ |
5 | if err != nil { |
6 | return nil, fmt.Errorf("could not get instance %q: %v", instanceName, err) |
7 | } |
8 | - instance := &azureInstance{service.HostedServiceDescriptor} |
9 | + instance := &azureInstance{service.HostedServiceDescriptor, env} |
10 | return instance, nil |
11 | } |
12 | |
13 | @@ -627,7 +627,7 @@ |
14 | return nil, environs.ErrNoInstances |
15 | } |
16 | |
17 | - instances := convertToInstances(services) |
18 | + instances := convertToInstances(services, env) |
19 | |
20 | // Check if we got a partial result. |
21 | if len(ids) != len(instances) { |
22 | @@ -652,7 +652,7 @@ |
23 | if err != nil { |
24 | return nil, err |
25 | } |
26 | - return convertToInstances(services), nil |
27 | + return convertToInstances(services, env), nil |
28 | } |
29 | |
30 | // getEnvPrefix returns the prefix used to name the objects specific to this |
31 | @@ -663,10 +663,10 @@ |
32 | |
33 | // convertToInstances converts a slice of gwacl.HostedServiceDescriptor objects |
34 | // into a slice of instance.Instance objects. |
35 | -func convertToInstances(services []gwacl.HostedServiceDescriptor) []instance.Instance { |
36 | +func convertToInstances(services []gwacl.HostedServiceDescriptor, env *azureEnviron) []instance.Instance { |
37 | instances := make([]instance.Instance, len(services)) |
38 | for i, service := range services { |
39 | - instances[i] = &azureInstance{service} |
40 | + instances[i] = &azureInstance{service, env} |
41 | } |
42 | return instances |
43 | } |
44 | |
45 | === modified file 'environs/azure/environ_test.go' |
46 | --- environs/azure/environ_test.go 2013-07-30 09:20:19 +0000 |
47 | +++ environs/azure/environ_test.go 2013-07-30 23:14:25 +0000 |
48 | @@ -586,7 +586,9 @@ |
49 | responses := buildDestroyAzureServiceResponses(c, services) |
50 | requests := gwacl.PatchManagementAPIResponses(responses) |
51 | env := makeEnviron(c) |
52 | - instances := convertToInstances([]gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc}) |
53 | + instances := convertToInstances( |
54 | + []gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc}, |
55 | + env) |
56 | |
57 | err := env.StopInstances(instances) |
58 | c.Check(err, IsNil) |
59 | @@ -630,7 +632,7 @@ |
60 | cleanupResponses := getVnetAndAffinityGroupCleanupResponses(c) |
61 | responses = append(responses, cleanupResponses...) |
62 | gwacl.PatchManagementAPIResponses(responses) |
63 | - instances := convertToInstances([]gwacl.HostedServiceDescriptor{}) |
64 | + instances := convertToInstances([]gwacl.HostedServiceDescriptor{}, env) |
65 | |
66 | err := env.Destroy(instances) |
67 | c.Check(err, IsNil) |
68 | @@ -665,7 +667,7 @@ |
69 | } |
70 | responses = append(responses, cleanupResponses...) |
71 | requests := gwacl.PatchManagementAPIResponses(responses) |
72 | - instances := convertToInstances([]gwacl.HostedServiceDescriptor{}) |
73 | + instances := convertToInstances([]gwacl.HostedServiceDescriptor{}, env) |
74 | |
75 | err = env.Destroy(instances) |
76 | c.Check(err, IsNil) |
77 | @@ -718,7 +720,9 @@ |
78 | requests := gwacl.PatchManagementAPIResponses(responses) |
79 | |
80 | // Call Destroy with service1 and service2. |
81 | - instances := convertToInstances([]gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc}) |
82 | + instances := convertToInstances( |
83 | + []gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc}, |
84 | + env) |
85 | err := env.Destroy(instances) |
86 | c.Check(err, IsNil) |
87 | |
88 | @@ -751,6 +755,9 @@ |
89 | c.Check(err, IsNil) |
90 | |
91 | c.Check(string(instance.Id()), Equals, serviceName) |
92 | + c.Check(instance, FitsTypeOf, &azureInstance{}) |
93 | + azInstance := instance.(*azureInstance) |
94 | + c.Check(azInstance.environ, Equals, env) |
95 | } |
96 | |
97 | func (*EnvironSuite) TestNewOSVirtualDisk(c *C) { |
98 | @@ -1008,3 +1015,15 @@ |
99 | // may have to become configurable. |
100 | c.Check(env.getImageMetadataSigningRequired(), Equals, true) |
101 | } |
102 | + |
103 | +func (*EnvironSuite) TestConvertToInstances(c *C) { |
104 | + services := []gwacl.HostedServiceDescriptor{ |
105 | + {ServiceName: "foo"}, {ServiceName: "bar"}, |
106 | + } |
107 | + env := makeEnviron(c) |
108 | + instances := convertToInstances(services, env) |
109 | + c.Check(instances, DeepEquals, []instance.Instance{ |
110 | + &azureInstance{services[0], env}, |
111 | + &azureInstance{services[1], env}, |
112 | + }) |
113 | +} |
114 | |
115 | === modified file 'environs/azure/instance.go' |
116 | --- environs/azure/instance.go 2013-07-19 01:18:26 +0000 |
117 | +++ environs/azure/instance.go 2013-07-30 23:14:25 +0000 |
118 | @@ -15,6 +15,7 @@ |
119 | type azureInstance struct { |
120 | // An instance contains an Azure Service (instance==service). |
121 | gwacl.HostedServiceDescriptor |
122 | + environ *azureEnviron |
123 | } |
124 | |
125 | // azureInstance implements Instance. |
126 | @@ -50,7 +51,46 @@ |
127 | |
128 | // OpenPorts is specified in the Instance interface. |
129 | func (azInstance *azureInstance) OpenPorts(machineId string, ports []instance.Port) error { |
130 | - // TODO: implement this. |
131 | + env := azInstance.environ |
132 | + context, err := env.getManagementAPI() |
133 | + if err != nil { |
134 | + return err |
135 | + } |
136 | + defer env.releaseManagementAPI(context) |
137 | + |
138 | + env.Lock() |
139 | + defer env.Unlock() |
140 | + |
141 | + deployments, err := context.ListAllDeployments(&gwacl.ListAllDeploymentsRequest{ |
142 | + ServiceName: azInstance.ServiceName, |
143 | + }) |
144 | + if err != nil { |
145 | + return err |
146 | + } |
147 | + |
148 | + for _, deployment := range deployments { |
149 | + for _, role := range deployment.RoleList { |
150 | + request := &gwacl.AddRoleEndpointsRequest{ |
151 | + ServiceName: azInstance.ServiceName, |
152 | + DeploymentName: deployment.Name, |
153 | + RoleName: role.RoleName, |
154 | + } |
155 | + for _, port := range ports { |
156 | + request.InputEndpoints = append( |
157 | + request.InputEndpoints, gwacl.InputEndpoint{ |
158 | + LocalPort: port.Number, |
159 | + Name: fmt.Sprintf("%s%d", port.Protocol, port.Number), |
160 | + Port: port.Number, |
161 | + Protocol: port.Protocol, |
162 | + }) |
163 | + } |
164 | + err := context.AddRoleEndpoints(request) |
165 | + if err != nil { |
166 | + return err |
167 | + } |
168 | + } |
169 | + } |
170 | + |
171 | return nil |
172 | } |
173 | |
174 | |
175 | === modified file 'environs/azure/instance_test.go' |
176 | --- environs/azure/instance_test.go 2013-07-16 03:10:34 +0000 |
177 | +++ environs/azure/instance_test.go 2013-07-30 23:14:25 +0000 |
178 | @@ -9,7 +9,9 @@ |
179 | . "launchpad.net/gocheck" |
180 | "launchpad.net/gwacl" |
181 | |
182 | + "fmt" |
183 | "launchpad.net/juju-core/instance" |
184 | + "net/http" |
185 | ) |
186 | |
187 | type InstanceSuite struct{} |
188 | @@ -26,7 +28,7 @@ |
189 | func (*StorageSuite) TestId(c *C) { |
190 | serviceName := "test-name" |
191 | testService := makeHostedServiceDescriptor(serviceName) |
192 | - azInstance := azureInstance{*testService} |
193 | + azInstance := azureInstance{*testService, nil} |
194 | c.Check(azInstance.Id(), Equals, instance.Id(serviceName)) |
195 | } |
196 | |
197 | @@ -34,7 +36,7 @@ |
198 | // An instance's DNS name is computed from its hosted-service name. |
199 | host := "hostname" |
200 | testService := makeHostedServiceDescriptor(host) |
201 | - azInstance := azureInstance{*testService} |
202 | + azInstance := azureInstance{*testService, nil} |
203 | dnsName, err := azInstance.DNSName() |
204 | c.Assert(err, IsNil) |
205 | c.Check(dnsName, Equals, host+"."+AZURE_DOMAIN_NAME) |
206 | @@ -45,8 +47,196 @@ |
207 | // waiting involved. |
208 | host := "hostname" |
209 | testService := makeHostedServiceDescriptor(host) |
210 | - azInstance := azureInstance{*testService} |
211 | + azInstance := azureInstance{*testService, nil} |
212 | dnsName, err := azInstance.WaitDNSName() |
213 | c.Assert(err, IsNil) |
214 | c.Check(dnsName, Equals, host+"."+AZURE_DOMAIN_NAME) |
215 | } |
216 | + |
217 | +func makeRole(name string) gwacl.Role { |
218 | + return gwacl.Role{ |
219 | + RoleName: name, |
220 | + ConfigurationSets: []gwacl.ConfigurationSet{ |
221 | + {ConfigurationSetType: gwacl.CONFIG_SET_NETWORK}, |
222 | + }, |
223 | + } |
224 | +} |
225 | + |
226 | +func makeDeployment(name string, roles ...gwacl.Role) gwacl.Deployment { |
227 | + return gwacl.Deployment{ |
228 | + Name: name, |
229 | + RoleList: roles, |
230 | + } |
231 | +} |
232 | + |
233 | +func makeInputEndpoint(port int, protocol string) gwacl.InputEndpoint { |
234 | + return gwacl.InputEndpoint{ |
235 | + LocalPort: port, |
236 | + Name: fmt.Sprintf("%s%d", protocol, port), |
237 | + Port: port, |
238 | + Protocol: protocol, |
239 | + } |
240 | +} |
241 | + |
242 | +func serialize(c *C, object gwacl.AzureObject) []byte { |
243 | + xml, err := object.Serialize() |
244 | + c.Assert(err, IsNil) |
245 | + return []byte(xml) |
246 | +} |
247 | + |
248 | +func (*StorageSuite) TestOpenPorts(c *C) { |
249 | + service := makeHostedServiceDescriptor("service-name") |
250 | + deployments := []gwacl.Deployment{ |
251 | + makeDeployment("deployment-one", makeRole("role-one"), makeRole("role-two")), |
252 | + makeDeployment("deployment-two", makeRole("role-three")), |
253 | + } |
254 | + responses := []gwacl.DispatcherResponse{ |
255 | + // First, GetHostedServiceProperties |
256 | + gwacl.NewDispatcherResponse( |
257 | + serialize(c, &gwacl.HostedService{ |
258 | + Deployments: deployments, |
259 | + HostedServiceDescriptor: *service, |
260 | + XMLNS: gwacl.XMLNS, |
261 | + }), |
262 | + http.StatusOK, nil), |
263 | + } |
264 | + for _, deployment := range deployments { |
265 | + for _, role := range deployment.RoleList { |
266 | + // GetRole returns a PersistentVMRole. |
267 | + persistentRole := &gwacl.PersistentVMRole{ |
268 | + XMLNS: gwacl.XMLNS, |
269 | + RoleName: role.RoleName, |
270 | + ConfigurationSets: role.ConfigurationSets, |
271 | + } |
272 | + responses = append(responses, gwacl.NewDispatcherResponse( |
273 | + serialize(c, persistentRole), http.StatusOK, nil)) |
274 | + // UpdateRole expects a 200 response, that's all. |
275 | + responses = append(responses, |
276 | + gwacl.NewDispatcherResponse(nil, http.StatusOK, nil)) |
277 | + } |
278 | + } |
279 | + record := gwacl.PatchManagementAPIResponses(responses) |
280 | + env := makeEnviron(c) |
281 | + azInstance := azureInstance{*service, env} |
282 | + |
283 | + err := azInstance.OpenPorts("machine-id", []instance.Port{ |
284 | + {"tcp", 79}, {"tcp", 587}, {"udp", 9}, |
285 | + }) |
286 | + |
287 | + c.Assert(err, IsNil) |
288 | + expected := []struct { |
289 | + method string |
290 | + urlpattern string |
291 | + }{ |
292 | + {"GET", ".*/services/hostedservices/service-name[?].*"}, // GetHostedServiceProperties |
293 | + {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole |
294 | + {"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole |
295 | + {"GET", ".*/deployments/deployment-one/roles/role-two"}, // GetRole |
296 | + {"PUT", ".*/deployments/deployment-one/roles/role-two"}, // UpdateRole |
297 | + {"GET", ".*/deployments/deployment-two/roles/role-three"}, // GetRole |
298 | + {"PUT", ".*/deployments/deployment-two/roles/role-three"}, // UpdateRole |
299 | + } |
300 | + c.Assert(*record, HasLen, len(expected)) |
301 | + for index, request := range *record { |
302 | + c.Check(request.Method, Equals, expected[index].method) |
303 | + c.Check(request.URL, Matches, expected[index].urlpattern) |
304 | + } |
305 | + |
306 | + // A representative UpdateRole payload includes configuration for the |
307 | + // ports requested. |
308 | + role := &gwacl.PersistentVMRole{} |
309 | + err = role.Deserialize((*record)[2].Payload) |
310 | + c.Assert(err, IsNil) |
311 | + c.Check( |
312 | + *(role.ConfigurationSets[0].InputEndpoints), |
313 | + DeepEquals, []gwacl.InputEndpoint{ |
314 | + makeInputEndpoint(79, "tcp"), |
315 | + makeInputEndpoint(587, "tcp"), |
316 | + makeInputEndpoint(9, "udp"), |
317 | + }) |
318 | +} |
319 | + |
320 | +func (*StorageSuite) TestOpenPortsFailsWhenUnableToGetServiceProperties(c *C) { |
321 | + service := makeHostedServiceDescriptor("service-name") |
322 | + responses := []gwacl.DispatcherResponse{ |
323 | + // GetHostedServiceProperties breaks. |
324 | + gwacl.NewDispatcherResponse(nil, http.StatusInternalServerError, nil), |
325 | + } |
326 | + record := gwacl.PatchManagementAPIResponses(responses) |
327 | + env := makeEnviron(c) |
328 | + azInstance := azureInstance{*service, env} |
329 | + |
330 | + err := azInstance.OpenPorts("machine-id", []instance.Port{ |
331 | + {"tcp", 79}, {"tcp", 587}, {"udp", 9}, |
332 | + }) |
333 | + |
334 | + c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") |
335 | + c.Check(*record, HasLen, 1) |
336 | +} |
337 | + |
338 | +func (*StorageSuite) TestOpenPortsFailsWhenUnableToGetRole(c *C) { |
339 | + service := makeHostedServiceDescriptor("service-name") |
340 | + deployments := []gwacl.Deployment{ |
341 | + makeDeployment("deployment-one", makeRole("role-one")), |
342 | + } |
343 | + responses := []gwacl.DispatcherResponse{ |
344 | + // First, GetHostedServiceProperties |
345 | + gwacl.NewDispatcherResponse( |
346 | + serialize(c, &gwacl.HostedService{ |
347 | + Deployments: deployments, |
348 | + HostedServiceDescriptor: *service, |
349 | + XMLNS: gwacl.XMLNS, |
350 | + }), |
351 | + http.StatusOK, nil), |
352 | + // Second, GetRole fails |
353 | + gwacl.NewDispatcherResponse( |
354 | + nil, http.StatusInternalServerError, nil), |
355 | + } |
356 | + record := gwacl.PatchManagementAPIResponses(responses) |
357 | + env := makeEnviron(c) |
358 | + azInstance := azureInstance{*service, env} |
359 | + |
360 | + err := azInstance.OpenPorts("machine-id", []instance.Port{ |
361 | + {"tcp", 79}, {"tcp", 587}, {"udp", 9}, |
362 | + }) |
363 | + |
364 | + c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") |
365 | + c.Check(*record, HasLen, 2) |
366 | +} |
367 | + |
368 | +func (*StorageSuite) TestOpenPortsFailsWhenUnableToUpdateRole(c *C) { |
369 | + service := makeHostedServiceDescriptor("service-name") |
370 | + deployments := []gwacl.Deployment{ |
371 | + makeDeployment("deployment-one", makeRole("role-one")), |
372 | + } |
373 | + responses := []gwacl.DispatcherResponse{ |
374 | + // First, GetHostedServiceProperties |
375 | + gwacl.NewDispatcherResponse( |
376 | + serialize(c, &gwacl.HostedService{ |
377 | + Deployments: deployments, |
378 | + HostedServiceDescriptor: *service, |
379 | + XMLNS: gwacl.XMLNS, |
380 | + }), |
381 | + http.StatusOK, nil), |
382 | + // Seconds, GetRole |
383 | + gwacl.NewDispatcherResponse( |
384 | + serialize(c, &gwacl.PersistentVMRole{ |
385 | + XMLNS: gwacl.XMLNS, |
386 | + RoleName: "role-one", |
387 | + }), |
388 | + http.StatusOK, nil), |
389 | + // Third, UpdateRole fails |
390 | + gwacl.NewDispatcherResponse( |
391 | + nil, http.StatusInternalServerError, nil), |
392 | + } |
393 | + record := gwacl.PatchManagementAPIResponses(responses) |
394 | + env := makeEnviron(c) |
395 | + azInstance := azureInstance{*service, env} |
396 | + |
397 | + err := azInstance.OpenPorts("machine-id", []instance.Port{ |
398 | + {"tcp", 79}, {"tcp", 587}, {"udp", 9}, |
399 | + }) |
400 | + |
401 | + c.Check(err, ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") |
402 | + c.Check(*record, HasLen, 3) |
403 | +} |
Reviewers: mp+177717_ code.launchpad. net,
Message:
Please take a look.
Description: OpenPorts( )
Implement azureInstance.
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: azure/environ. go azure/environ_ test.go azure/instance. go azure/instance_ test.go
A [revision details]
M environs/
M environs/
M environs/
M environs/