From 14bd5bfa11446311041ea4c4bf70d19450dd9b6e Mon Sep 17 00:00:00 2001 From: Jakub Martin Date: Mon, 21 Dec 2020 13:05:59 +0100 Subject: [PATCH 1/3] Add test for module nesting without resources. --- command/jsonstate/state_test.go | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/command/jsonstate/state_test.go b/command/jsonstate/state_test.go index efbaa182cbac..34dba0c5e00e 100644 --- a/command/jsonstate/state_test.go +++ b/command/jsonstate/state_test.go @@ -554,6 +554,64 @@ func TestMarshalModules_nested(t *testing.T) { } } +func TestMarshalModules_parent_no_resources(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.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + 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.NewDefaultProvider("test"), + Module: subModule.Module(), + }, + ) + }) + 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{ From d4fe9dad74abf6c774f6f7b20abf65757c8f90f6 Mon Sep 17 00:00:00 2001 From: Jakub Martin Date: Mon, 21 Dec 2020 13:09:33 +0100 Subject: [PATCH 2/3] Add test --- command/jsonstate/state_test.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/command/jsonstate/state_test.go b/command/jsonstate/state_test.go index 34dba0c5e00e..683d383782c7 100644 --- a/command/jsonstate/state_test.go +++ b/command/jsonstate/state_test.go @@ -555,7 +555,6 @@ func TestMarshalModules_nested(t *testing.T) { } func TestMarshalModules_parent_no_resources(t *testing.T) { - childModule, _ := addrs.ParseModuleInstanceStr("module.child") subModule, _ := addrs.ParseModuleInstanceStr("module.child.module.submodule") testState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( @@ -589,25 +588,21 @@ func TestMarshalModules_parent_no_resources(t *testing.T) { }, ) }) - moduleMap := make(map[string][]addrs.ModuleInstance) - moduleMap[""] = []addrs.ModuleInstance{childModule} - moduleMap[childModule.String()] = []addrs.ModuleInstance{subModule} - - got, err := marshalModules(testState, testSchemas(), moduleMap[""], moduleMap) + got, err := marshalRootModule(testState, testSchemas()) 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 len(got.ChildModules) != 1 { + t.Fatalf("wrong result! got %d modules, expected 1", len(got.ChildModules)) } - if got[0].Address != "module.child" { + if got.ChildModules[0].Address != "module.child" { t.Fatalf("wrong result! got %#v\n", got) } - if got[0].ChildModules[0].Address != "module.child.module.submodule" { + if got.ChildModules[0].ChildModules[0].Address != "module.child.module.submodule" { t.Fatalf("wrong result! got %#v\n", got) } } From 72970f5f1c2652d89ead4fdc387f83bcba38a49e Mon Sep 17 00:00:00 2001 From: Jakub Martin Date: Mon, 21 Dec 2020 13:10:23 +0100 Subject: [PATCH 3/3] Fix showing resources when a module has no resources, only submodules. --- command/jsonstate/state.go | 41 ++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/command/jsonstate/state.go b/command/jsonstate/state.go index fdf2d483fb55..bb5ba6a55929 100644 --- a/command/jsonstate/state.go +++ b/command/jsonstate/state.go @@ -198,14 +198,30 @@ func marshalRootModule(s *states.State, schemas *terraform.Schemas) (module, err return ret, err } - // build a map of module -> [child module addresses] - moduleMap := make(map[string][]addrs.ModuleInstance) + // build a map of module -> set[child module addresses] + moduleChildSet := make(map[string]map[string]struct{}) for _, mod := range s.Modules { if mod.Addr.IsRoot() { continue } else { - parent := mod.Addr.Parent().String() - moduleMap[parent] = append(moduleMap[parent], mod.Addr) + for childAddr := mod.Addr; !childAddr.IsRoot(); childAddr = childAddr.Parent() { + if _, ok := moduleChildSet[childAddr.Parent().String()]; !ok { + moduleChildSet[childAddr.Parent().String()] = map[string]struct{}{} + } + moduleChildSet[childAddr.Parent().String()][childAddr.String()] = struct{}{} + } + } + } + + // transform the previous map into map of module -> [child module addresses] + moduleMap := make(map[string][]addrs.ModuleInstance) + for parent, children := range moduleChildSet { + for child := range children { + childModuleInstance, diags := addrs.ParseModuleInstanceStr(child) + if diags.HasErrors() { + return ret, diags.Err() + } + moduleMap[parent] = append(moduleMap[parent], childModuleInstance) } } @@ -224,14 +240,19 @@ func marshalModules( ) ([]module, error) { var ret []module for _, child := range modules { - stateMod := s.Module(child) // cm for child module, naming things is hard. - cm := module{Address: stateMod.Addr.String()} - rs, err := marshalResources(stateMod.Resources, stateMod.Addr, schemas) - if err != nil { - return nil, err + cm := module{Address: child.String()} + + // the module may be resourceless and contain only submodules, it will then be nil here + stateMod := s.Module(child) + if stateMod != nil { + rs, err := marshalResources(stateMod.Resources, stateMod.Addr, schemas) + if err != nil { + return nil, err + } + cm.Resources = rs } - cm.Resources = rs + if moduleMap[child.String()] != nil { moreChildModules, err := marshalModules(s, schemas, moduleMap[child.String()], moduleMap) if err != nil {