Solita CD Tools

This is a collection of Continuous Delivery tools and documentation built by Solita. It is released under the MIT license.

Tutorials

Jenkins Configuration Management

This tutorial will show you how to manage the configuration of a Jenkins installation with Ansible and Jenkins Job DSL. At the end of the tutorial, you’ll have your Jenkins installation’s configuration under version control and you’ll be able to test changes to the configuration locally in a virtual machine before applying them to your CI server.

First we’ll create a configuration for a Jenkins installation that runs on your computer in a virtual machine. Then we’ll apply the same configuration to a Linux server, either updating the configuration of an existing Jenkins installation or creating a new installation.

This tutorial covers the basics of Vagrant, Ansible and Jenkins Job DSL. If you’re proficient with all these tools, you can probably skip this tutorial and jump straight to the reference documentation of the solita.jenkins Ansible role.

Prerequisites

Before continuing with this tutorial, you need to have the following software installed:

We’ll be using Git via its command-line interface, but don’t worry if you don’t have experience with it – the instructions should be clear enough. (If they are not, please create an issue!)

Setting up the Virtual Machine

One of the biggest benefits of configuration management is that we can test our changes on a test server before applying them to a production server. Virtual machines work especially well as test servers because if we make a mistake and get a server into a funny state, we can simply destroy it and recreate it in a known state.

We’ll manage our virtual machines with a tool called Vagrant, which allows us to describe what kind of machines we want with a simple text-based configuration file. Vagrant supports many different virtualization tools, but we’ll be using VirtualBox, which should have been installed along with Vagrant.

First we’ll create a directory that will hold both our Jenkins configuration and the Vagrant configuration of the virtual machine that we use for testing. As a starting point, we’ll use a Ubuntu-based Vagrant configuration that pre-installs Ansible, which we’ll need later in this tutorial. We need to clone it with Git, so open up a terminal (on Linux and OS X) or Git Bash (on Windows).

Note

The shell starts in your home directory (/home/yourname, /Users/yourname, or C:\Users\yourname). If you want to place your Jenkins configuration somewhere else, change the directory with cd:

# Linux and OS X
cd /where/you/want/the/conf
# Windows. Git Bash doesn't like the backslashes in Windows-style paths, so
# we need to put the path in single quotes.
cd 'C:\where\you\want\the\conf'

Clone the Vagrant configuration that we’ll use as our starting point:

git clone https://github.com/solita/vagrant-ansible.git jenkins

Note

The configuration we just cloned initializes the virtual machine with the IP address 192.168.50.76. If you want to use some other address, find 192.168.50.76 in the file called Vagrantfile and replace it with another value before you run vagrant up.

Now enter the cloned repository, which contains our Vagrant configuration, and start the virtual machine with vagrant up:

cd jenkins
vagrant up

With this command, Vagrant downloads a disk image of an empty Ubuntu server, boots a virtual machine, and sets it up according to its configuration file (the Vagrantfile). This will take several minutes, so now is good time to go grab a coffee (or a non-caffeinated beverage, if it’s getting late and you don’t want to mess up your night’s sleep).

If all goes well, the command’s output should look something like this:

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/trusty64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'ubuntu/trusty64' is up to date...
==> default: Setting the name of the VM: jenkins_default_1450333475157_82556
==> default: Clearing any previously set forwarded ports...
...
blah blah blah
...
==> default: Collecting pycrypto>=2.6 (from ansible==2.0.0)
==> default: Collecting ecdsa>=0.11 (from paramiko->ansible==2.0.0)
==> default:   Using cached ecdsa-0.13-py2.py3-none-any.whl
==> default: Collecting MarkupSafe (from jinja2->ansible==2.0.0)
==> default: Installing collected packages: ecdsa, pycrypto, paramiko, MarkupSafe, jinja2, PyYAML, ansible
==> default:   Running setup.py install for ansible
==> default: Successfully installed MarkupSafe-0.23 PyYAML-3.11 ansible-2.0.0 ecdsa-0.13 jinja2-2.8 paramiko-1.16.0 pycrypto-2.6.1

Now that the virtual machine is up and running, we can connect to it with SSH:

vagrant ssh

If everything’s working, you should be greeted with the following output:

Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-71-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

  System information as of Thu Dec 17 06:24:57 UTC 2015

  System load:  0.76              Processes:           80
  Usage of /:   3.4% of 39.34GB   Users logged in:     0
  Memory usage: 25%               IP address for eth0: 10.0.2.15
  Swap usage:   0%

  Graph this data and manage this system at:
    https://landscape.canonical.com/

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

0 packages can be updated.
0 updates are security updates.


(ansible-1.9-env)vagrant@vagrant-ubuntu-trusty-64:/ansible$

