diff --git a/command/jsonstate/state.go b/command/jsonstate/state.go index bcc12cd5583c..eb356d602639 100644 --- a/command/jsonstate/state.go +++ b/command/jsonstate/state.go @@ -191,7 +191,7 @@ func marshalRootModule(s *states.State, schemas *terraform.Schemas) (module, err var err error ret.Address = "" - ret.Resources, err = marshalResources(s.RootModule().Resources, schemas) + ret.Resources, err = marshalResources(s.RootModule().Resources, addrs.RootModuleInstance, schemas) if err != nil { return ret, err } @@ -225,7 +225,7 @@ func marshalModules( stateMod := s.Module(child) // cm for child module, naming things is hard. cm := module{Address: stateMod.Addr.String()} - rs, err := marshalResources(stateMod.Resources, schemas) + rs, err := marshalResources(stateMod.Resources, stateMod.Addr, schemas) if err != nil { return nil, err } @@ -244,14 +244,14 @@ func marshalModules( return ret, nil } -func marshalResources(resources map[string]*states.Resource, schemas *terraform.Schemas) ([]resource, error) { +func marshalResources(resources map[string]*states.Resource, module addrs.ModuleInstance, schemas *terraform.Schemas) ([]resource, error) { var ret []resource for _, r := range resources { for k, ri := range r.Instances { current := resource{ - Address: r.Addr.String(), + Address: r.Addr.Absolute(module).Instance(k).String(), Type: r.Addr.Type, Name: r.Addr.Name, ProviderName: r.ProviderConfig.Provider.LegacyString(), diff --git a/command/jsonstate/state_test.go b/command/jsonstate/state_test.go index cacb1198852e..a6c70f3c3479 100644 --- a/command/jsonstate/state_test.go +++ b/command/jsonstate/state_test.go @@ -186,6 +186,48 @@ func TestMarshalResources(t *testing.T) { "single resource": { map[string]*states.Resource{ "test_thing.baz": { + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_thing", + Name: "bar", + }, + EachMode: states.NoEach, + Instances: map[addrs.InstanceKey]*states.ResourceInstance{ + addrs.NoKey: { + Current: &states.ResourceInstanceObjectSrc{ + SchemaVersion: 1, + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), + }, + }, + }, + ProviderConfig: addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: addrs.RootModuleInstance, + }, + }, + }, + testSchemas(), + []resource{ + resource{ + Address: "test_thing.bar", + Mode: "managed", + Type: "test_thing", + Name: "bar", + Index: addrs.InstanceKey(nil), + ProviderName: "test", + SchemaVersion: 1, + AttributeValues: attributeValues{ + "foozles": json.RawMessage(`null`), + "woozles": json.RawMessage(`"confuzles"`), + }, + }, + }, + false, + }, + "resource with count": { + map[string]*states.Resource{ + "test_thing.bar": { Addr: addrs.Resource{ Mode: addrs.ManagedResourceMode, Type: "test_thing", @@ -210,7 +252,7 @@ func TestMarshalResources(t *testing.T) { testSchemas(), []resource{ resource{ - Address: "test_thing.bar", + Address: "test_thing.bar[0]", Mode: "managed", Type: "test_thing", Name: "bar", @@ -225,6 +267,48 @@ func TestMarshalResources(t *testing.T) { }, false, }, + "resource with for_each": { + map[string]*states.Resource{ + "test_thing.bar": { + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_thing", + Name: "bar", + }, + EachMode: states.EachMap, + Instances: map[addrs.InstanceKey]*states.ResourceInstance{ + addrs.StringKey("rockhopper"): { + Current: &states.ResourceInstanceObjectSrc{ + SchemaVersion: 1, + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), + }, + }, + }, + ProviderConfig: addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: addrs.RootModuleInstance, + }, + }, + }, + testSchemas(), + []resource{ + resource{ + Address: "test_thing.bar[\"rockhopper\"]", + Mode: "managed", + Type: "test_thing", + Name: "bar", + Index: addrs.StringKey("rockhopper"), + ProviderName: "test", + SchemaVersion: 1, + AttributeValues: attributeValues{ + "foozles": json.RawMessage(`null`), + "woozles": json.RawMessage(`"confuzles"`), + }, + }, + }, + false, + }, "deposed resource": { map[string]*states.Resource{ "test_thing.baz": { @@ -233,9 +317,9 @@ func TestMarshalResources(t *testing.T) { Type: "test_thing", Name: "bar", }, - EachMode: states.EachList, + EachMode: states.NoEach, Instances: map[addrs.InstanceKey]*states.ResourceInstance{ - addrs.IntKey(0): { + addrs.NoKey: { Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{ SchemaVersion: 1, @@ -258,7 +342,7 @@ func TestMarshalResources(t *testing.T) { Mode: "managed", Type: "test_thing", Name: "bar", - Index: addrs.IntKey(0), + Index: addrs.InstanceKey(nil), ProviderName: "test", DeposedKey: deposedKey.String(), AttributeValues: attributeValues{ @@ -277,9 +361,9 @@ func TestMarshalResources(t *testing.T) { Type: "test_thing", Name: "bar", }, - EachMode: states.EachList, + EachMode: states.NoEach, Instances: map[addrs.InstanceKey]*states.ResourceInstance{ - addrs.IntKey(0): { + addrs.NoKey: { Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{ SchemaVersion: 1, @@ -307,7 +391,7 @@ func TestMarshalResources(t *testing.T) { Mode: "managed", Type: "test_thing", Name: "bar", - Index: addrs.IntKey(0), + Index: addrs.InstanceKey(nil), ProviderName: "test", SchemaVersion: 1, AttributeValues: attributeValues{ @@ -320,7 +404,7 @@ func TestMarshalResources(t *testing.T) { Mode: "managed", Type: "test_thing", Name: "bar", - Index: addrs.IntKey(0), + Index: addrs.InstanceKey(nil), ProviderName: "test", DeposedKey: deposedKey.String(), AttributeValues: attributeValues{ @@ -335,7 +419,7 @@ func TestMarshalResources(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - got, err := marshalResources(test.Resources, test.Schemas) + got, err := marshalResources(test.Resources, addrs.RootModuleInstance, test.Schemas) if test.Err { if err == nil { t.Fatal("succeeded; want error") diff --git a/command/show_test.go b/command/show_test.go index 86a4a790cff2..3ad7b437d597 100644 --- a/command/show_test.go +++ b/command/show_test.go @@ -323,9 +323,7 @@ func TestShow_json_output(t *testing.T) { if !cmp.Equal(got, want) { t.Fatalf("wrong result:\n %v\n", cmp.Diff(got, want)) } - }) - } } @@ -405,9 +403,7 @@ func TestShow_json_output_state(t *testing.T) { if !cmp.Equal(got, want) { t.Fatalf("wrong result:\n %v\n", cmp.Diff(got, want)) } - }) - } } diff --git a/command/testdata/show-json-state/basic/output.json b/command/testdata/show-json-state/basic/output.json new file mode 100644 index 000000000000..67d485a6eb86 --- /dev/null +++ b/command/testdata/show-json-state/basic/output.json @@ -0,0 +1,36 @@ +{ + "format_version": "0.1", + "terraform_version": "0.12.0", + "values": { + "root_module": { + "resources": [ + { + "address": "test_instance.example[0]", + "mode": "managed", + "type": "test_instance", + "name": "example", + "index": 0, + "provider_name": "test", + "schema_version": 0, + "values": { + "ami": null, + "id": "621124146446964903" + } + }, + { + "address": "test_instance.example[1]", + "mode": "managed", + "type": "test_instance", + "name": "example", + "index": 1, + "provider_name": "test", + "schema_version": 0, + "values": { + "ami": null, + "id": "4330206298367988603" + } + } + ] + } + } +} diff --git a/command/testdata/show-json-state/basic/terraform.tfstate b/command/testdata/show-json-state/basic/terraform.tfstate new file mode 100644 index 000000000000..61983c6b0426 --- /dev/null +++ b/command/testdata/show-json-state/basic/terraform.tfstate @@ -0,0 +1,34 @@ +{ + "version": 4, + "terraform_version": "0.12.0", + "serial": 1, + "lineage": "00bfda35-ad61-ec8d-c013-14b0320bc416", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "test_instance", + "name": "example", + "each": "list", + "provider": "provider.test", + "instances": [ + { + "index_key": 0, + "schema_version": 0, + "attributes": { + "id": "621124146446964903" + }, + "private": "bnVsbA==" + }, + { + "index_key": 1, + "schema_version": 0, + "attributes": { + "id": "4330206298367988603" + }, + "private": "bnVsbA==" + } + ] + } + ] +} diff --git a/command/testdata/show-json-state/modules/bar/main.tf b/command/testdata/show-json-state/modules/bar/main.tf new file mode 100644 index 000000000000..5d1788a0e811 --- /dev/null +++ b/command/testdata/show-json-state/modules/bar/main.tf @@ -0,0 +1,11 @@ +variable "test_var" { + default = "bar-var" +} + +output "test" { + value = var.test_var +} + +resource "test_instance" "test" { + ami = var.test_var +} diff --git a/command/testdata/show-json-state/modules/foo/main.tf b/command/testdata/show-json-state/modules/foo/main.tf new file mode 100644 index 000000000000..d1ae7929a453 --- /dev/null +++ b/command/testdata/show-json-state/modules/foo/main.tf @@ -0,0 +1,14 @@ +variable "test_var" { + default = "foo-var" +} + +resource "test_instance" "test" { + ami = var.test_var + count = 1 +} + +output "test" { + value = var.test_var +} + +provider "test" {} diff --git a/command/testdata/show-json-state/modules/main.tf b/command/testdata/show-json-state/modules/main.tf new file mode 100644 index 000000000000..d1cabad3ec54 --- /dev/null +++ b/command/testdata/show-json-state/modules/main.tf @@ -0,0 +1,13 @@ +module "module_test_foo" { + source = "./foo" + test_var = "baz" +} + +module "module_test_bar" { + source = "./bar" +} + +output "test" { + value = module.module_test_foo.test + depends_on = [module.module_test_foo] +} diff --git a/command/testdata/show-json-state/modules/output.json b/command/testdata/show-json-state/modules/output.json new file mode 100644 index 000000000000..b5e1083d01fe --- /dev/null +++ b/command/testdata/show-json-state/modules/output.json @@ -0,0 +1,51 @@ +{ + "format_version": "0.1", + "terraform_version": "0.12.0", + "values": { + "outputs": { + "test": { + "sensitive": false, + "value": "baz" + } + }, + "root_module": { + "child_modules": [ + { + "resources": [ + { + "address": "module.module_test_foo.test_instance.example[0]", + "mode": "managed", + "type": "test_instance", + "name": "example", + "index": 0, + "provider_name": "test", + "schema_version": 0, + "values": { + "ami": "foo-var", + "id": null + } + } + ], + "address": "module.module_test_foo" + }, + { + "resources": [ + { + "address": "module.module_test_bar.test_instance.example", + "mode": "managed", + "type": "test_instance", + "name": "example", + "provider_name": "test", + "schema_version": 0, + "values": { + "ami": "bar-var", + "id": null + } + } + ], + "address": "module.module_test_bar" + } + ] + } + } +} diff --git a/command/testdata/show-json-state/modules/terraform.tfstate b/command/testdata/show-json-state/modules/terraform.tfstate new file mode 100644 index 000000000000..bee64095254b --- /dev/null +++ b/command/testdata/show-json-state/modules/terraform.tfstate @@ -0,0 +1,48 @@ +{ + "version": 4, + "terraform_version": "0.12.0", + "serial": 8, + "lineage": "00bfda35-ad61-ec8d-c013-14b0320bc416", + "outputs": { + "test": { + "value": "baz", + "type": "string" + } + }, + "resources": [ + { + "module": "module.module_test_foo", + "mode": "managed", + "type": "test_instance", + "name": "example", + "each": "list", + "provider": "provider.test", + "instances": [ + { + "index_key": 0, + "schema_version": 0, + "attributes": { + "ami": "foo-var" + }, + "private": "bnVsbA==" + } + ] + }, + { + "module": "module.module_test_bar", + "mode": "managed", + "type": "test_instance", + "name": "example", + "provider": "provider.test", + "instances": [ + { + "schema_version": 0, + "attributes": { + "ami": "bar-var" + }, + "private": "bnVsbA==" + } + ] + } + ] +} diff --git a/command/testdata/show-json/multi-resource-update/main.tf b/command/testdata/show-json/multi-resource-update/main.tf new file mode 100644 index 000000000000..3ead9dd32b79 --- /dev/null +++ b/command/testdata/show-json/multi-resource-update/main.tf @@ -0,0 +1,13 @@ +variable "test_var" { + default = "bar" +} + +// There is a single instance in state. The plan will add a resource. +resource "test_instance" "test" { + ami = var.test_var + count = 2 +} + +output "test" { + value = var.test_var +} diff --git a/command/testdata/show-json/multi-resource-update/output.json b/command/testdata/show-json/multi-resource-update/output.json new file mode 100644 index 000000000000..a5a3a5b3d8b0 --- /dev/null +++ b/command/testdata/show-json/multi-resource-update/output.json @@ -0,0 +1,167 @@ +{ + "format_version": "0.1", + "terraform_version": "0.13.0", + "variables": { + "test_var": { + "value": "bar" + } + }, + "planned_values": { + "outputs": { + "test": { + "sensitive": false, + "value": "bar" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test[0]", + "mode": "managed", + "type": "test_instance", + "name": "test", + "index": 0, + "provider_name": "test", + "schema_version": 0, + "values": { + "ami": "bar", + "id": "placeholder" + } + }, + { + "address": "test_instance.test[1]", + "mode": "managed", + "type": "test_instance", + "name": "test", + "index": 1, + "provider_name": "test", + "schema_version": 0, + "values": { + "ami": "bar" + } + } + ] + } + }, + "resource_changes": [ + { + "address": "test_instance.test[0]", + "mode": "managed", + "type": "test_instance", + "name": "test", + "index": 0, + "provider_name": "test", + "change": { + "actions": [ + "no-op" + ], + "before": { + "ami": "bar", + "id": "placeholder" + }, + "after": { + "ami": "bar", + "id": "placeholder" + }, + "after_unknown": {} + } + }, + { + "address": "test_instance.test[1]", + "mode": "managed", + "type": "test_instance", + "name": "test", + "index": 1, + "provider_name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "bar" + }, + "after_unknown": { + "id": true + } + } + } + ], + "output_changes": { + "test": { + "actions": [ + "create" + ], + "before": null, + "after": "bar", + "after_unknown": false + } + }, + "prior_state": { + "format_version": "0.1", + "terraform_version": "0.13.0", + "values": { + "outputs": { + "test": { + "sensitive": false, + "value": "bar" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test[0]", + "mode": "managed", + "type": "test_instance", + "name": "test", + "index": 0, + "provider_name": "test", + "schema_version": 0, + "values": { + "ami": "bar", + "id": "placeholder" + } + } + ] + } + } + }, + "configuration": { + "root_module": { + "outputs": { + "test": { + "expression": { + "references": [ + "var.test_var" + ] + } + } + }, + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "expressions": { + "ami": { + "references": [ + "var.test_var" + ] + } + }, + "schema_version": 0, + "count_expression": { + "constant_value": 2 + } + } + ], + "variables": { + "test_var": { + "default": "bar" + } + } + } + } +} diff --git a/command/testdata/show-json/multi-resource-update/terraform.tfstate b/command/testdata/show-json/multi-resource-update/terraform.tfstate new file mode 100644 index 000000000000..f68865a9ba57 --- /dev/null +++ b/command/testdata/show-json/multi-resource-update/terraform.tfstate @@ -0,0 +1,24 @@ +{ + "version": 4, + "terraform_version": "0.12.0", + "serial": 7, + "lineage": "configuredUnchanged", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider": "provider[\"registry.terraform.io/-/test\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "ami": "bar", + "id": "placeholder" + } + } + ] + } + ] +}