Lead Image © Amri Wolfgang, 123RF.com

Lead Image © Amri Wolfgang, 123RF.com

Jenkins Configuration as Code

Buttle Your Code

Article from ADMIN 51/2019
The move from Groovy scripts to Jenkins Configuration as Code simplifies the initialization of Jenkins and Jenkins plugins.

Jenkins is one of the most popular continuous integration and continuous delivery (CI/CD) tools in the DevOps world. During its lifetime, its configuration process has evolved: A complex GUI wizard for configuration appeared, DSL jobs and pipeline plugins debuted, and more and more Groovy scripts for initializing startup popped up around the Internet. From my point of view, the weakest elements were those initializing scripts, because each company – or even teams in the same company – implemented them differently. Today, thanks to Jenkins Configuration as Code (JCasC), you can fine tune both Jenkins and its plugins with one common approach through the use of easily understandable YAML files.

Jenkins Startup

When Jenkins starts, it reads all command-line parameters and some environmental variables and executes all *.groovy files in lexical order under the ${JENKINS_HOME}/init.groovy.d/ directory; therefore, administrators must learn the Groovy language to prepare scripts. Groovy is a nice, scriptable offspring of Java that lacks its elder's private variables and methods and is used in many Java virtual machine (JVM) systems. In Jenkins, Groovy can be used to modify virtually any aspect of a given CI instance. However, as stated above, you have to be able to write and maintain complex scripts in "yet another language."

Thanks to the JCasC authors, most of the initializing Groovy code becomes obsolete and replaceable by far more readable and maintainable YAML files.

The Chicken and the Egg

JCasC can install plugins, even specific versions, but it is a plugin itself; therefore, it has to be installed on Jenkins. Here comes the chicken and the egg problem: To install plugins, you have to have a plugin that installs them. This obstacle is easily solvable in Docker-based Jenkins instances by using the install-plugins.sh script. Personally, I prefer to install plugins on my own by the simplest method; hence, my Jenkins installation is one Dockerfile (Listing 1) that launches a container with Jenkins and the JCasC plugin. With this code, you can set up Jenkins and install all the components required by JCasC.

Listing 1

Sample Dockerfile

01 FROM openjdk:8-jdk-alpine
03 RUN apk add --no-cache git openssh-client curl bash # for Jenkins AWT
04    ttf-dejavu
07 ARG UID=1000
09 ARG JENKINS_HOME=/ephemeral/jenkins
11 # for main web interface:
15 ENV CASC_JENKINS_CONFIG=/jcasc_config
17 COPY jcasc_config/* ${CASC_JENKINS_CONFIG}/
19 RUN mkdir -p ${CASC_JENKINS_CONFIG} && mkdir -p ${JENKINS_HOME}/plugins && adduser -h ${JENKINS_HOME} -u ${UID} -s /bin/bash -D ${JENKINS_USER} && chown -R ${UID} ${JENKINS_HOME} ${CASC_JENKINS_CONFIG}
21 # Jenkins home directory is a volume, so configuration and build history
22 # can be persisted and survive image upgrades
26 ARG JENKINS_UC=https://updates.jenkins.io/stable-2.150/latest/
27 ENV PLUGINS="jdk-tool script-security command-launcher configuration-as-code configuration-as-code-support configuration-as-code-groovy"
29 RUN curl -sSfL --connect-timeout 20 --retry 3 --retry-delay 0 --retry-max-time 60 ${JENKINS_UC}/jenkins.war -o ${JENKINS_HOME}/jenkins.war
30 RUN for P in ${PLUGINS}; do curl -sSfL --connect-timeout 20 --retry 2 --retry-delay 0 --retry-max-time 60 ${JENKINS_UC}/${P}.hpi -o ${JENKINS_HOME}/plugins/${P}.jpi; done
32 ENV JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true -Djenkins.install.runSetupWizard=false ${JENKINS_JAVA_OPTIONS:-}"

First Configuration

Now that JCasC is present, you can make use of it. To begin, assume you need an update center proxy setup, a few additional plugins, LDAP authorization, a few administrators, the Jenkins URL, and a seed job that can create others on demand.

First things first: You can set up one big YAML file that does everything, but I honestly prefer small files, executed in a controlled order. The YAML file can go wherever you want. The CASC_JENKINS_CONFIG environment variable should be set to point to a single YAML file or directory, from which all *.yaml or *.yml files will be executed in alphabetical order. If you do not set the CASC_JENKINS_CONFIG variable, the plugin will look for a single config file in ${JENKINS_ROOT}/jenkins.yaml by default.

For easier maintenance, I put each configuration step in a separate file. JCasC loads files in alphabetical order, so each file is prefixed with two digits to control the execution sequence. To make this solution future-proof, I left gaps between file names in case I have to add other files later that need to be launched at a specific stage.

Setting up the Jenkins host URL though JCasC requires three lines:

### 02_baseURL.yml ###
   url: "http://misiu.pl:8080/"

Compare this with the code needed in a Groovy script (Listing 2). As you can see, Groovy is noticably more complicated, and the YAML file is more easily read.

Listing 2

Groovy Config Example

01 import jenkins.model.JenkinsLocationConfiguration;
03 String newRootURL = "http://misiu.pl:8080/";
05 JenkinsLocationConfiguration jlc = JenkinsLocationConfiguration.get()
07 jlc.setUrl(newRootURL);
08 jlc.save();

Listing 3 shows two sample YAML files used by JCasC to set up plugins. The first file (05_proxy.yml) sets the proxy for downloading plugins, and the second (10_plugins.yml) defines the proxy server access credentials (discussed later). Once the proxy is ready, you can install plugins.

Listing 3

Setting Up Plugins

01 ### 05_proxy.yml ###
02 plugins:
03  proxy:
04    name: ""
05    port: 8080
07 ### 10_plugins.yml ###
08 plugins:
09  required:
10    matrix-auth: latest
11    ldap: latest
12    my-pro-plugin: http://download.mis.com/my-pro-plugin-3.14.91.jpi

The definition of a plugin comprises a name and version – latest or explicit (e.g., 3.14) or the URL to the .hpi source. When installing plugins, you should note that the explicit version does not work with all update centers. Moreover, JCasC does not perform a Jenkins restart, so with a version change, more complex installations might be unstable without a manual restart. The JCasC team and Jenkins project are considering improving this plugin "feature," because it is a bit unreliable.

Access configuration consists of connecting to the LDAP server and assigning the appropriate permissions to chosen users, which you can put all in one YAML script (Listing 4). First, you configure the LDAP server. A YAML reference along with some examples are available in the GitHub plugins repository [3], so you don't need to memorize it here. Second, you define the permission strategy. All options are available, so I chose the most popular one: globalMatrix (line 12). Each permission is defined as a new entry in the list of strings.

Listing 4

Configuring Matrix Authorization

01 jenkins:
02  securityRealm:
03    ldap:
04      configurations:
05        - groupMembershipStrategy:
06            fromUserRecord:
07              attributeName: "user"
08          inhibitInferRootDN: false
09          rootDN: "dc=amecme,dc=org"
10          server: "ldaps://ldap.szandala.org"
11  authorizationStrategy:
12    globalMatrix:
13      grantedPermissions:
14        - "Overall/Read:anonymous"
15        - "Overall/Administer:szandala"
16        - "Job/Configure:karolinka"

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs

Support Our Work

ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.