Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

special handling for legacy ignore_changes = all #31914

Merged
merged 1 commit into from Oct 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 64 additions & 0 deletions internal/terraform/context_plan_test.go
Expand Up @@ -6689,6 +6689,70 @@ resource "test_instance" "a" {
}
}

func TestContext2Plan_legacyProviderIgnoreAll(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
resource "test_instance" "a" {
lifecycle {
ignore_changes = all
}
data = "foo"
}
`,
})

p := testProvider("test")
p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"test_instance": {
Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Computed: true},
"data": {Type: cty.String, Optional: true},
},
},
},
})
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
plan := req.ProposedNewState.AsValueMap()
// Update both the computed id and the configured data.
// Legacy providers expect terraform to be able to ignore these.

plan["id"] = cty.StringVal("updated")
plan["data"] = cty.StringVal("updated")
resp.PlannedState = cty.ObjectVal(plan)
resp.LegacyTypeSystem = true
return resp
}

state := states.NewState()
root := state.EnsureModule(addrs.RootModuleInstance)
root.SetResourceInstanceCurrent(
mustResourceInstanceAddr("test_instance.a").Resource,
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"orig","data":"orig"}`),
Dependencies: []addrs.ConfigResource{},
},
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
)

ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
if diags.HasErrors() {
t.Fatal(diags.Err())
}

for _, c := range plan.Changes.Resources {
if c.Action != plans.NoOp {
t.Fatalf("expected NoOp plan, got %s\n", c.Action)
}
}
}

func TestContext2Plan_dataRemovalNoProvider(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
Expand Down
13 changes: 12 additions & 1 deletion internal/terraform/node_resource_abstract_instance.go
Expand Up @@ -881,7 +881,10 @@ func (n *NodeAbstractResourceInstance) plan(
// providers that we must accommodate the behavior for now, so for
// ignore_changes to work at all on these values, we will revert the
// ignored values once more.
plannedNewVal, ignoreChangeDiags = n.processIgnoreChanges(unmarkedPriorVal, plannedNewVal, schema)
// A nil schema is passed to processIgnoreChanges to indicate that we
// don't want to fixup a config value according to the schema when
// ignoring "all", rather we are reverting provider imposed changes.
plannedNewVal, ignoreChangeDiags = n.processIgnoreChanges(unmarkedPriorVal, plannedNewVal, nil)
diags = diags.Append(ignoreChangeDiags)
if ignoreChangeDiags.HasErrors() {
return plan, state, keyData, diags
Expand Down Expand Up @@ -1160,6 +1163,14 @@ func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value, sch
}

if ignoreAll {
// Legacy providers need up to clean up their invalid plans and ensure
// no changes are passed though, but that also means making an invalid
// config with computed values. In that case we just don't supply a
// schema and return the prior val directly.
if schema == nil {
return prior, nil
}

// If we are trying to ignore all attribute changes, we must filter
// computed attributes out from the prior state to avoid sending them
// to the provider as if they were included in the configuration.
Expand Down