opentofu/internal/engine/planning/execgraph.go
Christian Mesh 8ce780b4e0
engine/ephemeral: Wire together basic ephemeral functionality (#3710)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
2026-02-04 16:44:20 -05:00

101 lines
4.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 (
"sync"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/engine/internal/exec"
"github.com/opentofu/opentofu/internal/engine/internal/execgraph"
)
// execGraphBuilder is a higher-level wrapper around [execgraph.Builder] that
// is tailored to the needs of the planning engine.
//
// Specifically:
// - Its exported methods that add to or modify the graph are all
// concurrency-safe, for convenient use during the concurrent planning work
// driven by the evaluator.
// - It keeps track of certain "singleton" collections of graph nodes that
// different parts of the planning engine all need to agree on for the
// execution graph to be correct, such as ensuring there's only one open
// and one close operation per distinct provider instance address.
// - Many of its methods can potentially add multiple operations to the graph
// at once, to let the planning engine work at a higher level of abstraction
// than just the individual raw operation types. The lower-level
// [execgraph.Builder] instead directly matches the abstraction level of
// [execgraph.Operations].
type execGraphBuilder struct {
// mu must be locked while accessing any of the other fields.
mu sync.Mutex
// lower is the lower-level graph builder that this utility is built in
// terms of.
lower *execgraph.Builder
// During construction we treat certain items as singletons so that
// we can do the associated work only once while providing it to
// multiple callers, and so these maps track those singletons but
// we throw these away after building is complete because the graph
// becomes immutable at that point.
resourceInstAddrRefs addrs.Map[addrs.AbsResourceInstance, execgraph.ResultRef[addrs.AbsResourceInstance]]
providerInstAddrRefs addrs.Map[addrs.AbsProviderInstanceCorrect, execgraph.ResultRef[addrs.AbsProviderInstanceCorrect]]
openProviderRefs addrs.Map[addrs.AbsProviderInstanceCorrect, execResultWithCloseBlockers[*exec.ProviderClient]]
openEphemeralRefs addrs.Map[addrs.AbsResourceInstance, registerExecCloseBlockerFunc]
}
// NOTE: There are additional methods for [execGraphBuilder] declared in
// the other files named execgraph_*.go , grouped by what kinds of objects they
// primarily work with.
func newExecGraphBuilder() *execGraphBuilder {
return &execGraphBuilder{
lower: execgraph.NewBuilder(),
resourceInstAddrRefs: addrs.MakeMap[addrs.AbsResourceInstance, execgraph.ResultRef[addrs.AbsResourceInstance]](),
providerInstAddrRefs: addrs.MakeMap[addrs.AbsProviderInstanceCorrect, execgraph.ResultRef[addrs.AbsProviderInstanceCorrect]](),
openProviderRefs: addrs.MakeMap[addrs.AbsProviderInstanceCorrect, execResultWithCloseBlockers[*exec.ProviderClient]](),
openEphemeralRefs: addrs.MakeMap[addrs.AbsResourceInstance, registerExecCloseBlockerFunc](),
}
}
// Finish returns the graph that has been built, which is then immutable.
//
// After calling this function the execGraphBuilder is invalid and must not be
// used anymore.
func (b *execGraphBuilder) Finish() *execgraph.Graph {
b.mu.Lock()
defer b.mu.Unlock()
return b.lower.Finish()
}
// execResultWithCloseBlockers associates a result for some object type that
// needs to be closed once other operations have finished using it with the
// operation that performs that close operation.
type execResultWithCloseBlockers[T any] struct {
Result execgraph.ResultRef[T]
CloseBlockerFunc registerExecCloseBlockerFunc
CloseBlockerResult execgraph.AnyResultRef
}
// registerExecCloseBlockerFunc is the signature of a function that adds a given
// result references as a blocker for something to be "closed".
//
// Exactly what means to be a "close blocker" depends on context. Refer to the
// documentation of whatever function is returning a value of this type.
type registerExecCloseBlockerFunc func(execgraph.AnyResultRef)
// makeCloseBlocker is a helper used by [execGraphBuilder] methods that produce
// open/close node pairs.
//
// Callers MUST hold a lock on b.mu throughout any call to this method.
func (b *execGraphBuilder) makeCloseBlocker() (execgraph.AnyResultRef, registerExecCloseBlockerFunc) {
waiter, lowerRegister := b.lower.MutableWaiter()
registerFunc := registerExecCloseBlockerFunc(func(ref execgraph.AnyResultRef) {
lowerRegister(ref)
})
return waiter, registerFunc
}