Merge lp:~rvb/gwacl/fix-destroydeploy2 into lp:gwacl

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: 166
Merged at revision: 163
Proposed branch: lp:~rvb/gwacl/fix-destroydeploy2
Merge into: lp:gwacl
Diff against target: 257 lines (+33/-102)
3 files modified
example/management/run.go (+4/-18)
management.go (+9/-19)
management_test.go (+20/-65)
To merge this branch: bzr merge lp:~rvb/gwacl/fix-destroydeploy2
Reviewer Review Type Date Requested Status
Gavin Panella Approve
Review via email: mp+173095@code.launchpad.net

Commit message

Do not attempt to stop the VMs inside DestroyDeployment.

Description of the change

I found that it's quicker a more reliable to just delete the deployment instead of trying to shutdown the VMs when destroying a deployment. When we delete the deployment object, the VMs are shut down are removed. The only thing left are the disks.

Added bonus: the testing becomes simpler as we issue less API requests.

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

I surprised this works, but looks good.

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

On Thursday 04 Jul 2013 22:17:24 you wrote:
> Review: Approve
>
> I surprised this works, but looks good.

Me too, it doesn't work in the UI!

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

> On Thursday 04 Jul 2013 22:17:24 you wrote:
> > Review: Approve
> >
> > I surprised this works, but looks good.

Surprisingly, it seems to be very reliable and the only long operation is removing the disk(s) (takes ~10 minutes for one disk). Can you confirm that it works fine for you too?

> Me too, it doesn't work in the UI!

Hum, AFAIK deployments are not exposed as first-class objects in the UI so how can you say that it does not work in the UI?

In the UI, when you delete a cloud service with a deployment in it, you get a popup to ask you if you want to delete the deployment as well. That's how I got the idea for this branch (this uses the API to mimic this behavior — plus the removal of the disks).

