diff --git a/.goreleaser.yml b/.goreleaser.yml index da036881b..4e4903ce5 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -23,7 +23,7 @@ builds: binary: atmos ldflags: # Set `atmos` version to the GitHub release tag using Go `ldflags` - - '-s -w -X "github.com/cloudposse/atmos/cmd.Version={{.Version}}"' + - '-s -w -X "github.com/cloudposse/atmos/pkg/version.Version={{.Version}}"' archives: - format: binary diff --git a/Makefile b/Makefile index bc12271ec..f90a5e578 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ get: go get build: get - env $(if $(GOOS),GOOS=$(GOOS)) $(if $(GOARCH),GOARCH=$(GOARCH)) go build -o build/atmos -v -ldflags "-X 'github.com/cloudposse/atmos/cmd.Version=${VERSION}'" + env $(if $(GOOS),GOOS=$(GOOS)) $(if $(GOARCH),GOARCH=$(GOARCH)) go build -o build/atmos -v -ldflags "-X 'github.com/cloudposse/atmos/pkg/version.Version=${VERSION}'" version: build chmod +x ./build/atmos diff --git a/build.sh b/build.sh index 309ee4de9..cf740b48a 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ version="0.0.1" -go build -o build/atmos -v -ldflags "-X 'github.com/cloudposse/atmos/cmd.Version=$version'" +go build -o build/atmos -v -ldflags "-X 'github.com/cloudposse/atmos/version.Version=$version'" # https://www.digitalocean.com/community/tutorials/using-ldflags-to-set-version-information-for-go-applications # https://blog.kowalczyk.info/article/vEja/embedding-build-number-in-go-executable.html diff --git a/cmd/version.go b/cmd/version.go index f2005dd91..ba70483ea 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -10,10 +10,9 @@ import ( tuiUtils "github.com/cloudposse/atmos/internal/tui/utils" "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" + "github.com/cloudposse/atmos/pkg/version" ) -var Version = "0.0.1" - var versionCmd = &cobra.Command{ Use: "version", Short: "Print the CLI version", @@ -27,14 +26,14 @@ var versionCmd = &cobra.Command{ u.LogErrorAndExit(schema.CliConfiguration{}, err) } - u.PrintMessage(fmt.Sprintf("\U0001F47D Atmos %s on %s/%s", Version, runtime.GOOS, runtime.GOARCH)) + u.PrintMessage(fmt.Sprintf("\U0001F47D Atmos %s on %s/%s", version.Version, runtime.GOOS, runtime.GOARCH)) fmt.Println() // Check for the latest Atmos release on GitHub latestReleaseTag, err := u.GetLatestGitHubRepoRelease("cloudposse", "atmos") if err == nil && latestReleaseTag != "" { latestRelease := strings.TrimPrefix(latestReleaseTag, "v") - currentRelease := strings.TrimPrefix(Version, "v") + currentRelease := strings.TrimPrefix(version.Version, "v") if latestRelease != currentRelease { printMessageToUpgradeToAtmosLatestRelease(latestRelease) } diff --git a/internal/exec/help.go b/internal/exec/help.go index bd168c6fa..91b2b4e74 100644 --- a/internal/exec/help.go +++ b/internal/exec/help.go @@ -5,6 +5,7 @@ import ( "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" + "github.com/cloudposse/atmos/pkg/version" ) // processHelp processes help commands @@ -45,6 +46,11 @@ func processHelp(componentType string, command string) error { u.PrintMessage(" - double-dash '--' can be used to signify the end of the options for Atmos and the start of the additional " + "native arguments and flags for the 'terraform' commands. " + "For example: atmos terraform plan -s -- -refresh=false -lock=false") + + u.PrintMessage(fmt.Sprintf(" - '--append-user-agent' flag sets the TF_APPEND_USER_AGENT environment variable to customize the User-Agent string in Terraform provider requests. "+ + "Example: 'Atmos/%s (Cloud Posse; +https://atmos.tools)'. "+ + "If not specified, defaults to 'atmos %s'\n", version.Version, version.Version)) + } if componentType == "helmfile" { diff --git a/internal/exec/terraform.go b/internal/exec/terraform.go index 1d2314100..93b272e1a 100644 --- a/internal/exec/terraform.go +++ b/internal/exec/terraform.go @@ -255,6 +255,16 @@ func ExecuteTerraform(info schema.ConfigAndStacksInfo) error { // https://developer.hashicorp.com/terraform/cli/config/environment-variables#tf_in_automation info.ComponentEnvList = append(info.ComponentEnvList, "TF_IN_AUTOMATION=true") + // Set 'TF_APPEND_USER_AGENT' ENV var based on precedence + // Precedence: Environment Variable > atmos.yaml > Default + appendUserAgent := cliConfig.Components.Terraform.AppendUserAgent + if envUA, exists := os.LookupEnv("TF_APPEND_USER_AGENT"); exists && envUA != "" { + appendUserAgent = envUA + } + if appendUserAgent != "" { + info.ComponentEnvList = append(info.ComponentEnvList, fmt.Sprintf("TF_APPEND_USER_AGENT=%s", appendUserAgent)) + } + // Print ENV vars if they are found in the component's stack config if len(info.ComponentEnvList) > 0 { u.LogDebug(cliConfig, "\nUsing ENV vars:") diff --git a/internal/exec/utils.go b/internal/exec/utils.go index 0a4b662c2..bf82b2ea4 100644 --- a/internal/exec/utils.go +++ b/internal/exec/utils.go @@ -35,6 +35,7 @@ var ( cfg.DeployRunInitFlag, cfg.InitRunReconfigure, cfg.AutoGenerateBackendFileFlag, + cfg.AppendUserAgentFlag, cfg.FromPlanFlag, cfg.PlanFileFlag, cfg.HelpFlag1, @@ -668,6 +669,19 @@ func processArgsAndFlags(componentType string, inputArgsAndFlags []string) (sche info.TerraformDir = terraformDirFlagParts[1] } + if arg == cfg.AppendUserAgentFlag { + if len(inputArgsAndFlags) <= (i + 1) { + return info, fmt.Errorf("invalid flag: %s", arg) + } + info.AppendUserAgent = inputArgsAndFlags[i+1] + } else if strings.HasPrefix(arg+"=", cfg.AppendUserAgentFlag) { + var appendUserAgentFlagParts = strings.Split(arg, "=") + if len(appendUserAgentFlagParts) != 2 { + return info, fmt.Errorf("invalid flag: %s", arg) + } + info.AppendUserAgent = appendUserAgentFlagParts[1] + } + if arg == cfg.HelmfileCommandFlag { if len(inputArgsAndFlags) <= (i + 1) { return info, fmt.Errorf("invalid flag: %s", arg) diff --git a/pkg/config/config.go b/pkg/config/config.go index 63014bf4d..937efb639 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -16,6 +16,7 @@ import ( "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" + "github.com/cloudposse/atmos/pkg/version" ) var ( @@ -43,6 +44,7 @@ var ( DeployRunInit: true, InitRunReconfigure: true, AutoGenerateBackendFile: true, + AppendUserAgent: fmt.Sprintf("Atmos/%s (Cloud Posse; +https://atmos.tools)", version.Version), }, Helmfile: schema.Helmfile{ BasePath: "components/helmfile", @@ -105,6 +107,7 @@ func InitCliConfig(configAndStacksInfo schema.ConfigAndStacksInfo, processStacks // Default configuration values v.SetDefault("components.helmfile.use_eks", true) + v.SetDefault("components.terraform.append_user_agent", fmt.Sprintf("Atmos/%s (Cloud Posse; +https://atmos.tools)", version.Version)) // Process config in system folder configFilePath1 := "" @@ -243,6 +246,11 @@ func InitCliConfig(configAndStacksInfo schema.ConfigAndStacksInfo, processStacks cliConfig.BasePath = configAndStacksInfo.AtmosBasePath } + // After unmarshalling, ensure AppendUserAgent is set if still empty + if cliConfig.Components.Terraform.AppendUserAgent == "" { + cliConfig.Components.Terraform.AppendUserAgent = fmt.Sprintf("Atmos/%s (Cloud Posse; +https://atmos.tools)", version.Version) + } + // Check config err = checkConfig(cliConfig) if err != nil { diff --git a/pkg/config/const.go b/pkg/config/const.go index 720f53507..924d97249 100644 --- a/pkg/config/const.go +++ b/pkg/config/const.go @@ -27,6 +27,7 @@ const ( DeployRunInitFlag = "--deploy-run-init" AutoGenerateBackendFileFlag = "--auto-generate-backend-file" + AppendUserAgentFlag = "--append-user-agent" InitRunReconfigure = "--init-run-reconfigure" FromPlanFlag = "--from-plan" diff --git a/pkg/config/utils.go b/pkg/config/utils.go index bd916b2bb..d6a20036e 100644 --- a/pkg/config/utils.go +++ b/pkg/config/utils.go @@ -327,6 +327,12 @@ func processEnvVars(cliConfig *schema.CliConfiguration) error { cliConfig.Logs.Level = logsLevel } + tfAppendUserAgent := os.Getenv("ATMOS_COMPONENTS_TERRAFORM_APPEND_USER_AGENT") + if len(tfAppendUserAgent) > 0 { + u.LogTrace(*cliConfig, fmt.Sprintf("Found ENV var ATMOS_COMPONENTS_TERRAFORM_APPEND_USER_AGENT=%s", tfAppendUserAgent)) + cliConfig.Components.Terraform.AppendUserAgent = tfAppendUserAgent + } + listMergeStrategy := os.Getenv("ATMOS_SETTINGS_LIST_MERGE_STRATEGY") if len(listMergeStrategy) > 0 { u.LogTrace(*cliConfig, fmt.Sprintf("Found ENV var ATMOS_SETTINGS_LIST_MERGE_STRATEGY=%s", listMergeStrategy)) diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index d77dd9213..89e90b061 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -62,6 +62,7 @@ type TemplatesSettingsGomplate struct { type Terraform struct { BasePath string `yaml:"base_path" json:"base_path" mapstructure:"base_path"` ApplyAutoApprove bool `yaml:"apply_auto_approve" json:"apply_auto_approve" mapstructure:"apply_auto_approve"` + AppendUserAgent string `yaml:"append_user_agent" json:"append_user_agent" mapstructure:"append_user_agent"` DeployRunInit bool `yaml:"deploy_run_init" json:"deploy_run_init" mapstructure:"deploy_run_init"` InitRunReconfigure bool `yaml:"init_run_reconfigure" json:"init_run_reconfigure" mapstructure:"init_run_reconfigure"` AutoGenerateBackendFile bool `yaml:"auto_generate_backend_file" json:"auto_generate_backend_file" mapstructure:"auto_generate_backend_file"` @@ -132,6 +133,7 @@ type ArgsAndFlagsInfo struct { DeployRunInit string InitRunReconfigure string AutoGenerateBackendFile string + AppendUserAgent string UseTerraformPlan bool PlanFile string DryRun bool diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 000000000..7db05562c --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,5 @@ +package version + +// Version holds the current version of the Atmos CLI. +// It can be set dynamically during build time using ldflags. +var Version = "0.0.1" // Default version; will be overridden during build diff --git a/website/docs/cli/commands/terraform/usage.mdx b/website/docs/cli/commands/terraform/usage.mdx index 096909fee..94709912b 100644 --- a/website/docs/cli/commands/terraform/usage.mdx +++ b/website/docs/cli/commands/terraform/usage.mdx @@ -114,6 +114,8 @@ atmos terraform workspace test/test-component-override-3 -s tenant1-ue2-dev --re atmos terraform workspace test/test-component-override-3 -s tenant1-ue2-dev --redirect-stderr ./errors.txt atmos terraform plan test/test-component -s tenant1-ue2-dev -- -refresh=false -lock=false + +atmos terraform plan test/test-component -s tenant1-ue2-dev --append-user-agent "Acme/1.0 (Build 1234; arm64)" ``` ## Arguments @@ -124,12 +126,12 @@ atmos terraform plan test/test-component -s tenant1-ue2-dev -- -refresh=false -l ## Flags -| Flag | Description | Alias | Required | -| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | :---- | :------- | -| `--stack` | Atmos stack | `-s` | yes | -| `--dry-run` | Dry run | | no | -| `--redirect-stderr` | File descriptor to redirect `stderr` to.
Errors can be redirected to any file or any standard file descriptor
(including `/dev/null`) | | no | - +| Flag | Description | Alias | Required | +| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | :---- | :------- | +| `--stack` | Atmos stack | `-s` | yes | +| `--dry-run` | Dry run | | no | +| `--redirect-stderr` | File descriptor to redirect `stderr` to.
Errors can be redirected to any file or any standard file descriptor
(including `/dev/null`) | | no | +| `--append-user-agent` | Append a custom User-Agent to Terraform requests. Can also be set using the ATMOS_COMPONENTS_TERRAFORM_APPEND_USER_AGENT environment variable.| | no |
:::note diff --git a/website/docs/core-concepts/projects/configuration/terraform.mdx b/website/docs/core-concepts/projects/configuration/terraform.mdx index 4a01b45fc..4ddd0a849 100644 --- a/website/docs/core-concepts/projects/configuration/terraform.mdx +++ b/website/docs/core-concepts/projects/configuration/terraform.mdx @@ -38,6 +38,8 @@ components: 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 + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_APPEND_USER_AGENT' ENV var, or '--append-user-agent' command-line argument + append_user_agent: "Acme/1.0 (Build 1234; arm64)" ```
diff --git a/website/docs/quick-start/install-atmos.mdx b/website/docs/quick-start/install-atmos.mdx index 0b387dc24..25f16565c 100644 --- a/website/docs/quick-start/install-atmos.mdx +++ b/website/docs/quick-start/install-atmos.mdx @@ -242,7 +242,7 @@ Atmos has a few other ways to install, including using Go, asdf, mise, aqua, bui or run this and replace `$version` with the version that should be returned with `atmos version`. ```shell - go build -o build/atmos -v -ldflags "-X 'github.com/cloudposse/atmos/cmd.Version=$version'" + go build -o build/atmos -v -ldflags "-X 'github.com/cloudposse/atmos/pkg/version.Version=$version'" ``` diff --git a/website/src/components/Screengrabs/atmos-terraform--help.html b/website/src/components/Screengrabs/atmos-terraform--help.html index b5e69a3f6..84c3512f0 100644 --- a/website/src/components/Screengrabs/atmos-terraform--help.html +++ b/website/src/components/Screengrabs/atmos-terraform--help.html @@ -28,6 +28,7 @@ - 'atmos terraform generate varfile' command generates a varfile for an 'atmos' component in a stack - 'atmos terraform generate varfiles' command generates varfiles for all 'atmos' components in all stacks - 'atmos terraform shell' command configures an environment for an 'atmos' component in a stack and starts a new shell allowing executing all native terraform commands inside the shell without using atmos-specific arguments and flags + - `--append-user-agent` flag allows you to customize the User-Agent string appended to Terraform requests for enhanced observability and traceability. - double-dash '--' can be used to signify the end of the options for Atmos and the start of the additional native arguments and flags for the 'terraform' commands. For example: atmos terraform plan <component> -s <stack> -- -refresh=false -lock=false Usage: terraform [global options] <subcommand> [args]