You’re now in a shell on the virtual machine and any commands you type here are executed on the virtual machine, not on your own. To get back to the shell on your own machine, use the exit command in the virtual machine’s shell:

(ansible-1.9-env)vagrant@vagrant-ubuntu-trusty-64:/ansible$ exit
logout
Connection to 127.0.0.1 closed.
Vagrant Survival Guide

You can always get back to the virtual machine’s shell by following the same steps we just used:

  1. Enter the Jenkins configuration directory: cd /path/to/jenkins
  2. Start the virtual machine if it has been stopped: vagrant up
  3. Open a shell on the virtual machine: vagrant ssh

If you don’t need the virtual machine and want to free the memory and CPU resources it’s using, you can stop it with vagrant halt.

To free the disk space allocated to the virtual machine, you can destroy it with vagrant destroy. Note that the virtual machine is not stored in the same directory as its Vagrantfile, so just removing the jenkins directory won’t destroy the virtual hard disk.

Finally, for advanced management and debugging, start Oracle VM VirtualBox, the VirtualBox management interface, from your operating system’s menu. There you can see VirtualBox’s logs or shut down and destroy a virtual machine even if you’ve lost Vagrant’s bookkeeping files and can’t manage the machine with Vagrant.

For more information, see the documentation for Vagrant and VirtualBox.

Installing Jenkins with Ansible

We want our Jenkins installations to be as reproducible as possible, so our goal is to configure nothing by hand. Instead, we describe the configuration that a server should have and let Ansible make the required changes. These descriptions are written in the YAML format in files called playbooks.

Let’s create a playbook that tells Ansible that the Vagrant-managed virtual machine should have Jenkins installed. Create a new file called jenkins.yml in the jenkins directory with the following contents:

1
2
3
4
---
- hosts: vagrant
  roles:
    - solita.jenkins

We won’t go into details of Ansible playbooks – for that, see Ansible’s documentation – but here’s a big-picture view of what each line in the file contains:

  1. Begin a new YAML document.
  2. Configure the server called “vagrant” (that’s our virtual machine).
  3. Apply some roles – reusable pieces of Ansible configuration – to the server.
  4. The only role we need is solita.jenkins.

Note

YAML is very fussy about indentation, so make sure that you indent each line exactly as shown. You should also change your editor to only add spaces and never tabs.

Installing and Updating Roles

We use the role solita.jenkins, so we need to tell Ansible where it can be found. Create a file called requirements.yml in the jenkins directory:

---
- src: https://github.com/solita/ansible-role-solita.jenkins.git
  name: solita.jenkins

Because Ansible does not officially support Windows, we’ll be running it in our virtual machine, so connect to it with vagrant ssh. There you can use update-roles.sh to install the role:

c:\> vagrant ssh
$ ./update-roles.sh

Under the hood the script uses Ansible galaxy to install role with dependencies.

Note

If you get a message saying [ERROR]: Unable to open requirements.yml, you may have created the file in the wrong directory. Make sure you have a file called requirements.yml in the jenkins directory, next to the Vagrantfile.

If you get message saying -bash: ./update-roles.sh: Permission denied the script has lost execution right somehow. Call chmod +x update-roles.sh to add the permission to execute the script.

Running the Playbook

Now that we have installed our playbook’s dependencies (the solita.jenkins role), you can run the playbook:

ansible-playbook -i environments/vagrant/inventory jenkins.yml

This will take some time as Ansible has to download some large packages (Java and Jenkins). Unfortunately there’s no way to monitor a task’s progress in Ansible, so you just have to trust that it’s not stuck, even if it’s taking a long time.

Once the playbook run is complete, Jenkins should be installed, running, and listening on the virtual machine’s port 8080. The virtual machine’s IP address is 192.168.50.76, so you can access the Jenkins installation with a web browser at http://192.168.50.76:8080/.

_images/jenkins_ansible_installation.png

Configuring Jenkins

The Jenkins installation we just created (in Installing Jenkins with Ansible) is now in its default settings. To change the settings, we set variables in the playbook.

By default the Jenkins installation is unsecured and anonymous users can act as administrators. Let’s secure the installation and add a password-protected user account.

We can see in the documentation of the solita.jenkins role that we need to set the variables solita_jenkins_security_realm and solita_jenkins_users. We can do that by changing the playbook jenkins.yml like this (the lines 3 to 6 are new):

1
2
3
4
5
6
7
8
---
- hosts: vagrant
  vars:
    solita_jenkins_security_realm: jenkins
    solita_jenkins_users:
      - some_user
  roles:
    - solita.jenkins

To apply the changes to the Jenkins installation, run the playbook just as before:

ansible-playbook -i environments/vagrant/inventory jenkins.yml

If you now try to access Jenkins, you will be redirected to a login screen:

_images/jenkins_ansible_login.png

