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

Improve atmos list stacks #979

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8311350
feat: add output format options for `atmos list stacks` command
Cerebrovinny Jan 28, 2025
faeec51
feat(list): enhance stack listing with configurable formats and columns
Cerebrovinny Jan 28, 2025
80d540c
Add stack list columns configuration to atmos.yaml
Cerebrovinny Jan 28, 2025
baa42e5
refactor: improve table formatting for non-TTY output
Cerebrovinny Jan 28, 2025
060d49b
Add list format and columns configuration to stacks config
Cerebrovinny Jan 28, 2025
c29f1e8
docs: update list-stacks command documentation and simplify default c…
Cerebrovinny Jan 30, 2025
d53379f
test: add test case for FilterAndListStacks function
Cerebrovinny Jan 30, 2025
9e79f60
Update website/docs/cli/commands/list/list-stacks.mdx
Cerebrovinny Jan 30, 2025
6bbe5b7
Update website/docs/cli/commands/list/list-stacks.mdx
Cerebrovinny Feb 2, 2025
1611285
Update website/docs/cli/commands/list/list-stacks.mdx
Cerebrovinny Feb 2, 2025
8690e35
Move stack list columns configuration to quick-start-advanced example
Cerebrovinny Feb 2, 2025
d7718bc
Update website/docs/cli/commands/list/list-stacks.mdx
Cerebrovinny Feb 2, 2025
6c33077
test: enhance FilterAndListStacks test with realistic stack configura…
Cerebrovinny Feb 2, 2025
1a871fa
fix: use quick-start-simple path for stacks when tenant info is incom…
Cerebrovinny Feb 2, 2025
7ad8f6e
refactor: simplify stack listing and improve output formatting
Cerebrovinny Feb 3, 2025
856c0eb
refactor: extract stack info creation and improve JSON output handling
Cerebrovinny Feb 3, 2025
d66c1c7
fix: use stack file from atmos configuration instead of hardcoded value
Cerebrovinny Feb 3, 2025
6b257de
Merge branch 'main' into DEV-2804
Cerebrovinny Feb 5, 2025
c976cf3
optimize: pre-parse templates for better list performance
Cerebrovinny Feb 5, 2025
c8dd900
Add atmos_stack_file to stack info and update column template
Cerebrovinny Feb 5, 2025
14e477f
Merge branch 'main' into DEV-2804
osterman Feb 6, 2025
98f5dfe
feat: enhance stack listing with variable extraction and dynamic columns
Cerebrovinny Feb 6, 2025
da45ed2
feat: improve JSON and CSV output formatting for empty values
Cerebrovinny Feb 6, 2025
aab2f73
Merge branch 'main' into DEV-2804
osterman Feb 6, 2025
6ead9fb
Remove unused list configuration from stacks section
Cerebrovinny Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion atmos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -394,4 +394,3 @@ version:
enabled: true
timeout: 1000 # ms
frequency: 1h

15 changes: 11 additions & 4 deletions cmd/list_stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ var listStacksCmd = &cobra.Command{
Short: "List all Atmos stacks or stacks for a specific component",
Long: "This command lists all Atmos stacks, or filters the list to show only the stacks associated with a specified component.",
Example: "atmos list stacks\n" +
"atmos list stacks -c <component>",
"atmos list stacks -c <component>\n" +
"atmos list stacks --format json\n" +
"atmos list stacks --format csv --delimiter ','\n" +
"atmos list stacks --format table",
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
// Check Atmos configuration
checkAtmosConfig()

componentFlag, _ := cmd.Flags().GetString("component")
formatFlag, _ := cmd.Flags().GetString("format")
delimiterFlag, _ := cmd.Flags().GetString("delimiter")

configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
Expand All @@ -35,13 +40,13 @@ var listStacksCmd = &cobra.Command{
return
}

stacksMap, err := e.ExecuteDescribeStacks(atmosConfig, "", nil, nil, nil, false, false, false, false, nil)
stacksMap, err := e.ExecuteDescribeStacks(atmosConfig, "", nil, nil, nil, false, true, true, false, nil)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error describing stacks: %v", err), theme.Colors.Error)
return
}

output, err := l.FilterAndListStacks(stacksMap, componentFlag)
output, err := l.FilterAndListStacks(stacksMap, componentFlag, atmosConfig.Stacks.List, formatFlag, delimiterFlag)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error filtering stacks: %v", err), theme.Colors.Error)
return
Expand All @@ -52,6 +57,8 @@ var listStacksCmd = &cobra.Command{

func init() {
listStacksCmd.DisableFlagParsing = false
listStacksCmd.PersistentFlags().StringP("component", "c", "", "atmos list stacks -c <component>")
listStacksCmd.PersistentFlags().StringP("component", "c", "", "Filter stacks by component")
listStacksCmd.PersistentFlags().StringP("format", "f", "", "Output format (table, json, csv)")
listStacksCmd.PersistentFlags().StringP("delimiter", "d", "\t", "Delimiter for table and csv formats")
listCmd.AddCommand(listStacksCmd)
}
10 changes: 10 additions & 0 deletions examples/quick-start-advanced/atmos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ stacks:
- "**/_defaults.yaml"
# Can also be set using 'ATMOS_STACKS_NAME_PATTERN' ENV var
name_pattern: "{tenant}-{environment}-{stage}"
list:
columns:
- name: Stack
value: '{{ .atmos_stack }}'
- name: Tenant
value: '{{ index .vars "tenant" }}'
- name: Environment
value: '{{ index .vars "environment" }}'
- name: File
value: '{{ .atmos_stack_file }}'

workflows:
# Can also be set using 'ATMOS_WORKFLOWS_BASE_PATH' ENV var, or '--workflows-dir' command-line argument
Expand Down
33 changes: 21 additions & 12 deletions internal/exec/describe_stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ func ExecuteDescribeStacks(
if !u.MapKeyExists(finalStacksMap, stackName) {
finalStacksMap[stackName] = make(map[string]any)
finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any)
finalStacksMap[stackName].(map[string]any)["atmos_stack_file"] = stackFileName
}

if componentsSection, ok := stackSection.(map[string]any)["components"].(map[string]any); ok {
Expand Down Expand Up @@ -307,15 +308,22 @@ func ExecuteDescribeStacks(
if err != nil {
return nil, err
}
} else {
} else if atmosConfig.Stacks.NamePattern != "" {
context = cfg.GetContextFromVars(varsSection)
configAndStacksInfo.Context = context
stackName, err = cfg.GetContextPrefix(stackFileName, context, GetStackNamePattern(atmosConfig), stackFileName)
if err != nil {
return nil, err
}
} else {
// If no name pattern or template is configured, use the stack file name
stackName = stackFileName
}

// Update the component section with the final stack name
configAndStacksInfo.ComponentSection["atmos_stack"] = stackName
configAndStacksInfo.ComponentSection["stack"] = stackName

if filterByStack != "" && filterByStack != stackFileName && filterByStack != stackName {
continue
}
Expand All @@ -327,18 +335,15 @@ func ExecuteDescribeStacks(
// Only create the stack entry if it doesn't exist
if !u.MapKeyExists(finalStacksMap, stackName) {
finalStacksMap[stackName] = make(map[string]any)
finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any)
finalStacksMap[stackName].(map[string]any)["atmos_stack_file"] = stackFileName
}

configAndStacksInfo.ComponentSection["atmos_component"] = componentName
configAndStacksInfo.ComponentSection["atmos_stack"] = stackName
configAndStacksInfo.ComponentSection["stack"] = stackName
configAndStacksInfo.ComponentSection["atmos_stack_file"] = stackFileName
configAndStacksInfo.ComponentSection["atmos_manifest"] = stackFileName

if len(components) == 0 || u.SliceContainsString(components, componentName) || u.SliceContainsString(derivedComponents, componentName) {
if !u.MapKeyExists(finalStacksMap[stackName].(map[string]any), "components") {
finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any)
}
if !u.MapKeyExists(finalStacksMap[stackName].(map[string]any)["components"].(map[string]any), "terraform") {
finalStacksMap[stackName].(map[string]any)["components"].(map[string]any)["terraform"] = make(map[string]any)
}
Expand Down Expand Up @@ -516,15 +521,22 @@ func ExecuteDescribeStacks(
if err != nil {
return nil, err
}
} else {
} else if atmosConfig.Stacks.NamePattern != "" {
context = cfg.GetContextFromVars(varsSection)
configAndStacksInfo.Context = context
stackName, err = cfg.GetContextPrefix(stackFileName, context, GetStackNamePattern(atmosConfig), stackFileName)
if err != nil {
return nil, err
}
} else {
// If no name pattern or template is configured, use the stack file name
stackName = stackFileName
}

// Update the component section with the final stack name
configAndStacksInfo.ComponentSection["atmos_stack"] = stackName
configAndStacksInfo.ComponentSection["stack"] = stackName

if filterByStack != "" && filterByStack != stackFileName && filterByStack != stackName {
continue
}
Expand All @@ -536,18 +548,15 @@ func ExecuteDescribeStacks(
// Only create the stack entry if it doesn't exist
if !u.MapKeyExists(finalStacksMap, stackName) {
finalStacksMap[stackName] = make(map[string]any)
finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any)
finalStacksMap[stackName].(map[string]any)["atmos_stack_file"] = stackFileName
}

configAndStacksInfo.ComponentSection["atmos_component"] = componentName
configAndStacksInfo.ComponentSection["atmos_stack"] = stackName
configAndStacksInfo.ComponentSection["stack"] = stackName
configAndStacksInfo.ComponentSection["atmos_stack_file"] = stackFileName
configAndStacksInfo.ComponentSection["atmos_manifest"] = stackFileName

if len(components) == 0 || u.SliceContainsString(components, componentName) || u.SliceContainsString(derivedComponents, componentName) {
if !u.MapKeyExists(finalStacksMap[stackName].(map[string]any), "components") {
finalStacksMap[stackName].(map[string]any)["components"] = make(map[string]any)
}
if !u.MapKeyExists(finalStacksMap[stackName].(map[string]any)["components"].(map[string]any), "helmfile") {
finalStacksMap[stackName].(map[string]any)["components"].(map[string]any)["helmfile"] = make(map[string]any)
}
Expand Down
11 changes: 10 additions & 1 deletion pkg/list/list_components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,16 @@ func TestListComponentsWithStack(t *testing.T) {
nil, false, false, false, false, nil)
assert.Nil(t, err)

output, err := FilterAndListStacks(stacksMap, testStack)
// Create test list config
listConfig := schema.ListConfig{
Format: "",
Columns: []schema.ListColumnConfig{
{Name: "Stack", Value: "{{ .atmos_stack }}"},
{Name: "File", Value: "{{ .atmos_stack_file }}"},
},
}

output, err := FilterAndListStacks(stacksMap, testStack, listConfig, "", "\t")
assert.Nil(t, err)
dependentsYaml, err := u.ConvertToYAML(output)
assert.Nil(t, err)
Expand Down
Loading
Loading