stacks: handle deferred import on import blocks

TF-13961
Daniel Schmidt 2 years ago
parent efdcc7a711
commit 928657b0ab
No known key found for this signature in database
GPG Key ID: 377C3A4D62FBBBE2

@ -2025,6 +2025,63 @@ output "a" {
},
},
}
importDeferredTest = deferredActionsTest{
configs: map[string]string{
"main.tf": `
variable "import_id" {
type = string
}
resource "test" "a" {
name = "a"
}
import {
id = var.import_id
to = test.a
}
`,
},
stages: []deferredActionsTestStage{
{
inputs: map[string]cty.Value{
"import_id": cty.StringVal("deferred"), // Telling the test case to defer the import
},
wantPlanned: map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"name": cty.StringVal("a"),
"upstream_names": cty.NullVal(cty.Set(cty.String)),
"output": cty.UnknownVal(cty.String),
}),
},
wantActions: make(map[string]plans.Action),
wantDeferred: map[string]ExpectedDeferred{
"test.a": {Reason: providers.DeferredReasonAbsentPrereq, Action: plans.NoOp},
},
wantApplied: make(map[string]cty.Value),
wantOutputs: make(map[string]cty.Value),
complete: false,
},
{
inputs: map[string]cty.Value{
"import_id": cty.StringVal("can_be_imported"),
},
wantPlanned: map[string]cty.Value{
"a": cty.ObjectVal(map[string]cty.Value{
"name": cty.StringVal("a"),
"upstream_names": cty.NullVal(cty.Set(cty.String)),
"output": cty.StringVal("can_be_imported"),
}),
},
wantActions: map[string]plans.Action{
"test.a": plans.Update,
},
wantDeferred: map[string]ExpectedDeferred{},
complete: true,
},
},
}
)
func TestContextApply_deferredActions(t *testing.T) {
@ -2050,6 +2107,7 @@ func TestContextApply_deferredActions(t *testing.T) {
"plan_force_replace_resource_change": planForceReplaceResourceChange,
"plan_delete_resource_change": planDeleteResourceChange,
"plan_destroy_resource_change": planDestroyResourceChange,
"import_deferred": importDeferredTest,
}
for name, test := range tests {
@ -2345,7 +2403,7 @@ func (provider *deferredActionsProvider) Provider() providers.Interface {
}
},
ImportResourceStateFn: func(request providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
return providers.ImportResourceStateResponse{
resp := providers.ImportResourceStateResponse{
ImportedResources: []providers.ImportedResource{
{
TypeName: request.TypeName,
@ -2357,6 +2415,13 @@ func (provider *deferredActionsProvider) Provider() providers.Interface {
},
},
}
if request.ID == "deferred" {
resp.Deferred = &providers.Deferred{
Reason: providers.DeferredReasonAbsentPrereq,
}
}
return resp
},
}
}

@ -534,6 +534,9 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
resp = provider.ImportResourceState(providers.ImportResourceStateRequest{
TypeName: addr.Resource.Resource.Type,
ID: importId,
ClientCapabilities: providers.ClientCapabilities{
DeferralAllowed: ctx.Deferrals().DeferralAllowed(),
},
})
}
diags = diags.Append(resp.Diagnostics)
@ -543,7 +546,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
imported := resp.ImportedResources
if len(imported) == 0 {
if len(imported) == 0 && resp.Deferred == nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Import returned no resources",
@ -570,6 +573,22 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
return nil, diags
}
// If the import was deferred we can't do more here
if resp.Deferred != nil {
ctx.Deferrals().ReportResourceInstanceDeferred(n.Addr, resp.Deferred.Reason, &plans.ResourceInstanceChange{
Addr: n.Addr,
Change: plans.Change{
Action: plans.NoOp,
Before: cty.UnknownVal(cty.DynamicPseudoType),
After: cty.UnknownVal(cty.DynamicPseudoType),
Importing: &plans.Importing{
ID: importId,
},
},
})
return nil, diags
}
// call post-import hook
diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
return h.PostPlanImport(hookResourceID, imported)
@ -601,14 +620,15 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
},
override: n.override,
}
instanceRefreshState, deferred, refreshDiags := riNode.refresh(ctx, states.NotDeposed, importedState)
instanceRefreshState, refreshDeferred, 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{
// report the refresh was deferred, we don't need to error since the import step succeeded
if refreshDeferred != nil {
ctx.Deferrals().ReportResourceInstanceDeferred(n.Addr, refreshDeferred.Reason, &plans.ResourceInstanceChange{
Addr: n.Addr,
Change: plans.Change{
Action: plans.Read,
@ -618,7 +638,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
}
// verify the existence of the imported resource
if instanceRefreshState.Value.IsNull() && deferred == nil {
if instanceRefreshState.Value.IsNull() && refreshDeferred == nil {
var diags tfdiags.Diagnostics
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,

Loading…
Cancel
Save