whoami
Presentation: https://bertvv.github.io/presentation-cfgmgmtcamp2017/
https://github.com/bertvv/ansible-role-skeleton
$ atb role --tests=vagrant ftp
$ tree ftp/
ftp/
├── CHANGELOG.md
├── defaults/
│ └── main.yml
├── handlers/
│ └── main.yml
├── LICENSE.md
├── meta/
│ └── main.yml
├── README.md
├── tasks/
│ └── main.yml
├── vagrant-tests/ # see below
└── vars/
└── RedHat.yml
5 directories, 8 files
git-worktree
$ tree vagrant-tests/
vagrant-tests/
├── README.md
├── roles
│ └── ftp -> ../..
├── test.yml
└── Vagrantfile
2 directories, 3 files
require 'rbconfig'
ROLE_NAME = 'ftp'
HOST_NAME = 'test' + ROLE_NAME
VAGRANTFILE_API_VERSION = '2'
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = 'bertvv/centos72'
config.vm.define HOST_NAME do |node|
node.vm.hostname = HOST_NAME
node.vm.network :private_network, ip: '192.168.56.42'
node.vm.provision 'ansible' do |ansible|
ansible.playbook = 'test.yml'
end
end
end
Test playbook:
# Test playbook for Ansible role bertvv.ftp
---
- hosts: all
become: true
roles:
- role_under_test
post_tasks:
- name: Put a file into the shared directory
copy:
dest: /var/ftp/pub/README
content: 'hello world!'
$ vagrant up
Bringing machine 'testftp' up with 'virtualbox' provider...
==> testftp: Importing base box 'bertvv/centos72'...
[...]
==> testftp: Running provisioner: ansible...
testftp: Running ansible-playbook...
PLAY [all] *********************************************************************
[...]
TASK [ftp : Ensure service is started] *****************************************
changed: [testftp]
PLAY RECAP *********************************************************************
testftp : ok=4 changed=2 unreachable=0 failed=0
Afterwards: curl ftp://192.168.56.42/pub/README
This still serves me fine, but...
Supporting multiple platforms is hard
=> search for a CI/CD tool
systemd
.travis.yml
:
ansible-playbook test.yml
inside the containerhttps://hub.docker.com/r/bertvv/ansible-testing/
FROM centos:7
MAINTAINER Bert Van Vreckem <bert.vanvreckem@gmail.com>
ENV container docker
# Install systemd -- See https://hub.docker.com/_/centos/
RUN \
(cd /lib/systemd/system/sysinit.target.wants/ || exit; for i in *; do [ "$i" = systemd-tmpfiles-setup.service ] || rm -f "$i"; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*; \
rm -f /etc/systemd/system/*.wants/*; \
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*; \
rm -f /lib/systemd/system/anaconda.target.wants/*; \
# Continued
yum -y upgrade; \
yum -y install epel-release; \
yum -y install git ansible sudo libselinux-python iproute; \
yum clean all; \
sed -i -e 's/^\(Defaults\s*requiretty\)/#--- \1/' /etc/sudoers; \
echo -e '[local]\nlocalhost ansible_connection=local' > /etc/ansible/hosts; \
echo -e '[defaults]\nretry_files_enabled = False' > /etc/ansible/ansible.cfg
VOLUME ["/sys/fs/cgroup"]
CMD ["/usr/sbin/init"]
# .travis.yml
---
sudo: required
env:
- CONTAINER_ID=$(mktemp)
services:
- docker
before_install:
- sudo apt-get update
- sudo docker pull bertvv/ansible-testing:centos_7
script:
- sudo docker run --detach --privileged \
--volume=/sys/fs/cgroup:/sys/fs/cgroup:ro \
--volume="${PWD}":/etc/ansible/roles/role_under_test:ro \
bertvv/ansible-testing:centos_7 /usr/sbin/init > "${CONTAINER_ID}"
- sudo docker exec "$(cat ${CONTAINER_ID})" \
ansible-playbook /etc/ansible/test.yml --syntax-check
- sudo docker exec "$(cat ${CONTAINER_ID})" \
ansible-playbook /etc/ansible/test.yml
.travis.yml
with environment matrixgeerlingguy.apache
# .travis.yml
sudo: required
env:
- distribution: centos
version: 6
init: /sbin/init
run_opts: ""
container_id: $(mktemp)
- distribution: centos
version: 7
init: /usr/lib/systemd/systemd
run_opts: "--privileged --volume=/sys/fs/cgroup:/sys/fs/cgroup:ro"
container_id: $(mktemp)
- distribution: ubuntu
version: 14.04
init: /sbin/init
run_opts: ""
container_id: $(mktemp)
- distribution: ubuntu
version: 12.04
init: /sbin/init
run_opts: ""
container_id: $(mktemp)
services:
- docker
before_install:
- sudo apt-get update
# Pull container
- sudo docker pull bertvv/ansible-testing:${distribution}_${version}
script:
# Run container in detached state
- sudo docker run --detach \
--volume="${PWD}":/etc/ansible/roles/role_under_test:ro \
${run_opts} bertvv/ansible-testing:${distribution}_${version} \
"${init}" > "${container_id}"
# Syntax check
- sudo docker exec --tty "$(cat ${container_id})" env TERM=xterm \
ansible-playbook /etc/ansible/roles/role_under_test/tests/test.yml \
--syntax-check
# Test role
- sudo docker exec --tty "$(cat ${container_id})" env TERM=xterm \
ansible-playbook /etc/ansible/roles/role_under_test/tests/test.yml \
# Idempotence test
- >
sudo docker exec "$(cat ${container_id})" env TERM=xterm
ansible-playbook /etc/ansible/roles/role_under_test/tests/test.yml
| grep -q 'changed=0.*failed=0'
&& (echo 'Idempotence test: pass' && exit 0)
|| (echo 'Idempotence test: fail' && exit 1)
.travis.yml
docker-tests.sh
# .travis.yml Execution script for role tests on Travis-CI
---
sudo: required
env:
matrix:
- DISTRIBUTION: centos
VERSION: 7
- DISTRIBUTION: ubuntu
VERSION: 14.04
- DISTRIBUTION: ubuntu
VERSION: 16.04
services:
- docker
before_install:
# Install latest Git
- sudo apt-get update
- sudo apt-get install --only-upgrade git
# Allow fetching other branches than master
- git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
# Fetch the branch with test code
- git fetch origin docker-tests
- git worktree add docker-tests origin/docker-tests
script:
# Create container and apply test playbook
- ./docker-tests/docker-tests.sh
# Run functional tests on the container
- SUT_IP=172.17.0.2 ./docker-tests/functional-tests.sh
docker-tests.sh
To run locally:
DISTRIBUTION=centos VERSION=7 docker-tests/docker-tests.sh
main() {
# Sets distribution-specific run-options for docker
configure_environment
start_container
run_syntax_check
run_test_playbook
run_idempotence_test
# cleanup
}
start_container() {
log "Starting container"
set -x
docker run --detach \
--volume="${PWD}:${role_dir}:ro" \
"${run_opts[@]}" \
"${docker_image}:${DISTRIBUTION}_${VERSION}" \
"${init}" \
> "${container_id}"
set +x
}
run_syntax_check() {
log 'Running syntax check on playbook'
exec_container ansible-playbook "${test_playbook}" --syntax-check
log 'Syntax check finished'
}
run_test_playbook() {
log 'Running playbook'
exec_container ansible-playbook "${test_playbook}"
log 'Run finished'
}
exec_container() {
local id
id="$(get_container_id)"
set -x
docker exec --tty \
"${id}" \
env TERM=xterm \
"${@}"
set +x
}
run_idempotence_test() {
log 'Running idempotence test'
local output
output="$(mktemp)"
exec_container ansible-playbook "${test_playbook}" 2>&1 | tee "${output}"
if grep -q 'changed=0.*failed=0' "${output}"; then
result='pass'
return_status=0
else
result='fail'
return_status=1
fi
rm "${output}"
log "Result: ${result}"
return "${return_status}"
}
functional-tests.sh
.bats
files and runs themExample: ftp.bats
#! /usr/bin/env bats
#
# Variable SUT_IP should be set outside the script and should contain
# the IP address of the System Under Test.
@test 'Anonymous user should be able to fetch README' {
run curl --silent "ftp://${SUT_IP}/pub/README"
echo "Result: ${output}"
[ "${status}" -eq "0" ]
[ "${output}" = "hello world!" ]
}
$ DISTRIBUTION=centos VERSION=7 ./docker-tests/docker-tests.sh
>>> Starting container
[...]
>>> Running syntax check on playbook
[...]
>>> Syntax check finished
>>> Running playbook
[...]
>>> Run finished
>>> Running idempotence test
[...]
>>> Result: pass
$ SUT_IP=172.17.0.2 ./docker-tests/functional-tests.sh
### Using BATS executable at: /usr/local/bin/bats
### Running test /home/bert/Downloads/ftp/docker-tests/ftp.bats
✓ Anonymous user should be able to fetch README
1 test, 0 failures
$ atb role --tests=docker ftp
$ cd ftp
$ vi tasks/main.yml
[ write your role... ]
$ vi docker-tests/test.yml
[...]
$ vi docker-tests/ftp.bats
[...]
$ DISTRIBUTION=centos VERSION=7 ./docker-tests/docker-tests.sh
$ SUT_IP=172.17.0.2 ./docker-tests/functional-tests.sh
Does this work? Ship it!
There's always something...
docker-tests.sh
hangs on CentOS 6 image
Feedback welcome!