mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
98 lines
3.5 KiB
Go
98 lines
3.5 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/engine/plugins"
|
|
"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
|
|
|
|
providers plugins.Providers
|
|
|
|
// 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, providers plugins.Providers) *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),
|
|
providers: providers,
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
}
|
|
}
|