To log in as some_user, we have to know the user’s password. When solita.jenkins creates a new user, it writes the user’s default password into a file called solita_jenkins_default_password next to the Ansible inventory file (the file we passed to ansible-playbook with the -i option). You can print the file’s contents with cat on the virtual machine:

cat environments/vagrant/solita_jenkins_default_password/some_user

Alternatively, since the virtual machine’s /ansible directory is synchronized with the jenkins directory on your machine, you can just open the file with a text editor.

Open the file and read the password, and you should be able to use it to log in as some_user.

Further configuration

solita.jenkins can do much more than just add users. See its documentation for all of the configuration options.

Ansible Roles

solita.jenkins

A Jenkins installation completely configured with Ansible. This role builds on top of geerlingguy.jenkins, adding the following features:

  • User management
  • Job and view configuration with Job DSL

This role is tested on Ubuntu 14.04 LTS (Trusty Thar), but it should work on all operating systems supported by the upstream role.

Example

With this role and the Job DSL plugin, your entire Jenkins configuration can be stored in text files that look something like this:

# playbook.yml
---
- hosts: jenkins-server
  vars:
    solita_jenkins_plugins:
      - timestamper
      - git
    solita_jenkins_security_realm: jenkins
    solita_jenkins_users:
      - alice
      - bob
    solita_jenkins_absent_users:
      - eve
  roles:
    - solita.jenkins
// jobs/Main.groovy
job('DSL-Tutorial-1-Test') {
    scm {
        git('git://github.com/jgritman/aws-sdk-test.git')
    }
    triggers {
        scm('*/15 * * * *')
    }
    steps {
        maven('-e clean test')
    }
}

Installation

You can install this role and its dependencies with ansible-galaxy. First add the following lines to your requirements.yml:

# requirements.yml
---
- src: https://github.com/solita/ansible-role-solita.jenkins.git
  version: v1.3.2
  name: solita.jenkins

Then run ansible-galaxy to install the role:

ansible-galaxy install -p path/to/your/roles -r requirements.yml

Plugins

To add plugins to your Jenkins installation, list their plugin IDs in the variable solita_jenkins_plugins. You can find a plugin’s ID on its wiki page.

Note

This role depends on the Job DSL plugin and always installs it.

To limit role application to plugins, use the tag solita_jenkins_plugins.

Examples

Install the timestamper and git plugins:

# playbook.yml
---
- hosts: jenkins-server
  vars:
    solita_jenkins_plugins:
      - timestamper
      - git
  roles:
    - solita.jenkins

Security

A security realm means the method that Jenkins uses to authenticate users. To enable or disable authentication for your Jenkins installation, set the variable solita_jenkins_security_realm to one of the following values:

none
Disables security.
jenkins
The default setting. Enables security, authentication against Jenkins’ own user database, and matrix-based authorization.
User Management

To add and remove users, add their usernames to the lists solita_jenkins_users and solita_jenkins_absent_users, respectively.

Note

User management is only available when solita_jenkins_security_realm is set to 'jenkins'.

Note

Currently only administrator users are supported.

When a new user is created, the user’s default password will be read from the file solita_jenkins_default_password/<username> in the inventory directory. If the file does not exist, a file containing a random password is created. For example, if your inventory file is environments/vagrant/inventory and you add the user alice, you can find their default password in the file environments/vagrant/solita_jenkins_default_password/alice.

Note

If you don’t have an inventory file (e.g. if you create the servers using the Ansible cloudformation module), solita.jenkins will try to write the generated passwords into /etc/ansible/solita_jenkins_default_password/ and fail. In this case you can set the variable solita_jenkins_password_dir to the directory where you want to place the passwords.

To limit role application to security settings and user management, use the tag solita_jenkins_security.

Examples

Enable security, add users alice and bob, and remove user eve:

# playbook.yml
---
- hosts: jenkins-server
  vars:
    solita_jenkins_security_realm: jenkins
    solita_jenkins_users:
      - alice
      - bob
    solita_jenkins_absent_users:
      - eve
  roles:
    - solita.jenkins

Disable security:

# playbook.yml
---
- hosts: jenkins-server
  vars:
    solita_jenkins_security_realm: none
  roles:
    - solita.jenkins

Only update security settings and users:

ansible-playbook playbook.yml --tags solita_jenkins_security

Place the generated passwords in /tmp/default-passwords:

ansible-playbook playbook.yml -e solita_jenkins_password_dir=/tmp/default-passwords

Credentials

The Jenkins credentials plugin allows you to store credentials in Jenkins. This role allows you to add, change and remove those credentials.

Managing Credentials

You should never store credentials in your regular playbooks or inventories. Instead use Ansible Vault to create an encrypted file for them:

# Create a new encrypted file:
ansible-vault create group_vars/all/credentials

