Jenkins Configuration as Code

Buttle Your Code

Keeping Secrets

Everyone has secrets, so sometimes during Jenkins configuration you need to use credentials. JCasC comes with support for HashiCorp Vault, an external tool that stores and controls access to secrets and other sensitive data and allows you to use encrypted values and decrypt them on the fly during configuration. To begin, you need a running instance of HashiCorp Vault and credentials – a token or username and password – to access Vault. Next, export a few variables or prepare a secrets file, set in the CASC_VAULT_FILE environment variable:

CASC_VAULT_PW=PASSWORD
CASC_VAULT_USER=medivh
CASC_VAULT_TOKEN=TOKEN
CASC_VAULT_PATH=secret/jenkins/master
CASC_VAULT_URL=https://vault.szandala.com
CASC_VAULT_MOUNT=ldap

Now you can feed data to Vault:

vault write   -address=https://vault.szandala.com   secret/jenkins/master   SECRET_PASS="garona-halforcen"   SSH_MAGIC_KEY=@/vault/file/secrets/  jenkins_ssh_key

JCasC then substitutes variables in YAML with data from Vault (Listing 5).

Listing 5

JCasC YAML

01 credentials:
02   system:
03     domainCredentials:
04       - credentials:
05           - usernamePassword:
06               scope: "GLOBAL"
07               id: "szandala"
08               username: "szandala"
09               password: "${SECRET_PASS}"
10               description: "User szandala's credentials"

Create a Job

Configuration is not just about setting up the Jenkins master; it can also include creating an initial set of jobs with job-dsl-plugin and a job-dsl script, which requires the Configuration as Code Support plugin in addition to the basic JCasC plugin already installed; otherwise, the job's root element cannot be used.

The Job DSL plugin uses Groovy syntax for its job configuration DSL (domain-specific language), so you have to mix YAML and Groovy within a YAML file. Listing 6 shows a YAML script with a jobs section that creates a so-called seed job that is used later to deploy other jobs.

Listing 6

YAML-Groovy Hybrid File

01 jenkins:
02   systemMessage: "Creating SEED job"
03 jobs:
04   - script: |
05         freeStyleJob('TOOL_JobsMakel').with {
06             displayName('Deploy Jenkins job definitions')
07             label('master')
08             parameters {
09                 stringParam('SCM_BRANCH','master',
10                     "Source branch for SCM repository, default is master")
11             }
12             scm {
13                 git('${SCM_REPO}', '${SCM_BRANCH}')
14             }
15             steps {
16                 dsl(['jenkins_jobs/*.groovy'])
17             }
18         }

Last Resort

Sadly, YAML configuration will hardly ever cover 100% of a Jenkins configuration. That gap could be easily filled with Groovy initializing scripts, but they come with disadvantages: They require a bit of Groovy knowledge, and they cannot be "reloaded" easily without a Jenkins restart. To overcome these cons, the configuration-as-code-groovy supporting plugin offers a last resort in the form of a groovy element with a list of Groovy scripts to be run. Although this solution still requires a bit of Groovy proficiency, the scripts can be rerun along with a basic JCasC reload.

The best example of an unsupported plugin is gerrit-trigger , which allows Jenkins to listen to events on a Gerrit server; however, before it can start listening, the host URL has to be added to the Jenkins configuration. Basic JCasC cannot do this (at the time of this writing), so the setup can be done through the GUI or a Groovy script. Because the GUI is considered impractical for true DevOps, you need to put the Groovy script into JCasC YAML (Listing 7).

Listing 7

YAML groovy Section

01 groovy:
02  - script: |
03      import jenkins.model.Jenkins;
04      import net.sf.json.JSONObject;
05      import com.sonyericsson.hudson.plugins.gerrit.trigger.GerritServer;
06
07      if ( Jenkins.instance.pluginManager.activePlugins.find { it.shortName == "gerrit-trigger" } != null ) {
08          println("Setting gerrit-trigger server plugin");
09
10          def gerritPlugin = Jenkins.instance.getPlugin(com.sonyericsson.hudson.plugins.gerrit.trigger.PluginImpl.class);
11          gerritPlugin.getPluginConfig().setNumberOfReceivingWorkerThreads(3);
12          gerritPlugin.getPluginConfig().setNumberOfSendingWorkerThreads(1);
13
14          def serverName = "grenoble-gerrit";
15          GerritServer server = new GerritServer(serverName);
16          def config = server.getConfig();
17
18          def triggerConfig = [
19              'gerritHostName':"gerrit.com",
20              'gerritSshPort':29418,
21              'gerritUserName':"szandala_jenkins",
22              'gerritFrontEndUrl':"https://gerrit.com"
23          ];
24
25          config.setValues(JSONObject.fromObject(triggerConfig));
26          server.setConfig(config);
27
28          // avoid duplicate servers on the server list;
29          if ( gerritPlugin.containsServer(serverName) ) {
30              gerritPlugin.removeServer(gerritPlugin.getServer(serverName));
31          }
32
33          gerritPlugin.addServer(server);
34          server.start();
35          server.startConnection();
36          println("Setting ${serverName} completed");

As you can see, it is just a copy of the init.groovy.d script as inline code to YAML. For the sake of readability, you can move this snippet to a separate file (e.g., GerritTriggerConfigurator.groovy) and run it locally (with a file predicate) or remotely (e.g., on GitHub; Listing 8).

Listing 8

Local/Remote Runs

01 groovy:
02  - file: /tmp/GerritTriggerConfigurator.groovy
03  - url: https://raw.githubusercontent.com/szandala/configuration-as-code-plugin/master/integrations/src/test/resources/io/jenkins/plugins/casc/GroovySetProxy.yml

Buy this article as PDF

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

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus