diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..40fec83 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,82 @@ +--- + +### +### Enable sudo (required for docker service) +### +sudo: required + + +### +### Language +### +language: python + + +### +### Add services +### +services: + - docker + + +### +### Build Matrix +### +env: + matrix: + - TF=0.11 TG=0.18 + - TF=0.12 TG=0.19 + - TF=latest TG=latest + + +### +### Install requirements +### +install: + # Get newer docker version + - while ! sudo apt-get update; do sleep 1; done + - while ! sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce; do sleep 1; done + - docker version + + +### +### Check generation changes, build and test +### +before_script: + - make lint + - while ! make build TF_VERSION=${TF} TG_VERSION=${TG}; do sleep 1; done + - while ! make test TF_VERSION=${TF} TG_VERSION=${TG}; do sleep 1; done + - git diff --quiet || { echo "Build Changes"; git diff; git status; false; } + + +### +### Push to Dockerhub +### +script: + # Push to docker hub on success + - if [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then + while ! make login USER="${DOCKER_USERNAME}" PASS="${DOCKER_PASSWORD}"; do sleep 1; done; + if [ -n "${TRAVIS_TAG}" ]; then + if [ "${TF}" == "latest" ] && [ "${TG}" == "latest" ]; then + while ! make push TAG="latest-${TRAVIS_TAG}"; do sleep 1; done; + else + while ! make push TAG="${TF}-${TG}-${TRAVIS_TAG}"; do sleep 1; done; + fi + elif [ "${TRAVIS_BRANCH}" == "master" ]; then + if [ "${TF}" == "latest" ] && [ "${TG}" == "latest" ]; then + while ! make push TAG=latest; do sleep 1; done; + else + while ! make push TAG=${TF}-${TG}; do sleep 1; done; + fi + elif [[ ${TRAVIS_BRANCH} =~ ^(release-[.0-9]+)$ ]]; then + if [ "${TF}" == "latest" ] && [ "${TG}" == "latest" ]; then + while ! make push TAG="latest-${TRAVIS_BRANCH}"; do sleep 1; done; + else + while ! make push TAG="${TF}-${TG}-${TRAVIS_BRANCH}"; do sleep 1; done; + fi + else + echo "Skipping branch ${TRAVIS_BRANCH}"; + fi + else + echo "Skipping push on PR"; + fi diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..accca85 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,61 @@ +FROM alpine:latest as builder + +# Install build dependencies +RUN set -eux \ + && apk add --no-cache \ + curl \ + git + +# Get Terraform +ARG TF_VERSION=latest +RUN set -eux \ + && if [ "${TF_VERSION}" = "latest" ]; then \ + VERSION="$( curl -sS https://releases.hashicorp.com/terraform/ \ + | tac | tac \ + | grep -Eo '/[.0-9]+/' \ + | grep -Eo '[.0-9]+' \ + | sort -V \ + | tail -1 )"; \ + else \ + VERSION="$( curl -sS https://releases.hashicorp.com/terraform/ \ + | tac | tac \ + | grep -Eo "/${TF_VERSION}\.[.0-9]+/" \ + | grep -Eo '[.0-9]+' \ + | sort -V \ + | tail -1 )"; \ + fi \ + && curl -sS -L -O \ + https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip \ + && unzip terraform_${VERSION}_linux_amd64.zip \ + && mv terraform /usr/bin/terraform \ + && chmod +x /usr/bin/terraform + +# Get Terragrunt +ARG TG_VERSION=latest +RUN set -eux \ + && git clone https://github.com/gruntwork-io/terragrunt /terragrunt \ + && cd /terragrunt \ + && if [ "${TG_VERSION}" = "latest" ]; then \ + VERSION="$( git describe --abbrev=0 --tags )"; \ + else \ + VERSION="$( git tag | grep -E "v${TG_VERSION}\.[.0-9]+" | sort -u | tail -1 )" ;\ + fi \ + && curl -sS -L \ + https://github.com/gruntwork-io/terragrunt/releases/download/${VERSION}/terragrunt_linux_amd64 \ + -o /usr/bin/terragrunt \ + && chmod +x /usr/bin/terragrunt + +# Use a clean tiny image to store artifacts in +FROM alpine:latest +LABEL \ + maintainer="cytopia " \ + repo="https://github.com/cytopia/docker-terragrunt" +RUN set -eux \ + && apk add --no-cache git +COPY --from=builder /usr/bin/terraform /usr/bin/terraform +COPY --from=builder /usr/bin/terragrunt /usr/bin/terragrunt + +ENV WORKDIR /data +WORKDIR /data + +CMD ["terragrunt", "--version"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..548d48e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 cytopia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6660412 --- /dev/null +++ b/Makefile @@ -0,0 +1,132 @@ +ifneq (,) +.error This Makefile requires GNU Make. +endif + +.PHONY: build rebuild lint test _test-tf-version _test-tg-version _test-tf _test-tg tag pull login push enter + +CURRENT_DIR = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +DIR = . +FILE = Dockerfile +IMAGE = cytopia/terragrunt +TAG = latest + +TF_VERSION = latest +TG_VERSION = latest + +build: + docker build --build-arg TF_VERSION=$(TF_VERSION) --build-arg TG_VERSION=$(TG_VERSION) -t $(IMAGE) -f $(DIR)/$(FILE) $(DIR) + +rebuild: pull + docker build --no-cache --build-arg TF_VERSION=$(TF_VERSION) --build-arg TG_VERSION=$(TG_VERSION) -t $(IMAGE) -f $(DIR)/$(FILE) $(DIR) + +lint: + @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint file-cr --text --ignore '.git/,.github/,tests/' --path . + @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint file-crlf --text --ignore '.git/,.github/,tests/' --path . + @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint file-trailing-single-newline --text --ignore '.git/,.github/,tests/' --path . + @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint file-trailing-space --text --ignore '.git/,.github/,tests/' --path . + @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint file-utf8 --text --ignore '.git/,.github/,tests/' --path . + @docker run --rm -v $(CURRENT_DIR):/data cytopia/file-lint file-utf8-bom --text --ignore '.git/,.github/,tests/' --path . + +test: + @$(MAKE) --no-print-directory _test-tf-version + @$(MAKE) --no-print-directory _test-tg-version + @$(MAKE) --no-print-directory _test-tf + @$(MAKE) --no-print-directory _test-tg + +_test-tf-version: + @echo "------------------------------------------------------------" + @echo "- Testing correct Terraform version" + @echo "------------------------------------------------------------" + @if [ "$(TF_VERSION)" = "latest" ]; then \ + echo "Fetching latest version from HashiCorp release page"; \ + LATEST="$$( \ + curl -L -sS https://releases.hashicorp.com/terraform/ \ + | tac | tac \ + | grep -Eo '/[.0-9]+/' \ + | grep -Eo '[.0-9]+' \ + | sort -V \ + | tail -1 \ + )"; \ + echo "Testing for latest: $${LATEST}"; \ + if ! docker run --rm $(IMAGE) terraform --version | grep -E "^Terraform[[:space:]]*v?$${LATEST}$$"; then \ + echo "Failed"; \ + exit 1; \ + fi; \ + else \ + echo "Testing for tag: $(TF_VERSION)"; \ + if ! docker run --rm $(IMAGE) terraform --version | grep -E "^Terraform[[:space:]]*v?$(TF_VERSION)\.[.0-9]+$$"; then \ + echo "Failed"; \ + exit 1; \ + fi; \ + fi; \ + echo "Success"; \ + +_test-tg-version: + @echo "------------------------------------------------------------" + @echo "- Testing correct Terragrunt version" + @echo "------------------------------------------------------------" + @if [ "$(TG_VERSION)" = "latest" ]; then \ + echo "Fetching latest version from GitHub"; \ + LATEST="$$( \ + curl -L -sS https://github.com/gruntwork-io/terragrunt/releases \ + | tac | tac \ + | grep -Eo '/v[.0-9]+/' \ + | grep -Eo 'v[.0-9]+' \ + | sort -u \ + | sort -V \ + | tail -1 \ + )"; \ + echo "Testing for latest: $${LATEST}"; \ + if ! docker run --rm $(IMAGE) terragrunt --version | grep -E "^terragrunt[[:space:]]*version[[:space:]]*v?$${LATEST}$$"; then \ + echo "Failed"; \ + exit 1; \ + fi; \ + else \ + echo "Testing for tag: $(TG_VERSION)"; \ + if ! docker run --rm $(IMAGE) terragrunt --version | grep -E "^terragrunt[[:space:]]*version[[:space:]]*v?$(TG_VERSION)\.[.0-9]+$$"; then \ + echo "Failed"; \ + exit 1; \ + fi; \ + fi; \ + echo "Success"; \ + +_test-tf: + @echo "------------------------------------------------------------" + @echo "- Testing Terraform" + @echo "------------------------------------------------------------" + @if ! docker run --rm -v $(CURRENT_DIR)/tests/terraform:/data $(IMAGE) terraform fmt; then \ + echo "Failed"; \ + exit 1; \ + fi; \ + echo "Success"; + +_test-tg: + @echo "------------------------------------------------------------" + @echo "- Testing Terragrunt" + @echo "------------------------------------------------------------" + @if ! docker run --rm -v $(CURRENT_DIR)/tests/terragrunt:/data $(IMAGE) terragrunt terragrunt-info; then \ + docker run --rm -v $(CURRENT_DIR)/tests/terragrunt:/data $(IMAGE) sh -c "if test -d .terragrunt-cache; then rm -rf .terragrunt-cache; fi"; \ + echo "Failed"; \ + exit 1; \ + fi; \ + docker run --rm -v $(CURRENT_DIR)/tests/terragrunt:/data $(IMAGE) sh -c "if test -d .terragrunt-cache; then rm -rf .terragrunt-cache; fi"; \ + echo "Success"; + +tag: + docker tag $(IMAGE) $(IMAGE):$(TAG) + +pull: + @grep -E '^\s*FROM' Dockerfile \ + | sed -e 's/^FROM//g' -e 's/[[:space:]]*as[[:space:]]*.*$$//g' \ + | xargs -n1 docker pull; + +login: + yes | docker login --username $(USER) --password $(PASS) + +push: + @$(MAKE) tag TAG=$(TAG) + docker push $(IMAGE):$(TAG) + +enter: + docker run --rm --name $(subst /,-,$(IMAGE)) -it --entrypoint=/bin/sh $(ARG) $(IMAGE):$(TAG) diff --git a/README.md b/README.md index 87d0f84..f330785 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@ and its corresponding version of [terraform](https://github.com/hashicorp/terraf The following Docker image tags are rolling releases and built and updated nightly. This means they always contain the latest stable version as shown below. -| Docker tag | Terraform version | Terragrunt version | -|------------|-------------------|--------------------| -| `latest` | latest stable | latest stable | -| `012-019` | latest `0.12.x` | latest `0.19.x` | -| `011-018` | latest `0.11.x` | latest `0.18.x` | +| Docker tag | Terraform version | Terragrunt version | +|--------------|------------------------|------------------------| +| `latest` | latest stable | latest stable | +| `0.12-0.19` | latest stable `0.12.x` | latest stable `0.19.x` | +| `0.11-0.18` | latest stable `0.11.x` | latest stable `0.18.x` | ### Point in time releases @@ -50,18 +50,18 @@ If you want to ensure to have reproducible Terraform/Terragrunt executions you s this repository. Tags are incremented for each new version, but never updated itself. This means you will have to take care yourself and update your CI tools every time a new tag is being released. -| Docker tag | docker-terragrunt | Terraform version | Terragrunt version | -|-----------------|-------------------|-----------------------------------|-----------------------------------| -| `latest-` | Tag: `` | latest stable during tag creation | latest stable during tag creation | -| `012-019-` | Tag: `` | latest 0.12.x during tag creation | latest stable during tag creation | -| `011-018-` | Tag: `` | latest 0.11.x during tag creation | latest stable during tag creation | +| Docker tag | docker-terragrunt | Terraform version | Terragrunt version | +|-------------------|-------------------|--------------------------------------------|--------------------------------------------| +| `latest-` | Tag: `` | latest stable during tag creation | latest stable during tag creation | +| `0.12-0.19-` | Tag: `` | latest stable `0.12.x` during tag creation | latest stable `0.12.x` during tag creation | +| `0.11-0.18-` | Tag: `` | latest stable `0.11.x` during tag creation | latest stable `0.11.x` during tag creation | Where `` refers to the chosen git tag from this repository. ## Docker mounts -The working directory inside the Docker container is `/data/` and should be mounted to your local filesystem. +The working directory inside the Docker container is **`/data/`** and should be mounted to your local filesystem. ## Usage @@ -75,13 +75,13 @@ docker run --rm -v $(pwd):/data cytopia/terragrunt terraform ### Provision single sub-project on AWS Let's assume your Terragrunt project setup is as follows: ``` -. # <-- Terragrunt project root +/my/tf # Terragrunt project root └── envs    └── aws    ├── dev    │   ├── eu-central-1    │   │   ├── infra -    │   │   │   └── vpc-k8s # <-- VPC sub-project directory +    │   │   │   └── vpc-k8s # VPC sub-project directory    │   │   │   ├── include_providers.tf    │   │   │   ├── terraform.tfvars    │   │   │   └── terragrunt.hcl @@ -99,9 +99,9 @@ Let's assume your Terragrunt project setup is as follows: ``` The VPC sub-project you want to provision is at the path `envs/aws/dev/eu-centra-1/infra/vpc-k8s/`. -1. Mount the terragrunt root project dir into `/data/` into the container +1. Mount the terragrunt root project dir (`/my/tf/`) into `/data/` into the container 2. Use the workding dir (`-w` or `--workdir`) to point to your project inside the container -3. Add AWS credentials from your environment +3. Add AWS credentials from your environment to the container ```bash # Initialize the VPC project @@ -109,7 +109,7 @@ docker run --rm \ -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN \ - -v $(pwd):/data cytopia/terragrunt \ + -v /my/tf:/data cytopia/terragrunt \ -w /data/envs/aws/dev/eu-central-1/infra/vpc-k8s \ terragrunt init @@ -118,7 +118,7 @@ docker run --rm \ -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN \ - -v $(pwd):/data cytopia/terragrunt \ + -v /my/tf:/data cytopia/terragrunt \ -w /data/envs/aws/dev/eu-central-1/infra/vpc-k8s \ terragrunt plan @@ -127,7 +127,7 @@ docker run --rm \ -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN \ - -v $(pwd):/data cytopia/terragrunt \ + -v /my/tf:/data cytopia/terragrunt \ -w /data/envs/aws/dev/eu-central-1/infra/vpc-k8s \ terragrunt --terragrunt-non-interactive apply ``` diff --git a/tests/terraform/main.tf b/tests/terraform/main.tf new file mode 100644 index 0000000..e54ceac --- /dev/null +++ b/tests/terraform/main.tf @@ -0,0 +1,17 @@ +resource "aws_iam_role" "roles" { + name = "my-role" + path = "/" + description = "my-role description" + + # This policy defines who/what is allowed to use the current role + assume_role_policy = "${file("policy.json")}" + + # Allow session for X seconds + max_session_duration = "3600" + force_detach_policies = true + + tags = { + Name = "my-role" + Owner = "terraform" + } +} diff --git a/tests/terragrunt/terraform.tfvars b/tests/terragrunt/terraform.tfvars new file mode 100644 index 0000000..e8cfdb3 --- /dev/null +++ b/tests/terragrunt/terraform.tfvars @@ -0,0 +1,5 @@ +terragrunt = { + terraform { + source = "./" + } +} diff --git a/tests/terragrunt/terragrunt.hcl b/tests/terragrunt/terragrunt.hcl new file mode 100644 index 0000000..e69de29