stacks: return deferral instead of recording it

pull/34887/head
Daniel Schmidt 2 years ago
parent b59edb4463
commit 3d80ec4aa2
No known key found for this signature in database
GPG Key ID: 377C3A4D62FBBBE2

@ -584,8 +584,10 @@ func (n *NodeAbstractResourceInstance) writeChange(ctx EvalContext, change *plan
}
// refresh does a refresh for a resource
func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey states.DeposedKey, state *states.ResourceInstanceObject) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
// if the second return value is non-nil, the refresh is deferred
func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey states.DeposedKey, state *states.ResourceInstanceObject) (*states.ResourceInstanceObject, *providers.Deferred, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
var deferred *providers.Deferred
absAddr := n.Addr
if deposedKey == states.NotDeposed {
log.Printf("[TRACE] NodeAbstractResourceInstance.refresh for %s", absAddr)
@ -594,25 +596,25 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
}
provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
if err != nil {
return state, diags.Append(err)
return state, deferred, diags.Append(err)
}
// If we have no state, we don't do any refreshing
if state == nil {
log.Printf("[DEBUG] refresh: %s: no state, so not refreshing", absAddr)
return state, diags
return state, deferred, diags
}
schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.Resource.ContainingResource())
if schema == nil {
// Should be caught during validation, so we don't bother with a pretty error here
diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type))
return state, diags
return state, deferred, diags
}
metaConfigVal, metaDiags := n.providerMetas(ctx)
diags = diags.Append(metaDiags)
if diags.HasErrors() {
return state, diags
return state, deferred, diags
}
// Call pre-refresh hook
@ -620,7 +622,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
return h.PreRefresh(n.HookResourceIdentity(), deposedKey, state.Value)
}))
if diags.HasErrors() {
return state, diags
return state, deferred, diags
}
// Refresh!
@ -649,14 +651,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
})
if resp.Deferred != nil {
deferrals := ctx.Deferrals()
deferrals.ReportResourceInstanceDeferred(absAddr, resp.Deferred.Reason, &plans.ResourceInstanceChange{
Addr: absAddr,
Change: plans.Change{
Action: plans.Create,
After: cty.DynamicVal,
},
})
deferred = resp.Deferred
}
}
if n.Config != nil {
@ -665,7 +660,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
diags = diags.Append(resp.Diagnostics)
if diags.HasErrors() {
return state, diags
return state, deferred, diags
}
if resp.NewState == cty.NilVal {
@ -699,7 +694,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
))
}
if diags.HasErrors() {
return state, diags
return state, deferred, diags
}
newState := objchange.NormalizeObjectFromLegacySDK(resp.NewState, schema)
@ -732,7 +727,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
return h.PostRefresh(n.HookResourceIdentity(), deposedKey, priorVal, ret.Value)
}))
if diags.HasErrors() {
return ret, diags
return ret, deferred, diags
}
// Mark the value with any prior marks from the state, and the marks from
@ -744,7 +739,7 @@ func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey state
ret.Value = ret.Value.MarkWithPaths(marks)
}
return ret, diags
return ret, deferred, diags
}
func (n *NodeAbstractResourceInstance) plan(

@ -232,7 +232,7 @@ func TestNodeAbstractResourceInstance_refresh_with_deferred_read(t *testing.T) {
resourceGraph := addrs.NewDirectedGraph[addrs.ConfigResource]()
evalCtx.DeferralsState = deferring.NewDeferred(resourceGraph, true)
rio, diags := node.refresh(evalCtx, states.NotDeposed, obj)
rio, deferred, diags := node.refresh(evalCtx, states.NotDeposed, obj)
if diags.HasErrors() {
t.Fatal(diags.Err())
}
@ -242,11 +242,11 @@ func TestNodeAbstractResourceInstance_refresh_with_deferred_read(t *testing.T) {
t.Fatalf("value was known: %v", value)
}
if !evalCtx.DeferralsCalled {
t.Fatalf("expected deferral to be called")
if deferred == nil {
t.Fatalf("expected deferral to be present")
}
if !evalCtx.DeferralsState.HaveAnyDeferrals() {
t.Fatalf("expected deferral to be present")
if deferred.Reason != providers.DeferredReasonAbsentPrereq {
t.Fatalf("expected deferral to be AbsentPrereq, got %s", deferred.Reason)
}
}

@ -120,7 +120,7 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walk
// resource during Delete correctly. If this is a simple refresh,
// Terraform is expected to remove the missing resource from the state
// entirely
refreshedState, refreshDiags := n.refresh(ctx, n.DeposedKey, state)
refreshedState, deferred, refreshDiags := n.refresh(ctx, n.DeposedKey, state)
diags = diags.Append(refreshDiags)
if diags.HasErrors() {
return diags
@ -133,7 +133,18 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walk
// If we refreshed then our subsequent planning should be in terms of
// the new object, not the original object.
state = refreshedState
if deferred == nil {
state = refreshedState
} else {
ctx.Deferrals().ReportResourceInstanceDeferred(n.Addr, deferred.Reason, &plans.ResourceInstanceChange{
Addr: n.Addr,
Change: plans.Change{
Action: plans.Read,
Before: state.Value,
After: refreshedState.Value,
},
})
}
}
if !n.skipPlanChanges {

@ -8,6 +8,7 @@ import (
"log"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
@ -228,29 +229,40 @@ func (n *graphNodeImportStateSub) Execute(ctx EvalContext, op walkOperation) (di
ResolvedProvider: n.ResolvedProvider,
},
}
state, refreshDiags := riNode.refresh(ctx, states.NotDeposed, state)
state, deferred, refreshDiags := riNode.refresh(ctx, states.NotDeposed, state)
diags = diags.Append(refreshDiags)
if diags.HasErrors() {
return diags
}
// Verify the existance of the imported resource
if state.Value.IsNull() {
var diags tfdiags.Diagnostics
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Cannot import non-existent remote object",
fmt.Sprintf(
"While attempting to import an existing object to %q, "+
"the provider detected that no object exists with the given id. "+
"Only pre-existing objects can be imported; check that the id "+
"is correct and that it is associated with the provider's "+
"configured region or endpoint, or use \"terraform apply\" to "+
"create a new remote object for this resource.",
n.TargetAddr,
),
))
return diags
// If the refresh is deferred we will need to do another cycle to import the resource
if deferred != nil {
ctx.Deferrals().ReportResourceInstanceDeferred(n.TargetAddr, deferred.Reason, &plans.ResourceInstanceChange{
Addr: n.TargetAddr,
Change: plans.Change{
Action: plans.Read,
After: state.Value,
},
})
} else {
// Verify the existance of the imported resource
if state.Value.IsNull() {
var diags tfdiags.Diagnostics
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Cannot import non-existent remote object",
fmt.Sprintf(
"While attempting to import an existing object to %q, "+
"the provider detected that no object exists with the given id. "+
"Only pre-existing objects can be imported; check that the id "+
"is correct and that it is associated with the provider's "+
"configured region or endpoint, or use \"terraform apply\" to "+
"create a new remote object for this resource.",
n.TargetAddr,
),
))
return diags
}
}
diags = diags.Append(riNode.writeResourceInstanceState(ctx, state, workingState))

@ -205,13 +205,24 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
// Refresh, maybe
// The import process handles its own refresh
if !n.skipRefresh && !importing {
s, refreshDiags := n.refresh(ctx, states.NotDeposed, instanceRefreshState)
s, deferred, refreshDiags := n.refresh(ctx, states.NotDeposed, instanceRefreshState)
diags = diags.Append(refreshDiags)
if diags.HasErrors() {
return diags
}
instanceRefreshState = s
if deferred == nil {
instanceRefreshState = s
} else {
ctx.Deferrals().ReportResourceInstanceDeferred(n.Addr, deferred.Reason, &plans.ResourceInstanceChange{
Addr: n.Addr,
Change: plans.Change{
Action: plans.Read,
Before: s.Value,
After: cty.DynamicVal,
},
})
}
if instanceRefreshState != nil {
// When refreshing we start by merging the stored dependencies and
@ -586,12 +597,22 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
},
override: n.override,
}
instanceRefreshState, refreshDiags := riNode.refresh(ctx, states.NotDeposed, importedState)
instanceRefreshState, deferred, refreshDiags := riNode.refresh(ctx, states.NotDeposed, importedState)
diags = diags.Append(refreshDiags)
if diags.HasErrors() {
return instanceRefreshState, diags
}
if deferred != nil {
ctx.Deferrals().ReportResourceInstanceDeferred(n.Addr, deferred.Reason, &plans.ResourceInstanceChange{
Addr: n.Addr,
Change: plans.Change{
Action: plans.Read,
After: instanceRefreshState.Value,
},
})
}
// verify the existence of the imported resource
if instanceRefreshState.Value.IsNull() {
var diags tfdiags.Diagnostics

@ -116,7 +116,7 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon
// plan before apply, and may not handle a missing resource during
// Delete correctly. If this is a simple refresh, Terraform is
// expected to remove the missing resource from the state entirely
refreshedState, refreshDiags := n.refresh(ctx, states.NotDeposed, oldState)
refreshedState, deferred, refreshDiags := n.refresh(ctx, states.NotDeposed, oldState)
diags = diags.Append(refreshDiags)
if diags.HasErrors() {
return diags
@ -129,7 +129,18 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon
// If we refreshed then our subsequent planning should be in terms of
// the new object, not the original object.
oldState = refreshedState
if deferred == nil {
oldState = refreshedState
} else {
ctx.Deferrals().ReportResourceInstanceDeferred(n.Addr, deferred.Reason, &plans.ResourceInstanceChange{
Addr: n.Addr,
Change: plans.Change{
Action: plans.Read,
Before: oldState.Value,
After: refreshedState.Value,
},
})
}
}
// If we're skipping planning, all we need to do is write the state. If the

Loading…
Cancel
Save