diff --git a/examples/quick-start-advanced/Dockerfile b/examples/quick-start-advanced/Dockerfile index 00595d096..23281b4e3 100644 --- a/examples/quick-start-advanced/Dockerfile +++ b/examples/quick-start-advanced/Dockerfile @@ -6,7 +6,7 @@ ARG GEODESIC_OS=debian # https://atmos.tools/ # https://github.com/cloudposse/atmos # https://github.com/cloudposse/atmos/releases -ARG ATMOS_VERSION=1.160.0 +ARG ATMOS_VERSION=1.160.4 # Terraform: https://github.com/hashicorp/terraform/releases ARG TF_VERSION=1.5.7 diff --git a/go.mod b/go.mod index 7da343a65..e3dc778a7 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssm v1.56.12 github.com/bmatcuk/doublestar/v4 v4.8.1 github.com/charmbracelet/bubbles v0.20.0 - github.com/charmbracelet/bubbletea v1.3.0 + github.com/charmbracelet/bubbletea v1.3.2 github.com/charmbracelet/glamour v0.8.0 github.com/charmbracelet/huh v0.6.0 github.com/charmbracelet/lipgloss v1.0.0 diff --git a/go.sum b/go.sum index 6b57251f2..c79e7fce4 100644 --- a/go.sum +++ b/go.sum @@ -865,8 +865,8 @@ github.com/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiw github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.3.0 h1:fPMyirm0u3Fou+flch7hlJN9krlnVURrkUVDwqXjoAc= -github.com/charmbracelet/bubbletea v1.3.0/go.mod h1:eTaHfqbIwvBhFQM/nlT1NsGc4kp8jhF8LfUK67XiTDM= +github.com/charmbracelet/bubbletea v1.3.2 h1:nc+gDivH0P8ii8CUcf3zCN/PiUz7LKbp3Iz+vYPScNY= +github.com/charmbracelet/bubbletea v1.3.2/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= diff --git a/internal/exec/terraform_outputs.go b/internal/exec/terraform_outputs.go index e1a4d0d32..1bcfc0439 100644 --- a/internal/exec/terraform_outputs.go +++ b/internal/exec/terraform_outputs.go @@ -130,9 +130,15 @@ func execTerraformOutput( if ok { envMap, ok2 := envSection.(map[string]any) if ok2 && len(envMap) > 0 { - l.Debug("Setting environment variables", "env", envMap) - m := u.MapOfInterfacesToMapOfStrings(envMap) - err = tf.SetEnv(m) + l.Debug("Setting environment variables from the component's 'env' section", "env", envMap) + // Get all environment variables from the parent process + environMap := u.EnvironToMap() + // Add/override the environment variables from the component's 'env' section + for k, v := range envMap { + environMap[k] = fmt.Sprintf("%v", v) + } + // Set the environment variables in the process that executes the `tfexec` functions + err = tf.SetEnv(environMap) if err != nil { return nil, err } diff --git a/pkg/utils/env_utils.go b/pkg/utils/env_utils.go index b7cdc9704..979be7ce4 100644 --- a/pkg/utils/env_utils.go +++ b/pkg/utils/env_utils.go @@ -1,8 +1,11 @@ package utils -import "fmt" +import ( + "fmt" + "os" +) -// ConvertEnvVars convert ENV vars from a map to a list of strings in the format ["key1=val1", "key2=val2", "key3=val3" ...] +// ConvertEnvVars converts ENV vars from a map to a list of strings in the format ["key1=val1", "key2=val2", "key3=val3" ...] func ConvertEnvVars(envVarsMap map[string]any) []string { res := []string{} @@ -13,3 +16,15 @@ func ConvertEnvVars(envVarsMap map[string]any) []string { } return res } + +// EnvironToMap converts all the environment variables in the environment into a map of strings +func EnvironToMap() map[string]string { + envMap := make(map[string]string) + for _, env := range os.Environ() { + pair := SplitStringAtFirstOccurrence(env, "=") + k := pair[0] + v := pair[1] + envMap[k] = v + } + return envMap +} diff --git a/pkg/utils/string_utils.go b/pkg/utils/string_utils.go index 26d7a6c1e..0dc513ce8 100644 --- a/pkg/utils/string_utils.go +++ b/pkg/utils/string_utils.go @@ -32,3 +32,12 @@ func SplitStringByDelimiter(str string, delimiter rune) ([]string, error) { return parts, nil } + +// SplitStringAtFirstOccurrence splits a string into two parts at the first occurrence of the separator +func SplitStringAtFirstOccurrence(s string, sep string) [2]string { + parts := strings.SplitN(s, sep, 2) + if len(parts) == 1 { + return [2]string{parts[0], ""} + } + return [2]string{parts[0], parts[1]} +} diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index 53a63d0b0..9c0e8702c 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -75,7 +75,6 @@ func TestCLITerraformClean(t *testing.T) { // Verify if state files have been deleted after clean verifyStateFilesDeleted(t, stateFiles) - } // runTerraformApply runs the terraform apply command for a given environment. @@ -148,6 +147,7 @@ func runCLITerraformCleanComponent(t *testing.T, binaryPath, environment string) t.Fatalf("Failed to run terraform clean: %v", stderr.String()) } } + func runCLITerraformClean(t *testing.T, binaryPath string) { cmd := exec.Command(binaryPath, "terraform", "clean") var stdout, stderr bytes.Buffer @@ -158,7 +158,6 @@ func runCLITerraformClean(t *testing.T, binaryPath string) { if err != nil { t.Fatalf("Failed to run terraform clean: %v", stderr.String()) } - } func runTerraformCleanCommand(t *testing.T, binaryPath string, args ...string) { diff --git a/tests/fixtures/components/terraform/mock/main.tf b/tests/fixtures/components/terraform/mock/main.tf index 236ce8896..25adaa06b 100644 --- a/tests/fixtures/components/terraform/mock/main.tf +++ b/tests/fixtures/components/terraform/mock/main.tf @@ -1,15 +1,15 @@ variable "foo" { - type = string + type = string default = "foo" } variable "bar" { - type = string + type = string default = "bar" } variable "baz" { - type = string + type = string default = "baz" } diff --git a/tests/fixtures/scenarios/atmos-functions/stacks/deploy/nonprod.yaml b/tests/fixtures/scenarios/atmos-functions/stacks/deploy/nonprod.yaml index f2dc2e644..09d62783b 100644 --- a/tests/fixtures/scenarios/atmos-functions/stacks/deploy/nonprod.yaml +++ b/tests/fixtures/scenarios/atmos-functions/stacks/deploy/nonprod.yaml @@ -28,4 +28,13 @@ components: foo: !terraform.output component-1 foo bar: !terraform.output component-2 bar baz: !terraform.output component-3 baz - + component-5: + metadata: + component: mock + vars: + foo: !env ATMOS_TEST_1 + component-6: + metadata: + component: mock + vars: + foo: !terraform.output component-5 foo diff --git a/tests/fixtures/scenarios/atmos-functions/stacks/workflows/terraform-output.yaml b/tests/fixtures/scenarios/atmos-functions/stacks/workflows/terraform-output.yaml index d19a7e50e..cb938d6b4 100644 --- a/tests/fixtures/scenarios/atmos-functions/stacks/workflows/terraform-output.yaml +++ b/tests/fixtures/scenarios/atmos-functions/stacks/workflows/terraform-output.yaml @@ -6,3 +6,5 @@ workflows: - command: terraform deploy component-2 -s nonprod - command: terraform deploy component-3 -s nonprod - command: terraform deploy component-4 -s nonprod + - command: terraform deploy component-5 -s nonprod + - command: terraform deploy component-6 -s nonprod diff --git a/tests/test-cases/atmos-functions.yaml b/tests/test-cases/atmos-functions.yaml index 2bf115a1f..ec28dfb87 100644 --- a/tests/test-cases/atmos-functions.yaml +++ b/tests/test-cases/atmos-functions.yaml @@ -45,3 +45,39 @@ tests: - "Fetching baz output from component-3 in nonprod" - "Fetching foo output from component-1 in nonprod" - "Fetching bar output from component-2 in nonprod" + + - name: "!env function test" + enabled: true + tty: false + description: "Ensure the !env function works." + workdir: "fixtures/scenarios/atmos-functions/" + command: "atmos" + args: + - "terraform" + - "deploy" + - "component-5" + - "-s" + - "nonprod" + env: + ATMOS_TEST_1: "test-env-and-terraform-output-functions" + expect: + exit_code: 0 + stdout: + - "test-env-and-terraform-output-functions" + + - name: "!terraform.output from component with !env function test" + enabled: true + tty: false + description: "Ensure !terraform.output from component with !env function works." + workdir: "fixtures/scenarios/atmos-functions/" + command: "atmos" + args: + - "describe" + - "component" + - "component-6" + - "-s" + - "nonprod" + expect: + exit_code: 0 + stdout: + - "test-env-and-terraform-output-functions" diff --git a/website/docs/core-concepts/stacks/yaml-functions/store.mdx b/website/docs/core-concepts/stacks/yaml-functions/store.mdx index f02eebf2a..c6e7f3957 100644 --- a/website/docs/core-concepts/stacks/yaml-functions/store.mdx +++ b/website/docs/core-concepts/stacks/yaml-functions/store.mdx @@ -19,18 +19,21 @@ The `!store` function can be called with either two or three parameters, and opt ```yaml # Get the `key` from the store of a `component` in the current stack - !store + !store # Get the `key` from the store of a `component` in a different stack - !store + !store # Get the `key` from the store of a `component` in a different stack, with a default value - !store | default + !store | default ``` ## Arguments
+
`store_name`
+
The name of the store to read from (as defined in the `atmos.yaml` file)
+
`component`
Atmos component name
@@ -60,11 +63,9 @@ components: my_lambda_component: vars: vpc_config: - # AWS SSM Parameter Store example - security_group_id: !store security-group/lambda id - security_group_id2: !store security-group/lambda2 {{ .stack }} id - security_group_id3: !store security-group/lambda3 {{ .atmos_stack }} id - config_value: !store config {{ printf "%s-%s" .vars.environment .vars.stage }} setting + security_group_id: !store prod/ssm security-group/lambda id + security_group_id2: !store prod/ssm security-group/lambda2 {{ .stack }} id + security_group_id3: !store prod/ssm security-group/lambda3 {{ .atmos_stack }} id ``` @@ -84,7 +85,7 @@ For example, you have a `tgw` component in a stack `plat-ue2-dev` that requires terraform: tgw: vars: - vpc_id: !store vpc plat-ue2-prod vpc_id + vpc_id: !store prod/ssm vpc plat-ue2-prod vpc_id ``` ### Reference the Current Stack Name @@ -93,8 +94,8 @@ Use the `.stack` (or `.atmos_stack`) template identifier to specify the same sta (for which the `!store` function is executed): ```yaml - !store {{ .stack }} - !store {{ .atmos_stack }} + !store {{ .stack }} + !store {{ .atmos_stack }} ``` For example, you have a `tgw` component that requires the `vpc_id` key from the store for the `vpc` component in the same stack: @@ -104,7 +105,7 @@ For example, you have a `tgw` component that requires the `vpc_id` key from the terraform: tgw: vars: - vpc_id: !store vpc {{ .stack }} vpc_id + vpc_id: !store prod/ssm vpc {{ .stack }} vpc_id ``` :::note Using the `.stack` or `.atmos_stack` template identifiers to specify the stack is the same as calling the @@ -118,11 +119,11 @@ Use the `printf` template function to construct stack names using static strings This is convenient when you want to override some identifiers in the stack name: ```yaml - !store {{ printf "%s-%s-%s" .vars.tenant .vars.environment .vars.stage }} + !store {{ printf "%s-%s-%s" .vars.tenant .vars.environment .vars.stage }} - !store {{ printf "plat-%s-prod" .vars.environment }} + !store {{ printf "plat-%s-prod" .vars.environment }} - !store {{ printf "%s-%s-%s" .settings.context.tenant .settings.context.region .settings.context.account }} + !store {{ printf "%s-%s-%s" .settings.context.tenant .settings.context.region .settings.context.account }} ```
@@ -142,7 +143,7 @@ tenant `net` (instead of `plat`): terraform: tgw: vars: - vpc_id: !store vpc {{ printf "net-%s-%s" .vars.environment .vars.stage }} vpc_id + vpc_id: !store prod/ssm vpc {{ printf "net-%s-%s" .vars.environment .vars.stage }} vpc_id ``` :::tip Important diff --git a/website/docs/integrations/atlantis.mdx b/website/docs/integrations/atlantis.mdx index bde07150b..de290ea0e 100644 --- a/website/docs/integrations/atlantis.mdx +++ b/website/docs/integrations/atlantis.mdx @@ -673,7 +673,7 @@ on: branches: [ main ] env: - ATMOS_VERSION: 1.160.0 + ATMOS_VERSION: 1.160.4 ATMOS_CLI_CONFIG_PATH: ./ jobs: