#atmos (2022-07)
2022-07-01
v1.4.23 what Fix atmos terraform clean command Add CLI docs for atmos terraform clean command why atmos terraform clean command, when deleting the folder that TF_DATA_DIR points to, was using the relative path and not the component’s folder. Use absolute path atmos terraform clean is not a standard terraform command, so we need to describe it separately in the docs
what Fix atmos terraform clean command Add CLI docs for atmos terraform clean command why atmos terraform clean command, when deleting the folder that TF_DATA_DIR points to, was using the relati…
v1.4.23 what Fix atmos terraform clean command Add CLI docs for atmos terraform clean command why atmos terraform clean command, when deleting the folder that TF_DATA_DIR points to, was using the relative path and not the component’s folder. Use absolute path atmos terraform clean is not a standard terraform command, so we need to describe it separately in the docs What’s Changed Add TF_CLI_ARGS_console env var to shell subcommand by <a class=”user-mention notranslate” data-hovercard-type=”user”…
2022-07-05
v1.4.24 what Refactor stacks to the latest pattern Refactor tests Update everything to the latest versions Update component processor why Refactor stacks in the examples to use orgs->tenants->accounts->regions + catalog and mixins - this is our latest pattern of stacks configuration Refactor tests to reflect the new structure of the stacks folder Use the latest versions of atmos, geodesic and terraform in the examples Small fixes in the component processor
what Refactor stacks to the latest pattern Refactor tests Update everything to the latest versions Update component processor why Refactor stacks in the examples to use orgs->tenants->accounts->…
v1.4.24 what Refactor stacks to the latest pattern Refactor tests Update everything to the latest versions Update component processor why Refactor stacks in the examples to use orgs->tenants->accounts->regions + catalog and mixins - this is our latest pattern of stacks configuration Refactor tests to reflect the new structure of the stacks folder Use the latest versions of atmos, geodesic and terraform in the examples Small fixes in the component processor
2022-07-07
Kind of new to atmos and I have a question about the backend config file generation.
We’ve noticed that each time we run atmos terraform …
the backend.tf.json
file is generated with a different order of the keys within the s3 element.
From what I can tell it’s due to go iterating over struct keys using a random order.
I’ve created a rough test that shows this. If you run it multiple times you can see the ‘actual’ output change the ordering of the keys.
I’ve started to look at a fix but I thought I’ll ask here first as it may be something you guys can do quickly; or you can tell me a better way to deal with it.
what
• adding a (rough) test that demonstrates randomly changing terraform.backend.s3
key order in backend config file generation
why
• We’ve noticed that backend.tf.json constantly changes the order of the keys within the s3 element. From what I can tell it’s due to go iterating over struct keys using a random order.
references
• N/A
test output
❯ make testacc TEST=github.com/cloudposse/atmos/pkg/utils
go get
go test github.com/cloudposse/atmos/pkg/utils -v -timeout 2m
=== RUN TestBackendConfig
json_utils_test.go:65:
Error Trace: /Users/joe/git-proj/cloudposse/atmos/pkg/utils/json_utils_test.go:65
Error: Not equal:
expected: "{\"terraform\":{\"backend\":{\"s3\":{\"encrypt\":true,\"key\":\"terraform.tfstate\",\"region\":\"us-east-1\",\"role_arn\":null,\"workspace_key_prefix\":\"app\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"sts-gbl-tfstate-backend\",\"dynamodb_table\":\"sts-gbl-tfstate-backend-lock\"}}}}"
actual : "{\"terraform\":{\"backend\":{\"s3\":{\"dynamodb_table\":\"cp-ue2-root-tfstate-lock\",\"profile\":\"cp-gb2-root-tfstate\",\"role_arn\":null,\"workspace_key_prefix\":\"test-test-component\",\"region\":\"us-east-2\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"cp-ue2-root-tfstate\",\"encrypt\":true,\"key\":\"terraform.tfstate\"}}}}"
Diff:
--- Expected
+++ Actual
@@ -1 +1 @@
-{"terraform":{"backend":{"s3":{"encrypt":true,"key":"terraform.tfstate","region":"us-east-1","role_arn":null,"workspace_key_prefix":"app","acl":"bucket-owner-full-control","bucket":"sts-gbl-tfstate-backend","dynamodb_table":"sts-gbl-tfstate-backend-lock"}}}}
+{"terraform":{"backend":{"s3":{"dynamodb_table":"cp-ue2-root-tfstate-lock","profile":"cp-gb2-root-tfstate","role_arn":null,"workspace_key_prefix":"test-test-component","region":"us-east-2","acl":"bucket-owner-full-control","bucket":"cp-ue2-root-tfstate","encrypt":true,"key":"terraform.tfstate"}}}}
Test: TestBackendConfig
--- FAIL: TestBackendConfig (0.06s)
FAIL
FAIL github.com/cloudposse/atmos/pkg/utils 0.404s
FAIL
make: *** [testacc] Error 1
❯ make testacc TEST=github.com/cloudposse/atmos/pkg/utils
go get
go test github.com/cloudposse/atmos/pkg/utils -v -timeout 2m
=== RUN TestBackendConfig
json_utils_test.go:65:
Error Trace: /Users/joe/git-proj/cloudposse/atmos/pkg/utils/json_utils_test.go:65
Error: Not equal:
expected: "{\"terraform\":{\"backend\":{\"s3\":{\"encrypt\":true,\"key\":\"terraform.tfstate\",\"region\":\"us-east-1\",\"role_arn\":null,\"workspace_key_prefix\":\"app\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"sts-gbl-tfstate-backend\",\"dynamodb_table\":\"sts-gbl-tfstate-backend-lock\"}}}}"
actual : "{\"terraform\":{\"backend\":{\"s3\":{\"workspace_key_prefix\":\"test-test-component\",\"encrypt\":true,\"role_arn\":null,\"dynamodb_table\":\"cp-ue2-root-tfstate-lock\",\"key\":\"terraform.tfstate\",\"profile\":\"cp-gb2-root-tfstate\",\"region\":\"us-east-2\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"cp-ue2-root-tfstate\"}}}}"
Diff:
--- Expected
+++ Actual
@@ -1 +1 @@
-{"terraform":{"backend":{"s3":{"encrypt":true,"key":"terraform.tfstate","region":"us-east-1","role_arn":null,"workspace_key_prefix":"app","acl":"bucket-owner-full-control","bucket":"sts-gbl-tfstate-backend","dynamodb_table":"sts-gbl-tfstate-backend-lock"}}}}
+{"terraform":{"backend":{"s3":{"workspace_key_prefix":"test-test-component","encrypt":true,"role_arn":null,"dynamodb_table":"cp-ue2-root-tfstate-lock","key":"terraform.tfstate","profile":"cp-gb2-root-tfstate","region":"us-east-2","acl":"bucket-owner-full-control","bucket":"cp-ue2-root-tfstate"}}}}
Test: TestBackendConfig
--- FAIL: TestBackendConfig (0.04s)
FAIL
FAIL github.com/cloudposse/atmos/pkg/utils 0.241s
FAIL
make: *** [testacc] Error 1
interesting. we don’t commit the generated backend anymore so never even noticed this.
if the backend.tf.json is not committed, then does the order of the keys even matter ?
what
• adding a (rough) test that demonstrates randomly changing terraform.backend.s3
key order in backend config file generation
why
• We’ve noticed that backend.tf.json constantly changes the order of the keys within the s3 element. From what I can tell it’s due to go iterating over struct keys using a random order.
references
• N/A
test output
❯ make testacc TEST=github.com/cloudposse/atmos/pkg/utils
go get
go test github.com/cloudposse/atmos/pkg/utils -v -timeout 2m
=== RUN TestBackendConfig
json_utils_test.go:65:
Error Trace: /Users/joe/git-proj/cloudposse/atmos/pkg/utils/json_utils_test.go:65
Error: Not equal:
expected: "{\"terraform\":{\"backend\":{\"s3\":{\"encrypt\":true,\"key\":\"terraform.tfstate\",\"region\":\"us-east-1\",\"role_arn\":null,\"workspace_key_prefix\":\"app\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"sts-gbl-tfstate-backend\",\"dynamodb_table\":\"sts-gbl-tfstate-backend-lock\"}}}}"
actual : "{\"terraform\":{\"backend\":{\"s3\":{\"dynamodb_table\":\"cp-ue2-root-tfstate-lock\",\"profile\":\"cp-gb2-root-tfstate\",\"role_arn\":null,\"workspace_key_prefix\":\"test-test-component\",\"region\":\"us-east-2\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"cp-ue2-root-tfstate\",\"encrypt\":true,\"key\":\"terraform.tfstate\"}}}}"
Diff:
--- Expected
+++ Actual
@@ -1 +1 @@
-{"terraform":{"backend":{"s3":{"encrypt":true,"key":"terraform.tfstate","region":"us-east-1","role_arn":null,"workspace_key_prefix":"app","acl":"bucket-owner-full-control","bucket":"sts-gbl-tfstate-backend","dynamodb_table":"sts-gbl-tfstate-backend-lock"}}}}
+{"terraform":{"backend":{"s3":{"dynamodb_table":"cp-ue2-root-tfstate-lock","profile":"cp-gb2-root-tfstate","role_arn":null,"workspace_key_prefix":"test-test-component","region":"us-east-2","acl":"bucket-owner-full-control","bucket":"cp-ue2-root-tfstate","encrypt":true,"key":"terraform.tfstate"}}}}
Test: TestBackendConfig
--- FAIL: TestBackendConfig (0.06s)
FAIL
FAIL github.com/cloudposse/atmos/pkg/utils 0.404s
FAIL
make: *** [testacc] Error 1
❯ make testacc TEST=github.com/cloudposse/atmos/pkg/utils
go get
go test github.com/cloudposse/atmos/pkg/utils -v -timeout 2m
=== RUN TestBackendConfig
json_utils_test.go:65:
Error Trace: /Users/joe/git-proj/cloudposse/atmos/pkg/utils/json_utils_test.go:65
Error: Not equal:
expected: "{\"terraform\":{\"backend\":{\"s3\":{\"encrypt\":true,\"key\":\"terraform.tfstate\",\"region\":\"us-east-1\",\"role_arn\":null,\"workspace_key_prefix\":\"app\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"sts-gbl-tfstate-backend\",\"dynamodb_table\":\"sts-gbl-tfstate-backend-lock\"}}}}"
actual : "{\"terraform\":{\"backend\":{\"s3\":{\"workspace_key_prefix\":\"test-test-component\",\"encrypt\":true,\"role_arn\":null,\"dynamodb_table\":\"cp-ue2-root-tfstate-lock\",\"key\":\"terraform.tfstate\",\"profile\":\"cp-gb2-root-tfstate\",\"region\":\"us-east-2\",\"acl\":\"bucket-owner-full-control\",\"bucket\":\"cp-ue2-root-tfstate\"}}}}"
Diff:
--- Expected
+++ Actual
@@ -1 +1 @@
-{"terraform":{"backend":{"s3":{"encrypt":true,"key":"terraform.tfstate","region":"us-east-1","role_arn":null,"workspace_key_prefix":"app","acl":"bucket-owner-full-control","bucket":"sts-gbl-tfstate-backend","dynamodb_table":"sts-gbl-tfstate-backend-lock"}}}}
+{"terraform":{"backend":{"s3":{"workspace_key_prefix":"test-test-component","encrypt":true,"role_arn":null,"dynamodb_table":"cp-ue2-root-tfstate-lock","key":"terraform.tfstate","profile":"cp-gb2-root-tfstate","region":"us-east-2","acl":"bucket-owner-full-control","bucket":"cp-ue2-root-tfstate"}}}}
Test: TestBackendConfig
--- FAIL: TestBackendConfig (0.04s)
FAIL
FAIL github.com/cloudposse/atmos/pkg/utils 0.241s
FAIL
make: *** [testacc] Error 1
Nope it wouldn’t matter in that case
In this particular project I’ve started working with an existing code base and these files are committed.
Still learning the full extent of atmos but I guess if this file is generated based on the auto_generate_backend_file
being true it really shouldn’t need to be in git
The encoding/json library apparently offers no way to decode data into a interface{} without scrambling the order of the keys in objects. Generally this isn’t a correctness issue (per the spec the order isn’t important), but it is super annoying to humans who may look at JSON data before and after it’s been processed by a go program.
The most popular yaml library solves the problem like this: https://godoc.org/gopkg.in/yaml.v2#MapSlice
someone has created a specific golib for it https://github.com/ake-persson/mapslice-json
Go MapSlice for ordered marshal/ unmarshal of maps in JSON
and here’s one example without the above lib to dump deterministic json https://go.dev/play/p/yZ5DxZLIMXC
Yep I’ve looked at those when starting to try to fix it. It’d be a fair assumption to gitignore this file if atmos is generating it.
I don’t know if it’s worth fixing or not.
if you want to run terraform without atmos, then you would need to commit the file i suppose
good point
you could also hard code the backend and setup atmos to not generate the backend
yeah true - in this case it never changes and is really just for convenience
Thanks, we will discuss further
all options
- we make backend generation deterministic in atmos
- ignore backend.tf.json from infra repo and allow atmos to do its thing (provided no one needs to run terraform without atmos)
- hard code the backend for all terraform modules and disable atmos from generating the backend
cc: @Andriy Knysh (Cloud Posse)
It’s Golang issue, it iterates a collection in random order, nothing can be done about it, at least not with something simple
If you have the backend files already, disable generation in atmos
Thanks - that seems like the best option right now
Although we can sort the keys before writing to a file, that would be a simple solution :)
I wasn’t sure if the iteration within json.Marshal is doing it
like will it respect order if you presort the value being passed in?
Yes, Marshal does it
So it’s not easy to fix
Ah cool - I think I saw people suggesting making an alternative Marshal function. Can’t seem to find the URL right now though.
Well maybe one for the backlog but probably it won’t affect many people anyway
I added a summary comment to your PR so we can discuss it further in the future if the above workarounds don’t work for you
i knew about the issue for a long time, did not get to fix it since it really dud not affect anything but was just annoying
@RB thanks for that. That’s really helpful.
@Andriy Knysh (Cloud Posse) ah that makes sense there’s a lot I don’t know about go so this was a surprise!
and yes it is an annoying “feature”
everything with golang and json is annoying
lol that doesn’t motivate me to learn golang better.. will stick with python :D
googling a little bit more and i see this library https://github.com/tidwall/gjson
Get JSON values quickly - JSON parser for Go
10.6k stars
@pretty:{"sortKeys":true}
Looks great @RB
So true @Andriy Knysh (Cloud Posse)
2022-07-08
2022-07-12
running into this error trying to deploy the eks module (https://github.com/cloudposse/terraform-aws-eks-cluster).
Get "<https://REDACTED.us-east-1.eks.amazonaws.com/api/v1/namespaces/kube-system/configmaps/aws-auth>": getting credentials: exec plugin is configured to use API version client.authentication.k8s.io/v1beta1, plugin returned version client.authentication.k8s.io/v1alpha1
my cluster version is 1.22
Terraform module for provisioning an EKS cluster
do i need to update the aws cli version in geodesic / atmos?
2022-07-13
@Andriy Knysh (Cloud Posse), I know this is not about the atmos
strictly, but I have been able to build Docker image with atmos
based on geodesic
and run it.
I don’t have problem with AWS session, but I have a problem with Terraform needing to authenticate against private GitLab Repo to clone underlying TF module and I need to think how to feed GitLab Personal Access Token safely to all git
operations done by that container. I am targeting public Spacelift workers 1st, than if that works great I will try private EC2 workers.
for private workers, we do it in user-data
%{ if github_netrc_enabled }
export SPACELIFT_WORKER_EXTRA_MOUNTS=/root/.netrc:/conf/.netrc
export GITHUB_TOKEN=$(aws ssm get-parameters --region=${region} --name ${github_netrc_ssm_path_token} --with-decryption --query "Parameters[0].Value" --output text)
export GITHUB_USER=$(aws ssm get-parameters --region=${region} --name ${github_netrc_ssm_path_user} --with-decryption --query "Parameters[0].Value" --output text)
echo "Creating .netrc" | tee -a /var/log/spacelift/info.log
NETRC="/root/.netrc"
printf "machine github.com\n" > "$NETRC"
printf "login %s\n" "$GITHUB_USER" >> "$NETRC"
printf "password %s\n" "$GITHUB_TOKEN" >> "$NETRC"
echo "Created .netrc" | tee -a /var/log/spacelift/info.log
%{ endif }
same can be done for public workers (not in user-data, but in Spacelift hooks), just the IAM role that you give the workers needs the permissions to read from SSM (also you need to consider security implications of that if any)
Curious if people keep these in Spacelift contexts & environment typically …
2022-07-17
v1.4.25 what Update component deps calculation Add tests to check this why When a component defines inherited YAML base components in metadata.inherits and the stack imported the YAML file(s) where the inherited YAML components are defined, add the imported YAML files to the deps labels Before this fix, only the base terraform component was used in the deps calculation. All imported YAML files where the base YAML components were defined were not included in the deps labels (and the YAML files were…
what Update component deps calculation Add tests to check this why When a component defines inherited YAML base components in metadata.inherits and the stack imported the YAML file(s) where the …
We’re building out a new project using atmos and I have hit the following error a few times:
Executing command:
/usr/bin/terraform plan -var-file root-account-map.terraform.tfvars.json -out root-account-map.planfile
Acquiring state lock. This may take a few moments...
╷
│ Error: stack name pattern must be provided in 'stacks.name_pattern' CLI config or 'ATMOS_STACKS_NAME_PATTERN' ENV variable
│
│ with module.accounts.data.utils_component_config.config,
│ on .terraform/modules/accounts/modules/remote-state/main.tf line 1, in data "utils_component_config" "config":
│ 1: data "utils_component_config" "config" {
│
╵
Releasing state lock. This may take a few moments...
exit status 1
The first time, it was within tfstate-backend
, and was resolved by not setting the access_roles
variable. We’re in the middle of the cold start phase with this project.
The one above is happening with account-map
and I’m not yet sure why!
I know I haven’t provided enough info but if I could get some help with debugging the cause, that would be much appreciated!
Here is the output of atmos describe config
:
{
"base_path": "",
"Components": {
"Terraform": {
"base_path": "components",
"apply_auto_approve": false,
"deploy_run_init": true,
"init_run_reconfigure": true,
"auto_generate_backend_file": true
},
"Helmfile": {
"base_path": "components/helmfile",
"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": [
"org/**/*"
],
"excluded_paths": [
"catalog/**/*",
"**/_defaults.yaml"
],
"name_pattern": "{stage}"
},
"Workflows": {
"base_path": "workflows"
},
"Logs": {
"verbose": true,
"colors": true
},
"Commands": null,
"Initialized": true
}
/cc: @Matt Gowie
Error: stack name pattern must be provided in ‘stacks.name_pattern’ CLI config or ‘ATMOS_STACKS_NAME_PATTERN’ ENV variable
can you share what the:
stacks:
name_pattern:
...
is in your atmos.yaml
?
That would be useful
It’s just:
name_pattern: "{stage}"
We did try alternate pattterns with dashes in case there’s an assumption somewhere. We also changed descriptor_formats to match the above.
remote-state
is processed by the utils
provider, which uses atmos
code. When TF executes the provider, it executes it from the component (terraform) folder (e.g. components/vpc
)
so the provider does not see atmos.yaml
in the repo root (although atmos
CLI does since you execute atmos
commands from the repo’s root)
there are a few patterns to fix it:
- Place
atmos.yaml
into/usr/local/etc/atmos/atmos.yaml
https://github.com/cloudposse/atmos/blob/master/examples/complete/rootfs/usr/local/etc/atmos/atmos.yaml -atmos
code knows this location, so all processes can see it and find the CLI config - that’s what we do, and it works both locally and in Docker container
# 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: ""
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_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:
verbose: false
colors: true
- Use
ATMOS_CLI_CONFIG_PATH
ENV var to pointatmos
andutils
provider to the location ofatmos.yaml
https://github.com/cloudposse/atmos/pull/168
what
• Add ATMOS_CLI_CONFIG_PATH
ENV var
• Detect more YAML stack misconfigurations
• Add functionality to define atmos
custom CLI commands
why
• ATMOS_CLI_CONFIG_PATH
ENV var allows specifying the location of atmos.yaml
CLI config file. This is useful for CI/CD environments (e.g. Spacelift) where an infrastructure repository gets loaded into a custom path and atmos.yaml
is not in the locations where atmos
expects to find it (no need to copy atmos.yaml
into /usr/local/etc/atmos/atmos.yaml
)
• Detect more YAML stack misconfigurations, e.g. when the same tenant/environment/stage is defined in more than one top-level YAML stack config file (directly or via imports).
For example, if the same `var.tenant = tenant1` is specified for `tenant1-ue2-dev` and `tenant2-ue2-dev` stacks, the
command `atmos describe component test/test-component-override -s tenant1-ue2-dev` will throw this error
Searching for stack config where the component 'test/test-component-override' is defined
Found config for the component 'test/test-component-override' for the stack 'tenant1-ue2-dev' in the file 'tenant1/ue2/dev'
Found config for the component 'test/test-component-override' for the stack 'tenant1-ue2-dev' in the file 'tenant2/ue2/dev'
Found duplicate config for the component 'test/test-component-override' for the stack 'tenant1-ue2-dev' in the files: tenant1/ue2/dev, tenant2/ue2/dev.
Check that all context variables in the stack name pattern '{tenant}-{environment}-{stage}' are correctly defined in the files and not duplicated.
Check that imports are valid.
• Allow extending atmos
with custom commands. Custom commands can be defined in atmos.yaml
CLI config file. Custom commands support subcommands at any level (e.g. atmos my-command subcommand1 suncommand2 argument1 argument2 flag1 flag2
)
# 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: play
description: This command plays games
steps:
- echo Playing...
# subcommands
commands:
- name: hello
description: This command says Hello world
steps:
- echo Saying Hello world...
- echo Hello world
- name: ping
description: This command plays ping-pong
steps:
- echo Playing ping-pong...
- echo pong
Custom commands support Go templates and ENV vars in commands steps, and Go templates in ENV vars values, as well as allow specifying an external executable to be called to get the value for an ENV var.
They are automatically added to atmos help
:
Available Commands:
aws Execute 'aws' commands
completion Generate the autocompletion script for the specified shell
describe Execute 'describe' commands
helmfile Execute 'helmfile' commands
help Help about any command
play This command plays games
terraform Execute 'terraform' commands
tf Execute terraform commands
validate Execute 'validate' commands
vendor Execute 'vendor' commands
version Print the CLI version
workflow Execute a workflow
Custom commands test
atmos play ping
Executing command:
/bin/echo Playing ping-pong...
Playing ping-pong...
Executing command:
/bin/echo pong
pong
atmos play hello
Executing command:
/bin/echo Saying Hello world...
Saying Hello world...
Executing command:
/bin/echo Hello world
Hello world
atmos terraform provision test/test-component-override -s tenant1-ue2-dev
Using ENV vars:
ATMOS_COMPONENT=test/test-component-override
ATMOS_STACK=tenant1-ue2-dev
Executing command:
/usr/local/bin/atmos terraform plan test/test-component-override -s tenant1-ue2-dev
....
Executing command:
/usr/local/bin/atmos terraform apply test/test-component-override -s tenant1-ue2-dev
atmos tf plan test/test-component-override -s tenant1-ue2-dev
# This command gets the value for the ENV var by calling an external executable
Executing command:
/bin/echo ENV_VAR_2_value
Executing command:
/usr/local/bin/atmos terraform plan test/test-component-override -s tenant1-ue2-dev
references
• YAML interface inspired by ahoy-cli and choria-io’s appbuilder. See discussion thread.
- Put
atmos.yaml
into the terraform component folder (e.g.components/vpc/atmos.yaml
) so the provider will see it. It’s duplication/multiplication of CLI config and not recommended at all
@Joe Niland
Good stuff @Andriy Knysh (Cloud Posse) — Thanks for the breakdown! I didn’t know it was possible that the Atmos CLI could find the config and the provider couldn’t. That is a bit confusing, but it’s a complicated system so I could see that being hard.
Two things:
- Would it make sense for the provider / atmos in general to search from the CWD of the component / root module (i.e.
/localhost/workspace/project/components/vpc
) to the system root directory (i.e./
) for theatmos.yaml
file? As in, first it would check thevpc
folder, thencomponents
, thenproject
, and then recursively down the stack? I feel like that would cover this use-case and would be more intuitive. - If #1 doesn’t make sense, I wonder if we can improve the logging or docs to call that out better? We’d be happy to do the work to contribute there… Joe and I will discuss what would’ve helped us debug that and submit a PR if something makes sense.
anything can be done in the code, but it should not add even more magic to the process
if we start searching in the current folder and then go up, how do we know where to stop and where is the root of the repo?
that’s why we put atmos.yaml
into the location where both atmos
and the utils
provider can find it, or we can use ATMOS_CLI_CONFIG_PATH
ENV var if your atmos.yaml
is in a diff location
@Andriy Knysh (Cloud Posse) thanks very much.
Putting the file in /usr/local/etc/atmos/atmos.yaml
has solved it locally.
On the Spacelift public runners we’ll set ATMOS_CLI_CONFIG_PATH
I agree with @Matt Gowie re improved logging. Could it not log when atmos.yaml is not found in the expected location(s)?
on Spacelift public runners, we use .spacelift/config.yml
version: "1"
stack_defaults:
before_init:
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-install-atmos"
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-configure-paths"
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-write-vars"
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-tf-workspace"
before_plan:
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-configure-paths"
before_apply:
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-configure-paths"
environment:
ATMOS_BASE_PATH: /mnt/workspace/source
in spacelift-install-atmos
#!/bin/bash
# Add -x for troubleshooting
set -ex -o pipefail
# This should match the Dockerfile
ATMOS_VERSION=1.4.25
# Using `registry.hub.docker.com/cloudposse/geodesic:latest-debian` as Spacelift runner image on public worker pool
apt-get update && apt-get install -y --allow-downgrades atmos="${ATMOS_VERSION}-*"
# If runner image is Alpine Linux
# apk add atmos@cloudposse~=${ATMOS_VERSION}
atmos version
# Copy the atmos CLI config file into the destination `/usr/local/etc/atmos` where all processes can see it
mkdir -p /usr/local/etc/atmos
cp /mnt/workspace/source/rootfs/usr/local/etc/atmos/atmos.yaml /usr/local/etc/atmos/atmos.yaml
cat /usr/local/etc/atmos/atmos.yaml
# Remove -x for security
set -e +x
but setting ATMOS_CLI_CONFIG_PATH=/mnt/workspace/source/rootfs/usr/local/etc/atmos/atmos.yaml
will work as well
version: "1"
stack_defaults:
before_init:
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-install-atmos"
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-configure-paths"
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-write-vars"
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-tf-workspace"
before_plan:
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-configure-paths"
before_apply:
- "/mnt/workspace/source/rootfs/usr/local/bin/spacelift-configure-paths"
environment:
ATMOS_CLI_CONFIG_PATH: /mnt/workspace/source/rootfs/usr/local/etc/atmos/atmos.yaml # instead of "cp /mnt/workspace/source/rootfs/usr/local/etc/atmos/atmos.yaml /usr/local/etc/atmos/atmos.yaml"
ATMOS_BASE_PATH: /mnt/workspace/source
@Andriy Knysh (Cloud Posse) sorry, I missed your latest reply.
Thanks again for that. We will try one of those approaches.
@Matt Gowie ^^
2022-07-18
I remember seeing example of multiple instances of same component in same stack possible with atmos
, but having hard time finding that example now.
Anyone knows how I can achieve this?
components:
terraform:
rds-defaults:
metadata:
type: abstract # we don't want to allow deploying this since it's just a collection of default values; like abstract base class in OOP which can't be instantiated
vars:
# default vars go here
rds-1:
metadata:
component: rds # point to Terraform component in components/terraform/rds
inherits:
- rds-defaults # inherit the default values
vars:
# specific vars go here, also can override any default vars like in OOP base/derived classes
rds-2:
metadata:
component: rds # point to Terraform component in components/terraform/rds
inherits:
- rds-defaults # inherit the default values
vars:
# specific vars go here, also can override any default vars like in OOP base/derived classes
atmos terraform plan rds-1 -s xxx
atmos terraform plan rds-2 -s xxx
atmos terraform plan rds-defaults -s xxx - will throw an error that an abstract component can't be provisioned
inherits:
can inherit from many YAML configs (e.g. you can have many mixins with default value and combine them via inheritance). Also, inheritance can go to any level: A inherits B inherits C inherits D etc.
got it! that’s it, exactly what I needed…
rds-defaults:
can be in a separate file (e.g. in catalog/rds/rds-default.yaml
and then you import it into each stack
import:
- catalog/rds/rds-defaults
Ok trying that now …
2022-07-19
2022-07-22
Guys.
I’ve been trying to setup new project with atmos
and after brew install atmos
I’m getting
docker: Error response from daemon: pull access denied for cloudposse/atmos, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
I tried to find that docker on docker hub but it seems to be nonexistent
we install it like this
ATMOS_VERSION=1.4.25
apt-get update && apt-get install -y --allow-downgrades atmos="${ATMOS_VERSION}-*"
# If runner image is Alpine Linux
# apk add atmos@cloudposse~=${ATMOS_VERSION}
or like this
# Install atmos
ARG ATMOS_VERSION
RUN wget <https://github.com/cloudposse/atmos/releases/download/v${ATMOS_VERSION}/atmos_${ATMOS_VERSION}_linux_amd64> && \
mv atmos_${ATMOS_VERSION}_linux_amd64 /usr/local/bin/atmos && \
chmod +x /usr/local/bin/atmos && \
atmos version
not sure why brew
is not working, @RB can you take a look?
➜ ~ brew uninstall atmos
Uninstalling /usr/local/Cellar/atmos/1.4.22... (6 files, 17.2MB)
➜ ~ brew install atmos
==> Downloading <https://ghcr.io/v2/homebrew/core/atmos/manifests/1.4.25>
######################################################################## 100.0%
==> Downloading <https://ghcr.io/v2/homebrew/core/atmos/blobs/sha256:e6a872c1467d>
==> Downloading from <https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh>
######################################################################## 100.0%
==> Pouring atmos--1.4.25.monterey.bottle.tar.gz
🍺 /usr/local/Cellar/atmos/1.4.25: 6 files, 17.2MB
==> Running `brew cleanup atmos`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
➜ ~ atmos version
1.4.25
works for me on intel osx 12.4
@Marcin Brański the issue youre seeing seems like a docker issue ? atmos doesn’t rely on docker so that’s an odd error
Hmmm, let me check what was installed with brew
:white_check_mark: I have uninstalled atmos
, removed usr/local/bin/atmos
file, reinstalled and now its correct binary.
This was the content of /usr/local/bin/atmos
before I deleted it:
https://gist.github.com/3h4x/883cf3c04d860ff6386b030d82be3d33
Some script wrapper to run geodesic
I worked with atmos
like a year ago or so, so it was probably some outdated file that got stuck and brew didn’t replace it on installing atmos upgrade.
Im not exactly sure.
if you have geodesic then you do not need to install atmos using brew
inside geodesic run apt update && apt install atmos -y
2022-07-28
is there a way to create atmos commands without having to fork the atmos repo and building a custom image?
# Custom CLI commands
commands:
it would be cool if i were able to add one to the atmos workspace
Hey folks — In the Component.yaml
spec I believe there should be a way to specify a non-remote, local file URI, but I haven’t been able to find an example of that. Do you folks know what that looks like? I remember digging for it weeks ago and went all the way down to the go-getter code level and couldn’t figure it out in a first pass. Wanted to ask here before I went down that rabbit hole again.
Matt, we don’t have that example, but we use go-getter which supports it
I’ll get to the computer and try to find it if you don’t find it before
I couldn’t find it in the go-getter repo or their tests. I’m just not sure what the YAML key is supposed to be or if it needs to be an absolute path or not. Go-getter’s docs are not the best.
Ah awesome — nice find @Joe Niland
But relative or some kind of convention where it searches from some location set by ENV would be good.. I think.
If relative paths not supported by go-getter, we could probably add that support in atmos
It seems to imply that it does but I can’t find a working example, or get it working myself. I always get the error “relative paths require a module with a pwd”
Running into this again — Implementing relative paths would be really nice. I’ll open an issue.
Created https://github.com/cloudposse/atmos/issues/185 for this one.
Describe the Feature
In a component.yaml
file, we should be able to supply relative paths to mixins
so that we can specify mixins as a local file instead of a remote file.
Use Case
The use-case is to use client specific mixin files. This enables mixin files to not need to be published to an open source repository and/or use token based GitHub URLs to fetch private repository files.
Describe Ideal Solution
Something like the following would be great:
apiVersion: atmos/v1
kind: ComponentVendorConfig
metadata:
name: s3-bucket-vendor-config
description: Source and mixins config for vendoring of 's3-bucket' component
spec:
source:
# ...
mixins:
- uri: file:///../../mixins/providers.mixin.tf
filename: providers.mixin.tf
Alternatives Considered
N/A
Additional Context
• See thread in Slack about using file:///
and how it doesn’t work for relative paths: https://sweetops.slack.com/archives/C031919U8A0/p1659032722469009
# mixins override files from 'source' with the same 'filename' (e.g. 'context.tf' will override 'context.tf' from the 'source')
# mixins are processed in the order they are declared in the list
mixins:
# <https://github.com/hashicorp/go-getter/issues/98>
# Mixins 'uri' supports the following protocols: local files (absolute and relative paths), Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP
# - uri: <https://raw.githubusercontent.com/cloudposse/terraform-null-label/0.25.0/exports/context.tf>
# This mixin `uri` is relative to the current `vpc-flow-logs-bucket` folder
- uri: ../../mixins/context.tf
filename: context.tf
- uri: <https://raw.githubusercontent.com/cloudposse/terraform-aws-components/{{.Version}}/modules/datadog-agent/introspection.mixin.tf>
version: 0.196.1
filename: introspection.mixin.tf
@Matt Gowie you don’t need to use file://…
to specify local files with absolute or relative path, just use uri: ../../mixins/context.tf
or uri: <absolute_path>/mixins/context.tf
Awesome — Thank you Andriy!