Files
opentf/internal/engine/planning/plan_context.go
Martin Atkins b6cdfd1afa lang/eval: Bind PlanningOracle to PlanGlue
Previously the PlanGlue methods all took PlanningOracle pointers as one
of their arguments, which is annoying since all of them should end up with
pointers to the same object and it makes it hard for the PlanGlue
implementation to do any work outside of and between the PlanGlue method
calls.

Instead then we'll have DrivePlanning take a function for building a
PlanGlue implementation given a PlanningOracle pointer, and then the
planning engine returns an implementation that binds a planContext to a
PlanningOracle it can then use to do all of its work.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-10-27 10:15:41 -07:00

94 lines
3.4 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package planning
import (
"log"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/engine/lifecycle"
"github.com/opentofu/opentofu/internal/lang/eval"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/states"
)
// planContext is our shared state for the various parts of a single call
// to [PlanChanges], and is mainly used as part of our [eval.PlanGlue]
// implementation [planGlue], through which the evaluator calls us to ask for
// planning results.
type planContext struct {
evalCtx *eval.EvalContext
plannedChanges *plans.ChangesSync
// TODO: The following should probably track a reason why each resource
// instance was deferred, but since deferral is not the focus of this
// current experiment we'll just keep this boolean for now.
deferred addrs.Map[addrs.AbsResourceInstance, struct{}]
// prevRoundState MUST be treated as immutable
prevRoundState *states.State
// refreshedState is where we record the results of refreshing
// resource instances as we visit them. This starts as a deep copy
// of prevRoundState.
refreshedState *states.SyncState
completion *completionTracker
providerInstances *providerInstances
// TODO: something to track which ephemeral resource instances are currently
// open? (Do we actually need that, or can we just rely on a background
// goroutine to babysit those based on the completion tracker?)
}
func newPlanContext(evalCtx *eval.EvalContext, prevRoundState *states.State) *planContext {
if prevRoundState == nil {
prevRoundState = states.NewState()
}
changes := plans.NewChanges()
refreshedState := prevRoundState.DeepCopy()
completion := lifecycle.NewCompletionTracker[completionEvent]()
return &planContext{
evalCtx: evalCtx,
plannedChanges: changes.SyncWrapper(),
prevRoundState: prevRoundState,
refreshedState: refreshedState.SyncWrapper(),
completion: completion,
providerInstances: newProviderInstances(completion),
}
}
// Close marks the end of the use of the [planContext] object, returning a
// [plans.Plan] representation of the plan that was created.
//
// After calling this function the [planContext] object is invalid and must
// not be used anymore.
func (p *planContext) Close() *plans.Plan {
// Before we return we'll make sure our completion tracker isn't waiting
// for anything else to complete, so that we can unblock closing of
// any provider instances or ephemeral resource instances that might've
// got left behind by panics/etc. We should not be relying on this in the
// happy path.
for event := range p.completion.PendingItems() {
log.Printf("[TRACE] planContext: synthetic completion of %#v", event)
p.completion.ReportCompletion(event)
}
return &plans.Plan{
UIMode: plans.NormalMode, // TODO: This PlanChanges function needs something analogous to [tofu.PlanOpts] for planning mode/options
Changes: p.plannedChanges.Close(),
PrevRunState: p.prevRoundState,
PriorState: p.refreshedState.Close(),
// TODO: various other fields that we need to actually make use
// of this plan result. But this is intentionally just a partial
// result for now because it's not clear that we'd even be using
// plans.Plan in a final version of this new approach.
}
}