diff --git a/internal/command/command_test.go b/internal/command/command_test.go index 4a56ecc73b23..2f75ec30de1b 100644 --- a/internal/command/command_test.go +++ b/internal/command/command_test.go @@ -171,7 +171,8 @@ func testFixturePath(name string) string { func metaOverridesForProvider(p providers.Interface) *testingOverrides { return &testingOverrides{ Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("test"): providers.FactoryFixed(p), + addrs.NewDefaultProvider("test"): providers.FactoryFixed(p), + addrs.NewProvider(addrs.DefaultProviderRegistryHost, "hashicorp2", "test"): providers.FactoryFixed(p), }, } } diff --git a/internal/command/jsonconfig/config.go b/internal/command/jsonconfig/config.go index 807444d6e4f1..f744c91086f8 100644 --- a/internal/command/jsonconfig/config.go +++ b/internal/command/jsonconfig/config.go @@ -231,6 +231,36 @@ func marshalProviderConfigs( p.VersionConstraint = getproviders.VersionConstraintsString(vc) } + if c.Parent != nil { + parentKey := opaqueProviderKey(pr.Name, c.Parent.Path.String()) + p.parentKey = findSourceProviderKey(parentKey, p.FullName, m) + } + + m[key] = p + } + + // Providers could be implicitly created or inherited from the parent module + // when no requirements and configuration block defined. + for req := range reqs { + // Only default providers could implicitly exist, + // so the provider name must be same as the provider type. + key := opaqueProviderKey(req.Type, c.Path.String()) + if _, exists := m[key]; exists { + continue + } + + p := providerConfig{ + Name: req.Type, + FullName: req.String(), + ModuleAddress: c.Path.String(), + } + + // In child modules, providers defined in the parent module can be implicitly used. + if c.Parent != nil { + parentKey := opaqueProviderKey(req.Type, c.Parent.Path.String()) + p.parentKey = findSourceProviderKey(parentKey, p.FullName, m) + } + m[key] = p } @@ -259,22 +289,7 @@ func marshalProviderConfigs( key := opaqueProviderKey(moduleProviderName, cc.Path.String()) parentKey := opaqueProviderKey(parentProviderName, cc.Parent.Path.String()) - - // Traverse up the module call tree until we find the provider - // configuration which has no linked parent config. This is then - // the source of the configuration used in this module call, so - // we link to it directly - for { - parent, exists := m[parentKey] - if !exists { - break - } - p.parentKey = parentKey - parentKey = parent.parentKey - if parentKey == "" { - break - } - } + p.parentKey = findSourceProviderKey(parentKey, p.FullName, m) m[key] = p } @@ -527,3 +542,24 @@ func opaqueProviderKey(provider string, addr string) (key string) { } return key } + +// Traverse up the module call tree until we find the provider +// configuration which has no linked parent config. This is then +// the source of the configuration used in this module call, so +// we link to it directly +func findSourceProviderKey(startKey string, fullName string, m map[string]providerConfig) string { + var parentKey string + + key := startKey + for key != "" { + parent, exists := m[key] + if !exists || parent.FullName != fullName { + break + } + + parentKey = key + key = parent.parentKey + } + + return parentKey +} diff --git a/internal/command/jsonconfig/config_test.go b/internal/command/jsonconfig/config_test.go new file mode 100644 index 000000000000..69aeae3f0318 --- /dev/null +++ b/internal/command/jsonconfig/config_test.go @@ -0,0 +1,100 @@ +package jsonconfig + +import ( + "testing" +) + +func TestFindSourceProviderConfig(t *testing.T) { + tests := []struct { + StartKey string + FullName string + ProviderMap map[string]providerConfig + Want string + }{ + { + StartKey: "null", + FullName: "hashicorp/null", + ProviderMap: map[string]providerConfig{}, + Want: "", + }, + { + StartKey: "null", + FullName: "hashicorp/null", + ProviderMap: map[string]providerConfig{ + "null": { + Name: "null", + FullName: "hashicorp/null", + ModuleAddress: "", + }, + }, + Want: "null", + }, + { + StartKey: "null2", + FullName: "hashicorp/null", + ProviderMap: map[string]providerConfig{ + "null": { + Name: "null", + FullName: "hashicorp/null", + ModuleAddress: "", + }, + }, + Want: "", + }, + { + StartKey: "null", + FullName: "hashicorp2/null", + ProviderMap: map[string]providerConfig{ + "null": { + Name: "null", + FullName: "hashicorp/null", + ModuleAddress: "", + }, + }, + Want: "", + }, + { + StartKey: "module.a:null", + FullName: "hashicorp/null", + ProviderMap: map[string]providerConfig{ + "null": { + Name: "null", + FullName: "hashicorp/null", + ModuleAddress: "", + }, + "module.a:null": { + Name: "module.a:null", + FullName: "hashicorp/null", + ModuleAddress: "module.a", + parentKey: "null", + }, + }, + Want: "null", + }, + { + StartKey: "module.a:null", + FullName: "hashicorp2/null", + ProviderMap: map[string]providerConfig{ + "null": { + Name: "null", + FullName: "hashicorp/null", + ModuleAddress: "", + }, + "module.a:null": { + Name: "module.a:null", + FullName: "hashicorp2/null", + ModuleAddress: "module.a", + parentKey: "null", + }, + }, + Want: "module.a:null", + }, + } + + for _, test := range tests { + got := findSourceProviderKey(test.StartKey, test.FullName, test.ProviderMap) + if got != test.Want { + t.Errorf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want) + } + } +} diff --git a/internal/command/show_test.go b/internal/command/show_test.go index 00a9e555a2e5..80ae6a9f2294 100644 --- a/internal/command/show_test.go +++ b/internal/command/show_test.go @@ -493,7 +493,8 @@ func TestShow_json_output(t *testing.T) { expectError := strings.Contains(entry.Name(), "error") providerSource, close := newMockProviderSource(t, map[string][]string{ - "test": {"1.2.3"}, + "test": {"1.2.3"}, + "hashicorp2/test": {"1.2.3"}, }) defer close() diff --git a/internal/command/testdata/show-json/basic-delete/output.json b/internal/command/testdata/show-json/basic-delete/output.json index e1779c04cc2a..4b10cc283a1c 100644 --- a/internal/command/testdata/show-json/basic-delete/output.json +++ b/internal/command/testdata/show-json/basic-delete/output.json @@ -130,6 +130,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "outputs": { "test": { diff --git a/internal/command/testdata/show-json/basic-update/output.json b/internal/command/testdata/show-json/basic-update/output.json index e4b4731426a1..a81cc2b5d174 100644 --- a/internal/command/testdata/show-json/basic-update/output.json +++ b/internal/command/testdata/show-json/basic-update/output.json @@ -96,6 +96,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "outputs": { "test": { diff --git a/internal/command/testdata/show-json/drift/output.json b/internal/command/testdata/show-json/drift/output.json index 2d5c071b4300..08029bf37e77 100644 --- a/internal/command/testdata/show-json/drift/output.json +++ b/internal/command/testdata/show-json/drift/output.json @@ -141,6 +141,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "resources": [ { diff --git a/internal/command/testdata/show-json/module-depends-on/output.json b/internal/command/testdata/show-json/module-depends-on/output.json index d02efaa22f0d..c76c762659d9 100644 --- a/internal/command/testdata/show-json/module-depends-on/output.json +++ b/internal/command/testdata/show-json/module-depends-on/output.json @@ -43,6 +43,12 @@ } ], "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "resources": [ { diff --git a/internal/command/testdata/show-json/modules/output.json b/internal/command/testdata/show-json/modules/output.json index 1728282592de..e4ef8deb18ae 100644 --- a/internal/command/testdata/show-json/modules/output.json +++ b/internal/command/testdata/show-json/modules/output.json @@ -293,6 +293,11 @@ "module_address": "module.module_test_foo", "name": "test", "full_name": "registry.terraform.io/hashicorp/test" + }, + "module.module_test_bar:test": { + "module_address": "module.module_test_bar", + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" } } } diff --git a/internal/command/testdata/show-json/moved-drift/output.json b/internal/command/testdata/show-json/moved-drift/output.json index ad6b0564135e..db3ac21c257e 100644 --- a/internal/command/testdata/show-json/moved-drift/output.json +++ b/internal/command/testdata/show-json/moved-drift/output.json @@ -143,6 +143,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "resources": [ { diff --git a/internal/command/testdata/show-json/moved/output.json b/internal/command/testdata/show-json/moved/output.json index 3e74a4ddb4e4..d8b9f2a245ea 100644 --- a/internal/command/testdata/show-json/moved/output.json +++ b/internal/command/testdata/show-json/moved/output.json @@ -68,6 +68,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "resources": [ { diff --git a/internal/command/testdata/show-json/multi-resource-update/output.json b/internal/command/testdata/show-json/multi-resource-update/output.json index 6da29965e29c..279a8d5593fb 100644 --- a/internal/command/testdata/show-json/multi-resource-update/output.json +++ b/internal/command/testdata/show-json/multi-resource-update/output.json @@ -137,6 +137,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "outputs": { "test": { diff --git a/internal/command/testdata/show-json/nested-modules/output.json b/internal/command/testdata/show-json/nested-modules/output.json index 6d0af8a1468c..cf1ab978c912 100644 --- a/internal/command/testdata/show-json/nested-modules/output.json +++ b/internal/command/testdata/show-json/nested-modules/output.json @@ -37,9 +37,7 @@ "name": "test", "provider_name": "registry.terraform.io/hashicorp/test", "change": { - "actions": [ - "create" - ], + "actions": ["create"], "before": null, "after": { "ami": "bar-var" @@ -53,6 +51,13 @@ } ], "configuration": { + "provider_config": { + "module.my_module.module.more:test": { + "module_address": "module.my_module.module.more", + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "module_calls": { "my_module": { @@ -71,9 +76,7 @@ "provider_config_key": "module.my_module.module.more:test", "expressions": { "ami": { - "references": [ - "var.test_var" - ] + "references": ["var.test_var"] } }, "schema_version": 0 diff --git a/internal/command/testdata/show-json/provider-aliasing-conflict/child/main.tf b/internal/command/testdata/show-json/provider-aliasing-conflict/child/main.tf new file mode 100644 index 000000000000..2479df3359f4 --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-conflict/child/main.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + test = { + source = "hashicorp2/test" + } + } +} + +resource "test_instance" "test" { + ami = "bar" +} diff --git a/internal/command/testdata/show-json/provider-aliasing-conflict/main.tf b/internal/command/testdata/show-json/provider-aliasing-conflict/main.tf new file mode 100644 index 000000000000..2d8bc0b90120 --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-conflict/main.tf @@ -0,0 +1,11 @@ +provider "test" { + region = "somewhere" +} + +resource "test_instance" "test" { + ami = "foo" +} + +module "child" { + source = "./child" +} diff --git a/internal/command/testdata/show-json/provider-aliasing-conflict/output.json b/internal/command/testdata/show-json/provider-aliasing-conflict/output.json new file mode 100644 index 000000000000..6b7ee48c8da0 --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-conflict/output.json @@ -0,0 +1,143 @@ +{ + "format_version": "1.0", + "terraform_version": "1.1.0-dev", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "foo" + }, + "sensitive_values": {} + } + ], + "child_modules": [ + { + "resources": [ + { + "address": "module.child.test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp2/test", + "schema_version": 0, + "values": { + "ami": "bar" + }, + "sensitive_values": {} + } + ], + "address": "module.child" + } + ] + } + }, + "resource_changes": [ + { + "address": "module.child.test_instance.test", + "module_address": "module.child", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp2/test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "bar" + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + }, + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "foo" + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + } + ], + "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test", + "expressions": { + "region": { + "constant_value": "somewhere" + } + } + }, + "module.child:test": { + "module_address": "module.child", + "name": "test", + "full_name": "registry.terraform.io/hashicorp2/test" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "expressions": { + "ami": { + "constant_value": "foo" + } + }, + "schema_version": 0 + } + ], + "module_calls": { + "child": { + "source": "./child", + "module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "module.child:test", + "expressions": { + "ami": { + "constant_value": "bar" + } + }, + "schema_version": 0 + } + ] + } + } + } + } + } +} diff --git a/internal/command/testdata/show-json/provider-aliasing-default/child/main.tf b/internal/command/testdata/show-json/provider-aliasing-default/child/main.tf new file mode 100644 index 000000000000..b865e3e14a97 --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-default/child/main.tf @@ -0,0 +1,20 @@ +terraform { + required_providers { + test = { + source = "hashicorp/test" + } + } +} + +resource "test_instance" "test" { + ami = "bar" +} + +module "with_requirement" { + source = "./nested" + depends_on = [module.no_requirements] +} + +module "no_requirements" { + source = "./nested-no-requirements" +} diff --git a/internal/command/testdata/show-json/provider-aliasing-default/child/nested-no-requirements/main.tf b/internal/command/testdata/show-json/provider-aliasing-default/child/nested-no-requirements/main.tf new file mode 100644 index 000000000000..20781869684d --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-default/child/nested-no-requirements/main.tf @@ -0,0 +1,3 @@ +resource "test_instance" "test" { + ami = "qux" +} diff --git a/internal/command/testdata/show-json/provider-aliasing-default/child/nested/main.tf b/internal/command/testdata/show-json/provider-aliasing-default/child/nested/main.tf new file mode 100644 index 000000000000..1590c5cea371 --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-default/child/nested/main.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + test = { + source = "hashicorp/test" + } + } +} + +resource "test_instance" "test" { + ami = "baz" +} diff --git a/internal/command/testdata/show-json/provider-aliasing-default/main.tf b/internal/command/testdata/show-json/provider-aliasing-default/main.tf new file mode 100644 index 000000000000..f5e63f0aa3bd --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-default/main.tf @@ -0,0 +1,19 @@ +provider "test" { + region = "somewhere" +} + +provider "test" { + alias = "backup" + region = "elsewhere" +} + +resource "test_instance" "test" { + ami = "foo" +} + +module "child" { + source = "./child" + providers = { + test = test.backup + } +} diff --git a/internal/command/testdata/show-json/provider-aliasing-default/output.json b/internal/command/testdata/show-json/provider-aliasing-default/output.json new file mode 100644 index 000000000000..d6b1706437de --- /dev/null +++ b/internal/command/testdata/show-json/provider-aliasing-default/output.json @@ -0,0 +1,271 @@ +{ + "format_version": "1.0", + "terraform_version": "1.1.0-dev", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "foo" + }, + "sensitive_values": {} + } + ], + "child_modules": [ + { + "resources": [ + { + "address": "module.child.test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar" + }, + "sensitive_values": {} + } + ], + "address": "module.child", + "child_modules": [ + { + "resources": [ + { + "address": "module.child.module.no_requirements.test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "qux" + }, + "sensitive_values": {} + } + ], + "address": "module.child.module.no_requirements" + }, + { + "resources": [ + { + "address": "module.child.module.with_requirement.test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "baz" + }, + "sensitive_values": {} + } + ], + "address": "module.child.module.with_requirement" + } + ] + } + ] + } + }, + "resource_changes": [ + { + "address": "module.child.module.no_requirements.test_instance.test", + "module_address": "module.child.module.no_requirements", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "qux" + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + }, + { + "address": "module.child.module.with_requirement.test_instance.test", + "module_address": "module.child.module.with_requirement", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "baz" + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + }, + { + "address": "module.child.test_instance.test", + "module_address": "module.child", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "bar" + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + }, + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "foo" + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + } + ], + "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test", + "expressions": { + "region": { + "constant_value": "somewhere" + } + } + }, + "test.backup": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test", + "alias": "backup", + "expressions": { + "region": { + "constant_value": "elsewhere" + } + } + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "expressions": { + "ami": { + "constant_value": "foo" + } + }, + "schema_version": 0 + } + ], + "module_calls": { + "child": { + "source": "./child", + "module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test.backup", + "expressions": { + "ami": { + "constant_value": "bar" + } + }, + "schema_version": 0 + } + ], + "module_calls": { + "no_requirements": { + "source": "./nested-no-requirements", + "module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test.backup", + "expressions": { + "ami": { + "constant_value": "qux" + } + }, + "schema_version": 0 + } + ] + } + }, + "with_requirement": { + "source": "./nested", + "depends_on": ["module.no_requirements"], + "module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test.backup", + "expressions": { + "ami": { + "constant_value": "baz" + } + }, + "schema_version": 0 + } + ] + } + } + } + } + } + } + } + } +} diff --git a/internal/command/testdata/show-json/requires-replace/output.json b/internal/command/testdata/show-json/requires-replace/output.json index e71df784f4f7..1eb37ea1cfbd 100644 --- a/internal/command/testdata/show-json/requires-replace/output.json +++ b/internal/command/testdata/show-json/requires-replace/output.json @@ -70,6 +70,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "resources": [ { diff --git a/internal/command/testdata/show-json/sensitive-values/output.json b/internal/command/testdata/show-json/sensitive-values/output.json index d7e4719c71f5..0047c0b60042 100644 --- a/internal/command/testdata/show-json/sensitive-values/output.json +++ b/internal/command/testdata/show-json/sensitive-values/output.json @@ -81,6 +81,12 @@ } }, "configuration": { + "provider_config": { + "test": { + "name": "test", + "full_name": "registry.terraform.io/hashicorp/test" + } + }, "root_module": { "outputs": { "test": {