From 67a615209181ef369a306f42fe749706d58a1996 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 9 Feb 2018 18:51:29 -0500 Subject: [PATCH] move backend operation cancellation into meta Create a single command method for running and operation with cancellation. --- command/apply.go | 44 ++--------------------------------------- command/meta.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++ command/plan.go | 40 ++----------------------------------- command/refresh.go | 44 ++--------------------------------------- 4 files changed, 55 insertions(+), 122 deletions(-) diff --git a/command/apply.go b/command/apply.go index dd86ab68d..9ecb4ef66 100644 --- a/command/apply.go +++ b/command/apply.go @@ -2,12 +2,10 @@ package command import ( "bytes" - "context" "fmt" "os" "sort" "strings" - "time" "github.com/hashicorp/terraform/tfdiags" @@ -159,47 +157,9 @@ func (c *ApplyCommand) Run(args []string) int { opReq.AutoApprove = autoApprove opReq.DestroyForce = destroyForce - // Perform the operation - op, err := b.Operation(context.Background(), opReq) + op, err := c.RunOperation(b, opReq) if err != nil { - c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err)) - return 1 - } - - // Wait for the operation to complete or an interrupt to occur - select { - case <-c.ShutdownCh: - // gracefully stop the operation - op.Stop() - - // Notify the user - c.Ui.Output(outputInterrupt) - - // Still get the result, since there is still one - select { - case <-c.ShutdownCh: - c.Ui.Error( - "Two interrupts received. Exiting immediately. Note that data\n" + - "loss may have occurred.") - - // cancel the operation completely - op.Cancel() - - // the operation should return asap - // but timeout just in case - select { - case <-op.Done(): - case <-time.After(5 * time.Second): - } - - return 1 - - case <-op.Done(): - } - case <-op.Done(): - if err := op.Err; err != nil { - diags = diags.Append(err) - } + diags = diags.Append(err) } c.showDiagnostics(diags) diff --git a/command/meta.go b/command/meta.go index dbcfb194b..91f1008fe 100644 --- a/command/meta.go +++ b/command/meta.go @@ -3,6 +3,7 @@ package command import ( "bufio" "bytes" + "context" "errors" "flag" "fmt" @@ -259,6 +260,54 @@ func (m *Meta) StdinPiped() bool { return fi.Mode()&os.ModeNamedPipe != 0 } +func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*backend.RunningOperation, error) { + op, err := b.Operation(context.Background(), opReq) + if err != nil { + return nil, fmt.Errorf("error starting operation: %s", err) + } + + // Wait for the operation to complete or an interrupt to occur + select { + case <-m.ShutdownCh: + // gracefully stop the operation + op.Stop() + + // Notify the user + m.Ui.Output(outputInterrupt) + + // Still get the result, since there is still one + select { + case <-m.ShutdownCh: + m.Ui.Error( + "Two interrupts received. Exiting immediately. Note that data\n" + + "loss may have occurred.") + + // cancel the operation completely + op.Cancel() + + // the operation should return asap + // but timeout just in case + select { + case <-op.Done(): + case <-time.After(5 * time.Second): + } + + return nil, errors.New("operation canceled") + + case <-op.Done(): + // operation completed after Stop + } + case <-op.Done(): + // operation completed normally + } + + if op.Err != nil { + return op, op.Err + } + + return op, nil +} + const ( ProviderSkipVerifyEnvVar = "TF_SKIP_PROVIDER_VERIFY" ) diff --git a/command/plan.go b/command/plan.go index 37bc90460..28bcbcb33 100644 --- a/command/plan.go +++ b/command/plan.go @@ -1,10 +1,8 @@ package command import ( - "context" "fmt" "strings" - "time" "github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/config" @@ -108,43 +106,9 @@ func (c *PlanCommand) Run(args []string) int { opReq.Type = backend.OperationTypePlan // Perform the operation - op, err := b.Operation(context.Background(), opReq) + op, err := c.RunOperation(b, opReq) if err != nil { - c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err)) - return 1 - } - - select { - case <-c.ShutdownCh: - // Cancel our context so we can start gracefully exiting - op.Stop() - - // Notify the user - c.Ui.Output(outputInterrupt) - - // Still get the result, since there is still one - select { - case <-c.ShutdownCh: - c.Ui.Error( - "Two interrupts received. Exiting immediately") - - // cancel the operation completely - op.Cancel() - - // the operation should return asap - // but timeout just in case - select { - case <-op.Done(): - case <-time.After(5 * time.Second): - } - - return 1 - case <-op.Done(): - } - case <-op.Done(): - if err := op.Err; err != nil { - diags = diags.Append(err) - } + diags = diags.Append(err) } c.showDiagnostics(diags) diff --git a/command/refresh.go b/command/refresh.go index 1329a190c..ce61c4233 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -1,10 +1,8 @@ package command import ( - "context" "fmt" "strings" - "time" "github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/config" @@ -76,47 +74,9 @@ func (c *RefreshCommand) Run(args []string) int { opReq.Type = backend.OperationTypeRefresh opReq.Module = mod - // Perform the operation - op, err := b.Operation(context.Background(), opReq) + op, err := c.RunOperation(b, opReq) if err != nil { - c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err)) - return 1 - } - - // Wait for the operation to complete or an interrupt to occur - select { - case <-c.ShutdownCh: - // gracefully stop the operation - op.Stop() - - // Notify the user - c.Ui.Output(outputInterrupt) - - // Still get the result, since there is still one - select { - case <-c.ShutdownCh: - c.Ui.Error( - "Two interrupts received. Exiting immediately. Note that data\n" + - "loss may have occurred.") - - // cancel the operation completely - op.Cancel() - - // the operation should return asap - // but timeout just in case - select { - case <-op.Done(): - case <-time.After(5 * time.Second): - } - - return 1 - - case <-op.Done(): - } - case <-op.Done(): - if err := op.Err; err != nil { - diags = diags.Append(err) - } + diags = diags.Append(err) } c.showDiagnostics(diags)