From e417d796a0dc4c7206abeefd6c29ab615da9470f Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 18 Feb 2021 10:17:56 -0500 Subject: [PATCH 1/5] don't add duplicate proxy provider nodes Fix the logic to add proxy provider nodes for implicitly passed in providers. The missing continue allowed multiple nodes satisfying the same provider address to be added to the graph. When attaching the providers to resources, the fist one encountered would be used, which could change each time the graph was built. --- terraform/transform_provider.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index f8f390b0ab04..ce8ac120e7e5 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -227,7 +227,7 @@ func (t *ProviderTransformer) Transform(g *Graph) error { break } - // see if this in an inherited provider + // see if this is a proxy provider pointing to another concrete config if p, ok := target.(*graphNodeProxyProvider); ok { g.Remove(p) target = p.Target() @@ -708,10 +708,13 @@ func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Confi concreteProvider := t.providers[fullName] - // replace the concrete node with the provider passed in - if concreteProvider != nil && t.proxiable[fullName] { - g.Replace(concreteProvider, proxy) - t.providers[fullName] = proxy + // replace the concrete node with the provider passed in only if it is + // proxyable + if concreteProvider != nil { + if t.proxiable[fullName] { + g.Replace(concreteProvider, proxy) + t.providers[fullName] = proxy + } continue } From 0da0b24527129c4fa89f82615b3c679968627ca3 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 18 Feb 2021 11:45:35 -0500 Subject: [PATCH 2/5] provider transformer test mixing legacy stuff Test a combination of new required_providers modules and legacy implied providers along with providers within modules and inheritance. --- terraform/transform_provider_test.go | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/terraform/transform_provider_test.go b/terraform/transform_provider_test.go index fbc9a4187274..cfe5e98f1922 100644 --- a/terraform/transform_provider_test.go +++ b/terraform/transform_provider_test.go @@ -434,6 +434,103 @@ func TestProviderConfigTransformer_grandparentProviders(t *testing.T) { } } +// Verify that configurations which are not recommended yet supported still work +func TestProviderConfigTransformer_nestedModuleProviders(t *testing.T) { + mod := testModuleInline(t, map[string]string{ + "main.tf": ` +terraform { + required_providers { + test = { + source = "registry.terraform.io/hashicorp/test" + } + } +} + +provider "test" { + alias = "z" + test_string = "config" +} + +module "moda" { + source = "./moda" + providers = { + test.x = test.z + } +} +`, + + "moda/main.tf": ` +terraform { + required_providers { + test = { + source = "registry.terraform.io/hashicorp/test" + configuration_aliases = [ test.x ] + } + } +} + +provider "test" { + test_string = "config" +} + +// this should connect to this module's provider +resource "test_object" "a" { +} + +resource "test_object" "x" { + provider = test.x +} + +module "modb" { + source = "./modb" +} +`, + + "moda/modb/main.tf": ` +# this should end up with the provider from the parent module +resource "test_object" "a" { +} +`, + }) + concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } + + g := Graph{Path: addrs.RootModuleInstance} + + { + tf := &ConfigTransformer{Config: mod} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { + tf := &AttachResourceConfigTransformer{Config: mod} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + tf := TransformProviders([]string{"registry.terraform.io/hashicorp/test"}, concrete, mod) + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + expected := `module.moda.module.modb.test_object.a + module.moda.provider["registry.terraform.io/hashicorp/test"] +module.moda.provider["registry.terraform.io/hashicorp/test"] +module.moda.test_object.a + module.moda.provider["registry.terraform.io/hashicorp/test"] +module.moda.test_object.x + provider["registry.terraform.io/hashicorp/test"].z +provider["registry.terraform.io/hashicorp/test"].z` + + actual := strings.TrimSpace(g.String()) + if actual != expected { + t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) + } +} + const testTransformProviderBasicStr = ` aws_instance.web provider["registry.terraform.io/hashicorp/aws"] From b1d8d856ca0cf275022f73ec99d2ff4641284ea7 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 18 Feb 2021 12:42:01 -0500 Subject: [PATCH 3/5] remove ParentProviderTransformer There is no longer any concept of a "parent provider". Inheritance of default providers is discovered and connected in the ProviderTransformer itself. --- terraform/transform_provider.go | 38 --------------------- terraform/transform_provider_test.go | 51 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index ce8ac120e7e5..e46fb013427f 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -31,8 +31,6 @@ func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, c }, // Remove unused providers and proxies &PruneProviderTransformer{}, - // Connect provider to their parent provider nodes - &ParentProviderTransformer{}, ) } @@ -357,42 +355,6 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { return err } -// ParentProviderTransformer connects provider nodes to their parents. -// -// This works by finding nodes that are both GraphNodeProviders and -// GraphNodeModuleInstance. It then connects the providers to their parent -// path. The parent provider is always at the root level. -type ParentProviderTransformer struct{} - -func (t *ParentProviderTransformer) Transform(g *Graph) error { - pm := providerVertexMap(g) - for _, v := range g.Vertices() { - // Only care about providers - pn, ok := v.(GraphNodeProvider) - if !ok { - continue - } - - // Also require non-empty path, since otherwise we're in the root - // module and so cannot have a parent. - if len(pn.ModulePath()) <= 1 { - continue - } - - // this provider may be disabled, but we can only get it's name from - // the ProviderName string - addr := pn.ProviderAddr() - parentAddr, ok := addr.Inherited() - if ok { - parent := pm[parentAddr.String()] - if parent != nil { - g.Connect(dag.BasicEdge(v, parent)) - } - } - } - return nil -} - // PruneProviderTransformer removes any providers that are not actually used by // anything, and provider proxies. This avoids the provider being initialized // and configured. This both saves resources but also avoids errors since diff --git a/terraform/transform_provider_test.go b/terraform/transform_provider_test.go index cfe5e98f1922..e5565fa62759 100644 --- a/terraform/transform_provider_test.go +++ b/terraform/transform_provider_test.go @@ -434,6 +434,57 @@ func TestProviderConfigTransformer_grandparentProviders(t *testing.T) { } } +func TestProviderConfigTransformer_inheritOldSkool(t *testing.T) { + mod := testModuleInline(t, map[string]string{ + "main.tf": ` +provider "test" { + test_string = "config" +} + +module "moda" { + source = "./moda" +} +`, + + "moda/main.tf": ` +resource "test_object" "a" { +} +`, + }) + concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } + + g := Graph{Path: addrs.RootModuleInstance} + + { + tf := &ConfigTransformer{Config: mod} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + { + tf := &AttachResourceConfigTransformer{Config: mod} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + tf := TransformProviders([]string{"registry.terraform.io/hashicorp/test"}, concrete, mod) + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + expected := `module.moda.test_object.a + provider["registry.terraform.io/hashicorp/test"] +provider["registry.terraform.io/hashicorp/test"]` + + actual := strings.TrimSpace(g.String()) + if actual != expected { + t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) + } +} + // Verify that configurations which are not recommended yet supported still work func TestProviderConfigTransformer_nestedModuleProviders(t *testing.T) { mod := testModuleInline(t, map[string]string{ From 8c1703d8df67bc3cb134af58cdea0dded438d617 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 18 Feb 2021 12:43:31 -0500 Subject: [PATCH 4/5] simplify the use of configuration_aliases Now that the possibility of extra provider nodes being added is gone, we can skip the separate handling of configuration_aliases in the ProviderConfigTransformer. Instead of adding concrete provider nodes for configuration_aliases (which could be surprising later as the code evolves), we implicitly add them based on the providers being passed in by the parent module, using the same mechanism as non-aliased providers. We can know that the `providers` map will be populated, because the provider structures are validated while loading the configuration. --- terraform/transform_provider.go | 42 --------------------------------- 1 file changed, 42 deletions(-) diff --git a/terraform/transform_provider.go b/terraform/transform_provider.go index e46fb013427f..882272c38545 100644 --- a/terraform/transform_provider.go +++ b/terraform/transform_provider.go @@ -567,43 +567,6 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config) t.proxiable[key] = !diags.HasErrors() } - if mod.ProviderRequirements != nil { - // Add implied provider configs from the required_providers - // Since we're still treating empty configs as proxies, we can just add - // these as empty configs too. We'll ensure that these are given a - // configuration during validation to prevent them from becoming - // fully-fledged config instances. - for _, p := range mod.ProviderRequirements.RequiredProviders { - for _, aliasAddr := range p.Aliases { - addr := addrs.AbsProviderConfig{ - Provider: mod.ProviderForLocalConfig(aliasAddr), - Module: path, - Alias: aliasAddr.Alias, - } - - key := addr.String() - if _, ok := t.providers[key]; ok { - continue - } - - abstract := &NodeAbstractProvider{ - Addr: addr, - } - var v dag.Vertex - if t.Concrete != nil { - v = t.Concrete(abstract) - } else { - v = abstract - } - - // Add it to the graph - g.Add(v) - t.providers[key] = v.(GraphNodeProvider) - t.proxiable[key] = true - } - } - } - // Now replace the provider nodes with proxy nodes if a provider was being // passed in, and create implicit proxies if there was no config. Any extra // proxies will be removed in the prune step. @@ -680,11 +643,6 @@ func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Confi continue } - // aliased configurations can't be implicitly passed in - if fullAddr.Alias != "" { - continue - } - // There was no concrete provider, so add this as an implicit provider. // The extra proxy will be pruned later if it's unused. g.Add(proxy) From dd5227a3bee9cadefa4f1bdae6f28946ed33d130 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 18 Feb 2021 13:21:19 -0500 Subject: [PATCH 5/5] provider transformer tests remove some boilerplate when setting up tests --- terraform/transform_provider_test.go | 240 ++++++--------------------- 1 file changed, 52 insertions(+), 188 deletions(-) diff --git a/terraform/transform_provider_test.go b/terraform/transform_provider_test.go index e5565fa62759..dc1d0ad43460 100644 --- a/terraform/transform_provider_test.go +++ b/terraform/transform_provider_test.go @@ -6,36 +6,39 @@ import ( "testing" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/dag" ) -func TestProviderTransformer(t *testing.T) { - mod := testModule(t, "transform-provider-basic") +func testProviderTransformerGraph(t *testing.T, cfg *configs.Config) *Graph { + t.Helper() - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } + g := &Graph{Path: addrs.RootModuleInstance} + ct := &ConfigTransformer{Config: cfg} + if err := ct.Transform(g); err != nil { + t.Fatal(err) } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } + arct := &AttachResourceConfigTransformer{Config: cfg} + if err := arct.Transform(g); err != nil { + t.Fatal(err) } + return g +} + +func TestProviderTransformer(t *testing.T) { + mod := testModule(t, "transform-provider-basic") + + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } @@ -49,23 +52,9 @@ func TestProviderTransformer(t *testing.T) { func TestProviderTransformer_ImportModuleChild(t *testing.T) { mod := testModule(t, "import-module") - g := Graph{Path: addrs.RootModuleInstance} + g := testProviderTransformerGraph(t, mod) { - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - tf := &ImportStateTransformer{ Config: mod, Targets: []*ImportTarget{ @@ -83,7 +72,7 @@ func TestProviderTransformer_ImportModuleChild(t *testing.T) { }, } - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } t.Logf("graph after ImportStateTransformer:\n%s", g.String()) @@ -91,7 +80,7 @@ func TestProviderTransformer_ImportModuleChild(t *testing.T) { { tf := &MissingProviderTransformer{Providers: []string{"foo", "bar"}} - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } t.Logf("graph after MissingProviderTransformer:\n%s", g.String()) @@ -99,7 +88,7 @@ func TestProviderTransformer_ImportModuleChild(t *testing.T) { { tf := &ProviderTransformer{} - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } t.Logf("graph after ProviderTransformer:\n%s", g.String()) @@ -117,30 +106,16 @@ func TestProviderTransformer_fqns(t *testing.T) { for _, mod := range []string{"fqns", "fqns-module"} { mod := testModule(t, fmt.Sprintf("transform-provider-%s", mod)) - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws"}, Config: mod} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } transform := &ProviderTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } @@ -154,39 +129,25 @@ func TestProviderTransformer_fqns(t *testing.T) { func TestCloseProviderTransformer(t *testing.T) { mod := testModule(t, "transform-provider-basic") - - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &CloseProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -201,9 +162,8 @@ func TestCloseProviderTransformer(t *testing.T) { func TestCloseProviderTransformer_withTargets(t *testing.T) { mod := testModule(t, "transform-provider-basic") - g := Graph{Path: addrs.RootModuleInstance} + g := testProviderTransformerGraph(t, mod) transforms := []GraphTransformer{ - &ConfigTransformer{Config: mod}, &MissingProviderTransformer{Providers: []string{"aws"}}, &ProviderTransformer{}, &CloseProviderTransformer{}, @@ -217,7 +177,7 @@ func TestCloseProviderTransformer_withTargets(t *testing.T) { } for _, tr := range transforms { - if err := tr.Transform(&g); err != nil { + if err := tr.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -232,38 +192,24 @@ func TestCloseProviderTransformer_withTargets(t *testing.T) { func TestMissingProviderTransformer(t *testing.T) { mod := testModule(t, "transform-provider-missing") - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws", "foo", "bar"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &CloseProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -280,30 +226,16 @@ func TestMissingProviderTransformer_grandchildMissing(t *testing.T) { concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := TransformProviders([]string{"aws", "foo", "bar"}, concrete, mod) - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &TransitiveReductionTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -318,45 +250,31 @@ func TestMissingProviderTransformer_grandchildMissing(t *testing.T) { func TestPruneProviderTransformer(t *testing.T) { mod := testModule(t, "transform-provider-prune") - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"foo"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &CloseProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &PruneProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -373,23 +291,10 @@ func TestProviderConfigTransformer_parentProviders(t *testing.T) { mod := testModule(t, "transform-provider-inherit") concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { tf := TransformProviders([]string{"aws"}, concrete, mod) - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -406,23 +311,10 @@ func TestProviderConfigTransformer_grandparentProviders(t *testing.T) { mod := testModule(t, "transform-provider-grandchild-inherit") concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { tf := TransformProviders([]string{"aws"}, concrete, mod) - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -453,24 +345,10 @@ resource "test_object" "a" { }) concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { tf := TransformProviders([]string{"registry.terraform.io/hashicorp/test"}, concrete, mod) - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -545,24 +423,10 @@ resource "test_object" "a" { }) concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { tf := TransformProviders([]string{"registry.terraform.io/hashicorp/test"}, concrete, mod) - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } }