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

feat: create atmos list command for listing stacks and components #797

Merged
merged 13 commits into from
Nov 26, 2024
Merged
53 changes: 0 additions & 53 deletions atmos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -191,59 +191,6 @@ commands:
- 'echo Dependencies: "{{ .ComponentConfig.deps }}"'
- 'echo settings.config.is_prod: "{{ .ComponentConfig.settings.config.is_prod }}"'
- 'echo ATMOS_IS_PROD: "$ATMOS_IS_PROD"'
- name: list
description: Execute 'atmos list' commands
# subcommands
commands:
- name: stacks
description: |
List all Atmos stacks.
steps:
- >
atmos describe stacks --process-templates=false --sections none | grep -e "^\S" | sed s/://g
- name: components
description: |
List all Atmos components in all stacks or in a single stack.

Example usage:
atmos list components
atmos list components -s plat-ue2-dev
atmos list components --stack plat-uw2-prod
atmos list components -s plat-ue2-dev --type abstract
atmos list components -s plat-ue2-dev -t enabled
atmos list components -s plat-ue2-dev -t disabled
flags:
- name: stack
shorthand: s
description: Name of the stack
required: false
- name: type
shorthand: t
description: Component types - abstract, enabled, or disabled
required: false
steps:
- >
{{ if .Flags.stack }}
{{ if eq .Flags.type "enabled" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == true)' | jq -r .key
{{ else if eq .Flags.type "disabled" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == false)' | jq -r .key
{{ else if eq .Flags.type "abstract" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.metadata.type == "abstract")' | jq -r .key
{{ else }}
atmos describe stacks --stack {{ .Flags.stack }} --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ end }}
{{ else }}
{{ if eq .Flags.type "enabled" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == true)' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else if eq .Flags.type "disabled" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == false)' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else if eq .Flags.type "abstract" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.metadata.type == "abstract")' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else }}
atmos describe stacks --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ end }}
{{ end }}

# Integrations
integrations:
Expand Down
17 changes: 17 additions & 0 deletions cmd/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cmd

import (
"github.com/spf13/cobra"
)

// listCmd commands list stacks and components
var listCmd = &cobra.Command{
Use: "list",
Short: "Execute 'list' commands",
Long: `This command lists stacks and components`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
}

func init() {
RootCmd.AddCommand(listCmd)
}
84 changes: 84 additions & 0 deletions cmd/list_components.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package cmd

import (
"fmt"
"sort"
"strings"

e "github.com/cloudposse/atmos/internal/exec"
"github.com/cloudposse/atmos/pkg/config"
"github.com/cloudposse/atmos/pkg/schema"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/fatih/color"
"github.com/samber/lo"
"github.com/spf13/cobra"
)

// listComponentsCmd lists atmos components
var listComponentsCmd = &cobra.Command{
Use: "components",
Short: "Execute 'list components' command",
Long: `This command lists all Atmos components or filters components by stacks.`,
Example: "atmos list components\n" +
"atmos list components -s <stack>",
Run: func(cmd *cobra.Command, args []string) {
// Check Atmos configuration
checkAtmosConfig()

stackFlag, _ := cmd.Flags().GetString("stack")

configAndStacksInfo := schema.ConfigAndStacksInfo{}
cliConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error initializing CLI config: %v", err), color.New(color.FgRed))
return
}

stacksMap, err := e.ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, false)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error describing stacks: %v", err), color.New(color.FgRed))
return
}

components := []string{}
if stackFlag != "" {
if stackData, ok := stacksMap[stackFlag]; ok {
if stackMap, ok := stackData.(map[string]any); ok {
if componentsMap, ok := stackMap["components"].(map[string]any); ok {
if terraformComponents, ok := componentsMap["terraform"].(map[string]any); ok {
components = append(components, lo.Keys(terraformComponents)...)
}
RoseSecurity marked this conversation as resolved.
Show resolved Hide resolved
}
}
RoseSecurity marked this conversation as resolved.
Show resolved Hide resolved
} else {
u.PrintMessageInColor(fmt.Sprintf("Stack '%s' not found", stackFlag), color.New(color.FgYellow))
return
}
} else {
// Get all components from all stacks
for _, stackData := range stacksMap {
if stackMap, ok := stackData.(map[string]any); ok {
if componentsMap, ok := stackMap["components"].(map[string]any); ok {
if terraformComponents, ok := componentsMap["terraform"].(map[string]any); ok {
components = append(components, lo.Keys(terraformComponents)...)
}
}
}
}
}

components = lo.Uniq(components)
sort.Strings(components)

if len(components) == 0 {
u.PrintMessageInColor("No components found", color.New(color.FgYellow))
} else {
u.PrintMessageInColor(strings.Join(components, "\n")+"\n", color.New(color.FgGreen))
}
},
}

func init() {
listComponentsCmd.PersistentFlags().StringP("stack", "s", "", "Filter components by stack (e.g., atmos list components -s stack1)")
listCmd.AddCommand(listComponentsCmd)
}
80 changes: 80 additions & 0 deletions cmd/list_stacks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package cmd

import (
"fmt"
"sort"
"strings"

e "github.com/cloudposse/atmos/internal/exec"
"github.com/cloudposse/atmos/pkg/config"
"github.com/cloudposse/atmos/pkg/schema"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/fatih/color"
"github.com/samber/lo"
"github.com/spf13/cobra"
)

// listStacksCmd lists atmos stacks
var listStacksCmd = &cobra.Command{
Use: "stacks",
Short: "Execute 'list stacks' command",
Long: `This command lists all Atmos stacks or all stacks for the specified component: atmos list stacks -c <component>`,
Example: "atmos list stacks\n" +
"atmos list stacks -c <component>",
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
// Check Atmos configuration
checkAtmosConfig()

componentFlag, _ := cmd.Flags().GetString("component")

configAndStacksInfo := schema.ConfigAndStacksInfo{}
cliConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error initializing CLI config: %v", err), color.New(color.FgRed))
return
}

