Terratest Guide
Terratest is a Go framework that is used to test Terraform infrastructure code. It executes the defined Terraform and then validates things you're asserting.
Basic Terratest example of a module
The most basic Terratest test you can write brings up an example in your examples
directory, tears it down, and will only test that it runs.
We've got some basics in the terraform-template-module repo so you can see it all in context.
Write an example in the examples
directory that includes the module(s) and configuration that you're testing. In the tests
directory create a file named terraform_aws<NAME_OF_MODULE>_test.go
. Basic test is as follows:
package test
import (
"fmt"
"strings"
"testing"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/terraform"
test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
)
func TestTerraformAwsEcrRepo(t *testing.T) {
t.Parallel()
tempTestFolder := test_structure.CopyTerraformFolderToTemp(t, "../", "examples/simple")
testName := fmt.Sprintf("terratest-%s", strings.ToLower(random.UniqueId()))
awsRegion := "us-west-2"
terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: tempTestFolder,
// Variables to pass to our Terraform code using -var options
Vars: map[string]interface{}{
"test_name": testName,
},
// Environment variables to set when running Terraform
EnvVars: map[string]string{
"AWS_DEFAULT_REGION": awsRegion,
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
}
Other Examples
Run manually
To run these tests manually against the trussworks-ci
AWS account you'll need AWS access in our AWS organization. You'll need help from someone in #infrasec and must follow the setup instructions.
You'll also need to install aws-vault
and ensure your ./aws/config
file is setup correctly.
In most of our modules, there is a makefile
that defines test
so you'll run the following from the root of the repo you're testing:
AWS_VAULT_KEYCHAIN_NAME=login aws-vault exec trussworks-ci -- make test
Configure CircleCi to run the tests automatically
Configure CircleCi Job
Add a job to the .circleci/config
file in the repository:
terratest:
docker:
- auth:
password: $DOCKER_PASSWORD
username: $DOCKER_USERNAME
image: *circleci_docker
environment:
- TEST_RESULTS: /tmp/test-results
steps:
- checkout
- restore_cache:
keys:
- pre-commit-dot-cache-{{ checksum ".pre-commit-config.yaml" }}
- go-mod-sources-v1-{{ checksum "go.sum" }}
- run:
command: |
temp_role=$(aws sts assume-role --role-arn arn:aws:iam::313564602749:role/circleci --role-session-name circleci)
export AWS_ACCESS_KEY_ID=$(echo $temp_role | jq .Credentials.AccessKeyId | xargs)
export AWS_SECRET_ACCESS_KEY=$(echo $temp_role | jq .Credentials.SecretAccessKey | xargs)
export AWS_SESSION_TOKEN=$(echo $temp_role | jq .Credentials.SessionToken | xargs)
make test
name: Assume role, run pre-commit and run terratest
- save_cache:
key: pre-commit-dot-cache-{{ checksum ".pre-commit-config.yaml" }}
paths:
- ~/.cache/pre-commit
- save_cache:
key: go-mod-sources-v1-{{ checksum "go.sum" }}
paths:
- ~/go/pkg/mod
- store_test_results:
path: /tmp/test-results/gotest
You'll either create a new workflow or add this job to an existing workflow
definition to be run on every commit/push etc.
Update the Key rotator configuration
We have automation in place that updates the AWS Access Keys used by CircleCI daily so you'll need to add this repo to rotator configuration if it is running Terratests against the trussworks-ci AWS account .
Update the rotate.yaml
file in Legendary Waddle to include a sink to your new repo. A sink stanza looks like this:
- kind: CircleCI
key_to_name:
accessKeyId: AWS_ACCESS_KEY_ID
secretAccessKey: AWS_SECRET_ACCESS_KEY
account: trussworks
repo: <REPO NAME>
You can run the rotator script manually in your local environment to populate the keys to your repository. To do so, you will need a personal API token set up in a .envrc.local
file in your local environment. See the CircleCI Documentation on creating a personal API token.
Rotate the keys via
aws-vault exec trussworks-id -- rotator rotate -f ./rotate.yaml -y
Instructions to install rotator can be found here.
Alternative: Configure AWS Keys for the CircleCI project
These tests are running as the circleci
user account configured in the trussworks-id
account.
To add the access keys go to the project settings page https://circleci.com/gh/trussworks/<PROJECT NAME>/edit#env-vars
.
Set AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
to the current values.
These keys are rotated daily.
Access test metadata stored in CircleCI
In order to access the test metadata we stored in the store_test_results
key of our .circleci/config
test environment, we'll need to make a few tweaks. This will allow us luxuries such as pinpointing flaky tests that cause intermittent failures.
Since CircleCI only reads metadata in xml format, first we need to convert our go test output
into a file CircleCI can read. We'll use package go-junit-report
. Add a bash script like so, following the usage directions:
#!/usr/bin/env bash
set -eu -o pipefail
go_test_output="/tmp/go-test.out"
go test -short -count 1 -v -timeout 90m github.com/trussworks/terraform-aws-logs/test/... | tee "${go_test_output}"
# Check if we are running tests inside of CircleCI by checking for a $CIRCLECI
# environment variable. The dash after $CIRCLECI substitutes a null value if
# CIRCLECI is unset. This prevents unbound variable errors
if [[ -n ${CIRCLECI-} ]]; then
mkdir -p "${TEST_RESULTS}"/gotest
go-junit-report < "${go_test_output}" \
> "${TEST_RESULTS}/gotest/go-test-report.xml"
fi
Save this script with a filename like make-test
and make it executable using chmod +x make-test
. Now we'll add a call to the executable in our Makefile like so:
.PHONY: test
test: bin/make-test
Finally we update our .circleci/config
by adding two steps prior to running terratest - one to go get
the package and another to access our shiny new executable:
- run:
name: Adding go binaries to $PATH
command: |
echo 'export PATH=${PATH}:~/go/bin' >> $BASH_ENV
source $BASH_ENV
- run: go get github.com/jstemmer/go-junit-report
Now we should be able to see both the tests and artifacts tabs in our CircleCI pipeline: