|
5 | 5 | "fmt"
|
6 | 6 | "io/ioutil"
|
7 | 7 | "log"
|
| 8 | + "regexp" |
8 | 9 | "time"
|
9 | 10 |
|
10 | 11 | "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
|
@@ -137,8 +138,7 @@ func (s BlueGreen) Deploy(appDeploy AppDeploy) (AppDeployResponse, error) {
|
137 | 138 | },
|
138 | 139 | {
|
139 | 140 | Forward: func(ctx Context) (Context, error) {
|
140 |
| - _, _, err := s.client.DeleteApplication(appDeploy.App.GUID) |
141 |
| - return ctx, err |
| 141 | + return ctx, SafeAppDeletion(*s.client, appDeploy.App.GUID, 5) |
142 | 142 | },
|
143 | 143 | },
|
144 | 144 | }
|
@@ -293,41 +293,7 @@ func (s BlueGreen) Restage(appDeploy AppDeploy) (AppDeployResponse, error) {
|
293 | 293 | },
|
294 | 294 | {
|
295 | 295 | Forward: func(ctx Context) (Context, error) {
|
296 |
| - appResp := ctx["app_response"].(AppDeployResponse) |
297 |
| - |
298 |
| - // Action code |
299 |
| - jobURL, _, err := s.client.DeleteApplication(appDeploy.App.GUID) |
300 |
| - if err != nil { |
301 |
| - return ctx, err |
302 |
| - } |
303 |
| - |
304 |
| - err = common.PollingWithTimeout(func() (bool, error) { |
305 |
| - job, _, err := s.client.GetJob(jobURL) |
306 |
| - if err != nil { |
307 |
| - return true, err |
308 |
| - } |
309 |
| - |
310 |
| - // Stop polling and return error if job failed |
311 |
| - if job.State == constantV3.JobFailed { |
312 |
| - return true, fmt.Errorf( |
313 |
| - "Venerable app deletion failed, reason: %+v", |
314 |
| - job.Errors(), |
315 |
| - ) |
316 |
| - } |
317 |
| - |
318 |
| - if job.State == constantV3.JobComplete { |
319 |
| - return true, nil |
320 |
| - } |
321 |
| - |
322 |
| - return false, nil |
323 |
| - }, 5*time.Second, 1*time.Minute) |
324 |
| - |
325 |
| - if err != nil { |
326 |
| - return ctx, err |
327 |
| - } |
328 |
| - |
329 |
| - ctx["app_response"] = appResp |
330 |
| - return ctx, nil |
| 296 | + return ctx, SafeAppDeletion(*s.client, appDeploy.App.GUID, 5) |
331 | 297 | },
|
332 | 298 | },
|
333 | 299 | }
|
@@ -470,3 +436,48 @@ func isAppStopped(clientV3 *ccv3.Client, appGUID string) (bool, error) {
|
470 | 436 | // All instances stopped
|
471 | 437 | return true, err
|
472 | 438 | }
|
| 439 | + |
| 440 | +func SafeAppDeletion(client ccv3.Client, appGuid string, remainingAttempts int) error { |
| 441 | + // start deletion job |
| 442 | + jobURL, _, err := client.DeleteApplication(appGuid) |
| 443 | + if err != nil { |
| 444 | + return err |
| 445 | + } |
| 446 | + |
| 447 | + // poll completion |
| 448 | + err = common.PollingWithTimeout(func() (bool, error) { |
| 449 | + job, _, err := client.GetJob(jobURL) |
| 450 | + if err != nil { |
| 451 | + return true, err |
| 452 | + } |
| 453 | + |
| 454 | + if job.State == constantV3.JobFailed { |
| 455 | + return true, fmt.Errorf( |
| 456 | + "App deletion failed, reason: %+v", |
| 457 | + job.Errors(), |
| 458 | + ) |
| 459 | + } |
| 460 | + |
| 461 | + if job.State == constantV3.JobComplete { |
| 462 | + return true, nil |
| 463 | + } |
| 464 | + |
| 465 | + return false, nil |
| 466 | + }, 5*time.Second, 1*time.Minute) |
| 467 | + |
| 468 | + // error handling |
| 469 | + if err != nil { |
| 470 | + // https://github.com/cloudfoundry/cloud_controller_ng/issues/3589 -> Delete app when bound to service fails with async service brokers -> Retry that |
| 471 | + specialError, _ := regexp.MatchString("An operation for the service binding between app .* and service instance .* is in progress.", err.Error()) |
| 472 | + if specialError { |
| 473 | + if remainingAttempts > 0 { |
| 474 | + time.Sleep(5 * time.Second) |
| 475 | + return SafeAppDeletion(client, appGuid, remainingAttempts-1) |
| 476 | + } |
| 477 | + return fmt.Errorf("Retries for app deletion exhausted: %+v", err) |
| 478 | + } |
| 479 | + return err |
| 480 | + } |
| 481 | + |
| 482 | + return nil |
| 483 | +} |
0 commit comments