diff --git a/pkg/list/atmos.yaml b/pkg/list/atmos.yaml new file mode 100644 index 000000000..95ec5e3fe --- /dev/null +++ b/pkg/list/atmos.yaml @@ -0,0 +1,350 @@ +# CLI config is loaded from the following locations (from lowest to highest priority): +# system dir ('/usr/local/etc/atmos' on Linux, '%LOCALAPPDATA%/atmos' on Windows) +# home dir (~/.atmos) +# current directory +# ENV vars +# Command-line arguments +# +# It supports POSIX-style Globs for file names/paths (double-star '**' is supported) +# https://en.wikipedia.org/wiki/Glob_(programming) + +# Base path for components, stacks and workflows configurations. +# Can also be set using 'ATMOS_BASE_PATH' ENV var, or '--base-path' command-line argument. +# Supports both absolute and relative paths. +# If not provided or is an empty string, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' +# are independent settings (supporting both absolute and relative paths). +# If 'base_path' is provided, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' +# are considered paths relative to 'base_path'. +base_path: "../../examples/tests" + +components: + terraform: + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_BASE_PATH' ENV var, or '--terraform-dir' command-line argument + # Supports both absolute and relative paths + base_path: "components/terraform" + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE' ENV var + apply_auto_approve: false + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INIT' ENV var, or '--deploy-run-init' command-line argument + deploy_run_init: true + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURE' ENV var, or '--init-run-reconfigure' command-line argument + init_run_reconfigure: true + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILE' ENV var, or '--auto-generate-backend-file' command-line argument + auto_generate_backend_file: false + helmfile: + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_BASE_PATH' ENV var, or '--helmfile-dir' command-line argument + # Supports both absolute and relative paths + base_path: "components/helmfile" + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_USE_EKS' ENV var + # If not specified, defaults to 'true' + use_eks: true + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_KUBECONFIG_PATH' ENV var + kubeconfig_path: "/dev/shm" + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_HELM_AWS_PROFILE_PATTERN' ENV var + helm_aws_profile_pattern: "{namespace}-{tenant}-gbl-{stage}-helm" + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_CLUSTER_NAME_PATTERN' ENV var + cluster_name_pattern: "{namespace}-{tenant}-{environment}-{stage}-eks-cluster" + +stacks: + # Can also be set using 'ATMOS_STACKS_BASE_PATH' ENV var, or '--config-dir' and '--stacks-dir' command-line arguments + # Supports both absolute and relative paths + base_path: "stacks" + # Can also be set using 'ATMOS_STACKS_INCLUDED_PATHS' ENV var (comma-separated values string) + included_paths: + - "orgs/**/*" + # Can also be set using 'ATMOS_STACKS_EXCLUDED_PATHS' ENV var (comma-separated values string) + excluded_paths: + - "**/_defaults.yaml" + # Can also be set using 'ATMOS_STACKS_NAME_PATTERN' ENV var + name_pattern: "{tenant}-{environment}-{stage}" + +workflows: + # Can also be set using 'ATMOS_WORKFLOWS_BASE_PATH' ENV var, or '--workflows-dir' command-line arguments + # Supports both absolute and relative paths + base_path: "stacks/workflows" + +logs: + # Can also be set using 'ATMOS_LOGS_FILE' ENV var, or '--logs-file' command-line argument + # File or standard file descriptor to write logs to + # Logs can be written to any file or any standard file descriptor, including `/dev/stdout`, `/dev/stderr` and `/dev/null` + file: "/dev/stderr" + # Supported log levels: Trace, Debug, Info, Warning, Off + # Can also be set using 'ATMOS_LOGS_LEVEL' ENV var, or '--logs-level' command-line argument + level: Info + +# Custom CLI commands +commands: + - name: tf + description: Execute 'terraform' commands + # subcommands + commands: + - name: plan + description: This command plans terraform components + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + env: + - key: ENV_VAR_1 + value: ENV_VAR_1_value + - key: ENV_VAR_2 + # 'valueCommand' is an external command to execute to get the value for the ENV var + # Either 'value' or 'valueCommand' can be specified for the ENV var, but not both + valueCommand: echo ENV_VAR_2_value + # steps support Go templates + steps: + - atmos terraform plan {{ .Arguments.component }} -s {{ .Flags.stack }} + - name: terraform + description: Execute 'terraform' commands + # subcommands + commands: + - name: provision + description: This command provisions terraform components + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + # ENV var values support Go templates + env: + - key: ATMOS_COMPONENT + value: "{{ .Arguments.component }}" + - key: ATMOS_STACK + value: "{{ .Flags.stack }}" + steps: + - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK + - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK + - name: show + description: Execute 'show' commands + # subcommands + commands: + - name: component + description: Execute 'show component' command + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + # ENV var values support Go templates and have access to {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables + env: + - key: ATMOS_COMPONENT + value: "{{ .Arguments.component }}" + - key: ATMOS_STACK + value: "{{ .Flags.stack }}" + - key: ATMOS_TENANT + value: "{{ .ComponentConfig.vars.tenant }}" + - key: ATMOS_STAGE + value: "{{ .ComponentConfig.vars.stage }}" + - key: ATMOS_ENVIRONMENT + value: "{{ .ComponentConfig.vars.environment }}" + - key: ATMOS_IS_PROD + value: "{{ .ComponentConfig.settings.config.is_prod }}" + # If a custom command defines 'component_config' section with 'component' and 'stack', 'atmos' generates the config for the component in the stack + # and makes it available in {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables, + # exposing all the component sections (which are also shown by 'atmos describe component' command) + component_config: + component: "{{ .Arguments.component }}" + stack: "{{ .Flags.stack }}" + # Steps support using Go templates and can access all configuration settings (e.g. {{ .ComponentConfig.xxx.yyy.zzz }}) + # Steps also have access to the ENV vars defined in the 'env' section of the 'command' + steps: + - 'echo Atmos component from argument: "{{ .Arguments.component }}"' + - 'echo ATMOS_COMPONENT: "$ATMOS_COMPONENT"' + - 'echo Atmos stack: "{{ .Flags.stack }}"' + - 'echo Terraform component: "{{ .ComponentConfig.component }}"' + - 'echo Backend S3 bucket: "{{ .ComponentConfig.backend.bucket }}"' + - 'echo Terraform workspace: "{{ .ComponentConfig.workspace }}"' + - 'echo Namespace: "{{ .ComponentConfig.vars.namespace }}"' + - 'echo Tenant: "{{ .ComponentConfig.vars.tenant }}"' + - 'echo Environment: "{{ .ComponentConfig.vars.environment }}"' + - 'echo Stage: "{{ .ComponentConfig.vars.stage }}"' + - 'echo settings.spacelift.workspace_enabled: "{{ .ComponentConfig.settings.spacelift.workspace_enabled }}"' + - '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 tenant1-ue1-dev + atmos list components --stack tenant2-uw2-prod + flags: + - name: stack + shorthand: s + description: Name of the stack + required: false + steps: + - > + {{ if .Flags.stack }} + atmos describe stacks --stack {{ .Flags.stack }} --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]" + {{ else }} + atmos describe stacks --format json --sections none | jq ".[].components.terraform" | jq -s add | jq -r "keys[]" + {{ end }} + + - name: set-eks-cluster + description: | + Download 'kubeconfig' and set EKS cluster. + + Example usage: + atmos set-eks-cluster eks/cluster -s tenant1-ue1-dev -r admin + atmos set-eks-cluster eks/cluster -s tenant2-uw2-prod --role reader + verbose: false # Set to `true` to see verbose outputs + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + - name: role + shorthand: r + description: IAM role to use + required: true + # If a custom command defines 'component_config' section with 'component' and 'stack', + # Atmos generates the config for the component in the stack + # and makes it available in {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables, + # exposing all the component sections (which are also shown by 'atmos describe component' command) + component_config: + component: "{{ .Arguments.component }}" + stack: "{{ .Flags.stack }}" + env: + - key: KUBECONFIG + value: /dev/shm/kubecfg.{{ .Flags.stack }}-{{ .Flags.role }} + steps: + - > + aws + --profile {{ .ComponentConfig.vars.namespace }}-{{ .ComponentConfig.vars.tenant }}-gbl-{{ .ComponentConfig.vars.stage }}-{{ .Flags.role }} + --region {{ .ComponentConfig.vars.region }} + eks update-kubeconfig + --name={{ .ComponentConfig.vars.namespace }}-{{ .Flags.stack }}-eks-cluster + --kubeconfig="${KUBECONFIG}" + > /dev/null + - chmod 600 ${KUBECONFIG} + - echo ${KUBECONFIG} + +# Integrations +integrations: + + # Atlantis integration + # https://www.runatlantis.io/docs/repo-level-atlantis-yaml.html + atlantis: + # Path and name of the Atlantis config file 'atlantis.yaml' + # Supports absolute and relative paths + # All the intermediate folders will be created automatically (e.g. 'path: /config/atlantis/atlantis.yaml') + # Can be overridden on the command line by using '--output-path' command-line argument in 'atmos atlantis generate repo-config' command + # If not specified (set to an empty string/omitted here, and set to an empty string on the command line), the content of the file will be dumped to 'stdout' + # On Linux/macOS, you can also use '--output-path=/dev/stdout' to dump the content to 'stdout' without setting it to an empty string in 'atlantis.path' + path: "atlantis.yaml" + + # Config templates + # Select a template by using the '--config-template ' command-line argument in 'atmos atlantis generate repo-config' command + config_templates: + config-1: + version: 3 + automerge: true + delete_source_branch_on_merge: true + parallel_plan: true + parallel_apply: true + allowed_regexp_prefixes: + - dev/ + - staging/ + - prod/ + + # Project templates + # Select a template by using the '--project-template ' command-line argument in 'atmos atlantis generate repo-config' command + project_templates: + project-1: + # generate a project entry for each component in every stack + name: "{tenant}-{environment}-{stage}-{component}" + workspace: "{workspace}" + dir: "{component-path}" + terraform_version: v1.2 + delete_source_branch_on_merge: true + autoplan: + enabled: true + when_modified: + - "**/*.tf" + - "varfiles/$PROJECT_NAME.tfvars.json" + apply_requirements: + - "approved" + + # Workflow templates + # https://www.runatlantis.io/docs/custom-workflows.html#custom-init-plan-apply-commands + # https://www.runatlantis.io/docs/custom-workflows.html#custom-run-command + workflow_templates: + workflow-1: + plan: + steps: + - run: terraform init -input=false + # When using workspaces, you need to select the workspace using the $WORKSPACE environment variable + - run: terraform workspace select $WORKSPACE || terraform workspace new $WORKSPACE + # You must output the plan using '-out $PLANFILE' because Atlantis expects plans to be in a specific location + - run: terraform plan -input=false -refresh -out $PLANFILE -var-file varfiles/$PROJECT_NAME.tfvars.json + apply: + steps: + - run: terraform apply $PLANFILE + +# Validation schemas (for validating atmos stacks and components) +schemas: + # https://json-schema.org + jsonschema: + # Can also be set using 'ATMOS_SCHEMAS_JSONSCHEMA_BASE_PATH' ENV var, or '--schemas-jsonschema-dir' command-line arguments + # Supports both absolute and relative paths + base_path: "stacks/schemas/jsonschema" + # https://www.openpolicyagent.org + opa: + # Can also be set using 'ATMOS_SCHEMAS_OPA_BASE_PATH' ENV var, or '--schemas-opa-dir' command-line arguments + # Supports both absolute and relative paths + base_path: "stacks/schemas/opa" + # JSON Schema to validate Atmos manifests + # https://atmos.tools/cli/schemas/ + # https://atmos.tools/cli/commands/validate/stacks/ + # https://atmos.tools/quick-start/advanced/configure-validation/ + # https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json + # https://json-schema.org/draft/2020-12/release-notes + # https://www.schemastore.org/json + # https://github.com/SchemaStore/schemastore + atmos: + # Can also be set using 'ATMOS_SCHEMAS_ATMOS_MANIFEST' ENV var, or '--schemas-atmos-manifest' command-line arguments + # Supports both absolute and relative paths (relative to the `base_path` setting in `atmos.yaml`) + manifest: "../quick-start-advanced/stacks/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json" + +# `Go` templates in Atmos manifests +# https://atmos.tools/core-concepts/stacks/templates +# https://pkg.go.dev/text/template +templates: + settings: + enabled: true + evaluations: 1 + # https://masterminds.github.io/sprig + sprig: + enabled: true + # https://docs.gomplate.ca + gomplate: + enabled: true + timeout: 5 + # https://docs.gomplate.ca/datasources + datasources: {} diff --git a/pkg/list/list_components_test.go b/pkg/list/list_components_test.go new file mode 100644 index 000000000..1aae42cf8 --- /dev/null +++ b/pkg/list/list_components_test.go @@ -0,0 +1,45 @@ +package list + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + e "github.com/cloudposse/atmos/internal/exec" + cfg "github.com/cloudposse/atmos/pkg/config" + "github.com/cloudposse/atmos/pkg/schema" + u "github.com/cloudposse/atmos/pkg/utils" +) + +func TestListComponents(t *testing.T) { + configAndStacksInfo := schema.ConfigAndStacksInfo{} + + cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) + assert.Nil(t, err) + + stacksMap, err := e.ExecuteDescribeStacks(cliConfig, "", nil, nil, + nil, false, false, false) + assert.Nil(t, err) + + output, err := FilterAndListComponents("", stacksMap) + dependentsYaml, err := u.ConvertToYAML(output) + assert.Nil(t, err) + t.Log(dependentsYaml) +} + +func TestListComponentsWithStack(t *testing.T) { + configAndStacksInfo := schema.ConfigAndStacksInfo{} + + cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) + assert.Nil(t, err) + stack := "tenant1-ue2-dev" + + stacksMap, err := e.ExecuteDescribeStacks(cliConfig, stack, nil, nil, + nil, false, false, false) + assert.Nil(t, err) + + output, err := FilterAndListStacks(stacksMap, stack) + dependentsYaml, err := u.ConvertToYAML(output) + assert.Nil(t, err) + t.Log(dependentsYaml) +} diff --git a/pkg/list/list_stacks_test.go b/pkg/list/list_stacks_test.go new file mode 100644 index 000000000..7ae726835 --- /dev/null +++ b/pkg/list/list_stacks_test.go @@ -0,0 +1,45 @@ +package list + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + e "github.com/cloudposse/atmos/internal/exec" + cfg "github.com/cloudposse/atmos/pkg/config" + "github.com/cloudposse/atmos/pkg/schema" + u "github.com/cloudposse/atmos/pkg/utils" +) + +func TestListStacks(t *testing.T) { + configAndStacksInfo := schema.ConfigAndStacksInfo{} + + cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) + assert.Nil(t, err) + + stacksMap, err := e.ExecuteDescribeStacks(cliConfig, "", nil, nil, + nil, false, false, false) + assert.Nil(t, err) + + output, err := FilterAndListStacks(stacksMap, "") + dependentsYaml, err := u.ConvertToYAML(output) + assert.Nil(t, err) + t.Log(dependentsYaml) +} + +func TestListStacksWithComponent(t *testing.T) { + configAndStacksInfo := schema.ConfigAndStacksInfo{} + + cliConfig, err := cfg.InitCliConfig(configAndStacksInfo, true) + assert.Nil(t, err) + component := "infra/vpc" + + stacksMap, err := e.ExecuteDescribeStacks(cliConfig, component, nil, nil, + nil, false, false, false) + assert.Nil(t, err) + + output, err := FilterAndListStacks(stacksMap, component) + dependentsYaml, err := u.ConvertToYAML(output) + assert.Nil(t, err) + t.Log(dependentsYaml) +}