command: Meta.RunOperation takes a context.Context

This is part of an ongoing effort to plumb a properly-connected series of
contexts through all of the layers where we might want to generate
telemetry (or similar) in future.

This is _just enough_ to connect the top-level context created by package
main with the various child contexts created by the local backend, so
that they could in principle access the root span that package main
generates.

This is not yet sufficient to propagate the context all the way into the
language runtime. More plumbing to follow in later commits!

This intentionally does not introduce any new OpenTelemetry-specific
context: the goal is only to get the context chain in place so that we
can use it for telemetry delivery in future.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
Martin Atkins 2024-11-12 14:05:56 -08:00
parent ce8c443754
commit 35bedc479f
5 changed files with 18 additions and 9 deletions

View File

@ -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)

View File

@ -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

View File

@ -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))
}

View File

@ -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

View File

@ -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