diff --git a/internal/backend/local/backend.go b/internal/backend/local/backend.go index 19ed8d1869..0bfa12b3bb 100644 --- a/internal/backend/local/backend.go +++ b/internal/backend/local/backend.go @@ -309,19 +309,24 @@ func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend. b.opLock.Lock() // Build our running operation - // the runningCtx is only used to block until the operation returns. - runningCtx, done := context.WithCancel(context.Background()) + // the runningCtx is only used to block until the operation returns. We + // intentionally detach it from any upstream cancellation because we + // need this to cancel only once our goroutine below is finished. + runningCtx, done := context.WithCancel(context.WithoutCancel(ctx)) runningOp := &backend.RunningOperation{ Context: runningCtx, } // stopCtx wraps the context passed in, and is used to signal a graceful Stop. + // This one _does_ inherit cancellations from the caller. stopCtx, stop := context.WithCancel(ctx) runningOp.Stop = stop // cancelCtx is used to cancel the operation immediately, usually - // indicating that the process is exiting. - cancelCtx, cancel := context.WithCancel(context.Background()) + // indicating that the process is exiting. This is intentionally + // detached from any upstream cancellation so that it will be + // cancelled only once our goroutine below is finished. + cancelCtx, cancel := context.WithCancel(context.WithoutCancel(ctx)) runningOp.Cancel = cancel op.StateLocker = op.StateLocker.WithContext(stopCtx) diff --git a/internal/command/apply.go b/internal/command/apply.go index 194ca59cd1..e9fc6b3c80 100644 --- a/internal/command/apply.go +++ b/internal/command/apply.go @@ -29,6 +29,7 @@ type ApplyCommand struct { func (c *ApplyCommand) Run(rawArgs []string) int { var diags tfdiags.Diagnostics + ctx := c.CommandContext() // Parse and apply global view arguments common, rawArgs := arguments.ParseView(rawArgs) @@ -134,7 +135,7 @@ func (c *ApplyCommand) Run(rawArgs []string) int { diags = nil // Run the operation - op, diags := c.RunOperation(be, opReq) + op, diags := c.RunOperation(ctx, be, opReq) view.Diagnostics(diags) if diags.HasErrors() { return 1 diff --git a/internal/command/meta.go b/internal/command/meta.go index ab4af57346..0e6c8a98ce 100644 --- a/internal/command/meta.go +++ b/internal/command/meta.go @@ -488,7 +488,7 @@ func (m *Meta) CommandContext() context.Context { // If the operation runs to completion then no error is returned even if the // operation itself is unsuccessful. Use the "Result" field of the // returned operation object to recognize operation-level failure. -func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*backend.RunningOperation, tfdiags.Diagnostics) { +func (m *Meta) RunOperation(ctx context.Context, b backend.Enhanced, opReq *backend.Operation) (*backend.RunningOperation, tfdiags.Diagnostics) { if opReq.View == nil { panic("RunOperation called with nil View") } @@ -505,7 +505,7 @@ func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*back return nil, diags } - op, err := b.Operation(context.Background(), opReq) + op, err := b.Operation(ctx, opReq) if err != nil { return nil, diags.Append(fmt.Errorf("error starting operation: %w", err)) } diff --git a/internal/command/plan.go b/internal/command/plan.go index ecdab84fb0..0e0620a60c 100644 --- a/internal/command/plan.go +++ b/internal/command/plan.go @@ -23,6 +23,8 @@ type PlanCommand struct { } func (c *PlanCommand) Run(rawArgs []string) int { + ctx := c.CommandContext() + // Parse and apply global view arguments common, rawArgs := arguments.ParseView(rawArgs) c.View.Configure(common) @@ -105,7 +107,7 @@ func (c *PlanCommand) Run(rawArgs []string) int { diags = nil // Perform the operation - op, diags := c.RunOperation(be, opReq) + op, diags := c.RunOperation(ctx, be, opReq) view.Diagnostics(diags) if diags.HasErrors() { return 1 diff --git a/internal/command/refresh.go b/internal/command/refresh.go index f5b651291d..18d7f23191 100644 --- a/internal/command/refresh.go +++ b/internal/command/refresh.go @@ -24,6 +24,7 @@ type RefreshCommand struct { func (c *RefreshCommand) Run(rawArgs []string) int { var diags tfdiags.Diagnostics + ctx := c.CommandContext() // Parse and apply global view arguments common, rawArgs := arguments.ParseView(rawArgs) @@ -103,7 +104,7 @@ func (c *RefreshCommand) Run(rawArgs []string) int { diags = nil // Perform the operation - op, diags := c.RunOperation(be, opReq) + op, diags := c.RunOperation(ctx, be, opReq) view.Diagnostics(diags) if diags.HasErrors() { return 1