Add the groovy automation blog post from last week

This commit is contained in:
R. Tyler Croy 2017-07-31 11:20:18 -07:00
parent 0741b217b5
commit d7ce778c41
No known key found for this signature in database
GPG Key ID: 1426C7DC3F51E16F
2 changed files with 164 additions and 0 deletions

View File

@ -0,0 +1,164 @@
---
layout: post
title: Developing Groovy Scripts to Automate Jenkins
tags:
- jenkins
- groovy
- masochism
---
Automation is a wonderful thing, and for the past eight or so years, I have
been a heavy user of [Jenkins](https://jenkins.io() as my hammer of choice for
just about every nail I needed to automate. There's one dirty little secret
about Jenkins however: it's a godawful nightmare to try to automate.
My pal [Josha Hoblitt](https://github.com/jhoblitt) and I have tried to make
automation of Jenkins configuration slightly less painful for Puppet users with the
[puppet-jenkins](https://github.com/jenkinsci/puppet-jenkins) module, but
that's still rather hackish. To this day, the most effective way to automate
configuration in Jenkins is to use its built-in
[Groovy](https://groovy-lang.org) scripting support and the little-known
feature of loading scripts from the `init.groovy.d/` directory.
The `init.groovy.d/` directory is an optional directory you can create in
`JENKINS_HOME`, typically `/var/lib/jenkins`. The directory can be filled with
Groovy scripts which Jenkins will run when it starts, _after_ it has loaded
plugins, but _before_ it starts servicing user requests and scheduled
workloads. The Groovy scripts in this directory are effectively the same as one
might execute in the [Script
Console](https://jenkins.io/doc/book/managing/script-console/) or via the
`groovy` [CLI command](https://jenkins.io/doc/book/managing/cli/).
![Script Console](/images/post-images/groovy-jenkins/script-console.png)
"Cool!" I'm sure you're thinking, but slow down there buckaroo. Developing
these Groovy scripts, as luck would have it, is **painful**. The Groovy scripts
manipulate internal state of the Jenkins instance, by constructing objects and
executing methods on Jenkins' own object graph.
Here's a simple example which sets the number of executors on the master to
zero:
/*
* Make sure the number of executors available on the master is set to zero
*/
import jenkins.model.*
Jenkins.instance.setNumExecutors(0)
----
### Detour!
Experienced Jenkins users and administrators might be familiar with all the
`.xml` files which litter `JENKINS_HOME`. "XML, that's used for uglier versions
of JSON documents right?" In most other cases, this is probably correct, but
not in Jenkins. In Jenkins, for the most part, these `.xml` files are
[XStream](https://en.wikipedia.org/wiki/XStream) serializations of objects; they
are **not** typical XML documents.
Some slices of the object graph in Jenkins is serialized and written to disk.
It's kind of like Smalltalk, but with the JVM and XML.
----
### Developing Groovy for Jenkins
So these Groovy scripts must manipulate the object graph in Jenkins in order to
accomplish anything, meaning that the poor sap who tries to automate Jenkins
must understand Jenkins internal APIs in order to be successful! Yay! Here are
some tips help:
1. Use the Docker image to quickly iterate.
1. Use the web UI to configure settings visually, then compare the XML
1. Rely on [javadoc.jenkins.io](http://javadoc.jenkins.io) and [plugin javadocs](http://javadoc.jenkins.io/plugin/) to get call invocations correct.
1. Verify in the Script Console
#### Iterating with Docker
In an example source tree, imagine I have `init.groovy.d/` with my Groovy
scripts, I rely heavily on the [Docker
image](https://github.com/jenkinsci/docker) and will typically use a command
like the following to run a local instance of Jenkins with my Groovy scripts:
docker run --rm -ti -p 8080:8080 \
-v $PWD/init.groovy.d:/var/jenkins_home/init.groovy.d \
-v $PWD/jenkins-tmp:/var/jenkins_home \
-e JAVA_OPTS=-Djenkins.install.runSetupWizard=false \
jenkins/jenkins:lts-alpine
This will map my current directory's `init.groovy.d` into the approprate path
inside the container. I also map in `jenkins-tmp` so I can inspect `.xml` files
after runnign Jenkins, and finally I disable the new installation "Setup
Wizard" making iteration easy.
Whenever I'm satisfied that my scripts are doing the right thing, I'll stop
the container, and restart it so it loads my script like any other "pristine" Jenkins.
#### Using the web UI to generate XML
Think of the Jenkins web UI as a pretty way of interacting with the gnarly
object graph underneath. By making changes in the web UI, and then inspecting
`.xml` files for the changes. Finding the right `.xml` file matching the right
web UI settings requires a little bit of guess and check, but here are some
rough pointers
* Most basic settings from "Configure System" live in `config.xml`
* Global settings provided by plugins might be found in files which start with
`org.jenkinsci.plugins.` or `hudson.plugins.`, e.g. `hudson.plugins.git.GitSCM.xml`.
If I look at the file `hudson.plugins.git.GitSCM.xml` after configuring some
Git specific settings, it contains:
<?xml version='1.0' encoding='UTF-8'?>
<hudson.plugins.git.GitSCM_-DescriptorImpl plugin="git@3.3.2">
<generation>1</generation>
<globalConfigName>tyler</globalConfigName>
<globalConfigEmail>tyler@example.com</globalConfigEmail>
<createAccountBasedOnEmail>false</createAccountBasedOnEmail>
</hudson.plugins.git.GitSCM_-DescriptorImpl>
Which gives me clues to which Javadocs to look at for API signatures in my
Groovy scripting. I'll be looking for a class named `GitSCM` and its
constructor agruments.
#### Referencing Javadoc
In the example above, I would refer to the [Git plugin's
Javadoc](http://javadoc.jenkins.io/plugin/git) and start digging around in
there. This is probably the most challenging stage of developing Groovy
automation for Jenkins, figuring out which classes need to be constructed and
which methods might need to be called. A **lot** of experimentation is needed
here, which is where the Script Console comes in.
#### Prototyping in the Script Console
In my Docker image-based environment, I can make breaking changes in the Script
Console without much consequence. As I develop a `.groovy` file, I'll paste
snippets in the Script Console and click **Run**, verifying that the script is
doing what I expect it to do, using the web UI to cross-reference.
Once my little bit of groovy is working as expected, I'll commit it and move
onto the next item.
----
Automating Jenkins isn't _easy_ but if you know where to look, it doesn't need
to be impossible either. The nice thing about the Groovy approach, as I have
used it in the past, is `init.groovy.d/` can run at the instance's boot time,
and then I can use the same file, run it through the `groovy` CLI command and
ensure that the instance's settings are correct.
I hope you have found this brief reprieve from [gardening blog
posts](/tag/garden.html) useful!

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB