diff --git a/command/jsonstate/state.go b/command/jsonstate/state.go index eb356d602..bf9a6f872 100644 --- a/command/jsonstate/state.go +++ b/command/jsonstate/state.go @@ -212,8 +212,8 @@ func marshalRootModule(s *states.State, schemas *terraform.Schemas) (module, err return ret, err } -// marshalModules is an ungainly recursive function to build a module -// structure out of a teraform state. +// marshalModules is an ungainly recursive function to build a module structure +// out of terraform state. func marshalModules( s *states.State, schemas *terraform.Schemas, @@ -241,6 +241,11 @@ func marshalModules( ret = append(ret, cm) } + // sort the child modules by address for consistency. + sort.Slice(ret, func(i, j int) bool { + return ret[i].Address < ret[j].Address + }) + return ret, nil } diff --git a/command/jsonstate/state_test.go b/command/jsonstate/state_test.go index a6c70f3c3..ef8cb869c 100644 --- a/command/jsonstate/state_test.go +++ b/command/jsonstate/state_test.go @@ -436,6 +436,148 @@ func TestMarshalResources(t *testing.T) { } } +func TestMarshalModules_basic(t *testing.T) { + childModule, _ := addrs.ParseModuleInstanceStr("module.child") + subModule, _ := addrs.ParseModuleInstanceStr("module.submodule") + testState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: addrs.RootModuleInstance, + }, + ) + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(childModule), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: childModule, + }, + ) + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(subModule), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: subModule, + }, + ) + }) + moduleMap := make(map[string][]addrs.ModuleInstance) + moduleMap[""] = []addrs.ModuleInstance{childModule, subModule} + + got, err := marshalModules(testState, testSchemas(), moduleMap[""], moduleMap) + + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + + if len(got) != 2 { + t.Fatalf("wrong result! got %d modules, expected 2", len(got)) + } + + if got[0].Address != "module.child" || got[1].Address != "module.submodule" { + t.Fatalf("wrong result! got %#v\n", got) + } + +} + +func TestMarshalModules_nested(t *testing.T) { + childModule, _ := addrs.ParseModuleInstanceStr("module.child") + subModule, _ := addrs.ParseModuleInstanceStr("module.child.module.submodule") + testState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: addrs.RootModuleInstance, + }, + ) + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(childModule), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: childModule, + }, + ) + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(subModule), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewLegacyProvider("test"), + Module: subModule, + }, + ) + }) + moduleMap := make(map[string][]addrs.ModuleInstance) + moduleMap[""] = []addrs.ModuleInstance{childModule} + moduleMap[childModule.String()] = []addrs.ModuleInstance{subModule} + + got, err := marshalModules(testState, testSchemas(), moduleMap[""], moduleMap) + + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + + if len(got) != 1 { + t.Fatalf("wrong result! got %d modules, expected 1", len(got)) + } + + if got[0].Address != "module.child" { + t.Fatalf("wrong result! got %#v\n", got) + } + + if got[0].ChildModules[0].Address != "module.child.module.submodule" { + t.Fatalf("wrong result! got %#v\n", got) + } +} + func testSchemas() *terraform.Schemas { return &terraform.Schemas{ Providers: map[addrs.Provider]*terraform.ProviderSchema{ @@ -447,6 +589,13 @@ func testSchemas() *terraform.Schemas { "foozles": {Type: cty.String, Optional: true}, }, }, + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "foo": {Type: cty.String, Optional: true}, + "bar": {Type: cty.String, Optional: true}, + }, + }, }, }, }, diff --git a/command/testdata/show-json-state/modules/output.json b/command/testdata/show-json-state/modules/output.json index b5e1083d0..77d53b238 100644 --- a/command/testdata/show-json-state/modules/output.json +++ b/command/testdata/show-json-state/modules/output.json @@ -10,6 +10,23 @@ }, "root_module": { "child_modules": [ + { + "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" + }, { "resources": [ { @@ -27,23 +44,6 @@ } ], "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" } ] }