stacksMap, err := e.ExecuteDescribeStacks(cliConfig, "", nil, nil, nil, false, false)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error describing stacks: %v", err), color.New(color.FgRed))
return
}

var output string
if componentFlag != "" {
// Filter stacks by component
filteredStacks := []string{}
for stackName, stackData := range stacksMap {
if v2, ok := stackData.(map[string]any); ok {
if v3, ok := v2["components"].(map[string]any); ok {
if v4, ok := v3["terraform"].(map[string]any); ok {
if _, exists := v4[componentFlag]; exists {
filteredStacks = append(filteredStacks, stackName)
}
}
}
}
}
RoseSecurity marked this conversation as resolved.
Show resolved Hide resolved

if len(filteredStacks) == 0 {
output = fmt.Sprintf("No stacks found for component '%s'", componentFlag)
} else {
sort.Strings(filteredStacks)
output += strings.Join(filteredStacks, "\n") + "\n"
}
} else {
// List all stacks
stacks := lo.Keys(stacksMap)
sort.Strings(stacks)
output = strings.Join(stacks, "\n") + "\n"
}
u.PrintMessageInColor(output, color.New(color.FgGreen))
},
}

func init() {
listStacksCmd.DisableFlagParsing = false
listStacksCmd.PersistentFlags().StringP("component", "c", "", "atmos list stacks -c <component>")
listCmd.AddCommand(listStacksCmd)
}
53 changes: 0 additions & 53 deletions examples/quick-start-advanced/atmos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -176,59 +176,6 @@ commands:
- 'echo Environment: "{{ .ComponentConfig.vars.environment }}"'
- 'echo Stage: "{{ .ComponentConfig.vars.stage }}"'
- 'echo Dependencies: "{{ .ComponentConfig.deps }}"'
- name: list
description: Execute 'atmos list' commands
# subcommands
commands:
- name: stacks
description: |
List all Atmos stacks.
steps:
- >
atmos describe stacks --process-templates=false --sections none | grep -e "^\S" | sed s/://g
- name: components
description: |
List all Atmos components in all stacks or in a single stack.

Example usage:
atmos list components
atmos list components -s plat-ue2-dev
atmos list components --stack plat-uw2-prod
atmos list components -s plat-ue2-dev --type abstract
atmos list components -s plat-ue2-dev -t enabled
atmos list components -s plat-ue2-dev -t disabled
flags:
- name: stack
shorthand: s
description: Name of the stack
required: false
- name: type
shorthand: t
description: Component types - abstract, enabled, or disabled
required: false
steps:
- >
{{ if .Flags.stack }}
{{ if eq .Flags.type "enabled" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == true)' | jq -r .key
{{ else if eq .Flags.type "disabled" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == false)' | jq -r .key
{{ else if eq .Flags.type "abstract" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.metadata.type == "abstract")' | jq -r .key
{{ else }}
atmos describe stacks --stack {{ .Flags.stack }} --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ end }}
{{ else }}
{{ if eq .Flags.type "enabled" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == true)' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else if eq .Flags.type "disabled" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == false)' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else if eq .Flags.type "abstract" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.metadata.type == "abstract")' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else }}
atmos describe stacks --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ end }}
{{ end }}

# Validation schemas (for validating atmos stacks and components)
schemas:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,60 +176,6 @@ commands:
- 'echo Environment: "{{ .ComponentConfig.vars.environment }}"'
- 'echo Stage: "{{ .ComponentConfig.vars.stage }}"'
- 'echo Dependencies: "{{ .ComponentConfig.deps }}"'
- name: list
description: Execute 'atmos list' commands
# subcommands
commands:
- name: stacks
description: |
List all Atmos stacks.
steps:
- >
atmos describe stacks --process-templates=false --sections none | grep -e "^\S" | sed s/://g
- name: components
description: |
List all Atmos components in all stacks or in a single stack.

Example usage:
atmos list components
atmos list components -s plat-ue2-dev
atmos list components --stack plat-uw2-prod
atmos list components -s plat-ue2-dev --type abstract
atmos list components -s plat-ue2-dev -t enabled
atmos list components -s plat-ue2-dev -t disabled
flags:
- name: stack
shorthand: s
description: Name of the stack
required: false
- name: type
shorthand: t
description: Component types - abstract, enabled, or disabled
required: false
steps:
- >
{{ if .Flags.stack }}
{{ if eq .Flags.type "enabled" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == true)' | jq -r .key
{{ else if eq .Flags.type "disabled" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == false)' | jq -r .key
{{ else if eq .Flags.type "abstract" }}
atmos describe stacks --stack {{ .Flags.stack }} --format json | jq '.[].components.terraform | to_entries[] | select(.value.metadata.type == "abstract")' | jq -r .key
{{ else }}
atmos describe stacks --stack {{ .Flags.stack }} --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ end }}
{{ else }}
{{ if eq .Flags.type "enabled" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == true)' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else if eq .Flags.type "disabled" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.vars.enabled == false)' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else if eq .Flags.type "abstract" }}
atmos describe stacks --format json | jq '.[].components.terraform | to_entries[] | select(.value.metadata.type == "abstract")' | jq -r '[.key]' | jq -s 'add' | jq 'unique | sort' | jq -r "values[]"
{{ else }}
atmos describe stacks --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]"
{{ end }}
{{ end }}


# Validation schemas (for validating atmos stacks and components)
schemas:
Expand Down
Loading