Also, the doc page for "delete hosted service" mentions that you have to delete the deployments inside the service before you can get rid of the service (http://msdn.microsoft.com/en-us/library/windowsazure/gg441305.aspx) but the doc page for "delete deployment" doesn't say you have to delete the VMs inside it (http://msdn.microsoft.com/en-us/library/windowsazure/ee460815.aspx).

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

On Friday 05 Jul 2013 05:57:25 you wrote:
> > On Thursday 04 Jul 2013 22:17:24 you wrote:
> > > Review: Approve
> > >
> > > I surprised this works, but looks good.
>
> Surprisingly, it seems to be very reliable and the only long operation is
> removing the disk(s) (takes ~10 minutes for one disk). Can you confirm
> that it works fine for you too?
> > Me too, it doesn't work in the UI!
>
> Hum, AFAIK deployments are not exposed as first-class objects in the UI so
> how can you say that it does not work in the UI?

I'm talking about deleting the service.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'example/management/run.go'
--- example/management/run.go 2013-07-02 13:53:21 +0000
+++ example/management/run.go 2013-07-04 20:25:31 +0000
@@ -172,17 +172,10 @@
172 fmt.Println("Done adding VM deployment\n")172 fmt.Println("Done adding VM deployment\n")
173173
174 defer func() {174 defer func() {
175 fmt.Println("Deleting VM disk...")175 fmt.Printf("Destroying deployment %s...\n", machineName)
176 err = api.DeleteDisk(diskName)176 requestDestroyDeployment := &gwacl.DestroyDeploymentRequest{ServiceName: hostServiceName, DeploymentName: machineName}
177 checkError(err)177 api.DestroyDeployment(requestDestroyDeployment)
178 fmt.Println("Done deleting VM disk\n")178 fmt.Println("Done destroying deployment\n")
179 }()
180
181 defer func() {
182 fmt.Println("Deleting deployment...")
183 err = api.DeleteDeployment(hostServiceName, deployment.Name)
184 checkError(err)
185 fmt.Println("Done deleting deployment\n")
186 }()179 }()
187180
188 fmt.Println("Starting VM...")181 fmt.Println("Starting VM...")
@@ -190,13 +183,6 @@
190 checkError(err)183 checkError(err)
191 fmt.Println("Done starting VM\n")184 fmt.Println("Done starting VM\n")
192185
193 defer func() {
194 fmt.Println("Stopping VM...")
195 err = api.ShutdownRole(&gwacl.ShutdownRoleRequest{hostServiceName, deployment.Name, role.RoleName})
196 checkError(err)
197 fmt.Println("Done stopping VM\n")
198 }()
199
200 fmt.Println("Listing VM...")186 fmt.Println("Listing VM...")
201 instances, err := api.ListInstances(&gwacl.ListInstancesRequest{hostServiceName})187 instances, err := api.ListInstances(&gwacl.ListInstancesRequest{hostServiceName})
202 checkError(err)188 checkError(err)
203189
=== modified file 'management.go'
--- management.go 2013-07-04 09:53:30 +0000
+++ management.go 2013-07-04 20:25:31 +0000
@@ -102,7 +102,7 @@
102}102}
103103
104// DestroyDeployment brings down all resources within a deployment - running104// DestroyDeployment brings down all resources within a deployment - running
105// instances, disks, etc. - then deletes the deployment itself.105// instances, disks, etc. - and deletes the deployment itself.
106func (api *ManagementAPI) DestroyDeployment(request *DestroyDeploymentRequest) error {106func (api *ManagementAPI) DestroyDeployment(request *DestroyDeploymentRequest) error {
107 deployment, err := api.GetDeployment(&GetDeploymentRequest{107 deployment, err := api.GetDeployment(&GetDeploymentRequest{
108 ServiceName: request.ServiceName,108 ServiceName: request.ServiceName,
@@ -114,42 +114,32 @@
114 }114 }
115 return err115 return err
116 }116 }
117 // 1. Stop VMs.117 // 1. Get the list of the VM disks.
118 for _, roleInstance := range deployment.RoleInstanceList {
119 err = api.ShutdownRole(&ShutdownRoleRequest{
120 ServiceName: request.ServiceName,
121 DeploymentName: request.DeploymentName,
122 RoleName: roleInstance.RoleName,
123 })
124 if err != nil && !IsNotFoundError(err) {
125 return err
126 }
127 }
128 // 2. Delete VM disks.
129 diskNameMap := make(map[string]bool)118 diskNameMap := make(map[string]bool)
130 for _, role := range deployment.RoleList {119 for _, role := range deployment.RoleList {
131 for _, osVHD := range role.OSVirtualHardDisk {120 for _, osVHD := range role.OSVirtualHardDisk {
132 diskNameMap[osVHD.DiskName] = true121 diskNameMap[osVHD.DiskName] = true
133 }122 }
134 }123 }
124 // 2. Delete deployment. This will delete all the role instances inside
125 // this deployment as a side effect.
126 err = api.DeleteDeployment(request.ServiceName, request.DeploymentName)
127 if err != nil && !IsNotFoundError(err) {
128 return err
129 }
135 // Sort the disk names to aid testing.130 // Sort the disk names to aid testing.
136 diskNames := []string{}131 diskNames := []string{}
137 for diskName := range diskNameMap {132 for diskName := range diskNameMap {
138 diskNames = append(diskNames, diskName)133 diskNames = append(diskNames, diskName)
139 }134 }
140 sort.Strings(diskNames)135 sort.Strings(diskNames)
141 // Actually delete them.136 // 3. Delete the disks.
142 for _, diskName := range diskNames {137 for _, diskName := range diskNames {
143 err = api.DeleteDisk(diskName)138 err = api.DeleteDisk(diskName)
144 if err != nil && !IsNotFoundError(err) {139 if err != nil && !IsNotFoundError(err) {
145 return err140 return err
146 }141 }
147 }142 }
148 // 3. Delete deployment.
149 err = api.DeleteDeployment(request.ServiceName, request.DeploymentName)
150 if err != nil && !IsNotFoundError(err) {
151 return err
152 }
153 // Done.143 // Done.
154 return nil144 return nil
155}145}
156146
=== modified file 'management_test.go'
--- management_test.go 2013-07-04 10:53:40 +0000
+++ management_test.go 2013-07-04 20:25:31 +0000
@@ -250,12 +250,10 @@
250 var responses []DispatcherResponse250 var responses []DispatcherResponse
251 // Prepare.251 // Prepare.
252 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)252 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)
253 // For shutting down VMs.253 // For deleting the deployment.
254 responses = append(responses, exampleOkayResponse, exampleOkayResponse)
255 // For deleting disks.
256 responses = append(responses, exampleOkayResponse, exampleOkayResponse, exampleOkayResponse)
257 // For other requests.
258 responses = append(responses, exampleOkayResponse)254 responses = append(responses, exampleOkayResponse)
255 // For deleting disks.
256 responses = append(responses, exampleOkayResponse, exampleOkayResponse, exampleOkayResponse)
259 record := []*X509Request{}257 record := []*X509Request{}
260 rigRecordingPreparedResponseDispatcher(&record, responses)258 rigRecordingPreparedResponseDispatcher(&record, responses)
261 // Exercise DestroyDeployment.259 // Exercise DestroyDeployment.
@@ -266,23 +264,14 @@
266 }264 }
267 err := api.DestroyDeployment(request)265 err := api.DestroyDeployment(request)
268 c.Assert(err, IsNil)266 c.Assert(err, IsNil)
269 c.Check(record, HasLen, 7)267 c.Check(record, HasLen, 5)
270 // The first request is for the deployment.
271 assertGetDeploymentRequest(c, api, &GetDeploymentRequest{268 assertGetDeploymentRequest(c, api, &GetDeploymentRequest{
272 request.ServiceName, request.DeploymentName}, record[0])269 request.ServiceName, request.DeploymentName}, record[0])
273 // The second is to shutdown RoleInstance "one".
274 assertShutdownRoleRequest(c, api, &ShutdownRoleRequest{
275 request.ServiceName, request.DeploymentName, "one"}, record[1])
276 // The third is to shutdown RoleInstance "two", similar to like the second.
277 assertShutdownRoleRequest(c, api, &ShutdownRoleRequest{
278 request.ServiceName, request.DeploymentName, "two"}, record[2])
279 // The fourth, fifth and sixth requests delete disks.
280 assertDeleteDiskRequest(c, api, "disk1", record[3])
281 assertDeleteDiskRequest(c, api, "disk2", record[4])
282 assertDeleteDiskRequest(c, api, "disk3", record[5])
283 // The fourth request deletes the deployment.
284 assertDeleteDeploymentRequest(c, api, request.ServiceName,270 assertDeleteDeploymentRequest(c, api, request.ServiceName,
285 request.DeploymentName, record[6])271 request.DeploymentName, record[1])
272 assertDeleteDiskRequest(c, api, "disk1", record[2])
273 assertDeleteDiskRequest(c, api, "disk2", record[3])
274 assertDeleteDiskRequest(c, api, "disk3", record[4])
286}275}
287276
288func (suite *suiteDestroyDeployment) TestOkayWhenDeploymentNotFound(c *C) {277func (suite *suiteDestroyDeployment) TestOkayWhenDeploymentNotFound(c *C) {
@@ -306,12 +295,10 @@
306 var responses []DispatcherResponse295 var responses []DispatcherResponse
307 // Prepare.296 // Prepare.
308 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)297 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)
309 // For shutting down VMs.298 // For deleting the deployment.
310 responses = append(responses, exampleNotFoundResponse, exampleNotFoundResponse)299 responses = append(responses, exampleNotFoundResponse)
311 // For deleting disks.300 // For deleting the disks.
312 responses = append(responses, exampleNotFoundResponse, exampleNotFoundResponse, exampleNotFoundResponse)301 responses = append(responses, exampleNotFoundResponse, exampleNotFoundResponse, exampleNotFoundResponse)
313 // For other requests.
314 responses = append(responses, exampleNotFoundResponse)
315 record := []*X509Request{}302 record := []*X509Request{}
316 rigRecordingPreparedResponseDispatcher(&record, responses)303 rigRecordingPreparedResponseDispatcher(&record, responses)
317 // Exercise DestroyDeployment.304 // Exercise DestroyDeployment.
@@ -322,23 +309,14 @@
322 }309 }
323 err := api.DestroyDeployment(request)310 err := api.DestroyDeployment(request)
324 c.Assert(err, IsNil)311 c.Assert(err, IsNil)
325 c.Check(record, HasLen, 7)312 c.Check(record, HasLen, 5)
326 // The first request is for the deployment.
327 assertGetDeploymentRequest(c, api, &GetDeploymentRequest{313 assertGetDeploymentRequest(c, api, &GetDeploymentRequest{
328 request.ServiceName, request.DeploymentName}, record[0])314 request.ServiceName, request.DeploymentName}, record[0])
329 // The second is to shutdown RoleInstance "one".
330 assertShutdownRoleRequest(c, api, &ShutdownRoleRequest{
331 request.ServiceName, request.DeploymentName, "one"}, record[1])
332 // The third is to shutdown RoleInstance "two", similar to like the second.
333 assertShutdownRoleRequest(c, api, &ShutdownRoleRequest{
334 request.ServiceName, request.DeploymentName, "two"}, record[2])
335 // The fourth, fifth and sixth requests delete disks.
336 assertDeleteDiskRequest(c, api, "disk1", record[3])
337 assertDeleteDiskRequest(c, api, "disk2", record[4])
338 assertDeleteDiskRequest(c, api, "disk3", record[5])
339 // The fourth request deletes the deployment.
340 assertDeleteDeploymentRequest(c, api, request.ServiceName,315 assertDeleteDeploymentRequest(c, api, request.ServiceName,
341 request.DeploymentName, record[6])316 request.DeploymentName, record[1])
317 assertDeleteDiskRequest(c, api, "disk1", record[2])
318 assertDeleteDiskRequest(c, api, "disk2", record[3])
319 assertDeleteDiskRequest(c, api, "disk3", record[4])
342}320}
343321
344func (suite *suiteDestroyDeployment) TestFailsGettingDeployment(c *C) {322func (suite *suiteDestroyDeployment) TestFailsGettingDeployment(c *C) {
@@ -359,10 +337,11 @@
359 c.Check(record, HasLen, 1)337 c.Check(record, HasLen, 1)
360}338}
361339
362func (suite *suiteDestroyDeployment) TestFailsShuttingDownRole(c *C) {340func (suite *suiteDestroyDeployment) TestFailsDeletingDisk(c *C) {
363 var responses []DispatcherResponse341 var responses []DispatcherResponse
364 // Prepare.342 // Prepare.
365 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)343 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)
344 // For deleting disks.
366 responses = append(responses, exampleFailResponse)345 responses = append(responses, exampleFailResponse)
367 record := []*X509Request{}346 record := []*X509Request{}
368 rigRecordingPreparedResponseDispatcher(&record, responses)347 rigRecordingPreparedResponseDispatcher(&record, responses)
@@ -374,38 +353,14 @@
374 }353 }
375 err := api.DestroyDeployment(request)354 err := api.DestroyDeployment(request)
376 c.Assert(err, NotNil)355 c.Assert(err, NotNil)
377 c.Check(err, ErrorMatches, "POST request failed [(]500: Internal Server Error[)]")356 c.Check(err, ErrorMatches, "DELETE request failed [(]500: Internal Server Error[)]")
378 c.Check(record, HasLen, 2)357 c.Check(record, HasLen, 2)
379}358}
380359
381func (suite *suiteDestroyDeployment) TestFailsDeletingDisk(c *C) {
382 var responses []DispatcherResponse
383 // Prepare.
384 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)
385 // For shutting down VMs.
386 responses = append(responses, exampleOkayResponse, exampleOkayResponse)
387 // For deleting disks.
388 responses = append(responses, exampleFailResponse)
389 record := []*X509Request{}
390 rigRecordingPreparedResponseDispatcher(&record, responses)
391 // Exercise DestroyDeployment.
392 api := makeAPI(c)
393 request := &DestroyDeploymentRequest{
394 ServiceName: "service-name",
395 DeploymentName: "deployment-name",
396 }
397 err := api.DestroyDeployment(request)
398 c.Assert(err, NotNil)
399 c.Check(err, ErrorMatches, "DELETE request failed [(]500: Internal Server Error[)]")
400 c.Check(record, HasLen, 4)
401}
402
403func (suite *suiteDestroyDeployment) TestFailsDeletingDeployment(c *C) {360func (suite *suiteDestroyDeployment) TestFailsDeletingDeployment(c *C) {
404 var responses []DispatcherResponse361 var responses []DispatcherResponse
405 // Prepare.362 // Prepare.
406 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)363 responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...)
407 // For shutting down VMs.
408 responses = append(responses, exampleOkayResponse, exampleOkayResponse)
409 // For deleting disks.364 // For deleting disks.
410 responses = append(responses, exampleOkayResponse, exampleOkayResponse, exampleOkayResponse)365 responses = append(responses, exampleOkayResponse, exampleOkayResponse, exampleOkayResponse)
411 // For other requests.366 // For other requests.
@@ -421,5 +376,5 @@
421 err := api.DestroyDeployment(request)376 err := api.DestroyDeployment(request)
422 c.Assert(err, NotNil)377 c.Assert(err, NotNil)
423 c.Check(err, ErrorMatches, "DELETE request failed [(]500: Internal Server Error[)]")378 c.Check(err, ErrorMatches, "DELETE request failed [(]500: Internal Server Error[)]")
424 c.Check(record, HasLen, 7)379 c.Check(record, HasLen, 5)
425}380}

Subscribers

People subscribed via source and target branches

to all changes: