Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter out empty results from describe stacks #764

Merged
merged 13 commits into from
Nov 20, 2024
2 changes: 2 additions & 0 deletions cmd/describe_stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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-stacks")

describeCmd.AddCommand(describeStacksCmd)
}
2 changes: 1 addition & 1 deletion internal/exec/atmos.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions internal/exec/describe_affected_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion internal/exec/describe_dependents.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
119 changes: 113 additions & 6 deletions internal/exec/describe_stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ func ExecuteDescribeStacksCmd(cmd *cobra.Command, args []string) error {
return err
}

includeEmptyStacks, err := cmd.Flags().GetBool("include-empty-stacks")
aknysh marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

componentsCsv, err := flags.GetString("components")
if err != nil {
return err
Expand Down Expand Up @@ -97,6 +102,7 @@ func ExecuteDescribeStacksCmd(cmd *cobra.Command, args []string) error {
sections,
false,
processTemplates,
includeEmptyStacks,
)
if err != nil {
return err
Expand All @@ -119,6 +125,7 @@ func ExecuteDescribeStacks(
sections []string,
ignoreMissingFiles bool,
processTemplates bool,
includeEmptyStacks bool,
) (map[string]any, error) {

stacksMap, _, err := FindStacksMap(cliConfig, ignoreMissingFiles)
Expand All @@ -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
Expand All @@ -136,12 +144,48 @@ 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")

// Check if components section exists and has explicit components
hasExplicitComponents := false
if componentsSection, ok := stackSection.(map[string]any)["components"]; ok {
if componentsSection != nil {
if terraformSection, ok := componentsSection.(map[string]any)["terraform"].(map[string]any); ok {
hasExplicitComponents = len(terraformSection) > 0
}
if helmfileSection, ok := componentsSection.(map[string]any)["helmfile"].(map[string]any); ok {
hasExplicitComponents = hasExplicitComponents || len(helmfileSection) > 0
}
}
}

// Also check for imports
hasImports := false
if importsSection, ok := stackSection.(map[string]any)["import"].([]any); ok {
hasImports = len(importsSection) > 0
}

// Skip stacks without components or imports when includeEmptyStacks is false
if !includeEmptyStacks && !hasExplicitComponents && !hasImports {
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") {
Expand Down Expand Up @@ -242,9 +286,13 @@ func ExecuteDescribeStacks(

if stackName == "" {
stackName = stackFileName
} else if strings.HasPrefix(stackFileName, "deploy/") {
aknysh marked this conversation as resolved.
Show resolved Hide resolved
// 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/") {
Cerebrovinny marked this conversation as resolved.
Show resolved Hide resolved
finalStacksMap[stackName] = make(map[string]any)
}

Expand Down Expand Up @@ -430,9 +478,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)
}

Expand Down Expand Up @@ -511,11 +563,66 @@ func ExecuteDescribeStacks(
}
}
}
}

// Filter out empty stacks (stacks without any components)
if st, ok := finalStacksMap[stackName].(map[string]any); ok {
if len(st) == 0 {
// Filter out empty stacks after processing all stack files
if !includeEmptyStacks {
for stackName := range finalStacksMap {
if stackName == "" {
delete(finalStacksMap, stackName)
continue
}

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 {
delete(finalStacksMap, stackName)
continue
}

// Check if any component type (terraform/helmfile) has components
hasNonEmptyComponents := false
for _, components := range componentsSection {
if compTypeMap, ok := components.(map[string]any); ok {
for _, comp := range compTypeMap {
if compContent, ok := comp.(map[string]any); ok {
// Check for any meaningful content
relevantSections := []string{"vars", "metadata", "settings", "env", "workspace"}
for _, section := range relevantSections {
if _, hasSection := compContent[section]; hasSection {
hasNonEmptyComponents = true
break
}
}
}
}
}
if hasNonEmptyComponents {
break
}
}

if !hasNonEmptyComponents {
delete(finalStacksMap, stackName)
continue
}

// Check for duplicate stacks (deploy/ prefix)
if strings.HasPrefix(stackName, "deploy/") {
baseStackName := strings.TrimPrefix(stackName, "deploy/")
delete(finalStacksMap, baseStackName)
}
}
} 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)
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/describe/describe_stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Cerebrovinny marked this conversation as resolved.
Show resolved Hide resolved
}
Loading