From 85295e5c23b1ab79c25a12825ee589f2d62409d1 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Sat, 2 Dec 2017 17:31:28 -0500 Subject: [PATCH] watch for cancellation in plan and refresh Cancellation in the local backend was only implemented for apply. --- backend/local/backend_apply.go | 8 -------- backend/local/backend_plan.go | 32 +++++++++++++++++++++++++------ backend/local/backend_refresh.go | 33 ++++++++++++++++++++++++++++---- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/backend/local/backend_apply.go b/backend/local/backend_apply.go index 446343181..9789e0b7c 100644 --- a/backend/local/backend_apply.go +++ b/backend/local/backend_apply.go @@ -151,14 +151,6 @@ func (b *Local) opApply( _, applyErr = tfCtx.Apply() // we always want the state, even if apply failed applyState = tfCtx.State() - - /* - // Record any shadow errors for later - if err := ctx.ShadowError(); err != nil { - shadowErr = multierror.Append(shadowErr, multierror.Prefix( - err, "apply operation:")) - } - */ }() // Wait for the apply to finish or for us to be interrupted so diff --git a/backend/local/backend_plan.go b/backend/local/backend_plan.go index a4e92c1c7..380ce1742 100644 --- a/backend/local/backend_plan.go +++ b/backend/local/backend_plan.go @@ -101,14 +101,34 @@ func (b *Local) opPlan( } } - // Perform the plan - log.Printf("[INFO] backend/local: plan calling Plan") - plan, err := tfCtx.Plan() - if err != nil { - runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", err) - return + // Perform the plan in a goroutine so we can be interrupted + var plan *terraform.Plan + var planErr error + doneCh := make(chan struct{}) + go func() { + defer close(doneCh) + log.Printf("[INFO] backend/local: plan calling Plan") + plan, planErr = tfCtx.Plan() + }() + + select { + case <-ctx.Done(): + if b.CLI != nil { + b.CLI.Output("stopping plan operation...") + } + + // Stop execution + go tfCtx.Stop() + + // Wait for completion still + <-doneCh + case <-doneCh: } + if planErr != nil { + runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", planErr) + return + } // Record state runningOp.PlanEmpty = plan.Diff.Empty() diff --git a/backend/local/backend_refresh.go b/backend/local/backend_refresh.go index 282e63045..0cf50b759 100644 --- a/backend/local/backend_refresh.go +++ b/backend/local/backend_refresh.go @@ -3,6 +3,7 @@ package local import ( "context" "fmt" + "log" "os" "strings" @@ -12,6 +13,7 @@ import ( "github.com/hashicorp/terraform/command/clistate" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/state" + "github.com/hashicorp/terraform/terraform" ) func (b *Local) opRefresh( @@ -78,11 +80,34 @@ func (b *Local) opRefresh( } } - // Perform operation and write the resulting state to the running op - newState, err := tfCtx.Refresh() + // Perform the refresh in a goroutine so we can be interrupted + var newState *terraform.State + var refreshErr error + doneCh := make(chan struct{}) + go func() { + defer close(doneCh) + newState, err = tfCtx.Refresh() + log.Printf("[INFO] backend/local: plan calling Plan") + }() + + select { + case <-ctx.Done(): + if b.CLI != nil { + b.CLI.Output("stopping refresh operation...") + } + + // Stop execution + go tfCtx.Stop() + + // Wait for completion still + <-doneCh + case <-doneCh: + } + + // write the resulting state to the running op runningOp.State = newState - if err != nil { - runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", err) + if refreshErr != nil { + runningOp.Err = errwrap.Wrapf("Error refreshing state: {{err}}", refreshErr) return }