From f01915876d4cfa675850732ad4e29a66d925844a Mon Sep 17 00:00:00 2001 From: Luke Kysow Date: Fri, 29 Jun 2018 20:20:55 +0200 Subject: [PATCH] Add env vars to run step --- .../docs/atlantis-yaml-reference.md | 14 ++++++-- .../events/runtime/apply_step_runner_test.go | 2 +- .../events/runtime/init_step_runner_test.go | 2 +- .../events/runtime/plan_step_runner_test.go | 2 +- server/events/runtime/run_step_runner.go | 15 ++++++++ server/events/runtime/run_step_runner_test.go | 35 ++++++++++++++++--- server/events/terraform/terraform_client.go | 11 +++--- server/events_controller_e2e_test.go | 4 ++- server/server.go | 4 ++- .../test-repos/tfvars-yaml/atlantis.yaml | 1 + .../tfvars-yaml/exp-output-autoplan.txt | 2 ++ 11 files changed, 75 insertions(+), 17 deletions(-) diff --git a/runatlantis.io/docs/atlantis-yaml-reference.md b/runatlantis.io/docs/atlantis-yaml-reference.md index 029b67e29..6009f72e5 100644 --- a/runatlantis.io/docs/atlantis-yaml-reference.md +++ b/runatlantis.io/docs/atlantis-yaml-reference.md @@ -66,7 +66,7 @@ workflows: | -------------| --- |-------------| -----|---| | version | int | none | yes | This key is required and must be set to `2`| | projects | array[[Project](atlantis-yaml-reference.html#project)] | [] | no | Lists the projects in this repo | -| workflows | map string -> [Workflow](atlantis-yaml-reference.html#workflow) | {} | no | Custom workflows | +| workflows | map[string -> [Workflow](atlantis-yaml-reference.html#workflow)] | {} | no | Custom workflows | ### Project ```yaml @@ -153,7 +153,7 @@ A map from string to `extra_args` for a built-in command with extra arguments. ``` | Key | Type | Default | Required | Description | | -------------| --- |-------------| -----|---| -| init/plan/apply | map `extra_args` -> array[string] | none | no | Use a built-in command and append `extra_args`. Only `init`, `plan` and `apply` are supported as keys and only `extra_args` is supported as a value|| +| init/plan/apply | map[`extra_args` -> array[string]] | none | no | Use a built-in command and append `extra_args`. Only `init`, `plan` and `apply` are supported as keys and only `extra_args` is supported as a value|| #### Custom Command Or a custom command ```yaml @@ -161,7 +161,15 @@ Or a custom command ``` | Key | Type | Default | Required | Description | | -------------| --- |-------------| -----|---| -| run | string| "" | no | Run a custom command| +| run | string| none | no | Run a custom command| + +::: tip +`run` steps are executed with the following environment variables: +* `WORKSPACE` - The Terraform workspace used for this project, ex. `default`. + * NOTE: if the step is executed before `init` then Atlantis won't have switched to this workspace yet. +* `ATLANTIS_TERRAFORM_VERSION` - The version of Terraform used for this project, ex. `0.11.0`. +* `DIR` - Absolute path to the current directory. +::: ## Next Steps Check out the [atlantis.yaml Use Cases](../guide/atlantis-yaml-use-cases.html) for diff --git a/server/events/runtime/apply_step_runner_test.go b/server/events/runtime/apply_step_runner_test.go index 8603fbc26..0f3a7a957 100644 --- a/server/events/runtime/apply_step_runner_test.go +++ b/server/events/runtime/apply_step_runner_test.go @@ -9,9 +9,9 @@ import ( . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events/mocks/matchers" "github.com/runatlantis/atlantis/server/events/models" - matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" + matchers2 "github.com/runatlantis/atlantis/server/events/terraform/mocks/matchers" "github.com/runatlantis/atlantis/server/events/yaml/valid" . "github.com/runatlantis/atlantis/testing" ) diff --git a/server/events/runtime/init_step_runner_test.go b/server/events/runtime/init_step_runner_test.go index 8422f4190..ff0ec9f08 100644 --- a/server/events/runtime/init_step_runner_test.go +++ b/server/events/runtime/init_step_runner_test.go @@ -7,9 +7,9 @@ import ( . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events/mocks/matchers" "github.com/runatlantis/atlantis/server/events/models" - matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" + matchers2 "github.com/runatlantis/atlantis/server/events/terraform/mocks/matchers" "github.com/runatlantis/atlantis/server/logging" . "github.com/runatlantis/atlantis/testing" ) diff --git a/server/events/runtime/plan_step_runner_test.go b/server/events/runtime/plan_step_runner_test.go index 392279f1e..ed10a5090 100644 --- a/server/events/runtime/plan_step_runner_test.go +++ b/server/events/runtime/plan_step_runner_test.go @@ -11,9 +11,9 @@ import ( "github.com/pkg/errors" "github.com/runatlantis/atlantis/server/events/mocks/matchers" "github.com/runatlantis/atlantis/server/events/models" - matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" + matchers2 "github.com/runatlantis/atlantis/server/events/terraform/mocks/matchers" "github.com/runatlantis/atlantis/server/events/yaml/valid" "github.com/runatlantis/atlantis/server/logging" . "github.com/runatlantis/atlantis/testing" diff --git a/server/events/runtime/run_step_runner.go b/server/events/runtime/run_step_runner.go index 1b406737a..0bdb7d802 100644 --- a/server/events/runtime/run_step_runner.go +++ b/server/events/runtime/run_step_runner.go @@ -2,15 +2,18 @@ package runtime import ( "fmt" + "os" "os/exec" "strings" + "github.com/hashicorp/go-version" "github.com/pkg/errors" "github.com/runatlantis/atlantis/server/events/models" ) // RunStepRunner runs custom commands. type RunStepRunner struct { + DefaultTFVersion *version.Version } func (r *RunStepRunner) Run(ctx models.ProjectCommandContext, command []string, path string) (string, error) { @@ -20,6 +23,18 @@ func (r *RunStepRunner) Run(ctx models.ProjectCommandContext, command []string, cmd := exec.Command("sh", "-c", strings.Join(command, " ")) // #nosec cmd.Dir = path + tfVersion := r.DefaultTFVersion.String() + if ctx.ProjectConfig != nil && ctx.ProjectConfig.TerraformVersion != nil { + tfVersion = ctx.ProjectConfig.TerraformVersion.String() + } + baseEnvVars := os.Environ() + customEnvVars := []string{ + fmt.Sprintf("WORKSPACE=%s", ctx.Workspace), + fmt.Sprintf("ATLANTIS_TERRAFORM_VERSION=%s", tfVersion), + fmt.Sprintf("DIR=%s", path), + } + finalEnvVars := append(baseEnvVars, customEnvVars...) + cmd.Env = finalEnvVars out, err := cmd.CombinedOutput() commandStr := strings.Join(command, " ") diff --git a/server/events/runtime/run_step_runner_test.go b/server/events/runtime/run_step_runner_test.go index 36262b235..c206e1c49 100644 --- a/server/events/runtime/run_step_runner_test.go +++ b/server/events/runtime/run_step_runner_test.go @@ -4,8 +4,10 @@ import ( "strings" "testing" + "github.com/hashicorp/go-version" "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/events/runtime" + "github.com/runatlantis/atlantis/server/events/yaml/valid" "github.com/runatlantis/atlantis/server/logging" . "github.com/runatlantis/atlantis/testing" ) @@ -16,6 +18,10 @@ func TestRunStepRunner_Run(t *testing.T) { ExpOut string ExpErr string }{ + { + Command: "", + ExpErr: "no commands for run step", + }, { Command: "echo hi", ExpOut: "hi\n", @@ -28,23 +34,44 @@ func TestRunStepRunner_Run(t *testing.T) { Command: "lkjlkj", ExpErr: "exit status 127: running \"lkjlkj\" in", }, + { + Command: "echo workspace=$WORKSPACE version=$ATLANTIS_TERRAFORM_VERSION dir=$DIR", + ExpOut: "workspace=myworkspace version=0.11.0 dir=$DIR\n", + }, } - r := runtime.RunStepRunner{} + projVersion, err := version.NewVersion("v0.11.0") + Ok(t, err) + defaultVersion, _ := version.NewVersion("0.8") + r := runtime.RunStepRunner{ + DefaultTFVersion: defaultVersion, + } ctx := models.ProjectCommandContext{ - Log: logging.NewNoopLogger(), + Log: logging.NewNoopLogger(), + Workspace: "myworkspace", + RepoRelDir: "mydir", + ProjectConfig: &valid.Project{ + TerraformVersion: projVersion, + Workspace: "myworkspace", + Dir: "mydir", + }, } for _, c := range cases { t.Run(c.Command, func(t *testing.T) { tmpDir, cleanup := TempDir(t) defer cleanup() - out, err := r.Run(ctx, strings.Split(c.Command, " "), tmpDir) + var split []string + if c.Command != "" { + split = strings.Split(c.Command, " ") + } + out, err := r.Run(ctx, split, tmpDir) if c.ExpErr != "" { ErrContains(t, c.ExpErr, err) return } Ok(t, err) - Equals(t, c.ExpOut, out) + expOut := strings.Replace(c.ExpOut, "dir=$DIR", "dir="+tmpDir, -1) + Equals(t, expOut, out) }) } } diff --git a/server/events/terraform/terraform_client.go b/server/events/terraform/terraform_client.go index 73d21d197..ce290b13e 100644 --- a/server/events/terraform/terraform_client.go +++ b/server/events/terraform/terraform_client.go @@ -99,11 +99,8 @@ func (c *DefaultClient) RunCommandWithVersion(log *logging.SimpleLogger, path st tfVersionStr = v.String() } - // set environment variables - // this is to support scripts to use the WORKSPACE, ATLANTIS_TERRAFORM_VERSION - // and DIR variables in their scripts - // append current process's environment variables - // this is to prevent the $PATH variable being removed from the environment + // We add custom variables so that if `extra_args` is specified with env + // vars then they'll be substituted. envVars := []string{ // Will de-emphasize specific commands to run in output. "TF_IN_AUTOMATION=true", @@ -120,11 +117,15 @@ func (c *DefaultClient) RunCommandWithVersion(log *logging.SimpleLogger, path st fmt.Sprintf("ATLANTIS_TERRAFORM_VERSION=%s", tfVersionStr), fmt.Sprintf("DIR=%s", path), } + // Append current Atlantis process's environment variables so PATH is + // preserved and any vars that users purposely exec'd Atlantis with. envVars = append(envVars, os.Environ()...) // append terraform executable name with args tfCmd := fmt.Sprintf("%s %s", tfExecutable, strings.Join(args, " ")) + // We use 'sh -c' so that if extra_args have been specified with env vars, + // ex. -var-file=$WORKSPACE.tfvars, then they get substituted. terraformCmd := exec.Command("sh", "-c", tfCmd) // #nosec terraformCmd.Dir = path terraformCmd.Env = envVars diff --git a/server/events_controller_e2e_test.go b/server/events_controller_e2e_test.go index be87412bb..00048b63b 100644 --- a/server/events_controller_e2e_test.go +++ b/server/events_controller_e2e_test.go @@ -252,7 +252,9 @@ func setupE2E(t *testing.T) (server.EventsController, *vcsmocks.MockClientProxy, ApplyStepRunner: runtime.ApplyStepRunner{ TerraformExecutor: terraformClient, }, - RunStepRunner: runtime.RunStepRunner{}, + RunStepRunner: runtime.RunStepRunner{ + DefaultTFVersion: defaultTFVersion, + }, PullApprovedChecker: e2eVCSClient, WorkingDir: workingDir, Webhooks: &mockWebhookSender{}, diff --git a/server/server.go b/server/server.go index f122c32fc..efeddef90 100644 --- a/server/server.go +++ b/server/server.go @@ -253,7 +253,9 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { ApplyStepRunner: runtime.ApplyStepRunner{ TerraformExecutor: terraformClient, }, - RunStepRunner: runtime.RunStepRunner{}, + RunStepRunner: runtime.RunStepRunner{ + DefaultTFVersion: defaultTfVersion, + }, PullApprovedChecker: vcsClient, WorkingDir: workingDir, Webhooks: webhooksManager, diff --git a/server/testfixtures/test-repos/tfvars-yaml/atlantis.yaml b/server/testfixtures/test-repos/tfvars-yaml/atlantis.yaml index 217e8fd99..a6f517140 100644 --- a/server/testfixtures/test-repos/tfvars-yaml/atlantis.yaml +++ b/server/testfixtures/test-repos/tfvars-yaml/atlantis.yaml @@ -15,6 +15,7 @@ workflows: extra_args: [-backend-config=default.backend.tfvars] - plan: extra_args: [-var-file=default.tfvars] + - run: echo workspace=$WORKSPACE staging: plan: steps: diff --git a/server/testfixtures/test-repos/tfvars-yaml/exp-output-autoplan.txt b/server/testfixtures/test-repos/tfvars-yaml/exp-output-autoplan.txt index abf01fb6f..20a4e02fb 100644 --- a/server/testfixtures/test-repos/tfvars-yaml/exp-output-autoplan.txt +++ b/server/testfixtures/test-repos/tfvars-yaml/exp-output-autoplan.txt @@ -21,6 +21,8 @@ Terraform will perform the following actions: id: Plan: 1 to add, 0 to change, 0 to destroy. +workspace=default + ``` * To **discard** this plan click [here](lock-url).