From b1eec0fbcd69476b971455712efb0fc4bf138ae6 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 20 Aug 2020 17:30:08 -0700 Subject: [PATCH] core: NodeAbstractResourceInstance.Provider correct implied provider When we need to select a qualified provider address based on an implied provider name, we have a special case that the name "terraform" maps to terraform.io/builtin/terraform instead of registry.terraform.io/hashicorp/terraform as would be the case for other prefixes. However, in order for that to work properly we need to use addrs.ImpliedProviderForUnqualifiedType instead of addrs.NewDefaultProvider, because the latter just unconditionally always produces a "default" provider configuration (belonging to the "hashicorp" namespace on the public registry). --- terraform/node_resource_abstract.go | 2 +- terraform/node_resource_abstract_test.go | 211 +++++++++++++++++++++++ 2 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 terraform/node_resource_abstract_test.go diff --git a/terraform/node_resource_abstract.go b/terraform/node_resource_abstract.go index 35c6ce27f..50abadb38 100644 --- a/terraform/node_resource_abstract.go +++ b/terraform/node_resource_abstract.go @@ -360,7 +360,7 @@ func (n *NodeAbstractResourceInstance) Provider() addrs.Provider { if n.Config != nil { return n.Config.Provider } - return addrs.NewDefaultProvider(n.Addr.Resource.ContainingResource().ImpliedProvider()) + return addrs.ImpliedProviderForUnqualifiedType(n.Addr.Resource.ContainingResource().ImpliedProvider()) } // GraphNodeProvisionerConsumer diff --git a/terraform/node_resource_abstract_test.go b/terraform/node_resource_abstract_test.go new file mode 100644 index 000000000..570f84079 --- /dev/null +++ b/terraform/node_resource_abstract_test.go @@ -0,0 +1,211 @@ +package terraform + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs" +) + +func TestNodeAbstractResourceProvider(t *testing.T) { + tests := []struct { + Addr addrs.ConfigResource + Config *configs.Resource + Want addrs.Provider + }{ + { + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "null_resource", + Name: "baz", + }.InModule(addrs.RootModule), + Want: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "hashicorp", + Type: "null", + }, + }, + { + Addr: addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "terraform_remote_state", + Name: "baz", + }.InModule(addrs.RootModule), + Want: addrs.Provider{ + // As a special case, the type prefix "terraform_" maps to + // the builtin provider, not the default one. + Hostname: addrs.BuiltInProviderHost, + Namespace: addrs.BuiltInProviderNamespace, + Type: "terraform", + }, + }, + { + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "null_resource", + Name: "baz", + }.InModule(addrs.RootModule), + Config: &configs.Resource{ + // Just enough configs.Resource for the Provider method. Not + // actually valid for general use. + Provider: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + // The config overrides the default behavior. + Want: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + { + Addr: addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "terraform_remote_state", + Name: "baz", + }.InModule(addrs.RootModule), + Config: &configs.Resource{ + // Just enough configs.Resource for the Provider method. Not + // actually valid for general use. + Provider: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + // The config overrides the default behavior. + Want: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + } + + for _, test := range tests { + var name string + if test.Config != nil { + name = fmt.Sprintf("%s with configured %s", test.Addr, test.Config.Provider) + } else { + name = fmt.Sprintf("%s with no configuration", test.Addr) + } + t.Run(name, func(t *testing.T) { + node := &NodeAbstractResource{ + // Just enough NodeAbstractResource for the Provider function. + // (This would not be valid for some other functions.) + Addr: test.Addr, + Config: test.Config, + } + got := node.Provider() + if got != test.Want { + t.Errorf("wrong result\naddr: %s\nconfig: %#v\ngot: %s\nwant: %s", test.Addr, test.Config, got, test.Want) + } + }) + } +} + +func TestNodeAbstractResourceInstanceProvider(t *testing.T) { + tests := []struct { + Addr addrs.AbsResourceInstance + Config *configs.Resource + Want addrs.Provider + }{ + { + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "null_resource", + Name: "baz", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + Want: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "hashicorp", + Type: "null", + }, + }, + { + Addr: addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "terraform_remote_state", + Name: "baz", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + Want: addrs.Provider{ + // As a special case, the type prefix "terraform_" maps to + // the builtin provider, not the default one. + Hostname: addrs.BuiltInProviderHost, + Namespace: addrs.BuiltInProviderNamespace, + Type: "terraform", + }, + }, + { + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "null_resource", + Name: "baz", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + Config: &configs.Resource{ + // Just enough configs.Resource for the Provider method. Not + // actually valid for general use. + Provider: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + // The config overrides the default behavior. + Want: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + { + Addr: addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "terraform_remote_state", + Name: "baz", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + Config: &configs.Resource{ + // Just enough configs.Resource for the Provider method. Not + // actually valid for general use. + Provider: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + // The config overrides the default behavior. + Want: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "awesomecorp", + Type: "happycloud", + }, + }, + } + + for _, test := range tests { + var name string + if test.Config != nil { + name = fmt.Sprintf("%s with configured %s", test.Addr, test.Config.Provider) + } else { + name = fmt.Sprintf("%s with no configuration", test.Addr) + } + t.Run(name, func(t *testing.T) { + node := &NodeAbstractResourceInstance{ + // Just enough NodeAbstractResourceInstance for the Provider + // function. (This would not be valid for some other functions.) + Addr: test.Addr, + NodeAbstractResource: NodeAbstractResource{ + Config: test.Config, + }, + } + got := node.Provider() + if got != test.Want { + t.Errorf("wrong result\naddr: %s\nconfig: %#v\ngot: %s\nwant: %s", test.Addr, test.Config, got, test.Want) + } + }) + } +}