From c85adf191a2d6a27cc4506d22655223316a96381 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 16 Dec 2020 14:10:51 -0500 Subject: [PATCH] modify ProvidedBy to indicate no provider needed Because of the composition pattern used within core, we can't easily remove a behavior from an embedded type. Rather than trying to re-implement all necessary methods on the NodePlannableResourceInstnaceOrphan to exclude orphaned data resources from GraphNodeProviderConsumer, we can modify ProvidedBy to indicate when there is no provider required. --- terraform/context_plan_test.go | 52 ++++++++++++++++++++++++++ terraform/node_resource_plan_orphan.go | 9 +++++ terraform/transform_provider.go | 8 +++- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index 3731c69a0..60f84550c 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -6616,3 +6616,55 @@ resource "test_instance" "a" { t.Fatal(diags.Err()) } } + +func TestContext2Plan_dataRemovalNoProvider(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { +} +`, + }) + + p := testProvider("test") + p.PlanResourceChangeFn = testDiffFn + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_instance.a").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"a","data":"foo"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + // the provider for this data source is no longer in the config, but that + // should not matter for state removal. + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("data.test_data_source.d").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"d"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/local/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + // We still need to be able to locate the provider to decode the + // state, since we do not know during init that this provider is + // only used for an orphaned data source. + addrs.NewProvider("registry.terraform.io", "local", "test"): testProviderFuncFixed(p), + }, + State: state, + }) + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} diff --git a/terraform/node_resource_plan_orphan.go b/terraform/node_resource_plan_orphan.go index 6ac75c171..4653758a3 100644 --- a/terraform/node_resource_plan_orphan.go +++ b/terraform/node_resource_plan_orphan.go @@ -27,6 +27,7 @@ var ( _ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstanceOrphan)(nil) _ GraphNodeAttachResourceState = (*NodePlannableResourceInstanceOrphan)(nil) _ GraphNodeExecutable = (*NodePlannableResourceInstanceOrphan)(nil) + _ GraphNodeProviderConsumer = (*NodePlannableResourceInstanceOrphan)(nil) ) func (n *NodePlannableResourceInstanceOrphan) Name() string { @@ -48,6 +49,14 @@ func (n *NodePlannableResourceInstanceOrphan) Execute(ctx EvalContext, op walkOp } } +func (n *NodePlannableResourceInstanceOrphan) ProvidedBy() (addr addrs.ProviderConfig, exact bool) { + if n.Addr.Resource.Resource.Mode == addrs.DataResourceMode { + // indicate that this node does not require a configured provider + return nil, true + } + return n.NodeAbstractResourceInstance.ProvidedBy() +} + func (n *NodePlannableResourceInstanceOrphan) dataResourceExecute(ctx EvalContext) tfdiags.Diagnostics { // A data source that is no longer in the config is removed from the state log.Printf("[TRACE] NodePlannableResourceInstanceOrphan: removing state object for %s", n.Addr) diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index 2b3ebfea0..2b8728cd3 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -67,6 +67,7 @@ type GraphNodeProviderConsumer interface { // ProvidedBy returns the address of the provider configuration the node // refers to, if available. The following value types may be returned: // + // nil + exact true: the node does not require a provider // * addrs.LocalProviderConfig: the provider was set in the resource config // * addrs.AbsProviderConfig + exact true: the provider configuration was // taken from the instance state. @@ -111,9 +112,14 @@ func (t *ProviderTransformer) Transform(g *Graph) error { for _, v := range g.Vertices() { // Does the vertex _directly_ use a provider? if pv, ok := v.(GraphNodeProviderConsumer); ok { + providerAddr, exact := pv.ProvidedBy() + if providerAddr == nil && exact { + // no provider is required + continue + } + requested[v] = make(map[string]ProviderRequest) - providerAddr, exact := pv.ProvidedBy() var absPc addrs.AbsProviderConfig switch p := providerAddr.(type) {