Originally posted in http://espinosa.io/blog/2016-03-31-whole-jenkins-in-code.html
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()