Skip to content

Commit

Permalink
Add default CLI configuration to Atmos code. Update/improve examples …
Browse files Browse the repository at this point in the history
…and docs (#529)

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* Updates

* chore: update demo gif

* Updates

* chore: update demo gif

* Updates

* Updates

* chore: update demo gif

---------

Co-authored-by: vhs-action 📼 <actions@github.com>
  • Loading branch information
aknysh and actions-user committed Feb 2, 2024
1 parent 31e6073 commit 6c5cb02
Show file tree
Hide file tree
Showing 16 changed files with 544 additions and 249 deletions.
4 changes: 1 addition & 3 deletions demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Sleep 1s

Type "# We will start by installing some 3rd-party components and other artifacts..." Sleep 500ms Enter
Type "atmos vendor pull" Sleep 500ms Enter
Sleep 7s
Sleep 8s

Type "# In Atmos you can easily explore components, stacks, and run commands..." Sleep 500ms Enter
Type "atmos" Sleep 500ms Enter
Expand All @@ -56,8 +56,6 @@ Up Sleep 500ms
Up Sleep 1s
Right Sleep 1s
Down Sleep 500ms
Down Sleep 500ms
Down Sleep 500ms
Right Sleep 1s
Down Sleep 500ms
Up Sleep 1s
Expand Down
Binary file modified docs/demo.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions examples/quick-start/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ ARG GEODESIC_VERSION=2.8.4
ARG GEODESIC_OS=debian

# atmos: https://github.com/cloudposse/atmos
ARG ATMOS_VERSION=1.56.1
ARG ATMOS_VERSION=1.57.0

# Terraform: https://github.com/hashicorp/terraform/releases
ARG TF_VERSION=1.7.1
ARG TF_VERSION=1.7.2

FROM cloudposse/geodesic:${GEODESIC_VERSION}-${GEODESIC_OS}

Expand Down
15 changes: 11 additions & 4 deletions examples/quick-start/components/terraform/vpc/remote-state.tf
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
module "vpc_flow_logs_bucket" {
count = var.vpc_flow_logs_enabled ? 1 : 0
count = local.vpc_flow_logs_enabled ? 1 : 0

source = "cloudposse/stack-config/yaml//modules/remote-state"
version = "1.5.0"

component = "vpc-flow-logs-bucket"
environment = var.vpc_flow_logs_bucket_environment_name
stage = var.vpc_flow_logs_bucket_stage_name
# Specify the Atmos component name (defined in YAML stack config files)
# for which to get the remote state outputs
component = "vpc-flow-logs-bucket"

# Override the context variables to point to a different Atmos stack if the
# `vpc-flow-logs-bucket` Atmos component is provisioned in another AWS account, OU or region
stage = try(coalesce(var.vpc_flow_logs_bucket_stage_name, module.this.stage), null)
environment = try(coalesce(var.vpc_flow_logs_bucket_environment_name, module.this.environment), null)
tenant = try(coalesce(var.vpc_flow_logs_bucket_tenant_name, module.this.tenant), null)

# `context` input is a way to provide the information about the stack (using the context
# variables `namespace`, `tenant`, `environment`, and `stage` defined in the stack config)
context = module.this.context
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ module "vpc_flow_logs_bucket" {
source = "cloudposse/stack-config/yaml//modules/remote-state"
version = "1.5.0"

component = var.vpc_flow_logs_bucket_component_name
# Specify the Atmos component name (defined in YAML stack config files)
# for which to get the remote state outputs
component = var.vpc_flow_logs_bucket_component_name

# Override the context variables to point to a different Atmos stack if the
# `vpc-flow-logs-bucket` Atmos component is provisioned in another AWS account, OU or region
stage = try(coalesce(var.vpc_flow_logs_bucket_stage_name, module.this.stage), null)
environment = try(coalesce(var.vpc_flow_logs_bucket_environment_name, module.this.environment), null)
tenant = try(coalesce(var.vpc_flow_logs_bucket_tenant_name, module.this.tenant), null)

# `context` input is a way to provide the information about the stack (using the context
# variables `namespace`, `tenant`, `environment`, and `stage` defined in the stack config)
context = module.this.context
}
97 changes: 87 additions & 10 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,75 @@
package config

import (
"bytes"
"encoding/json"
"fmt"
"github.com/cloudposse/atmos/pkg/schema"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
"os"
"path"
"path/filepath"
"runtime"

"github.com/fatih/color"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"

"github.com/cloudposse/atmos/pkg/schema"
u "github.com/cloudposse/atmos/pkg/utils"
)

var NotFound = errors.New("\n'atmos.yaml' CLI config files not found in any of the searched paths: system dir, home dir, current dir, ENV vars." +
"\nYou can download a sample config and adapt it to your requirements from " +
"https://raw.githubusercontent.com/cloudposse/atmos/master/examples/quick-start/atmos.yaml")
var (
NotFound = errors.New("\n'atmos.yaml' CLI config was not found in any of the searched paths: system dir, home dir, current dir, ENV vars." +
"\nYou can download a sample config and adapt it to your requirements from " +
"https://raw.githubusercontent.com/cloudposse/atmos/master/examples/quick-start/atmos.yaml")

defaultCliConfig = schema.CliConfiguration{
BasePath: ".",
Stacks: schema.Stacks{
BasePath: "stacks",
NamePattern: "{tenant}-{environment}-{stage}",
IncludedPaths: []string{
"orgs/**/*",
},
ExcludedPaths: []string{
"**/_defaults.yaml",
},
},
Components: schema.Components{
Terraform: schema.Terraform{
BasePath: "components/terraform",
ApplyAutoApprove: false,
DeployRunInit: true,
InitRunReconfigure: true,
AutoGenerateBackendFile: true,
},
Helmfile: schema.Helmfile{
BasePath: "components/helmfile",
KubeconfigPath: "/dev/shm",
HelmAwsProfilePattern: "{namespace}-{tenant}-gbl-{stage}-helm",
ClusterNamePattern: "{namespace}-{tenant}-{environment}-{stage}-eks-cluster",
UseEKS: true,
},
},
Workflows: schema.Workflows{
BasePath: "stacks/workflows",
},
Logs: schema.Logs{
File: "/dev/stdout",
Level: "Info",
},
Schemas: schema.Schemas{
JsonSchema: schema.JsonSchema{
BasePath: "stacks/schemas/jsonschema",
},
Opa: schema.Opa{
BasePath: "stacks/schemas/opa",
},
},
Initialized: true,
}
)

// InitCliConfig finds and merges CLI configurations in the following order: system dir, home dir, current dir, ENV vars, command-line arguments
// https://dev.to/techschoolguru/load-config-from-file-environment-variables-in-golang-with-viper-2j2d
Expand Down Expand Up @@ -126,7 +179,31 @@ func InitCliConfig(configAndStacksInfo schema.ConfigAndStacksInfo, processStacks
}

if !configFound {
return cliConfig, NotFound
// If `atmos.yaml` not found, use the default config
// Set `ATMOS_LOGS_LEVEL` ENV var to "Debug" to see the message about Atmos using the default CLI config
logsLevelEnvVar := os.Getenv("ATMOS_LOGS_LEVEL")
if logsLevelEnvVar == "Debug" {
u.PrintMessageInColor("'atmos.yaml' CLI config was not found in any of the searched paths: system dir, home dir, current dir, ENV vars.\n"+
"Refer to https://atmos.tools/cli/configuration for details on how to configure 'atmos.yaml'.\n"+
"Using the default CLI config:\n", color.New(color.FgCyan))

err = u.PrintAsYAML(defaultCliConfig)
if err != nil {
return cliConfig, err
}
fmt.Println()
}

j, err := json.Marshal(defaultCliConfig)
if err != nil {
return cliConfig, err
}

reader := bytes.NewReader(j)
err = v.MergeConfig(reader)
if err != nil {
return cliConfig, err
}
}

// https://gist.github.com/chazcheadle/45bf85b793dea2b71bd05ebaa3c28644
Expand Down
42 changes: 21 additions & 21 deletions pkg/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ type CliConfiguration struct {
BasePath string `yaml:"base_path" json:"base_path" mapstructure:"base_path"`
Components Components `yaml:"components" json:"components" mapstructure:"components"`
Stacks Stacks `yaml:"stacks" json:"stacks" mapstructure:"stacks"`
Workflows Workflows `yaml:"workflows" json:"workflows" mapstructure:"workflows"`
Logs Logs `yaml:"logs" json:"logs" mapstructure:"logs"`
Commands []Command `yaml:"commands" json:"commands" mapstructure:"commands"`
Integrations Integrations `yaml:"integrations" json:"integrations" mapstructure:"integrations"`
Schemas Schemas `yaml:"schemas" json:"schemas" mapstructure:"schemas"`
Workflows Workflows `yaml:"workflows,omitempty" json:"workflows,omitempty" mapstructure:"workflows"`
Logs Logs `yaml:"logs,omitempty" json:"logs,omitempty" mapstructure:"logs"`
Commands []Command `yaml:"commands,omitempty" json:"commands,omitempty" mapstructure:"commands"`
Integrations Integrations `yaml:"integrations,omitempty" json:"integrations,omitempty" mapstructure:"integrations"`
Schemas Schemas `yaml:"schemas,omitempty" json:"schemas,omitempty" mapstructure:"schemas"`
Initialized bool `yaml:"initialized" json:"initialized" mapstructure:"initialized"`
StacksBaseAbsolutePath string `yaml:"stacksBaseAbsolutePath" json:"stacksBaseAbsolutePath"`
IncludeStackAbsolutePaths []string `yaml:"includeStackAbsolutePaths" json:"includeStackAbsolutePaths"`
ExcludeStackAbsolutePaths []string `yaml:"excludeStackAbsolutePaths" json:"excludeStackAbsolutePaths"`
TerraformDirAbsolutePath string `yaml:"terraformDirAbsolutePath" json:"terraformDirAbsolutePath"`
HelmfileDirAbsolutePath string `yaml:"helmfileDirAbsolutePath" json:"helmfileDirAbsolutePath"`
StackConfigFilesRelativePaths []string `yaml:"stackConfigFilesRelativePaths" json:"stackConfigFilesRelativePaths"`
StackConfigFilesAbsolutePaths []string `yaml:"stackConfigFilesAbsolutePaths" json:"stackConfigFilesAbsolutePaths"`
StackType string `yaml:"stackType" json:"StackType"`
StacksBaseAbsolutePath string `yaml:"stacksBaseAbsolutePath,omitempty" json:"stacksBaseAbsolutePath,omitempty" mapstructure:"stacksBaseAbsolutePath"`
IncludeStackAbsolutePaths []string `yaml:"includeStackAbsolutePaths,omitempty" json:"includeStackAbsolutePaths,omitempty" mapstructure:"includeStackAbsolutePaths"`
ExcludeStackAbsolutePaths []string `yaml:"excludeStackAbsolutePaths,omitempty" json:"excludeStackAbsolutePaths,omitempty" mapstructure:"excludeStackAbsolutePaths"`
TerraformDirAbsolutePath string `yaml:"terraformDirAbsolutePath,omitempty" json:"terraformDirAbsolutePath,omitempty" mapstructure:"terraformDirAbsolutePath"`
HelmfileDirAbsolutePath string `yaml:"helmfileDirAbsolutePath,omitempty" json:"helmfileDirAbsolutePath,omitempty" mapstructure:"helmfileDirAbsolutePath"`
StackConfigFilesRelativePaths []string `yaml:"stackConfigFilesRelativePaths,omitempty" json:"stackConfigFilesRelativePaths,omitempty" mapstructure:"stackConfigFilesRelativePaths"`
StackConfigFilesAbsolutePaths []string `yaml:"stackConfigFilesAbsolutePaths,omitempty" json:"stackConfigFilesAbsolutePaths,omitempty" mapstructure:"stackConfigFilesAbsolutePaths"`
StackType string `yaml:"stackType,omitempty" json:"StackType,omitempty" mapstructure:"stackType"`
}

type Terraform struct {
Expand Down Expand Up @@ -329,26 +329,26 @@ type AtlantisConfigOutput struct {
// Validation schemas

type JsonSchema struct {
BasePath string `yaml:"base_path" json:"base_path" mapstructure:"base_path"`
BasePath string `yaml:"base_path,omitempty" json:"base_path,omitempty" mapstructure:"base_path"`
}

type Cue struct {
BasePath string `yaml:"base_path" json:"base_path" mapstructure:"base_path"`
BasePath string `yaml:"base_path,omitempty" json:"base_path,omitempty" mapstructure:"base_path"`
}

type Opa struct {
BasePath string `yaml:"base_path" json:"base_path" mapstructure:"base_path"`
BasePath string `yaml:"base_path,omitempty" json:"base_path,omitempty" mapstructure:"base_path"`
}

type AtmosSchema struct {
Manifest string `yaml:"manifest" json:"manifest" mapstructure:"manifest"`
Manifest string `yaml:"manifest,omitempty" json:"manifest,omitempty" mapstructure:"manifest"`
}

type Schemas struct {
JsonSchema JsonSchema `yaml:"jsonschema" json:"jsonschema" mapstructure:"jsonschema"`
Cue Cue `yaml:"cue" json:"cue" mapstructure:"cue"`
Opa Opa `yaml:"opa" json:"opa" mapstructure:"opa"`
Atmos AtmosSchema `yaml:"atmos" json:"atmos" mapstructure:"atmos"`
JsonSchema JsonSchema `yaml:"jsonschema,omitempty" json:"jsonschema,omitempty" mapstructure:"jsonschema"`
Cue Cue `yaml:"cue,omitempty" json:"cue,omitempty" mapstructure:"cue"`
Opa Opa `yaml:"opa,omitempty" json:"opa,omitempty" mapstructure:"opa"`
Atmos AtmosSchema `yaml:"atmos,omitempty" json:"atmos,omitempty" mapstructure:"atmos"`
}

type ValidationItem struct {
Expand Down
56 changes: 48 additions & 8 deletions website/docs/cli/commands/describe/describe-dependents.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,63 @@ Use this command to show a list of Atmos components in Atmos stacks that depend

## Description

In Atmos, you can define component dependencies by using the `settings.depends_on` section. The section used to define all the Atmos components (in
the same or different stacks) that the current component depends on.
In Atmos, you can define component dependencies by using the `settings.depends_on` section. The section used to define
all the Atmos components (in the same or different stacks) that the current component depends on.

The `settings.depends_on` section is a map of objects. The map keys are just the descriptions of dependencies and can be strings or numbers.
Provide meaningful descriptions so that people can understand what the dependencies are about.

Each object in the `settings.depends_on` section has the following schema:

- `component` (required) - an Atmos component that the current component depends on
- `namespace` (optional) - the `namespace` where the Atmos component is provisioned
- `tenant` (optional) - the `tenant` where the Atmos component is provisioned
- `environment` (optional) - the `environment` where the Atmos component is provisioned
- `stage` (optional) - the `stage` where the Atmos component is provisioned
- `file` (optional) - a file on the local filesystem that the current component depends on
- `folder` (optional) - a folder on the local filesystem that the current component depends on
- `component` (required if `file` or `folder` is not specified) - an Atmos component that the current component depends on
- `namespace` (optional) - the `namespace` where the `component` is provisioned
- `tenant` (optional) - the `tenant` where the `component` is provisioned
- `environment` (optional) - the `environment` where the `component` is provisioned
- `stage` (optional) - the `stage` where the `component` is provisioned

<br/>

The `component` attribute is required. The rest are the context variables and are used to define Atmos stacks other than the current stack.
One of `component`, `file` or `folder` is required.

Dependencies on external files (not in the component's folder) are defined using the `file` attribute. For example:

```yaml title="stacks/catalog/terraform/top-level-component3.yaml"
components:
terraform:
top-level-component3:
metadata:
component: "top-level-component1"
settings:
depends_on:
1:
file: "examples/tests/components/terraform/mixins/introspection.mixin.tf"
```

In the configuration above, we specify that the Atmos component `top-level-component3` depends on the file
`examples/tests/components/terraform/mixins/introspection.mixin.tf` (which is not in the component's folder).

Dependencies on external folders are defined using the `folder` attribute. For example:

```yaml title="stacks/catalog/terraform/top-level-component3.yaml"
components:
terraform:
top-level-component3:
metadata:
component: "top-level-component1"
settings:
depends_on:
1:
file: "examples/tests/components/terraform/mixins/introspection.mixin.tf"
2:
folder: "examples/tests/components/helmfile/infra/infra-server"
```

In the configuration above, we specify that the Atmos component `top-level-component3` depends on the folder
`examples/tests/components/helmfile/infra/infra-server`.

If `component` is specified, the rest of the attributes are the context variables and are used to define Atmos stacks other than the current stack.
For example, you can specify:

- `namespace` if the `component` is from a different Organization
Expand Down
47 changes: 47 additions & 0 deletions website/docs/cli/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,53 @@ names/paths (double-star/globstar `**` is supported as well)

<br/>

If `atmos.yaml` is not found in any of the searched locations, Atmos will use the default CLI configuration:

```yaml
base_path: "."
components:
terraform:
base_path: components/terraform
apply_auto_approve: false
deploy_run_init: true
init_run_reconfigure: true
auto_generate_backend_file: true
helmfile:
base_path: components/helmfile
use_eks: true
kubeconfig_path: /dev/shm
helm_aws_profile_pattern: '{namespace}-{tenant}-gbl-{stage}-helm'
cluster_name_pattern: '{namespace}-{tenant}-{environment}-{stage}-eks-cluster'
stacks:
base_path: stacks
included_paths:
- orgs/**/*
excluded_paths:
- '**/_defaults.yaml'
name_pattern: '{tenant}-{environment}-{stage}'
workflows:
base_path: stacks/workflows
logs:
file: /dev/stdout
level: Info
schemas:
jsonschema:
base_path: stacks/schemas/jsonschema
opa:
base_path: stacks/schemas/opa
```

<br/>

If Atmos does not find an `atmos.yaml` file and the default CLI config is used, and if you set the ENV variable `ATMOS_LOGS_LEVEL` to `Debug`
(e.g. `export ATMOS_LOGS_LEVEL=Debug`) before executing Atmos commands, you'll see the following message:

<br/>

![`atmos` CLI command mode 1](/img/cli/atmos.yaml/atmos-default-cli-config-message.png)

<br/>

What follows are all the sections of the `atmos.yaml` configuration file.

## Base Path
Expand Down

0 comments on commit 6c5cb02

Please sign in to comment.