# Edit an existing encrypted file:
ansible-vault edit group_vars/all/credentials

To add or modify credentials, add them to solita_jenkins_credentials, which is a map from credential ID to the credential itself. To remove credentials, list their IDs in solita_jenkins_absent_credentials.

Examples

Add a username/password credential with the ID alice, and an SSH key with the id bob:

# Encrypted var file
---
solita_jenkins_credentials:
  alice:
    username: alice
    password: swordfish
    description: Alice's password       # Optional

  bob:
    username: bob                       # Optional
    private_key: |
      -----BEGIN RSA PRIVATE KEY-----
      MIIJKgIBAAKCAgEAr959S9hp6tUFqrVzxs31+vYZWyKHia9SBWtmRthDlO/uMnr/
      VoEnRVqUmjlJcgSMhIl7d5Daqkc8sxMjzipklD6ZvIliQRsiEMePuIQs5i8/u9jO
      ...
      gTUbb3MzN7f+G2zihIl5uu8Lp7hzeRnvJ6tP3jeVPog9SRcX6Ve8kZr/T+chVQ4t
      da0O2tRUD1uRrlEovhL3PQT2fTzkV8F4YEOl5afVopLb1fK6sDef2i0jr1P0vw==
      -----END RSA PRIVATE KEY-----
    passphrase: swordfish               # Optional
    description: Bob's SSH Key          # Optional
# playbook.yml
---
- hosts: jenkins-server
  roles:
    - solita.jenkins

Note

Use YAML’s pipe syntax to keep the linebreaks in the private key.

Remove the credentials with the ID eve:

# playbook.yml
---
- hosts: jenkins-server
  vars:
    solita_jenkins_absent_credentials:
      - eve
  roles:
    - solita.jenkins

Only update credentials:

ansible-playbook playbook.yml --tags solita_jenkins_credentials

Jobs and Views

You can define jobs and views with a Job DSL script. The role expects your Job DSL scripts to be stored in files ending with .groovy in the jobs directory next to your playbook. If you want to use Ansible variables in your script, you can turn the script file into a Jinja2 template by changing its filename to end with .groovy.j2.

To change the Job DSL script directory, set the variable solita_jenkins_jobs_dir.

To limit role application to job and view updates, use the tag solita_jenkins_jobs.

Examples

If you create your script in the default location, no configuration is needed:

// jobs/Main.groovy
job('my-new-job') {
    // ...
}
# playbook.yml
---
- hosts: jenkins-server
  roles:
    - solita.jenkins

If the script’s filename ends in .groovy.j2, it can contain Ansible variables:

// jobs/Main.groovy.j2
job('{{ job_name | default("foo") }}') {
    // ...
}

If you want to place your scripts somewhere else, set the variable solita_jenkins_jobs_dir:

# playbook.yml
---
- hosts: jenkins-server
  vars:
    solita_jenkins_jobs_dir: "{{ playbook_dir }}/files/jenkins/jobs"
  roles:
    - solita.jenkins

Only update jobs and views:

ansible-playbook playbook.yml --tags solita_jenkins_jobs

solita.ansible

An Ansible role for installing Ansible.

Example

# playbook.yml
---
- hosts: servers
  vars:
    solita_ansible_version: 2.1.0.0
  roles:
     - solita.ansible

Installation

You can install this role with ansible-galaxy. First add the following lines to your requirements.yml:

 # requirements.yml
 ---
 - src: https://github.com/solita/ansible-role-solita.ansible.git
   version: v1.1.0
   name: solita.ansible

Then run ansible-galaxy to install the role:

ansible-galaxy install -p path/to/your/roles -r requirements.yml

Ansible version

By default, the role will install the latest version of Ansible. To install a specific version, set the variable solita_ansible_version.

Examples
# playbook.yml
---
- hosts: servers
  vars:
    solita_ansible_version: 2.1.0.0
  roles:
     - solita.ansible

Roles

To install roles, list them in a requirements file called requirements.yml next to your playbook. The contents of /etc/ansible/roles will be updated to match requirements.yml.

Note

/etc/ansible/roles will be removed before installing the new roles, so if you create requirements.yml, make sure to list all your roles!

If you don’t have a requirements.yml, no changes will be made to /etc/ansible/roles.

You can change the path to the requirements file by setting the variable solita_ansible_requirements_file. Set it to an empty string to disable role installation.

Examples

Change the path to the requirements file:

# playbook.yml
---
- hosts: servers
  vars:
    solita_ansible_requirements_file: roles.yml
  roles:
     - solita.ansible

Disable role installation:

# playbook.yml
---
- hosts: servers
  vars:
    solita_ansible_requirements_file: ""
  roles:
     - solita.ansible

License

The MIT License (MIT)

Copyright (c) 2015 Solita

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.