Skip to content

Instantly share code, notes, and snippets.

@Turgon37
Last active June 9, 2018 12:30
Show Gist options
  • Save Turgon37/5b6e6553ef92dc65d1c7df3e1a1e290e to your computer and use it in GitHub Desktop.
Save Turgon37/5b6e6553ef92dc65d1c7df3e1a1e290e to your computer and use it in GitHub Desktop.
A script to run basic tests on Ansible playbooks
#!/bin/bash
#
# Ansible role test shim.
#
# Usage: [OPTIONS] ./tests/test.sh
# - DISTRIBUTION: a supported Docker distro version (default = "centos7")
# - PLAYBOOK: a playbook in the tests directory (default = "test.yml")
# - CLEANUP: whether to remove the Docker container (default = true)
# - CONTAINER_ID: the --name to set for the container (default = timestamp)
# - DOCKER_OPTS: specific docker options
# - TEST_IDEMPOTENCE: whether to test playbook's idempotence (default = true)
# - ANSIBLE_DIFF: whether to enable diff option for Ansible
#
# Inspired by https://gist.githubusercontent.com/geerlingguy/73ef1e5ee45d8694570f334be385e181/
# License: MIT
# Exit on any individual command failure.
set -e
# Pretty colors.
red='\033[0;31m'
green='\033[0;32m'
blue='\033[0;34m'
neutral='\033[0m'
timestamp=$(date +%s)
# Allow environment variables to override defaults.
DISTRIBUTION=${DISTRIBUTION:-"debian9"}
PLAYBOOK=${PLAYBOOK:-"test.yml"}
CLEANUP=${CLEANUP:-"true"}
CONTAINER_ID=${CONTAINER_ID:-$timestamp}
TEST_PLAYBOOK=${TEST_PLAYBOOK:-"true"}
TEST_IDEMPOTENCE=${TEST_IDEMPOTENCE:-"true"}
ANSIBLE_DIFF=${ANSIBLE_DIFF:-"false"}
ANSIBLE_VERBOSE=${ANSIBLE_VERBOSE:-"true"}
## Set up vars for Docker setup.
# CentOS 7
if [ $DISTRIBUTION = 'centos7' ]; then
init="/usr/lib/systemd/systemd"
opts="--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro"
# Debian 9
elif [ $DISTRIBUTION = 'debian9' ]; then
init="/lib/systemd/systemd"
opts="--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro"
# Debian 8
elif [ $DISTRIBUTION = 'debian8' ]; then
init="/lib/systemd/systemd"
opts="--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro"
else
echo "Distribution $DISTRIBUTION is not supported, No any docker image is suitable to test it"
exit $(false)
fi
# Role local variables
role_name=role_under_test
real_role_name="$(basename $TRAVIS_REPO_SLUG |cut -d - -f 2-)"
# Docker local settings
image=turgon37/docker-$DISTRIBUTION-ansible
docker_volumes="--volume=$(pwd):/etc/ansible/roles/${role_name}:rw"
if [ "$role_name" != "$real_role_name" ]; then
docker_volumes="$docker_volumes --volume=$(pwd):/etc/ansible/roles/${real_role_name}:ro"
fi
# return the docker default environment variables
function docker_env() {
echo -n ' --env TERM=xterm'
}
# Ansible local settings
function ansible_options() {
if [ "x$ANSIBLE_DIFF" = 'xtrue' ]; then
echo -n ' --diff'
fi
if [ "x$ANSIBLE_VERBOSE" = 'xtrue' ]; then
echo -n ' --verbose'
fi
}
function ansible_env() {
echo -n ' --env ANSIBLE_FORCE_COLOR=1'
}
#
## Test requirements
#
# Run the container using the supplied OS.
function test_begin() {
printf "\n"
printf ${green}"Starting Docker container: $image."${neutral}"\n"
docker pull $image:latest
docker run --detach $docker_volumes --name "$CONTAINER_ID" $opts $DOCKER_OPTS $image:latest $init
echo $CONTAINER_ID > /tmp/container_id
if [ -n "$TEST_BEGIN_POST_COMMAND" ]; then
docker exec `docker_env` $CONTAINER_ID bash -c "$TEST_BEGIN_POST_COMMAND"
fi
}
# Remove the Docker container (if configured).
function test_end() {
printf "\n"
printf "Removing Docker container...\n"
docker rm -f $CONTAINER_ID
rm -f /tmp/container_id
}
# Show ansible version
function test_show_version() {
printf "\n"
printf ${green}"Print Ansible version."${neutral}"\n"
docker exec `docker_env` $CONTAINER_ID ansible --version
}
# Install requirements if `requirements.yml` is present.
function test_install_requirements() {
printf "\n"
local pip_requires=pip-requirements.txt
local ansible_requires=ansible-requirements.txt
if [[ -f tests/$pip_requires ]]; then
printf ${green}"PIP Requirements file detected; installing dependencies."${neutral}"\n"
docker exec `docker_env` $CONTAINER_ID pip install -r "/etc/ansible/roles/${role_name}/tests/${pip_requires}"
else
printf ${green}"No PIP requirements file detected."${neutral}"\n"
fi
if [[ -f tests/$ansible_requires ]]; then
printf ${green}"Ansible Requirements file detected; installing dependencies."${neutral}"\n"
docker exec `docker_env` $CONTAINER_ID ansible-galaxy install -r "/etc/ansible/roles/${role_name}/tests/${ansible_requires}"
else
printf ${green}"No ANSIBLE requirements file detected."${neutral}"\n"
fi
}
#
## Common roles tests
#
# Test Ansible syntax.
function test_check_syntax() {
printf "\n"
printf ${green}"Checking Ansible playbook syntax."${neutral}
docker exec `docker_env` $CONTAINER_ID ansible-playbook /etc/ansible/roles/role_under_test/tests/$PLAYBOOK --syntax-check
}
# Check ansible lint
function test_check_lint() {
printf "\n"
if command -v ansible-lint; then
printf ${green}"Checking Ansible coding style and lint."${neutral}
tips=$(/usr/bin/env ansible-lint --nocolor -p . || true)
echo "ansible-lint has detected '$(echo "$tips" | wc --lines)' improvable thing(s) in role"
fi
}
# Run Ansible playbook.
function test_run_playbook() {
local playbook="${1:-/etc/ansible/roles/role_under_test/tests/$PLAYBOOK}"
printf "\n"
printf ${green}"Running command: docker exec $docker_env $CONTAINER_ID ansible-playbook $playbook"${neutral}
docker exec `docker_env` `ansible_env` $CONTAINER_ID ansible-playbook $playbook `ansible_options`
}
# Run ansible playbook second time to
# check if playbook run without changes
function test_run_playbook_idempotence() {
# Run Ansible playbook again (idempotence test).
printf ${green}"Running playbook again: idempotence test"${neutral}
idempotence=$(mktemp)
docker exec `docker_env` `ansible_env` $CONTAINER_ID ansible-playbook /etc/ansible/roles/role_under_test/tests/$PLAYBOOK `ansible_options` | tee -a $idempotence
tail $idempotence \
| grep --quiet 'changed=0.*failed=0' \
&& (printf ${green}'Idempotence test: pass'${neutral}"\n") \
|| (printf ${red}'Idempotence test: fail'${neutral}"\n" && exit 1)
}
# Ensure piped stdout equals a value
# param1: the expected value
function ensureEquals() {
local value="$(cat - )"
if [ "$value" = "$1" ]; then
printf ${green}"OK $value = $1"${neutral}
return $(true)
else
printf ${red}"NOK $value != $1"${neutral}
return $(false)
fi
}
# Ensure piped stdout contains the value
# param1: the value to check for
function ensureContains() {
local value="$(cat -)"
echo '### VALUE ###'
echo "$value"
echo '### VALUE ###'
if echo "$value" | grep --quiet "$1"; then
printf ${green}"OK value contains $1"${neutral}
return $(true)
else
printf ${red}"NOK value does not contains $1"${neutral}
return $(false)
fi
}
test_begin
test_show_version
test_install_requirements
test_check_syntax
test_check_lint
if [ "x$TEST_PLAYBOOK" = "xtrue" ]; then
test_run_playbook
fi
if [ "x$TEST_IDEMPOTENCE" = "xtrue" ]; then
test_run_playbook_idempotence
fi
if [ "x$CLEANUP" = "xtrue" ]; then
test_end
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment