Your whole Jenkins configuration in Code.. no XML!!
2016 March 31
There various pieces in configuration management to manage a Jenkins installation. You have the Job DSL plugin 1, a Chef cookbook 2, the Script Console 3. There are others you can use, but I will focus on using the three to provide a zero-to-Jenkins setup without clicking on forms or writing XML files. I will make details on the following steps to get it done:
- Secure Jenkins
- Update plugins
- Disable unnecessary plugins
- Install other plugins
- Create a seed job
Secure Jenkins
First we setup Chef to load a private key so that the chef-client can be authenticated when connecting to Jenkins. In the code below, this uses the chef-client’s client key:
ruby_block 'load jenkins credential' do
block do
require 'openssl'
require 'net/ssh'
key = ::OpenSSL::PKey::RSA.new ::File.read Chef::Config[:client_key]
node.run_state[:jenkins_private_key] = key.to_pem
jenkins = resources('jenkins_user[chef]')
jenkins.public_keys ["#{key.ssh_type} #{[key.to_blob].pack('m0')}"]
end
end
Next is to create a Jenkins user for the chef-client by and specify its public key from the private key above.
jenkins_user 'chef' do
id "chef@#{Chef::Config[:node_name]}"
full_name "Chef"
end
Finally we lock down Jenkins to authenticate with GitHub:
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.GithubSecurityRealm;
Jenkins.instance.securityRealm = new GithubSecurityRealm(
'https://github.com', 'https://api.github.com', 'x', 'y')
permissions = new hudson.security.GlobalMatrixAuthorizationStrategy()
permissions.add(Jenkins.ADMINISTER, 'aespinosa')
permissions.add(Jenkins.ADMINISTER, '#{resources('jenkins_user[chef]').id}')
permissions.add(hudson.model.View.READ, 'anonymous')
permissions.add(hudson.model.Item.READ, 'anonymous')
permissions.add(Jenkins.READ, 'anonymous')
Jenkins.instance.authorizationStrategy = permissions
Jenkins.instance.save()
Update plugins
When using the LTS version of Jenkins, some of the plugins are out of date. The following Groovy script:
First, we have to get the list of latest packages from the update center. The not_if
guard makes sure that we only check for updates once a day even if the chef-client tried to converge every 30 minutes.
jenkins_script 'get list of latest plugins' do
command <<-eos.gsub(/^\s+/, '')
pm = jenkins.model.instance.pluginManager
pm.doCheckUpdatesServer()
eos
not_if do
update_frequency = 86_400 # daily
update_file = '/var/lib/jenkins/updates/default.json'
::File.exists?(update_file) &&
::File.mtime(update_file) > Time.now - update_frequency
end
end
Next, we finally download the plugins. Note the check between the version in the updateCenter
and the pluginManager
so that updates are made idempotently.
import jenkins.model.Jenkins;
pm = Jenkins.instance.pluginManager
uc = Jenkins.instance.updateCenter
updated = false
pm.plugins.each { plugin ->
if (uc.getPlugin(plugin.shortName).version != plugin.version) {
update = uc.getPlugin(plugin.shortName).deploy(true)
update.get()
updated = true
}
}
if (updated) {
Jenkins.instance.restart()
}
Install plugins
Next in the plugin setup phase, we disable the plugins we don’t need and install only the plugins we need. This is similar to what I described earlier in a previous post.
import jenkins.model.Jenkins;
pm = Jenkins.instance.pluginManager
uc = Jenkins.instance.updateCenter
pm.plugins.each { plugin ->
plugin.disable()
}
deployed = false
def activatePlugin(plugin) {
if (! plugin.isEnabled()) {
plugin.enable()
deployed = true
}
plugin.getDependencies().each {
activatePlugin(pm.getPlugin(it.shortName))
}
}
["git", "workflow-aggregator", "github-oauth", "job-dsl", "extended-read-permission"].each {
if (! pm.getPlugin(it)) {
deployment = uc.getPlugin(it).deploy(true)
deployment.get()
}
activatePlugin(pm.getPlugin(it))
}
if (deployed) {
Jenkins.instance.restart()
}
Create the Seed job
The Job DSL plugin lets us specify our Jenkins job. But how to you set up the seed job automatically? The Groovy script below uses the Script console to create the Seed Job. The samples.groovy
file can then be deployed in any way you see fit like Git or as a Chef File resource.
import jenkins.model.Jenkins;
import hudson.model.FreeStyleProject;
job = Jenkins.instance.createProject(FreeStyleProject, 'seed-job')
job.displayName = 'Seed Job'
builder = new javaposse.jobdsl.plugin.ExecuteDslScripts(
new javaposse.jobdsl.plugin.ExecuteDslScripts.ScriptLocation(
'false',
'samples.groovy',
null),
false,
javaposse.jobdsl.plugin.RemovedJobAction.DELETE,
javaposse.jobdsl.plugin.RemovedViewAction.DELETE,
javaposse.jobdsl.plugin.LookupStrategy.JENKINS_ROOT
)
job.buildersList.add(builder)
job.save()