diff --git a/.circleci/config.yml b/.circleci/config.yml index d0520d799..0061c2cb9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,9 +3,9 @@ env: &env GRUNTWORK_INSTALLER_VERSION: v0.0.36 MODULE_CI_VERSION: v0.46.0 MODULE_GCP_CI_VERSION: v0.1.1 - TERRAFORM_VERSION: 1.1.4 + TERRAFORM_VERSION: 1.5.7 PACKER_VERSION: 1.7.4 - TERRAGRUNT_VERSION: v0.36.0 + TERRAGRUNT_VERSION: v0.52.0 OPA_VERSION: v0.33.1 GO_VERSION: 1.21.1 GO111MODULE: auto @@ -63,6 +63,16 @@ install_gruntwork_utils: &install_gruntwork_utils sudo ln -s /usr/local/go/bin/go /usr/bin/go echo "The installed version of Go is now $(go version)" +install_tofu: &install_tofu + name: Install OpenTofu + command: | + curl -L "https://github.com/opentofu/opentofu/releases/download/v1.6.0-alpha1/tofu_1.6.0-alpha1_linux_amd64.zip" -o tofu.zip + unzip -o tofu.zip + sudo install -m 0755 tofu /usr/local/bin/tofu + rm -rf tofu + rm -rf tofu.zip + tofu --version + install_docker_buildx: &install_docker_buildx name: install docker buildx command: | @@ -158,7 +168,8 @@ jobs: paths: - project - test: + # run tests with terraform binary + test_terraform: <<: *defaults resource_class: large steps: @@ -195,6 +206,48 @@ jobs: - store_test_results: path: /tmp/logs + # run tests with tofu binary + test_tofu: + <<: *defaults + resource_class: large + steps: + - attach_workspace: + at: /home/circleci + + - run: + <<: *install_gruntwork_utils + - run: + <<: *install_docker_buildx + - run: + <<: *install_tofu + + # The weird way you have to set PATH in Circle 2.0 + - run: | + echo 'export PATH=$HOME/.local/bin:$HOME/terraform:$HOME/packer:$PATH' >> $BASH_ENV + # remove terraform binary so tofu will be used + sudo rm -f $(which terraform) + + # Run the tests. Note that we set the "-p 1" flag to tell Go to run tests in each package sequentially. Without + # this, Go buffers all log output until all packages are done, which with slower running tests can cause CircleCI + # to kill the build after more than 10 minutes without log output. + # NOTE: because this doesn't build with the kubernetes tag, it will not run the kubernetes tests. See + # kubernetes_test build steps. + - run: mkdir -p /tmp/logs + # check we can compile the azure code, but don't actually run the tests + - run: run-go-tests --packages "-p 1 -tags=azure -run IDontExist ./modules/azure" + - run: run-go-tests --packages "-p 1 ./..." | tee /tmp/logs/test_output.log + + - run: + command: | + ./cmd/bin/terratest_log_parser_linux_amd64 --testlog /tmp/logs/test_output.log --outputdir /tmp/logs + when: always + + # Store test result and log artifacts for browsing purposes + - store_artifacts: + path: /tmp/logs + - store_test_results: + path: /tmp/logs + # We run the GCP tests in a separate build step using the Docker executor for better isolation and resiliency. Using # The Docker executor ensures GCP tests do not erroneously make metadata network calls within CircleCI's private # environment. For more information see: https://github.com/gruntwork-io/terratest/pull/765. @@ -363,7 +416,20 @@ workflows: tags: only: /^v.*/ - - test: + - test_terraform: + context: + - AWS__PHXDEVOPS__circle-ci-test + - GITHUB__PAT__gruntwork-ci + - SLACK__TOKEN__refarch-deployer-test + - SLACK__WEBHOOK__refarch-deployer-test + - SLACK__CHANNEL__test-workflow-approvals + requires: + - setup + filters: + tags: + only: /^v.*/ + + - test_tofu: context: - AWS__PHXDEVOPS__circle-ci-test - GITHUB__PAT__gruntwork-ci diff --git a/modules/terraform/cmd.go b/modules/terraform/cmd.go index 3591c083a..ff7f425c1 100644 --- a/modules/terraform/cmd.go +++ b/modules/terraform/cmd.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "os/exec" "github.com/gruntwork-io/terratest/modules/collections" "github.com/gruntwork-io/terratest/modules/retry" @@ -30,10 +31,20 @@ var commandsWithParallelism = []string{ "destroy-all", } +const ( + // TofuDefaultPath command to run tofu + TofuDefaultPath = "tofu" + + // TerraformDefaultPath to run terraform + TerraformDefaultPath = "terraform" +) + +var DefaultExecutable = defaultTerraformExecutable() + // GetCommonOptions extracts commons terraform options func GetCommonOptions(options *Options, args ...string) (*Options, []string) { if options.TerraformBinary == "" { - options.TerraformBinary = "terraform" + options.TerraformBinary = DefaultExecutable } if options.TerraformBinary == "terragrunt" { @@ -112,3 +123,17 @@ func GetExitCodeForTerraformCommandE(t testing.TestingT, additionalOptions *Opti } return DefaultErrorExitCode, getExitCodeErr } + +func defaultTerraformExecutable() string { + cmd := exec.Command(TerraformDefaultPath, "-version") + cmd.Stdin = nil + cmd.Stdout = nil + cmd.Stderr = nil + + if err := cmd.Run(); err == nil { + return TerraformDefaultPath + } + + // fallback to Tofu if terraform is not available + return TofuDefaultPath +} diff --git a/modules/terraform/plan_test.go b/modules/terraform/plan_test.go index 33c627ea2..4f6517513 100644 --- a/modules/terraform/plan_test.go +++ b/modules/terraform/plan_test.go @@ -3,6 +3,7 @@ package terraform import ( "fmt" "path/filepath" + "strings" "testing" "github.com/gruntwork-io/terratest/modules/files" @@ -79,8 +80,10 @@ func TestInitAndPlanWithPlanFile(t *testing.T) { out, err := InitAndPlanE(t, options) require.NoError(t, err) + // clean output to be consistent in checks + out = strings.ReplaceAll(out, "\n", "") assert.Contains(t, out, "1 to add, 0 to change, 0 to destroy.") - assert.Contains(t, out, fmt.Sprintf("Saved the plan to: %s", planFilePath)) + assert.Contains(t, out, fmt.Sprintf("Saved the plan to:%s", planFilePath)) assert.FileExists(t, planFilePath, "Plan file was not saved to expected location:", planFilePath) } diff --git a/modules/terraform/show_test.go b/modules/terraform/show_test.go index d844fff09..f47ac4eeb 100644 --- a/modules/terraform/show_test.go +++ b/modules/terraform/show_test.go @@ -3,6 +3,7 @@ package terraform import ( "fmt" "path/filepath" + "strings" "testing" "github.com/gruntwork-io/terratest/modules/files" @@ -25,7 +26,8 @@ func TestShowWithInlinePlan(t *testing.T) { } out := InitAndPlan(t, options) - require.Contains(t, out, fmt.Sprintf("Saved the plan to: %s", planFilePath)) + out = strings.ReplaceAll(out, "\n", "") + require.Contains(t, out, fmt.Sprintf("Saved the plan to:%s", planFilePath)) require.FileExists(t, planFilePath, "Plan file was not saved to expected location:", planFilePath) // show command does not accept Vars @@ -55,7 +57,8 @@ func TestShowWithStructInlinePlan(t *testing.T) { } out := InitAndPlan(t, options) - require.Contains(t, out, fmt.Sprintf("Saved the plan to: %s", planFilePath)) + out = strings.ReplaceAll(out, "\n", "") + require.Contains(t, out, fmt.Sprintf("Saved the plan to:%s", planFilePath)) require.FileExists(t, planFilePath, "Plan file was not saved to expected location:", planFilePath) // show command does not accept Vars diff --git a/modules/version-checker/version_checker.go b/modules/version-checker/version_checker.go index a6ac4cca7..73d22e194 100644 --- a/modules/version-checker/version_checker.go +++ b/modules/version-checker/version_checker.go @@ -4,6 +4,8 @@ import ( "fmt" "regexp" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/gruntwork-io/terratest/modules/shell" "github.com/gruntwork-io/terratest/modules/testing" "github.com/hashicorp/go-version" @@ -125,7 +127,7 @@ func getBinary(params CheckVersionParams) (string, error) { case Packer: return "packer", nil case Terraform: - return "terraform", nil + return terraform.DefaultExecutable, nil default: return "", fmt.Errorf("unsupported Binary for checking versions {%d}", params.Binary) }