diff --git a/Makefile b/Makefile index 09170aa76..9b31cba66 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ endef clean: rm -Rf $(PREFIX)/bin/* + rm -f $(PREFIX)/test/integration/gomplate build-x: $(patsubst %,$(PREFIX)/bin/$(PKG_NAME)_%,$(platforms)) @@ -46,7 +47,17 @@ build: $(PREFIX)/bin/$(PKG_NAME)$(call extension,$(GOOS)) test: $(GO) test -v -race `glide novendor` +build-integration-image: $(PREFIX)/bin/$(PKG_NAME)_linux-amd64$(call extension,$(GOOS)) + cp $(PREFIX)/bin/$(PKG_NAME)_linux-amd64 test/integration/gomplate + docker build -f test/integration/Dockerfile -t gomplate-test test/integration/ + +test-integration-docker: build-integration-image + docker run -it --rm gomplate-test + +test-integration: build + @test/integration/test.sh + gen-changelog: github_changelog_generator --no-filter-by-milestone --exclude-labels duplicate,question,invalid,wontfix,admin -.PHONY: gen-changelog +.PHONY: gen-changelog clean test build-x compress-all build-release build build-integration-image test-integration-docker diff --git a/circle.yml b/circle.yml index fc903f327..dc9ecca38 100644 --- a/circle.yml +++ b/circle.yml @@ -1,13 +1,24 @@ -dependencies: - pre: - - hash glide || go get -u github.com/Masterminds/glide - - hash go-junit-report || go get -u github.com/jstemmer/go-junit-report - - hash gometalinter || (go get -u github.com/alecthomas/gometalinter && gometalinter --install) - -test: - override: - - | - set -e - set -o pipefail - make test | go-junit-report > $CIRCLE_TEST_REPORTS/report.xml - - gometalinter --deadline 20s --disable gotype --enable gofmt --enable goimports --enable misspell --enable unused +version: 2 +jobs: + build: + docker: + - image: hairyhenderson/gomplate-ci-build:latest + environment: + - CIRCLE_TEST_REPORTS: /tmp/test-results + working_directory: /go/src/github.com/hairyhenderson/gomplate + steps: + - checkout + - run: make build + - run: + name: Unit Tests + command: | + set -e + set -o pipefail + make test | go-junit-report > $CIRCLE_TEST_REPORTS/report.xml + - run: gometalinter -j 1 --vendor --deadline 70s --disable gotype --enable gofmt --enable goimports --enable misspell --enable unused --disable gas + - run: make test-integration + - store_artifacts: + path: bin + destination: binaries + - store_test_results: + path: /tmp/test-results diff --git a/test/integration/.gitignore b/test/integration/.gitignore new file mode 100644 index 000000000..b99778724 --- /dev/null +++ b/test/integration/.gitignore @@ -0,0 +1 @@ +gomplate diff --git a/test/integration/Dockerfile b/test/integration/Dockerfile new file mode 100644 index 000000000..d26ca1be4 --- /dev/null +++ b/test/integration/Dockerfile @@ -0,0 +1,22 @@ +FROM alpine:edge + +ENV VAULT_VER 0.7.0 +RUN apk add --no-cache \ + curl \ + bash \ + bats \ + && curl -L -o /tmp/vault.zip https://releases.hashicorp.com/vault/${VAULT_VER}/vault_${VAULT_VER}_linux_amd64.zip \ + && unzip /tmp/vault.zip \ + && mv vault /bin/vault \ + && rm /tmp/vault.zip \ + && apk del curl + +RUN mkdir /lib64 \ + && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 + +COPY gomplate /bin/gomplate +COPY *.sh /tests/ +COPY *.bash /tests/ +COPY *.bats /tests/ + +CMD ["/tests/test.sh"] diff --git a/test/integration/basic.bats b/test/integration/basic.bats new file mode 100644 index 000000000..259b4567c --- /dev/null +++ b/test/integration/basic.bats @@ -0,0 +1,59 @@ +#!/usr/bin/env bats + +load helper + +tmpdir=$(mktemp -u) + +function setup () { + mkdir -p $tmpdir + echo 'hi' > $tmpdir/one + echo 'hello' > $tmpdir/two +} + +function teardown () { + rm -rf $tmpdir +} + +@test "takes stdin by default" { + gomplate_stdin "hello world" + [ "$status" -eq 0 ] + [[ "${output}" == "hello world" ]] +} + +@test "takes stdin with --file -" { + gomplate_stdin "hello world" --file - + [ "$status" -eq 0 ] + [[ "${output}" == "hello world" ]] +} + +@test "writes to stdout with --out -" { + gomplate_stdin "hello world" --out - + [ "$status" -eq 0 ] + [[ "${output}" == "hello world" ]] +} + +@test "ignores stdin with --in" { + gomplate_stdin "hello world" --in "hi" + [ "$status" -eq 0 ] + [[ "${output}" == "hi" ]] +} + +@test "errors given more inputs than outputs" { + skip + gomplate -f $tmpdir/one -f $tmpdir/two -o $tmpdir/out + [ "$status" -eq 2 ] + [[ "${output}" == "still need to make this real..." ]] +} + +@test "routes inputs to their proper outputs" { + gomplate -f $tmpdir/one -f $tmpdir/two -o $tmpdir/one.out -o $tmpdir/two.out + [ "$status" -eq 0 ] + [[ "$(cat $tmpdir/one.out)" == "hi" ]] + [[ "$(cat $tmpdir/two.out)" == "hello" ]] +} + +@test "number of input files irrelevant given input string" { + gomplate -i 'HELLO WORLD' -f foo -f bar + [ "$status" -eq 0 ] + [[ "${output}" == "HELLO WORLD" ]] +} diff --git a/test/integration/datasources_vault.bats b/test/integration/datasources_vault.bats new file mode 100644 index 000000000..bfa437c7a --- /dev/null +++ b/test/integration/datasources_vault.bats @@ -0,0 +1,98 @@ +#!/usr/bin/env bats + +load helper + +function setup () { + cat <& /dev/null +path "*" { + policy = "write" +} +EOF +} + +function teardown () { + vault delete secret/foo + vault auth-disable userpass + vault auth-disable userpass2 + vault auth-disable approle + vault auth-disable approle2 + vault auth-disable app-id + vault auth-disable app-id2 +} + +@test "Testing userpass vault auth" { + vault write secret/foo value="$BATS_TEST_DESCRIPTION" + vault auth-enable userpass + vault write auth/userpass/users/dave password=foo policies=pol + VAULT_AUTH_USERNAME=dave + VAULT_AUTH_PASSWORD=foo + gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' + [ "$status" -eq 0 ] + [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] +} + +@test "Testing userpass vault auth with custom mount" { + vault write secret/foo value="$BATS_TEST_DESCRIPTION" + vault auth-enable -path=userpass2 userpass + vault write auth/userpass2/users/dave password=foo policies=pol + VAULT_AUTH_USERPASS_MOUNT=userpass2 + VAULT_AUTH_USERNAME=dave + VAULT_AUTH_PASSWORD=foo + gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' + [ "$status" -eq 0 ] + [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] +} + +@test "Testing approle vault auth" { + vault write secret/foo value="$BATS_TEST_DESCRIPTION" + vault auth-enable approle + vault write auth/approle/role/testrole secret_id_ttl=1m token_ttl=2m token_max_ttl=3m secret_id_num_uses=5 policies=pol + VAULT_ROLE_ID=$(vault read -field role_id auth/approle/role/testrole/role-id) + VAULT_SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/testrole/secret-id) + gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' + [ "$status" -eq 0 ] + [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] +} + +@test "Testing approle vault auth with custom mount" { + vault write secret/foo value="$BATS_TEST_DESCRIPTION" + vault auth-enable -path=approle2 approle + vault write auth/approle2/role/testrole secret_id_ttl=1m token_ttl=2m token_max_ttl=3m secret_id_num_uses=5 policies=pol + VAULT_ROLE_ID=$(vault read -field role_id auth/approle2/role/testrole/role-id) + VAULT_SECRET_ID=$(vault write -f -field=secret_id auth/approle2/role/testrole/secret-id) + VAULT_AUTH_APPROLE_MOUNT=approle2 + gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' + [ "$status" -eq 0 ] + [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] +} + +@test "Testing app-id vault auth" { + vault write secret/foo value="$BATS_TEST_DESCRIPTION" + vault auth-enable app-id + vault write auth/app-id/map/app-id/testappid value=pol display_name=test_app_id + vault write auth/app-id/map/user-id/testuserid value=testappid + VAULT_APP_ID=testappid + VAULT_USER_ID=testuserid + gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' + [ "$status" -eq 0 ] + [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] +} + +@test "Testing app-id vault auth with custom mount" { + vault write secret/foo value="$BATS_TEST_DESCRIPTION" + vault auth-enable -path=app-id2 app-id + + vault write auth/app-id2/map/app-id/testappid value=pol display_name=test_app_id + vault write auth/app-id2/map/user-id/testuserid value=testappid + + VAULT_APP_ID=testappid + VAULT_USER_ID=testuserid + VAULT_AUTH_APPID_MOUNT=approle2 + gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' + [ "$status" -eq 0 ] + [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] +} + +# TODO: test the github auth backend at some point... this needs a github token though, so... +# vault write auth/github/config organization=DockerOttawaMeetup +# vault write auth/github/map/teams/organizers value=pol diff --git a/test/integration/envvars.bats b/test/integration/envvars.bats new file mode 100644 index 000000000..6bec5695a --- /dev/null +++ b/test/integration/envvars.bats @@ -0,0 +1,40 @@ +#!/usr/bin/env bats + +load helper + +# function setup () { +# } + +# function teardown () { +# # rm -rf $tmpdir +# } + +@test "errors with non-existant env var using .Env" { + gomplate -i '{{.Env.FOO}}' + [ "$status" -eq 2 ] + [[ "${lines[0]}" == *"map has no entry for key"* ]] +} + +@test "empty string with non-existant env var using getenv" { + gomplate -i '{{getenv "FOO" }}' + [ "$status" -eq 0 ] + [[ "${output}" == "" ]] +} + +@test "default string with non-existant env var using getenv" { + gomplate -i '{{getenv "FOO" "foo"}}' + [ "$status" -eq 0 ] + [[ "${output}" == "foo" ]] +} + +@test "existant env var using .Env" { + gomplate -i '{{.Env.HOME}}' + [ "$status" -eq 0 ] + [[ "${output}" == "${HOME}" ]] +} + +@test "existant env var using getenv" { + gomplate -i '{{getenv "HOME"}}' + [ "$status" -eq 0 ] + [[ "${output}" == "${HOME}" ]] +} diff --git a/test/integration/helper.bash b/test/integration/helper.bash new file mode 100644 index 000000000..f2d0a3227 --- /dev/null +++ b/test/integration/helper.bash @@ -0,0 +1,27 @@ +#!/bin/bash + +function gomplate () { + run bin/gomplate "$@" + + # Some debug information to make life easier. bats will only print it if the + # test failed, in which case the output is useful. + echo "gomplate $@ (status=$status):" >&2 + echo "$output" >&2 +} + +function gomplate_stdin () { + run __gomplate_stdin "$@" + + # Some debug information to make life easier. bats will only print it if the + # test failed, in which case the output is useful. + in=$1 + shift + echo "echo \"$in\" | gomplate $@ (status=$status):" >&2 + echo "$output" >&2 +} + +function __gomplate_stdin () { + in=$1 + shift 1 + echo "$in" | bin/gomplate "$@" +} diff --git a/test/integration/test.sh b/test/integration/test.sh new file mode 100755 index 000000000..443179d1c --- /dev/null +++ b/test/integration/test.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -euo pipefail + +# This is useful for killing vault after the script exits, but causes the CircleCI +# build to fail, so... ¯\_(ツ)_/¯ +# trap "exit" INT TERM +# trap "kill 0" EXIT + +# TODO: export these in a bats helper, as well as only launch vault in a vault helper +export VAULT_ADDR=http://127.0.0.1:8200 +export VAULT_TOKEN=00000000-1111-2222-3333-444455556666 + +# fire up vault in dev mode for the vault tests +vault server -dev -dev-root-token-id=${VAULT_TOKEN} -log-level=err >&/dev/null & + +bats $(dirname $0)