Django Testing in a Jenkins Docker Container

Django is a popular Python-based web application framework that I’ve been using to write sample applications for my OpenStack testing. Jenkins is a great continuous integration tool for automated builds and testing. Why not combine the two to make my life a little easier?

My Django application development is being done on my laptop. For Jenkins, I wanted to run the server in a self-contained environment that would not be affected by versions of software on my local desktop.

Vagrant would normally be my go-to solution for a quick dev environment, but I’m overseas using questionable broadband services: a few vagrant up/vagrant provision/vagrant destroy cycles would bring my connection to a halt. I opted, instead, to use a Docker container for Jenkins. This way, if I mess up any package dependencies, it’s a simple matter of adding another layer to my existing container instead of building from scratch each iteration.

Before I go into detail regarding my build process, here is the TL;DR version:

  1. The official Jenkins container is a great starting point, but it does not support Django applications out-of-the-box
  2. I extended the Jenkins container (GitHub Repo) definition to include the Jenkins plugins that the django-jenkins Python module requires.
  3. I added the django-jenkins module to my Django application and modified my settings.py file to include directives for django-jenkins to follow during build steps.
  4. I deployed my Jenkins container and successfully performed unit tests for much win.
  5. Software versions:
    • Python 3.4.3
    • Django 1.8.4
    • Jenkins 1.625.3

Still with me? Alright, let’s get into the build…

Django Application Changes

First, on my development machine, I installed the django-jenkins module.

pip3 install django-jenkins

Next, I edited my application’s settings.py to include the new django-jenkins module and to specify tasks that I want Jenkins to perform above and beyond my unit tests.

#application-root/application/settings.py
INSTALLED_APPS = (
 'django.contrib.admin',
 ...
 'django.contrib.staticfiles',
 'my-app',
 'django_jenkins',
)
JENKINS_TASKS = (
    'django_jenkins.tasks.run_pylint',
)

NOTE: For the pylint task to run, the pylint PyPI module needs to be installed on the Jenkins master or slave that will be performing the Django testing.

Jenkins Container Modifications

Out-of-the-box, Jenkins supports the Java programming language and the SVN version control system. However, as I covered in the DevOps for VMware Administrators book, there are many plugins available to extend a Jenkins server’s functionality to suit your application.

First, for my custom container specification, I created the following Dockerfile:

FROM jenkins
# Change to root user to install required packages
USER root
RUN apt-get update -qq && apt-get install -qqy python3 python3-pip chromedriver
RUN pip3 install -q pymongo coverage jinja2 django==1.8.4
RUN pip3 install --upgrade selenium
# Change to the jenkins user for jenkins-specific modifications
USER jenkins
COPY plugins.txt /usr/share/jenkins/ref/
RUN /usr/local/bin/plugins.sh /usr/share/jenkins/ref/plugins.txt
# Back to root again?!??
USER root
RUN pip3 install django-jenkins
RUN pip3 install pylint
# Update per (Jason) Voorhees - Thanks!
# switch back to jenkins user instead of
# continuing to run as root!
USER jenkins

In the container build, I first switch to the root user to install the required versions of Python, the Python Package Index utility (pip), Chrome web driver for selenium GUI tests, and numerous python packages.

Next, I switch to the jenkins user to perform Jenkins-specific modifications including specifying the Jenkins plugins that will be required to support Django applications. The Jenkins container will not resolve plugin dependencies automatically. So, you will need to list out the pre-requisite packages in addition to three packages that are actaully required to support Django (violations and cobertura) and Git (git).

Before moving on, note that I switched back to the root user to install a couple of python packages that I realized were missing during my testing. Because I am using Docker, my image update takes a relatively short time since I only add new layers for the binaries that were missing.

Within the directory that the Dockerfile resides, I build the container image and then (optionally) push it to the Docker Hub:


docker build -t vmtrooper/jenkins:0.0.4 .
docker push vmtrooper/jenkins:0.0.4

I can specify my current image as the latest version and then push out this tag to the Docker Hub as follows:

docker tag -f vmtrooper/jenkins:0.0.4 vmtrooper/jenkins:latest
docker push vmtrooper/jenkins:latest

My First Django Build Job in Jenkins

Now, I can run my jenkins container and start building out my build job:

docker run -d -p 8080 -p 50000 vmtrooper/jenkins:0.0.4

I monitor the Jenkins container’s logs using its container ID as follows:


docker ps

docker logs <container id>

When the container is running, I access the Jenkins container using my Docker-Machine VM’s IP and the port mapped to the default 8080 Jenkins port:

New Jenkins Server Container Ready to Go!
New Jenkins Server Container Ready to Go!

 

Click on “New Item” or “create new jobs”, enter any name for your django appplication, and select the “Freestyle project” option

Select the Type of Project You Will Create
Select the Type of Project You Will Create

 

On the next screen, specify the Git repository where your code resides.

Source code repository settings
Source code repository settings

 

Next, specify how often to poll the repository for changes. I specified every 10 minutes.

Specify your repository poll interval
Specify your repository poll interval

 

Then, specify your Build Step using the “Execute Shell” option.

Adding a build step to the project.
Adding a build step to the project.

Here, I verify that Jenkins is in the workspace directory that contains the source code cloned from my Git repository, then I execute my Django tests using the jenkins keyword:


python3 manage.py jenkins --enable-coverage

Finally, specify the Post-Build Step to determine the folder that test results will be written to.

Django unit test results will be stored in this location

Click Save, and you’ll be taken to the project home screen. From here, you can click the Build Now link or wait for the new scheduled Git poll. When the build is complete, you can examine the test results to understand why the build succeeds or fails.

Project home screen. From here, you can execute ad hoc builds and view the results for all tests that have run.
Project home screen. From here, you can execute ad hoc builds and view the results for all tests that have run.

Do you have any tips for continuous integration of Django or other types of web applications? Share them below!

4 thoughts on “Django Testing in a Jenkins Docker Container”

  1. Cool write up. Just want to let you know that the USER root on line 12 causes jenkins to run as the root user when the container is run and is a security issue.

    root 6 79.2 2.4 5115064 147952 ? Sl 13:20 0:15 java -jar /usr/share/jenkins/jenkins.war

    adding USER jenkins on line 15 of your Dockerfile will resolve this.

    1. Jason, you are absolutely correct! I totally forgot to change back after adding the missing packages. I will update the image and the blog post accordingly. Thanks for checking out the blog even though it’s not Friday…or the 13th……… o_0

Leave a Reply

Your email address will not be published. Required fields are marked *