diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..1ed37cd8 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,56 @@ +version: 2 +jobs: + build: + working_directory: /go/src/github.com/hootsuite/atlantis + docker: + - image: circleci/golang:1.8 + environment: + TERRAFORM_VERSION: 0.10.0 + steps: + - checkout + - setup_remote_docker: + reusable: true + - run: make deps + - run: make test + - run: make build-service + - run: + name: Install e2e dependencies + command: make end-to-end-deps + - run: + name: Starting atlantis server in the background + command: cd "${CIRCLE_WORKING_DIRECTORY}/e2e" && ./atlantis server --gh-user="$GITHUB_USERNAME" --gh-token="$GITHUB_PASSWORD" --data-dir="/tmp" --log-level="debug" &> /tmp/atlantis-server.log + background: true + - run: sleep 2 + - run: + name: Starting ngrok + command: cd "${CIRCLE_WORKING_DIRECTORY}/e2e" && ./ngrok http 4141 > /tmp/ngrok.log + background: true + - run: sleep 2 + - run: echo 'export ATLANTIS_URL=$(curl -s 'http://localhost:4040/api/tunnels' | jq -r '.tunnels[1].public_url')' >> $BASH_ENV + - run: + name: Run e2e tests + command: make end-to-end-tests + - run: + name: Build image + command: | + if [ "${CIRCLE_BRANCH}" == "master" ]; then + docker build -t hootsuite/atlantis:latest . + fi + - run: + name: Push image + command: | + if [ "${CIRCLE_BRANCH}" == "master" ]; then + docker login -e "$DOCKER_EMAIL" -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" + docker push hootsuite/atlantis:latest + fi + - run: + name: Tag and push version if exists + # work around until tags are properly supported + # https://discuss.circleci.com/t/git-tag-deploys-in-2-0/9493/6 + command: | + if [ "${CIRCLE_BRANCH}" == "master" ]; then + for TAG in $(git tag --contains $CIRCLE_SHA1); do + docker tag hootsuite/atlantis:latest hootsuite/atlantis:$TAG + docker push hootsuite/atlantis:$TAG + done + fi \ No newline at end of file diff --git a/.gitignore b/.gitignore index 995f674d..fef243e7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ atlantis *.iml .vscode atlantis.db +output diff --git a/CHANGELOG.md b/CHANGELOG.md index ab5bcf5f..aef12d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ assume role session with the GitHub username of the user running the Atlantis co use the `atlantis_user` terraform variable alongside Terraform's [built-in support](https://www.terraform.io/docs/providers/aws/#assume-role) for assume role (see https://github.com/hootsuite/atlantis/blob/master/README.md#assume-role-session-names) +* Atlantis has a docker image now ([#123](https://github.com/hootsuite/atlantis/pull/123)). Here is how you can try it out: + +```bash +docker run -it hootsuite/atlantis server --gh-user=GITHUB_USERNAME --gh-token=GITHUB_TOKEN +``` ### Improvements * Support for HTTPS cloning using GitHub username and token provided to atlantis server ([#117](https://github.com/hootsuite/atlantis/pull/117)) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..20802ada --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +FROM alpine:3.6 +LABEL authors="Anubhav Mishra, Luke Kysow" +LABEL maintainer="anubhav.mishra@hootsuite.com,luke.kysow@hootsuite.com" + +# create atlantis user +RUN addgroup atlantis && \ + adduser -S -G atlantis atlantis + +ENV ATLANTIS_HOME_DIR=/home/atlantis + +# install atlantis dependencies +ENV DUMB_INIT_VERSION=1.2.0 +ENV GOSU_VERSION=1.10 +RUN apk add --no-cache ca-certificates gnupg curl git unzip bash openssh libcap openssl && \ + wget -O /bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_amd64 && \ + chmod +x /bin/dumb-init && \ + mkdir -p /tmp/build && \ + cd /tmp/build && \ + wget -O gosu "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64" && \ + wget -O gosu.asc "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64.asc" && \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \ + gpg --batch --verify gosu.asc gosu && \ + chmod +x gosu && \ + cp gosu /bin && \ + cd /tmp && \ + rm -rf /tmp/build && \ + apk del gnupg openssl && \ + rm -rf /root/.gnupg && rm -rf /var/cache/apk/* + +# install terraform binaries +ENV DEFAULT_TERRAFORM_VERSION=0.10.0 + +RUN AVAILABLE_TERRAFORM_VERSIONS="0.8.8 0.9.11 0.10.0" && \ + for VERSION in ${AVAILABLE_TERRAFORM_VERSIONS}; do curl -LOk https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip && \ + mkdir -p /usr/local/bin/tf/versions/${VERSION} && \ + unzip terraform_${VERSION}_linux_amd64.zip -d /usr/local/bin/tf/versions/${VERSION} && \ + ln -s /usr/local/bin/tf/versions/${VERSION}/terraform /usr/local/bin/terraform${VERSION};rm terraform_${VERSION}_linux_amd64.zip;done && \ + ln -s /usr/local/bin/tf/versions/${DEFAULT_TERRAFORM_VERSION}/terraform /usr/local/bin/terraform + +# copy binary +COPY atlantis /usr/local/bin/atlantis + +# copy docker entrypoint +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["server"] \ No newline at end of file diff --git a/Makefile b/Makefile index 8a6b9cb8..4eb0a7e5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ BUILD_ID := $(shell git rev-parse --short HEAD 2>/dev/null || echo no-commit-id) WORKSPACE := $(shell pwd) PKG := $(shell go list ./... | grep -v e2e | grep -v vendor | grep -v static) +IMAGE_NAME := hootsuite/atlantis .PHONY: test @@ -34,10 +35,16 @@ dist: ## Package up everything in static/ using go-bindata-assetfs so it can be go-bindata-assetfs -pkg server static/... && mv bindata_assetfs.go server release: ## Create packages for a release - gox -os="darwin linux" -arch="amd64" + ./scripts/binary-release.sh vendor-status: @govendor status fmt: ## Run goimports (which also formats) goimports -w $$(find . -type f -name '*.go' ! -path "./vendor/*" ! -path "./server/bindata_assetfs.go") + +end-to-end-deps: ## Install e2e dependencies + ./scripts/e2e-deps.sh + +end-to-end-tests: ## Run e2e tests + ./scripts/e2e.sh \ No newline at end of file diff --git a/README.md b/README.md index cd9f7743..b16eda72 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ * [Locking](#locking) * [Approvals](#approvals) * [Production-Ready Deployment](#production-ready-deployment) + * [Docker](#docker) * [Server Configuration](#server-configuration) * [AWS Credentials](#aws-credentials) * [Glossary](#glossary) @@ -258,6 +259,41 @@ $ atlantis server --atlantis-url $URL --gh-user $USERNAME --gh-token $TOKEN --gh Atlantis is now running! **We recommend running it under something like Systemd or Supervisord.** +### Docker +Atlantis also ships inside a docker image with Terraform versions `0.8.8`, `0.9.11` and `0.10.0`. Run the docker image: + +```bash +docker run hootsuite/atlantis server --gh-user=GITHUB_USERNAME --gh-token=GITHUB_TOKEN +``` + +#### Usage +If you would like to add things like [AWS credential files](http://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html) to the docker image, you can do something like this: + +* Create a custom docker file +```bash +vim Dockerfile-custom +``` + +```dockerfile +FROM hootsuite/atlantis + +# copy aws credentials +COPY credentials /home/atlantis/.aws/credentials +``` + +* Build docker image + +```bash +docker build -t {YOUR_DOCKER_ORG}/atlantis-custom -f Dockerfile-custom +``` + +* Run docker image + +```bash +docker run {YOUR_DOCKER_ORG}/atlantis-custom server --gh-user=GITHUB_USERNAME --gh-token=GITHUB_TOKEN +``` + + ### Testing Out Atlantis If you'd like to test out Atlantis before running it on your own repositories you can fork our example repo. diff --git a/circle.yml b/circle.yml deleted file mode 100644 index c4c13f22..00000000 --- a/circle.yml +++ /dev/null @@ -1,37 +0,0 @@ -machine: - # Environment variables for build - environment: - GOPATH: "${HOME}/.go_workspace" - WORKDIR: "${GOPATH}/src/github.com/hootsuite/atlantis" - TERRAFORM_VERSION: 0.9.11 - -dependencies: - pre: - # remove old go files - - rm -rf "$GOPATH" - - override: - # move repository to the canonical import path - - mkdir -p "$(dirname ${WORKDIR})" - - cp -R "${HOME}/atlantis" "${WORKDIR}" - # Install dependencies - - cd "${WORKDIR}" && make deps - -test: - override: - # Run tests - - cd "${WORKDIR}" && ./scripts/build.sh - - post: - # Run e2e tests - - cd "${WORKDIR}" && ./scripts/e2e-deps.sh - # Start atlantis server - - cd "${WORKDIR}/e2e" && ./atlantis server --gh-user="$GITHUB_USERNAME" --gh-token="$GITHUB_PASSWORD" --data-dir="/tmp" --log-level="debug" &> /tmp/atlantis-server.log: - background: true - - sleep 2 - - cd "${WORKDIR}/e2e" && ./ngrok http 4141: - background: true - - sleep 2 - # Set ATLANTIS_URL environment variable to be used by atlantis e2e test to create the webhook - - echo '' >> ~/.circlerc && echo 'export ATLANTIS_URL=$(curl -s 'http://localhost:4040/api/tunnels' | jq -r '.tunnels[1].public_url') ' >> ~/.circlerc - - cd "${WORKDIR}/e2e" && ../scripts/e2e.sh diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 00000000..b4c1ae91 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/dumb-init /bin/sh +set -e + +# Modified: https://github.com/hashicorp/docker-consul/blob/2c2873f9d619220d1eef0bc46ec78443f55a10b5/0.X/docker-entrypoint.sh + +# If the user is trying to run atlantis directly with some arguments, then +# pass them to atlantis. +if [ "${1:0:1}" = '-' ]; then + set -- atlantis "$@" +fi + +# Look for atlantis subcommands. +if atlantis --help "$1" 2>&1 | grep -q "atlantis $1"; then + # We can't use the return code to check for the existence of a subcommand, so + # we have to use grep to look for a pattern in the help output. + set -- atlantis "$@" +fi + +# If we are running atlantis, make sure it executes as the proper user. +if [ "$1" = 'atlantis' ]; then + # If requested, set the capability to bind to privileged ports before + # we drop to the non-root user. Note that this doesn't work with all + # storage drivers (it won't work with AUFS). + if [ ! -z ${ATLANTIS_ALLOW_PRIVILEGED_PORTS+x} ]; then + setcap "cap_net_bind_service=+ep" /bin/atlantis + fi + + set -- gosu atlantis "$@" +fi + +exec "$@" \ No newline at end of file diff --git a/e2e/secrets-envs b/e2e/secrets-envs deleted file mode 100644 index 7efd927c..00000000 --- a/e2e/secrets-envs +++ /dev/null @@ -1 +0,0 @@ -Salted__VOF|bmoj6&Djԁa|kN˖- Pz50yΥΨS :r#ajuex qFk;\X#{{bm/DU: &b \ No newline at end of file diff --git a/scripts/binary-release.sh b/scripts/binary-release.sh new file mode 100755 index 00000000..8d2bd136 --- /dev/null +++ b/scripts/binary-release.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# define architecture we want to build +XC_ARCH=${XC_ARCH:-"386 amd64 arm"} +XC_OS=${XC_OS:-linux darwin} +XC_EXCLUDE_OSARCH="!darwin/arm !darwin/386" + +# clean up +echo "-> running clean up...." +rm -rf output/* + +if ! which gox > /dev/null; then + echo "-> installing gox..." + go get -u github.com/mitchellh/gox +fi + +# build +# we want to build statically linked binaries +export CGO_ENABLED=0 +echo "-> building..." +gox \ + -os="${XC_OS}" \ + -arch="${XC_ARCH}" \ + -osarch="${XC_EXCLUDE_OSARCH}" \ + -output "output/{{.OS}}_{{.Arch}}/atlantis" \ + . + +# Zip and copy to the dist dir +echo "" +echo "Packaging..." +for PLATFORM in $(find ./output -mindepth 1 -maxdepth 1 -type d); do + OSARCH=$(basename ${PLATFORM}) + echo "--> ${OSARCH}" + + pushd $PLATFORM >/dev/null 2>&1 + zip ../atlantis_${OSARCH}.zip ./* + popd >/dev/null 2>&1 +done + +echo "" +echo "" +echo "-----------------------------------" +echo "Output:" +ls -alh output/ \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index df251f9a..00000000 --- a/scripts/build.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail -IFS=$'\n\t' - -run_unit_test() { - echo "Running unit tests: 'make test'" - make test -} - -# Run unit tests -run_unit_test - -# Get deps -echo "Running 'make deps'" -make deps - -# Build packages to make sure they can be compiled -echo "Running 'make build'" -make build-service - -# TODO: add parallel builds for every make target that has anything to do with testing. diff --git a/scripts/e2e-deps.sh b/scripts/e2e-deps.sh index 9657dcdc..6cd0a81a 100755 --- a/scripts/e2e-deps.sh +++ b/scripts/e2e-deps.sh @@ -1,15 +1,19 @@ #!/usr/bin/env bash echo "Preparing to run e2e tests" -mv atlantis ${WORKDIR}/e2e/ +if [ ! -f atlantis ]; then + echo "atlantis binary not found. exiting...." + exit 1 +fi +mv atlantis ${CIRCLE_WORKING_DIRECTORY}/e2e/ # cd into e2e folder cd e2e/ -# Decrypting secrets for atlantis runtime: https://github.com/circleci/encrypted-files -openssl aes-256-cbc -d -in secrets-envs -k $KEY >> ~/.circlerc # Download terraform curl -LOk https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip -unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /home/ubuntu/bin +unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip +chmod +x terraform +cp terraform /go/bin/ # Download ngrok to create a tunnel to expose atlantis server wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip unzip ngrok-stable-linux-amd64.zip diff --git a/scripts/e2e.sh b/scripts/e2e.sh index c95884db..a97ba99f 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -3,6 +3,8 @@ set -euo pipefail IFS=$'\n\t' +cd e2e/ + # Download dependencies echo "Running 'make deps'" make deps