From 3aaa1e9d04cda706b75df3e733f0ba73ac52432a Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 1 Dec 2017 11:03:41 -0500 Subject: [PATCH] make plans cancellable There was no cancellation context for a plan, so it would always have to run to completion as SIGINT was being swallowed. Move the shutdown channel to the command Meta since it's used in multiple commands. --- command/apply.go | 3 --- command/apply_test.go | 3 +-- command/console.go | 3 --- command/meta.go | 3 +++ command/plan.go | 30 ++++++++++++++++++++++++------ 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/command/apply.go b/command/apply.go index 18f3f981f3..a9588f7e54 100644 --- a/command/apply.go +++ b/command/apply.go @@ -24,9 +24,6 @@ type ApplyCommand struct { // If true, then this apply command will become the "destroy" // command. It is just like apply but only processes a destroy. Destroy bool - - // When this channel is closed, the apply will be cancelled. - ShutdownCh <-chan struct{} } func (c *ApplyCommand) Run(args []string) int { diff --git a/command/apply_test.go b/command/apply_test.go index 808bbbe2a7..b30e287d73 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -837,9 +837,8 @@ func TestApply_shutdown(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + ShutdownCh: shutdownCh, }, - - ShutdownCh: shutdownCh, } p.DiffFn = func( diff --git a/command/console.go b/command/console.go index 8235583112..b1361c26fd 100644 --- a/command/console.go +++ b/command/console.go @@ -17,9 +17,6 @@ import ( // configuration and actually builds or changes infrastructure. type ConsoleCommand struct { Meta - - // When this channel is closed, the apply will be cancelled. - ShutdownCh <-chan struct{} } func (c *ConsoleCommand) Run(args []string) int { diff --git a/command/meta.go b/command/meta.go index 92ddd83154..27f7765f95 100644 --- a/command/meta.go +++ b/command/meta.go @@ -76,6 +76,9 @@ type Meta struct { // is not suitable, e.g. because of a read-only filesystem. OverrideDataDir string + // When this channel is closed, the command will be cancelled. + ShutdownCh <-chan struct{} + //---------------------------------------------------------- // Protected: commands can set these //---------------------------------------------------------- diff --git a/command/plan.go b/command/plan.go index 757984f8fa..ac557bbc8a 100644 --- a/command/plan.go +++ b/command/plan.go @@ -104,17 +104,35 @@ func (c *PlanCommand) Run(args []string) int { opReq.Type = backend.OperationTypePlan // Perform the operation - op, err := b.Operation(context.Background(), opReq) + ctx, ctxCancel := context.WithCancel(context.Background()) + defer ctxCancel() + + op, err := b.Operation(ctx, opReq) if err != nil { c.Ui.Error(fmt.Sprintf("Error starting operation: %s", err)) return 1 } - // Wait for the operation to complete - <-op.Done() - if err := op.Err; err != nil { - c.showDiagnostics(err) - return 1 + select { + case <-c.ShutdownCh: + // Cancel our context so we can start gracefully exiting + ctxCancel() + // 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") + return 1 + case <-op.Done(): + } + case <-op.Done(): + if err := op.Err; err != nil { + c.showDiagnostics(err) + return 1 + } } /*