From 5367378116bc6a2276372a3bc697442080f87731 Mon Sep 17 00:00:00 2001 From: Cerebrovinny Date: Tue, 5 Nov 2024 09:00:41 +0000 Subject: [PATCH 1/7] filter empty results --- cmd/describe_stacks.go | 2 + internal/exec/atmos.go | 2 +- internal/exec/describe_affected_utils.go | 4 +- internal/exec/describe_dependents.go | 2 +- internal/exec/describe_stacks.go | 132 ++++++++++++++++++++++- 5 files changed, 133 insertions(+), 9 deletions(-) diff --git a/cmd/describe_stacks.go b/cmd/describe_stacks.go index 3fd38ef4e..c36e8b67b 100644 --- a/cmd/describe_stacks.go +++ b/cmd/describe_stacks.go @@ -45,5 +45,7 @@ func init() { describeStacksCmd.PersistentFlags().Bool("process-templates", true, "Enable/disable Go template processing in Atmos stack manifests when executing the command: atmos describe stacks --process-templates=false") + describeStacksCmd.PersistentFlags().Bool("include-empty-stacks", false, "Include stacks with no components in the output: atmos describe stacks --include-empty") + describeCmd.AddCommand(describeStacksCmd) } diff --git a/internal/exec/atmos.go b/internal/exec/atmos.go index fe3d29b22..446c42ca1 100644 --- a/internal/exec/atmos.go +++ b/internal/exec/atmos.go @@ -41,7 +41,7 @@ func ExecuteAtmosCmd() error { // Get a map of stacks and components in the stacks // Don't process `Go` templates in Atmos stack manifests since we just need to display the stack and component names in the TUI - stacksMap, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, false) + stacksMap, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, false, false) if err != nil { return err } diff --git a/internal/exec/describe_affected_utils.go b/internal/exec/describe_affected_utils.go index ec7ca3f14..1f8d30511 100644 --- a/internal/exec/describe_affected_utils.go +++ b/internal/exec/describe_affected_utils.go @@ -408,7 +408,7 @@ func executeDescribeAffected( u.LogTrace(cliConfig, fmt.Sprintf("Current HEAD: %s", localRepoHead)) u.LogTrace(cliConfig, fmt.Sprintf("BASE: %s", remoteRepoHead)) - currentStacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, nil, false, true) + currentStacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, nil, false, true, false) if err != nil { return nil, nil, nil, err } @@ -444,7 +444,7 @@ func executeDescribeAffected( return nil, nil, nil, err } - remoteStacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, nil, true, true) + remoteStacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, nil, true, true, false) if err != nil { return nil, nil, nil, err } diff --git a/internal/exec/describe_dependents.go b/internal/exec/describe_dependents.go index c3ba57083..647f11185 100644 --- a/internal/exec/describe_dependents.go +++ b/internal/exec/describe_dependents.go @@ -78,7 +78,7 @@ func ExecuteDescribeDependents( var ok bool // Get all stacks with all components - stacks, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, true) + stacks, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, true, false) if err != nil { return nil, err } diff --git a/internal/exec/describe_stacks.go b/internal/exec/describe_stacks.go index c45d18541..b680ac476 100644 --- a/internal/exec/describe_stacks.go +++ b/internal/exec/describe_stacks.go @@ -55,6 +55,11 @@ func ExecuteDescribeStacksCmd(cmd *cobra.Command, args []string) error { return err } + includeEmptyStacks, err := cmd.Flags().GetBool("include-empty-stacks") + if err != nil { + return err + } + componentsCsv, err := flags.GetString("components") if err != nil { return err @@ -97,6 +102,7 @@ func ExecuteDescribeStacksCmd(cmd *cobra.Command, args []string) error { sections, false, processTemplates, + includeEmptyStacks, ) if err != nil { return err @@ -119,6 +125,7 @@ func ExecuteDescribeStacks( sections []string, ignoreMissingFiles bool, processTemplates bool, + includeEmptyStacks bool, ) (map[string]any, error) { stacksMap, _, err := FindStacksMap(cliConfig, ignoreMissingFiles) @@ -127,6 +134,7 @@ func ExecuteDescribeStacks( } finalStacksMap := make(map[string]any) + processedStacks := make(map[string]bool) var varsSection map[string]any var metadataSection map[string]any var settingsSection map[string]any @@ -142,6 +150,49 @@ func ExecuteDescribeStacks( // Delete the stack-wide imports delete(stackSection.(map[string]any), "imports") + // Check if components section exists and has explicit components + hasExplicitComponents := false + if componentsSection, ok := stackSection.(map[string]any)["components"]; ok { + fmt.Printf("DEBUG: Found components section for %s\n", stackFileName) + if componentsSection != nil { + if terraformSection, ok := componentsSection.(map[string]any)["terraform"].(map[string]any); ok { + hasExplicitComponents = len(terraformSection) > 0 + fmt.Printf("DEBUG: Terraform components found: %v (count: %d)\n", hasExplicitComponents, len(terraformSection)) + } + if helmfileSection, ok := componentsSection.(map[string]any)["helmfile"].(map[string]any); ok { + hasExplicitComponents = hasExplicitComponents || len(helmfileSection) > 0 + fmt.Printf("DEBUG: Helmfile components found: %v\n", hasExplicitComponents) + } + } + } + + // Also check for imports + hasImports := false + if importsSection, ok := stackSection.(map[string]any)["import"].([]any); ok { + hasImports = len(importsSection) > 0 + fmt.Printf("DEBUG: Imports found for %s: %v (count: %d)\n", stackFileName, hasImports, len(importsSection)) + } + + fmt.Printf("DEBUG: Stack %s - hasExplicitComponents: %v, hasImports: %v, includeEmptyStacks: %v\n", + stackFileName, hasExplicitComponents, hasImports, includeEmptyStacks) + + // Skip stacks without components or imports when includeEmptyStacks is false + if !includeEmptyStacks && !hasExplicitComponents && !hasImports { + fmt.Printf("DEBUG: Skipping empty stack: %s\n", stackFileName) + continue + } + + stackName = stackFileName + if processedStacks[stackName] { + continue + } + processedStacks[stackName] = true + + if !u.MapKeyExists(finalStacksMap, stackName) { + finalStacksMap[stackName] = make(map[string]any) + finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any) + } + if componentsSection, ok := stackSection.(map[string]any)["components"].(map[string]any); ok { if len(componentTypes) == 0 || u.SliceContainsString(componentTypes, "terraform") { @@ -242,9 +293,13 @@ func ExecuteDescribeStacks( if stackName == "" { stackName = stackFileName + } else if strings.HasPrefix(stackFileName, "deploy/") { + // If we have a deploy/ prefixed version, use that as the canonical name + stackName = stackFileName } - if !u.MapKeyExists(finalStacksMap, stackName) { + // Only create the stack entry if it doesn't exist or if we're using the canonical name + if !u.MapKeyExists(finalStacksMap, stackName) || strings.HasPrefix(stackName, "deploy/") { finalStacksMap[stackName] = make(map[string]any) } @@ -430,9 +485,13 @@ func ExecuteDescribeStacks( if stackName == "" { stackName = stackFileName + } else if strings.HasPrefix(stackFileName, "deploy/") { + // If we have a deploy/ prefixed version, use that as the canonical name + stackName = stackFileName } - if !u.MapKeyExists(finalStacksMap, stackName) { + // Only create the stack entry if it doesn't exist or if we're using the canonical name + if !u.MapKeyExists(finalStacksMap, stackName) || strings.HasPrefix(stackName, "deploy/") { finalStacksMap[stackName] = make(map[string]any) } @@ -512,13 +571,76 @@ func ExecuteDescribeStacks( } } - // Filter out empty stacks (stacks without any components) - if st, ok := finalStacksMap[stackName].(map[string]any); ok { - if len(st) == 0 { + fmt.Printf("DEBUG: Final stack map for %s: %+v\n", stackName, finalStacksMap[stackName]) + } + + fmt.Printf("DEBUG: Before final filtering - Stack count: %d\n", len(finalStacksMap)) + + // Filter out empty stacks after processing all stack files + if !includeEmptyStacks { + fmt.Printf("DEBUG: Starting empty stack filtering\n") + for stackName := range finalStacksMap { + fmt.Printf("DEBUG: Checking final stack: %s\n", stackName) + + if stackName == "" { + fmt.Printf("DEBUG: Removing empty stack name\n") delete(finalStacksMap, stackName) + continue + } + + stackEntry := finalStacksMap[stackName].(map[string]any) + componentsSection, hasComponents := stackEntry["components"].(map[string]any) + fmt.Printf("DEBUG: Stack %s has components section: %v\n", stackName, hasComponents) + + if !hasComponents { + fmt.Printf("DEBUG: Removing stack %s - no components section\n", stackName) + delete(finalStacksMap, stackName) + continue + } + + // Check if any component type (terraform/helmfile) has components + hasNonEmptyComponents := false + for componentType, components := range componentsSection { + fmt.Printf("DEBUG: Checking component type: %s\n", componentType) + if compTypeMap, ok := components.(map[string]any); ok { + for compName, comp := range compTypeMap { + fmt.Printf("DEBUG: Checking component: %s\n", compName) + if compContent, ok := comp.(map[string]any); ok { + // Check for specific fields that indicate a real component + if _, hasVars := compContent["vars"]; hasVars { + hasNonEmptyComponents = true + fmt.Printf("DEBUG: Found non-empty component %s with vars\n", compName) + break + } + } + } + } + if hasNonEmptyComponents { + break + } + } + + if !hasNonEmptyComponents { + fmt.Printf("DEBUG: Removing stack %s - no non-empty components\n", stackName) + delete(finalStacksMap, stackName) + continue + } + + // Check for duplicate stacks (deploy/ prefix) + if strings.HasPrefix(stackName, "deploy/") { + baseStackName := strings.TrimPrefix(stackName, "deploy/") + if _, exists := finalStacksMap[baseStackName]; exists { + fmt.Printf("DEBUG: Found duplicate stack %s (base: %s)\n", stackName, baseStackName) + // Use the deploy/ prefixed version as canonical and remove the base name + delete(finalStacksMap, baseStackName) + } } } + } else { + fmt.Printf("DEBUG: Skipping empty stack filtering because includeEmptyStacks is true\n") } + fmt.Printf("DEBUG: After final filtering - Stack count: %d\n", len(finalStacksMap)) + return finalStacksMap, nil } From c003b25d70dfb077c8a63e564f75ab3b4dae2509 Mon Sep 17 00:00:00 2001 From: Cerebrovinny Date: Tue, 5 Nov 2024 09:39:25 +0000 Subject: [PATCH 2/7] fix flag description --- cmd/describe_stacks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/describe_stacks.go b/cmd/describe_stacks.go index c36e8b67b..8b6d87fb1 100644 --- a/cmd/describe_stacks.go +++ b/cmd/describe_stacks.go @@ -45,7 +45,7 @@ func init() { describeStacksCmd.PersistentFlags().Bool("process-templates", true, "Enable/disable Go template processing in Atmos stack manifests when executing the command: atmos describe stacks --process-templates=false") - describeStacksCmd.PersistentFlags().Bool("include-empty-stacks", false, "Include stacks with no components in the output: atmos describe stacks --include-empty") + describeStacksCmd.PersistentFlags().Bool("include-empty-stacks", false, "Include stacks with no components in the output: atmos describe stacks --include-empty-stacks") describeCmd.AddCommand(describeStacksCmd) } From 958d9215855fa60d4288570ebb0e7df2eb9a7394 Mon Sep 17 00:00:00 2001 From: Cerebrovinny Date: Tue, 5 Nov 2024 09:48:56 +0000 Subject: [PATCH 3/7] clean up --- internal/exec/describe_stacks.go | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/internal/exec/describe_stacks.go b/internal/exec/describe_stacks.go index b680ac476..515c83aeb 100644 --- a/internal/exec/describe_stacks.go +++ b/internal/exec/describe_stacks.go @@ -153,15 +153,12 @@ func ExecuteDescribeStacks( // Check if components section exists and has explicit components hasExplicitComponents := false if componentsSection, ok := stackSection.(map[string]any)["components"]; ok { - fmt.Printf("DEBUG: Found components section for %s\n", stackFileName) if componentsSection != nil { if terraformSection, ok := componentsSection.(map[string]any)["terraform"].(map[string]any); ok { hasExplicitComponents = len(terraformSection) > 0 - fmt.Printf("DEBUG: Terraform components found: %v (count: %d)\n", hasExplicitComponents, len(terraformSection)) } if helmfileSection, ok := componentsSection.(map[string]any)["helmfile"].(map[string]any); ok { hasExplicitComponents = hasExplicitComponents || len(helmfileSection) > 0 - fmt.Printf("DEBUG: Helmfile components found: %v\n", hasExplicitComponents) } } } @@ -170,15 +167,10 @@ func ExecuteDescribeStacks( hasImports := false if importsSection, ok := stackSection.(map[string]any)["import"].([]any); ok { hasImports = len(importsSection) > 0 - fmt.Printf("DEBUG: Imports found for %s: %v (count: %d)\n", stackFileName, hasImports, len(importsSection)) } - fmt.Printf("DEBUG: Stack %s - hasExplicitComponents: %v, hasImports: %v, includeEmptyStacks: %v\n", - stackFileName, hasExplicitComponents, hasImports, includeEmptyStacks) - // Skip stacks without components or imports when includeEmptyStacks is false if !includeEmptyStacks && !hasExplicitComponents && !hasImports { - fmt.Printf("DEBUG: Skipping empty stack: %s\n", stackFileName) continue } @@ -606,11 +598,13 @@ func ExecuteDescribeStacks( for compName, comp := range compTypeMap { fmt.Printf("DEBUG: Checking component: %s\n", compName) if compContent, ok := comp.(map[string]any); ok { - // Check for specific fields that indicate a real component - if _, hasVars := compContent["vars"]; hasVars { - hasNonEmptyComponents = true - fmt.Printf("DEBUG: Found non-empty component %s with vars\n", compName) - break + // Check for any meaningful content + relevantSections := []string{"vars", "metadata", "settings", "env"} + for _, section := range relevantSections { + if _, hasSection := compContent[section]; hasSection { + hasNonEmptyComponents = true + break + } } } } From ad8a683504d0b3c922115de0a382a681090cc885 Mon Sep 17 00:00:00 2001 From: Cerebrovinny Date: Tue, 5 Nov 2024 11:04:36 +0000 Subject: [PATCH 4/7] refactoring and clean up --- internal/exec/describe_stacks.go | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/internal/exec/describe_stacks.go b/internal/exec/describe_stacks.go index 515c83aeb..7a3bef4d5 100644 --- a/internal/exec/describe_stacks.go +++ b/internal/exec/describe_stacks.go @@ -144,9 +144,10 @@ func ExecuteDescribeStacks( var backendSection map[string]any var backendTypeSection string var stackName string - context := schema.Context{} for stackFileName, stackSection := range stacksMap { + var context schema.Context + // Delete the stack-wide imports delete(stackSection.(map[string]any), "imports") @@ -562,41 +563,29 @@ func ExecuteDescribeStacks( } } } - - fmt.Printf("DEBUG: Final stack map for %s: %+v\n", stackName, finalStacksMap[stackName]) } - fmt.Printf("DEBUG: Before final filtering - Stack count: %d\n", len(finalStacksMap)) - // Filter out empty stacks after processing all stack files if !includeEmptyStacks { - fmt.Printf("DEBUG: Starting empty stack filtering\n") for stackName := range finalStacksMap { - fmt.Printf("DEBUG: Checking final stack: %s\n", stackName) - if stackName == "" { - fmt.Printf("DEBUG: Removing empty stack name\n") delete(finalStacksMap, stackName) continue } stackEntry := finalStacksMap[stackName].(map[string]any) componentsSection, hasComponents := stackEntry["components"].(map[string]any) - fmt.Printf("DEBUG: Stack %s has components section: %v\n", stackName, hasComponents) if !hasComponents { - fmt.Printf("DEBUG: Removing stack %s - no components section\n", stackName) delete(finalStacksMap, stackName) continue } // Check if any component type (terraform/helmfile) has components hasNonEmptyComponents := false - for componentType, components := range componentsSection { - fmt.Printf("DEBUG: Checking component type: %s\n", componentType) + for _, components := range componentsSection { if compTypeMap, ok := components.(map[string]any); ok { - for compName, comp := range compTypeMap { - fmt.Printf("DEBUG: Checking component: %s\n", compName) + for _, comp := range compTypeMap { if compContent, ok := comp.(map[string]any); ok { // Check for any meaningful content relevantSections := []string{"vars", "metadata", "settings", "env"} @@ -615,7 +604,6 @@ func ExecuteDescribeStacks( } if !hasNonEmptyComponents { - fmt.Printf("DEBUG: Removing stack %s - no non-empty components\n", stackName) delete(finalStacksMap, stackName) continue } @@ -623,18 +611,11 @@ func ExecuteDescribeStacks( // Check for duplicate stacks (deploy/ prefix) if strings.HasPrefix(stackName, "deploy/") { baseStackName := strings.TrimPrefix(stackName, "deploy/") - if _, exists := finalStacksMap[baseStackName]; exists { - fmt.Printf("DEBUG: Found duplicate stack %s (base: %s)\n", stackName, baseStackName) - // Use the deploy/ prefixed version as canonical and remove the base name - delete(finalStacksMap, baseStackName) - } + delete(finalStacksMap, baseStackName) } } } else { - fmt.Printf("DEBUG: Skipping empty stack filtering because includeEmptyStacks is true\n") } - fmt.Printf("DEBUG: After final filtering - Stack count: %d\n", len(finalStacksMap)) - return finalStacksMap, nil } From f76f9145711bf71e91aa2668183fc820483a465f Mon Sep 17 00:00:00 2001 From: Cerebrovinny Date: Tue, 5 Nov 2024 16:53:06 +0000 Subject: [PATCH 5/7] added tests and test fixes for empty stacks --- pkg/describe/describe_stacks.go | 3 +- pkg/describe/describe_stacks_test.go | 89 +++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/pkg/describe/describe_stacks.go b/pkg/describe/describe_stacks.go index e3aabc7b9..f2ba77075 100644 --- a/pkg/describe/describe_stacks.go +++ b/pkg/describe/describe_stacks.go @@ -13,7 +13,8 @@ func ExecuteDescribeStacks( componentTypes []string, sections []string, ignoreMissingFiles bool, + includeEmptyStacks bool, ) (map[string]any, error) { - return e.ExecuteDescribeStacks(cliConfig, filterByStack, components, componentTypes, sections, ignoreMissingFiles, true) + return e.ExecuteDescribeStacks(cliConfig, filterByStack, components, componentTypes, sections, ignoreMissingFiles, false, includeEmptyStacks) } diff --git a/pkg/describe/describe_stacks_test.go b/pkg/describe/describe_stacks_test.go index 977d8ab7d..749b543a3 100644 --- a/pkg/describe/describe_stacks_test.go +++ b/pkg/describe/describe_stacks_test.go @@ -16,7 +16,7 @@ func TestDescribeStacks(t *testing.T) { cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) assert.Nil(t, err) - stacks, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false) + stacks, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, false) assert.Nil(t, err) dependentsYaml, err := u.ConvertToYAML(stacks) @@ -32,7 +32,7 @@ func TestDescribeStacksWithFilter1(t *testing.T) { stack := "tenant1-ue2-dev" - stacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, nil, false) + stacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, nil, false, false) assert.Nil(t, err) dependentsYaml, err := u.ConvertToYAML(stacks) @@ -49,7 +49,7 @@ func TestDescribeStacksWithFilter2(t *testing.T) { stack := "tenant1-ue2-dev" components := []string{"infra/vpc"} - stacks, err := ExecuteDescribeStacks(cliConfig, stack, components, nil, nil, false) + stacks, err := ExecuteDescribeStacks(cliConfig, stack, components, nil, nil, false, false) assert.Nil(t, err) dependentsYaml, err := u.ConvertToYAML(stacks) @@ -66,7 +66,7 @@ func TestDescribeStacksWithFilter3(t *testing.T) { stack := "tenant1-ue2-dev" sections := []string{"vars"} - stacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, sections, false) + stacks, err := ExecuteDescribeStacks(cliConfig, stack, nil, nil, sections, false, false) assert.Nil(t, err) dependentsYaml, err := u.ConvertToYAML(stacks) @@ -83,7 +83,7 @@ func TestDescribeStacksWithFilter4(t *testing.T) { componentTypes := []string{"terraform"} sections := []string{"none"} - stacks, err := ExecuteDescribeStacks(cliConfig, "", nil, componentTypes, sections, false) + stacks, err := ExecuteDescribeStacks(cliConfig, "", nil, componentTypes, sections, false, false) assert.Nil(t, err) dependentsYaml, err := u.ConvertToYAML(stacks) @@ -101,7 +101,7 @@ func TestDescribeStacksWithFilter5(t *testing.T) { components := []string{"top-level-component1"} sections := []string{"vars"} - stacks, err := ExecuteDescribeStacks(cliConfig, "", components, componentTypes, sections, false) + stacks, err := ExecuteDescribeStacks(cliConfig, "", components, componentTypes, sections, false, false) assert.Nil(t, err) assert.Equal(t, 8, len(stacks)) @@ -133,7 +133,7 @@ func TestDescribeStacksWithFilter6(t *testing.T) { components := []string{"top-level-component1"} sections := []string{"workspace"} - stacks, err := ExecuteDescribeStacks(cliConfig, "tenant1-ue2-dev", components, componentTypes, sections, false) + stacks, err := ExecuteDescribeStacks(cliConfig, "tenant1-ue2-dev", components, componentTypes, sections, false, false) assert.Nil(t, err) assert.Equal(t, 1, len(stacks)) @@ -160,7 +160,7 @@ func TestDescribeStacksWithFilter7(t *testing.T) { components := []string{"test/test-component-override-3"} sections := []string{"workspace"} - stacks, err := ExecuteDescribeStacks(cliConfig, stack, components, componentTypes, sections, false) + stacks, err := ExecuteDescribeStacks(cliConfig, stack, components, componentTypes, sections, false, false) assert.Nil(t, err) assert.Equal(t, 1, len(stacks)) @@ -175,3 +175,76 @@ func TestDescribeStacksWithFilter7(t *testing.T) { assert.Nil(t, err) t.Log(stacksYaml) } + +func TestDescribeStacksWithEmptyStacks(t *testing.T) { + configAndStacksInfo := schema.ConfigAndStacksInfo{} + + cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) + assert.Nil(t, err) + + stacks, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, false) + assert.Nil(t, err) + + initialStackCount := len(stacks) + + stacksWithEmpty, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, true) + assert.Nil(t, err) + + assert.Greater(t, len(stacksWithEmpty), initialStackCount, "Should include more stacks when empty stacks are included") + + foundEmptyStack := false + for _, stackContent := range stacksWithEmpty { + if components, ok := stackContent.(map[string]any)["components"].(map[string]any); ok { + if len(components) == 0 || (len(components) == 1 && len(components["terraform"].(map[string]any)) == 0) { + foundEmptyStack = true + break + } + } + } + assert.True(t, foundEmptyStack, "Should find at least one empty stack") +} + +func TestDescribeStacksWithVariousEmptyStacks(t *testing.T) { + configAndStacksInfo := schema.ConfigAndStacksInfo{} + + cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) + assert.Nil(t, err) + + stacksWithoutEmpty, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, false) + assert.Nil(t, err) + initialCount := len(stacksWithoutEmpty) + + stacksWithEmpty, err := ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, true) + assert.Nil(t, err) + + assert.Greater(t, len(stacksWithEmpty), initialCount, "Should have more stacks when including empty ones") + + var ( + emptyStacks []string + nonEmptyStacks []string + ) + + for stackName, stackContent := range stacksWithEmpty { + if stack, ok := stackContent.(map[string]any); ok { + if components, hasComponents := stack["components"].(map[string]any); hasComponents { + // Check for completely empty components + if len(components) == 0 || (len(components) == 1 && len(components["terraform"].(map[string]any)) == 0) { + emptyStacks = append(emptyStacks, stackName) + continue + } + + // If we have any components at all, consider it non-empty + for _, compType := range components { + if compMap, ok := compType.(map[string]any); ok && len(compMap) > 0 { + nonEmptyStacks = append(nonEmptyStacks, stackName) + break + } + } + } + } + } + + // Verify we found both types of stacks + assert.NotEmpty(t, emptyStacks, "Should find at least one empty stack") + assert.NotEmpty(t, nonEmptyStacks, "Should find at least one non-empty stack") +} From 7629b93f376a40d0d4989a689857636495ce1833 Mon Sep 17 00:00:00 2001 From: Cerebrovinny Date: Mon, 11 Nov 2024 23:40:28 +0000 Subject: [PATCH 6/7] fixes test filter --- internal/exec/describe_stacks.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/exec/describe_stacks.go b/internal/exec/describe_stacks.go index 7a3bef4d5..706ce0a1e 100644 --- a/internal/exec/describe_stacks.go +++ b/internal/exec/describe_stacks.go @@ -588,7 +588,7 @@ func ExecuteDescribeStacks( for _, comp := range compTypeMap { if compContent, ok := comp.(map[string]any); ok { // Check for any meaningful content - relevantSections := []string{"vars", "metadata", "settings", "env"} + relevantSections := []string{"vars", "metadata", "settings", "env", "workspace"} for _, section := range relevantSections { if _, hasSection := compContent[section]; hasSection { hasNonEmptyComponents = true @@ -615,6 +615,13 @@ func ExecuteDescribeStacks( } } } else { + // When including empty stacks, we still need to handle deploy/ prefix duplicates + for stackName := range finalStacksMap { + if strings.HasPrefix(stackName, "deploy/") { + baseStackName := strings.TrimPrefix(stackName, "deploy/") + delete(finalStacksMap, baseStackName) + } + } } return finalStacksMap, nil From 21c59b884b28fad1716d40ed45466b22ddba75eb Mon Sep 17 00:00:00 2001 From: Cerebrovinny Date: Wed, 13 Nov 2024 21:39:05 +0000 Subject: [PATCH 7/7] refactoring suggestions --- internal/exec/describe_stacks.go | 5 ++++- pkg/describe/describe_stacks_test.go | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/internal/exec/describe_stacks.go b/internal/exec/describe_stacks.go index 706ce0a1e..93f1f058b 100644 --- a/internal/exec/describe_stacks.go +++ b/internal/exec/describe_stacks.go @@ -573,7 +573,10 @@ func ExecuteDescribeStacks( continue } - stackEntry := finalStacksMap[stackName].(map[string]any) + stackEntry, ok := finalStacksMap[stackName].(map[string]any) + if !ok { + return nil, fmt.Errorf("invalid stack entry type for stack %s", stackName) + } componentsSection, hasComponents := stackEntry["components"].(map[string]any) if !hasComponents { diff --git a/pkg/describe/describe_stacks_test.go b/pkg/describe/describe_stacks_test.go index 749b543a3..eecf17fa9 100644 --- a/pkg/describe/describe_stacks_test.go +++ b/pkg/describe/describe_stacks_test.go @@ -195,10 +195,18 @@ func TestDescribeStacksWithEmptyStacks(t *testing.T) { foundEmptyStack := false for _, stackContent := range stacksWithEmpty { if components, ok := stackContent.(map[string]any)["components"].(map[string]any); ok { - if len(components) == 0 || (len(components) == 1 && len(components["terraform"].(map[string]any)) == 0) { + if len(components) == 0 { foundEmptyStack = true break } + if len(components) == 1 { + if terraformComps, hasTerraform := components["terraform"].(map[string]any); hasTerraform { + if len(terraformComps) == 0 { + foundEmptyStack = true + break + } + } + } } } assert.True(t, foundEmptyStack, "Should find at least one empty stack") @@ -228,11 +236,21 @@ func TestDescribeStacksWithVariousEmptyStacks(t *testing.T) { if stack, ok := stackContent.(map[string]any); ok { if components, hasComponents := stack["components"].(map[string]any); hasComponents { // Check for completely empty components - if len(components) == 0 || (len(components) == 1 && len(components["terraform"].(map[string]any)) == 0) { + if len(components) == 0 { emptyStacks = append(emptyStacks, stackName) continue } + // Check if only terraform exists and is empty + if len(components) == 1 { + if terraformComps, hasTerraform := components["terraform"].(map[string]any); hasTerraform { + if len(terraformComps) == 0 { + emptyStacks = append(emptyStacks, stackName) + continue + } + } + } + // If we have any components at all, consider it non-empty for _, compType := range components { if compMap, ok := compType.(map[string]any); ok && len(compMap) > 0 {