Last week I was finishing up coding the last feature on a brand new Rails app. I happily closed out that Jira ticket and jumped into the next one.

Set up the Rails application test suite on the internal Jenkins CI

My initial reaction was: oh great, I’ll probably be spending the next 5 hours ssh’d into a Jenkins server trying to figure out why Nokogiri can’t build native extensions on Ubuntu 12.04 because I decided to use Ruby 2.3 and for some reason no Ruby package manager was ever installed. Argh. Then I read the next line.

This should be done in Docker containers.

Challenge accepted. In this post, I will show you how GE Digital uses Docker containers for running our automated test suite. We will walk through an sample a Rails application that is backed by MongoDB as its persistence layer. You follow along with the source code over here: https://github.com/imranraja85/docker_rails_test

Why Docker Containers?

Running automated tests with Docker containers is great way to get started with learning Docker. It is also low risk because you are only utilizing Docker for testing (until you are ready to go a bit further into container orchestration). Perhaps the biggest advantage here is portability. You can set up a containerized test suite to run locally on your laptop and once its done just drop it in your favorite Continuous Integration service.

Before we start, there are 3 things you will need to install:

brew install docker brew install docker-compose brew install docker-machine

Docker Machine Setup

Docker Machine is only required if you’re on OS X because the Docker engine only runs on native linux. Native Docker for OS X is in beta but until then this will have to do.

Once you have those installed and have set up a docker-machine.

docker-machine create \

-d virtualbox \

--engine-env http_proxy=$PROXY \

--engine-env https_proxy=$PROXY \

--virtualbox-host-dns-resolver \

dev

If you work behind a proxy you can configure it here by utilizing the engine-env flags. Also, if you need to access an internal GitHub repository then you will want to use the `virtualbox-host-dns-resolver` which instructs the VM to use the DNS revolver of the host machine.

In order to get this running there are 3 files we will create: a Dockerfile, a Docker Compose file and a bash script. Let’s go through each one.

Dockerfile

The Dockerfile is a set of instructions for building your application into an image. Here is an example Dockerfile:

FROM ruby:2.2.2-wheezy

MAINTAINER Imran Raja

RUN apt-get update && apt-get install -qq -y build-essential git-core — fix-missing — no-install-recommends

WORKDIR /tmp

RUN echo “gem: — no-ri — no-rdoc” > ~/.gemrc

ADD Gemfile /tmp/Gemfile

ADD Gemfile.lock /tmp/Gemfile.lock

RUN bundle install — system — jobs 10 — retry 5

WORKDIR /home/app

RUN pwd

COPY . /home/app

EXPOSE 3000

CMD [“rake”, “assets:precompile”]

CMD [“rails”, “server”]

This Dockerfile is using a base image of ruby 2.2 to build our application as well as some caching enhancements, and a few commands to run our container.

Docker Compose

The Docker Compose file is YAML configuration file used to define and run multiple container applications. Let’s take a look at our Docker Compose file, aptly named test.yml:

version: ‘2’

services:

mongodb:

image: mongo:3.2

app:

image: rails_app

build: .

depends_on:

- mongodb

We are defining two 2 containers: ‘app’, our Rails application, and ‘database’, our MongoDB engine. For ‘app’, we set the build context to be the current directory. By default, Version 2 of docker-compose makes containers discoverable and reachable to each other by host name as long as they exist on the same network. All you have to do is specify the host name inside your database.yml or mongoid.yml.

Test Runner Bash Script

Let’s create a bash script called test_runner.sh that will start up our containers, execute our test suite, tear down our containers and then depending on the test suite success, upload the image to a Docker Repository.

# Make sure old containers are gone

docker-compose -f test.yml -p docker_rails_test stop

docker-compose -f test.yml -p docker_rails_test rm — force — all -v # Build the GEAR controller image

docker-compose -f test.yml -p docker_rails_test build # Run RSpec tests

docker-compose -f test.yml -p docker_rails_test run — rm app rspec

ERR=$? # Stop and remove GEAR Controller containers

docker-compose -f test.yml -p docker_rails_test stop

docker-compose -f test.yml -p docker_rails_test rm — force — all -v if [ $ERR -eq 0 ]; then

docker push imranraja85/railsapp

fi

Here we are first doing a little clean up by stopping and removing any containers that may have been left over from a previous run. Then we run the ‘web’ container and pass in the test runner command, rspec. Since RSpec returns a proper exit status depending on whether the test suite passed or failed, we store the code for later use. We stop and remove all running containers.

If you want to save your Docker images then we can check on the test exit status code and push the image up to Docker Hub. Or if you are running an open source registry or the commercial Docker Trusted Registry you can login to that by adding this line before the docker push:

docker login -u imran -p supersecretpassword your.internal.registry.com

Note that this example does not include any image tagging. Depending on your tagging strategy, you will want to modify this script to tag before pushing the image.

You should now be able to execute this script locally and watch as it starts 2 containers, runs your test suite inside those containers, tears it down and pushes the result up to a Docker repository.

./test_runner.sh

Now you have an executable, version controlled test script that you can easily execute either locally or on your Jenkins slave.

Jenkins Setup

Since we have the script embedded in our code repository, all we need to do now is setup a new Jenkins project, add our GitHub repository link and add a build step to execute our shell script!

Check out the source code here: https://github.com/imranraja85/docker_rails_test

Sources:

Using Docker by Adrian Mouat: http://shop.oreilly.com/product/0636920035671.do

Docker Compose Reference: https://docs.docker.com/compose/overview/

Dockerfile Reference: https://docs.docker.com/engine/reference/builder/

Docker Machine Reference: https://docs.docker.com/machine/