Files
opentf/internal/engine/planning/execgraph_ephemeral.go
Martin Atkins 6ec04c1227 planning: Further simplify provider instance items in execgraph
The previous commit split the handling of provider instance items into
separate dependency-analysis and execgraph construction steps, with the
intention of avoiding the need for the execGraphBuilder to directly
interact with the planning oracle and thus indirectly with the evaluator.
Overall the hope is that execGraphBuilder will be a self-contained object
that doesn't depend on anything else in the planning engine so that it's
easier to write unit tests for it that don't require creating an entire
fake planning context.

However, on reflection that change introduced a completely unnecessary
extra handoff from the execGraphBuilder to _another part of itself_, which
is obviously unnecessary complexity because it doesn't serve to separate
any concerns.

This is therefore a further simplification that returns to just doing the
entire handling of a provider instance's presence in the execution graph
only once we've decided that at least one resource instance will
definitely use the provider instance during the apply phase.

There is still a separation of concerns where the planGlue type is
responsible for calculating the provider dependencies and then the
execGraphBuilder is only responsible for adding items to the execution
graph based on that information. That separation makes sense because
planGlue's job is to bridge between the planning engine and the evaluator,
and it's the evaluator's job to calculate the dependencies for a provider
instance, whereas execGraphBuilder is the component responsible for
deciding exactly which low-level execgraph operations we'll use to describe
the situation to the apply engine.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2026-02-05 13:06:39 -08:00

57 lines
2.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 (
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/engine/internal/exec"
"github.com/opentofu/opentofu/internal/engine/internal/execgraph"
"github.com/opentofu/opentofu/internal/lang/eval"
)
////////////////////////////////////////////////////////////////////////////////
// This file contains methods of [execGraphBuilder] that are related to the
// parts of an execution graph that deal with resource instances of mode
// [addrs.EphemeralResourceMode] in particular.
////////////////////////////////////////////////////////////////////////////////
// EphemeralResourceSubgraph adds graph nodes needed to apply changes for a
// ephemeral resource instance, and returns what should be used as its final
// result to propagate into to downstream references.
//
// TODO: This is definitely not sufficient for the full complexity of all of the
// different ways ephemeral resources can potentially need to be handled in an
// execution graph. It's just a simple placeholder adapted from code that was
// originally written inline in [planGlue.planDesiredEphemeralResourceInstance]
// just to preserve the existing functionality for now until we design a more
// complete approach in later work.
func (b *execGraphBuilder) EphemeralResourceInstanceSubgraph(desired *eval.DesiredResourceInstance, plannedValue cty.Value, providerClientRef execgraph.ResultRef[*exec.ProviderClient]) execgraph.ResourceInstanceResultRef {
b.mu.Lock()
defer b.mu.Unlock()
closeWait, registerCloseBlocker := b.makeCloseBlocker()
b.openEphemeralRefs.Put(desired.Addr, registerCloseBlocker)
// We need to explicitly model our dependency on any upstream resource
// instances in the resource instance graph. These don't naturally emerge
// from the data flow because these results are intermediated through the
// evaluator, which indirectly incorporates the results into the
// desiredInstRef result we'll build below.
dependencyWaiter, closeDependencyAfter := b.waiterForResourceInstances(desired.RequiredResourceInstances.All())
instAddrRef := b.lower.ConstantResourceInstAddr(desired.Addr)
desiredInstRef := b.lower.ResourceInstanceDesired(instAddrRef, dependencyWaiter)
openRef := b.lower.EphemeralOpen(desiredInstRef, providerClientRef)
stateRef := b.lower.EphemeralState(openRef)
closeRef := b.lower.EphemeralClose(openRef, closeWait)
closeDependencyAfter(closeRef)
return stateRef
}