execgraph: Use states.ResourceInstanceObjectFull

Instead of states.ResourceInstanceObject, we'll now use the "full"
representation added in a recent commit. This new representation is a
better fit for execgraph because it tracks all of the relevant information
about a resource instance object inline without assuming that the object
is always used in conjunction with a pointer to the entire state.

We're not really making any special use of those new capabilities yet. This
is mostly just mechanical updates to refer to the new type, and to fill
in values for the extra fields as far as that's possible without doing
further refactoring.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
Martin Atkins
2025-11-26 15:58:29 -08:00
parent c22d156f4e
commit a484919556
10 changed files with 49 additions and 27 deletions

View File

@@ -38,7 +38,7 @@ type Builder struct {
// we throw these away after building is complete because the graph
// becomes immutable at that point.
desiredStateRefs addrs.Map[addrs.AbsResourceInstance, ResultRef[*eval.DesiredResourceInstance]]
priorStateRefs addrs.Map[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObject]]
priorStateRefs addrs.Map[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObjectFull]]
providerAddrRefs map[addrs.Provider]ResultRef[addrs.Provider]
providerInstConfigRefs addrs.Map[addrs.AbsProviderInstanceCorrect, ResultRef[cty.Value]]
openProviderRefs addrs.Map[addrs.AbsProviderInstanceCorrect, resultWithCloseBlockers[providers.Configured]]
@@ -48,10 +48,10 @@ type Builder struct {
func NewBuilder() *Builder {
return &Builder{
graph: &Graph{
resourceInstanceResults: addrs.MakeMap[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObject]](),
resourceInstanceResults: addrs.MakeMap[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObjectFull]](),
},
desiredStateRefs: addrs.MakeMap[addrs.AbsResourceInstance, ResultRef[*eval.DesiredResourceInstance]](),
priorStateRefs: addrs.MakeMap[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObject]](),
priorStateRefs: addrs.MakeMap[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObjectFull]](),
providerAddrRefs: make(map[addrs.Provider]ResultRef[addrs.Provider]),
providerInstConfigRefs: addrs.MakeMap[addrs.AbsProviderInstanceCorrect, ResultRef[cty.Value]](),
openProviderRefs: addrs.MakeMap[addrs.AbsProviderInstanceCorrect, resultWithCloseBlockers[providers.Configured]](),
@@ -122,7 +122,7 @@ func (b *Builder) DesiredResourceInstance(addr addrs.AbsResourceInstance) Result
// state model, but a real implementation of this might benefit from a slightly
// different model tailored to be used in isolation, without the rest of the
// state tree it came from.
func (b *Builder) ResourceInstancePriorState(addr addrs.AbsResourceInstance) ResultRef[*states.ResourceInstanceObject] {
func (b *Builder) ResourceInstancePriorState(addr addrs.AbsResourceInstance) ResultRef[*states.ResourceInstanceObjectFull] {
b.mu.Lock()
defer b.mu.Unlock()
@@ -148,7 +148,7 @@ func (b *Builder) ResourceInstancePriorState(addr addrs.AbsResourceInstance) Res
// codepath attempting to register the chain of nodes for any deposed object,
// and no resource instance should depend on the result of applying changes
// to a deposed object.
func (b *Builder) ResourceDeposedObjectState(instAddr addrs.AbsResourceInstance, deposedKey states.DeposedKey) ResultRef[*states.ResourceInstanceObject] {
func (b *Builder) ResourceDeposedObjectState(instAddr addrs.AbsResourceInstance, deposedKey states.DeposedKey) ResultRef[*states.ResourceInstanceObjectFull] {
b.mu.Lock()
defer b.mu.Unlock()
@@ -288,7 +288,7 @@ func (b *Builder) CloseProviderClient(clientResult ResultRef[providers.Configure
// resource-instance-graph dependencies have had their changes applied.
func (b *Builder) ManagedResourceObjectFinalPlan(
desiredInst ResultRef[*eval.DesiredResourceInstance],
priorState ResultRef[*states.ResourceInstanceObject],
priorState ResultRef[*states.ResourceInstanceObjectFull],
plannedVal ResultRef[cty.Value],
providerClient ResultRef[providers.Configured],
waitFor AnyResultRef,
@@ -312,11 +312,11 @@ func (b *Builder) ManagedResourceObjectFinalPlan(
func (b *Builder) ApplyManagedResourceObjectChanges(
finalPlan ResultRef[*ManagedResourceObjectFinalPlan],
providerClient ResultRef[providers.Configured],
) ResultRef[*states.ResourceInstanceObject] {
) ResultRef[*states.ResourceInstanceObjectFull] {
b.mu.Lock()
defer b.mu.Unlock()
return operationRef[*states.ResourceInstanceObject](b, operationDesc{
return operationRef[*states.ResourceInstanceObjectFull](b, operationDesc{
opCode: opManagedApplyChanges,
operands: []AnyResultRef{finalPlan, providerClient},
})
@@ -326,12 +326,12 @@ func (b *Builder) DataRead(
desiredInst ResultRef[*eval.DesiredResourceInstance],
providerClient ResultRef[providers.Configured],
waitFor AnyResultRef,
) ResultRef[*states.ResourceInstanceObject] {
) ResultRef[*states.ResourceInstanceObjectFull] {
b.mu.Lock()
defer b.mu.Unlock()
waiter := b.ensureWaiterRef(waitFor)
return operationRef[*states.ResourceInstanceObject](b, operationDesc{
return operationRef[*states.ResourceInstanceObjectFull](b, operationDesc{
opCode: opDataRead,
operands: []AnyResultRef{desiredInst, providerClient, waiter},
})
@@ -345,7 +345,7 @@ func (b *Builder) DataRead(
// Only one call is allowed per distinct [addrs.AbsResourceInstance] value. If
// two callers try to register for the same address then the second call will
// panic.
func (b *Builder) SetResourceInstanceFinalStateResult(addr addrs.AbsResourceInstance, result ResultRef[*states.ResourceInstanceObject]) {
func (b *Builder) SetResourceInstanceFinalStateResult(addr addrs.AbsResourceInstance, result ResultRef[*states.ResourceInstanceObjectFull]) {
b.mu.Lock()
defer b.mu.Unlock()

View File

@@ -176,7 +176,7 @@ func (c *compiler) Compile() (*CompiledGraph, tfdiags.Diagnostics) {
if !ok {
return cty.DynamicVal
}
finalStateObj := rawResult.(*states.ResourceInstanceObject)
finalStateObj := rawResult.(*states.ResourceInstanceObjectFull)
return finalStateObj.Value
})
}

View File

@@ -21,7 +21,7 @@ import (
func (c *compiler) compileOpManagedFinalPlan(operands *compilerOperands) nodeExecuteRaw {
getDesired := nextOperand[*eval.DesiredResourceInstance](operands)
getPrior := nextOperand[*states.ResourceInstanceObject](operands)
getPrior := nextOperand[*states.ResourceInstanceObjectFull](operands)
getInitialPlanned := nextOperand[cty.Value](operands)
getProviderClient := nextOperand[providers.Configured](operands)
waitForDeps := operands.OperandWaiter()
@@ -178,10 +178,15 @@ func (c *compiler) compileOpManagedApplyChanges(operands *compilerOperands) node
// directly, which is annoying since it would be nice if that were all
// encapsulated away somewhere.
ret := &states.ResourceInstanceObject{
ret := &states.ResourceInstanceObjectFull{
Value: resp.NewState,
Private: resp.Private,
Status: states.ObjectReady,
// TODO: ProviderInstanceAddr, which we don't currently have here
// because we're just holding an already-open client for that
// provider. Should we extend [providers.Interface] with a method
// to find which provider instance the client is acting on behalf of?
ResourceType: finalPlan.ResourceType,
// TODO: Dependencies ... they come from the "desired" object
// so maybe we should send that whole thing over here instead of
// just the ConfigVal?

View File

@@ -81,12 +81,20 @@ func TestCompiler_resourceInstanceBasics(t *testing.T) {
ProviderInstance: &providerInstAddr,
}
},
ResourceInstancePriorStateFunc: func(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObject {
return &states.ResourceInstanceObject{
ResourceInstancePriorStateFunc: func(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObjectFull {
return &states.ResourceInstanceObjectFull{
Status: states.ObjectReady,
Value: cty.ObjectVal(map[string]cty.Value{
"name": cty.StringVal("prior"),
}),
ProviderInstanceAddr: addrs.AbsProviderInstanceCorrect{
Config: addrs.AbsProviderConfigCorrect{
Config: addrs.ProviderConfigCorrect{
Provider: addrs.NewBuiltInProvider("test"),
},
},
},
ResourceType: addr.Resource.Resource.Type,
}
},
ProviderInstanceConfigFunc: func(ctx context.Context, addr addrs.AbsProviderInstanceCorrect) cty.Value {
@@ -185,11 +193,19 @@ func TestCompiler_resourceInstanceBasics(t *testing.T) {
{
MethodName: "ResourceInstancePriorState",
Args: []any{resourceInstAddr, states.NotDeposed},
Result: &states.ResourceInstanceObject{
Result: &states.ResourceInstanceObjectFull{
Status: states.ObjectReady,
Value: cty.ObjectVal(map[string]cty.Value{
"name": cty.StringVal("prior"),
}),
ProviderInstanceAddr: addrs.AbsProviderInstanceCorrect{
Config: addrs.AbsProviderConfigCorrect{
Config: addrs.ProviderConfigCorrect{
Provider: addrs.NewBuiltInProvider("test"),
},
},
},
ResourceType: "bar_thing",
},
},
{

View File

@@ -48,7 +48,7 @@ type ExecContext interface {
// the planning engine, because it should only generate requests for
// prior state objects that were present and valid in the refreshed state
// during the planning step.
ResourceInstancePriorState(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObject
ResourceInstancePriorState(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObjectFull
// ProviderInstanceConfig returns the value that should be sent when
// configuring the specified provider instance, or [cty.NilVal] if

View File

@@ -25,7 +25,7 @@ type MockExecContext struct {
DesiredResourceInstanceFunc func(ctx context.Context, addr addrs.AbsResourceInstance) *eval.DesiredResourceInstance
NewProviderClientFunc func(ctx context.Context, addr addrs.Provider, configVal cty.Value) (providers.Configured, tfdiags.Diagnostics)
ProviderInstanceConfigFunc func(ctx context.Context, addr addrs.AbsProviderInstanceCorrect) cty.Value
ResourceInstancePriorStateFunc func(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObject
ResourceInstancePriorStateFunc func(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObjectFull
mu sync.Mutex
}
@@ -64,8 +64,8 @@ func (m *MockExecContext) ProviderInstanceConfig(ctx context.Context, addr addrs
}
// ResourceInstancePriorState implements ExecContext.
func (m *MockExecContext) ResourceInstancePriorState(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObject {
var result *states.ResourceInstanceObject
func (m *MockExecContext) ResourceInstancePriorState(ctx context.Context, addr addrs.AbsResourceInstance, deposedKey states.DeposedKey) *states.ResourceInstanceObjectFull {
var result *states.ResourceInstanceObjectFull
if m.ResourceInstancePriorStateFunc != nil {
result = m.ResourceInstancePriorStateFunc(ctx, addr, deposedKey)
}

View File

@@ -95,7 +95,7 @@ type Graph struct {
// operation for each resource instance should also directly depend on
// the results of any resource instances that were identified as
// resource-instance-graph dependencies during the planning process.
resourceInstanceResults addrs.Map[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObject]]
resourceInstanceResults addrs.Map[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObjectFull]]
}
// DebugRepr returns a relatively-concise string representation of the

View File

@@ -63,7 +63,7 @@ func (m *graphMarshaler) EnsureOperationPresent(idx int) uint64 {
return m.ensureRefTarget(erasedRef)
}
func (m *graphMarshaler) EnsureResourceInstanceResultsPresent(results addrs.Map[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObject]]) {
func (m *graphMarshaler) EnsureResourceInstanceResultsPresent(results addrs.Map[addrs.AbsResourceInstance, ResultRef[*states.ResourceInstanceObjectFull]]) {
m.resourceInstanceResults = make(map[string]uint64)
for _, mapElem := range results.Elems {
instAddr := mapElem.Key

View File

@@ -116,7 +116,7 @@ func UnmarshalGraph(src []byte) (*Graph, error) {
if diags.HasErrors() {
return nil, fmt.Errorf("invalid resource instance address %q: %w", instAddrStr, diags.Err())
}
resultRef, err := unmarshalGetPrevResultOf[*states.ResourceInstanceObject](results, resultIdx)
resultRef, err := unmarshalGetPrevResultOf[*states.ResourceInstanceObjectFull](results, resultIdx)
if err != nil {
return nil, fmt.Errorf("invalid result element for %s: %w", instAddr, err)
}
@@ -156,7 +156,7 @@ func unmarshalOpManagedFinalPlan(rawOperands []uint64, prevResults []AnyResultRe
if err != nil {
return nil, fmt.Errorf("invalid opManagedFinalPlan desiredInst: %w", err)
}
priorState, err := unmarshalGetPrevResultOf[*states.ResourceInstanceObject](prevResults, rawOperands[1])
priorState, err := unmarshalGetPrevResultOf[*states.ResourceInstanceObjectFull](prevResults, rawOperands[1])
if err != nil {
return nil, fmt.Errorf("invalid opManagedFinalPlan priorState: %w", err)
}

View File

@@ -75,13 +75,14 @@ type resourceInstancePriorStateResultRef struct {
index int
}
var _ ResultRef[*states.ResourceInstanceObject] = resourceInstancePriorStateResultRef{}
var _ ResultRef[*states.ResourceInstanceObjectFull] = resourceInstancePriorStateResultRef{}
// anyResultPlaceholderSigil implements ResultRef.
func (r resourceInstancePriorStateResultRef) anyResultPlaceholderSigil() {}
// resultPlaceholderSigil implements ResultRef.
func (r resourceInstancePriorStateResultRef) resultPlaceholderSigil(*states.ResourceInstanceObject) {}
func (r resourceInstancePriorStateResultRef) resultPlaceholderSigil(*states.ResourceInstanceObjectFull) {
}
// providerInstanceConfigResultRef is a [ResultRef] referring to an item in a
// graph's table of provider instance configuration requests.