4 min read

Continuous Deployment Testing of GitHub Hosted Code

How do you make sure your code runs as you would expect in every supported environment?

For application developers the answer would be to ship applications in process containers like Docker or snaps.

What about us system level developers? We often do not have the luxury of having container environments or package managers attending to our every need of knowledge about and handling of differences in the underlying operating environment our code is deployed on.

This is no excuse to not do Continuous Deployment testing of your code. Every operating environment evolves through the course of its lifespan, and every change to your code should be tested against the most recent versions of every supported environment.

There are multiple Continuous Integration (CI) services freely available for use with Open Source GitHub hosted code.

CI services are originally made for automating unit- and functional- test on the code itself, and stands like a gate that either accepts or denies new code into your source tree. There is nothing stopping us from using said services to actually deploy the code in multiple operating environments, running functional tests towards your code in each of them verifying that it runs as expected.

For this post I will focus on Travis CI.

Travis CI is based on Ubuntu. Ubuntu has built-in support for Machine Containers through LXD. We can leverage this for our purpose of performing deployment tests for our code across multiple operating environments.

Example 1

This is a simple proof of concept set-up. It is available in the travis-lxd repository on GitHub.

# .travis.yml - place in root of your GitHub repository
---
sudo: required
dist: trusty
language: python
env:
 - LINT=1
 - IMAGE="ubuntu:precise"
 - IMAGE="ubuntu:trusty"
 - IMAGE="ubuntu:xenial"
 - IMAGE="ubuntu:yakkety"
 - IMAGE="images:ubuntu/zesty"
 - IMAGE="images:centos/6"
 - IMAGE="images:centos/7"
script:
 - if [ ! -z ${LINT} ]; then echo "Hello, world!"; fi
# setup dependencies here to save time on simpler test environments
# sudo back to ourself to activate lxd group membership
 - if [ ! -z ${IMAGE} ]; then
      sudo add-apt-repository -y ppa:ubuntu-lxc/lxd-stable;
      sudo apt-get -qq update;
      sudo apt-get -y install lxd;
      sudo lxd init --auto;
      sudo usermod -a -G lxd travis;
      sudo su travis -c 'lxc network create lxdbr0';
      sudo su travis -c 'lxc network attach-profile lxdbr0 default eth0';
   fi
 - if [ ! -z ${IMAGE} ]; then
      sudo su travis -c 'lxc launch ${IMAGE} mymachinecontainer';
      sudo su travis -c 'lxc exec mymachinecontainer -- sh -c "lsb_release -a || cat /etc/redhat-release"';
   fi

Result: Click image to visit build on Travis CI and view output

Example 2

This is a full end-to-end test where we set up Juju to deploy a charm to LXD containers on Travis CI. Repository can be viewed on GitHub here.

# .travis.yml - place in root of your GitHub repository
---
sudo: required
dist: trusty
language: python
env:
 - UNIT_TEST=1
 - AMULET_TEST=gate-basic-precise
 - AMULET_TEST=gate-basic-trusty
 - AMULET_TEST=gate-basic-xenial
 - AMULET_TEST=gate-basic-yakkety
script:
 - if [ ! -z ${UNIT_TEST} ]; then make lint; fi
 - if [ ! -z ${UNIT_TEST} ]; then make test; fi
# setup dependencies here to save time on simpler test envs
# upgrade python 2.7 to mitigate juju-deployer ssl.SSLError
# sudo back to ourself to activate lxd group membership
 - if [ ! -z ${AMULET_TEST} ]; then
    sudo add-apt-repository -y ppa:ubuntu-lxc/lxd-stable;
    sudo add-apt-repository -y ppa:juju/stable;
    sudo add-apt-repository -y ppa:fkrull/deadsnakes-python2.7;
    sudo apt-get -qq update;
    sudo apt-get -y install lxd juju python-tox libpython2.7 libpython2.7-dev libpython2.7-minimal libpython2.7-stdlib python-crypto python-pkg-resources python-yaml python2.7 python2.7-dev python2.7-minimal;
    sudo lxd init --auto;
    sudo usermod -a -G lxd travis;
    sudo su travis -c 'lxc network create lxdbr0';
    sudo su travis -c 'lxc network attach-profile lxdbr0 default eth0';
    sudo su travis -c 'juju bootstrap localhost';
    make virtualenv;
   fi
 - if [ ! -z ${AMULET_TEST} ]; then
    sudo su travis -c '. .venv/bin/activate && bundletester -vl DEBUG 
                      --test-pattern "${AMULET_TEST}*"';
   fi
 - if [ ! -z ${AMULET_TEST} ]; then sudo su travis -c 'juju status'; fi
 - if [ ! -z ${AMULET_TEST} ]; then
    sudo su travis -c \
       'juju ssh mysql/0 sudo cat /var/log/juju/unit-mysql-0.log';
   fi
 - if [ ! -z ${AMULET_TEST} ]; then
    sudo su travis -c 'juju ssh mysql/0 sudo cat /var/log/mysql/error.log';
   fi

Result: Click image to visit build on Travis CI and view output