martedì 12 maggio 2015

Installing and configuring Jenkins using Chef

Installing Jenkins with Chef is really simple. There is a Jenkins cookbook in the Chef Supermarket so it's a matter of including a couple of recipes from it.

But installation is only the first part of the work. We also need configuration, job creation, etc. The same Jenkins cookbook offers a set of resources that help us but if you've always configured Jenkins using the web interface (like me) it's not so obvious how to use them.

So I started some experiments and finally I obtained a full working job. And this is what I did...

The goal was to have a working Maven job that downloads the sources from a Subversion repository and build the project.

Doing this manually requires the following steps:
  • install Jenkins
  • configure JDK installation
  • configure Maven installation
  • create the job
We want to do all this automatically using Chef so, after creating a new Chef cookbook with

chef generate cookbook cookbooks/jenkins_server 

the first thing to do is adding a dependency in metadata.rb so Berkshelf can automatically download the Jenkins cookbook and all its dependencies from the supermarket

depends 'jenkins', '~> 2.2.2'

Then we need to write our recipe, so in recipes/default.rb we add:

include_recipe 'jenkins::java'
include_recipe 'jenkins::master'

These two recipes, included from the Jenkins cookbook, install the JDK and Jenkins itself. The Jenkins service is started too.

Then we have to start configuration. It can be done in multiple ways but I chose to directly manipulate jenkins config files. Probably using a groovy script is more elegant but it requires more study!

template "#{node['jenkins']['master']['home']}/config.xml" do
  source "config.xml.erb"
end

These 3 lines simply copy the config.xml file from the templates cookbooks directory in the right position.

To obtain a template config.xml simply copy it from an existing Jenkins installation and remove all the unnecessary part.

I got the following:

<?xml version='1.0' encoding='UTF-8'?>
<hudson>
  <disabledAdministrativeMonitors/>
  <version>1.612</version>
  <numExecutors>2</numExecutors>
  <mode>NORMAL</mode>
  <useSecurity>true</useSecurity>
  <authorizationStrategy class="hudson.security.AuthorizationStrategy$Unsecured"/>
  <securityRealm class="hudson.security.SecurityRealm$None"/>
  <disableRememberMe>false</disableRememberMe>
  <projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
  <workspaceDir>${ITEM_ROOTDIR}/workspace</workspaceDir>
  <buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
  <systemMessage>This is jenkins</systemMessage>
  <jdks>
    <jdk>
      <name>Default JDK</name>
      <home>/usr/lib/jvm/java-1.7.0-openjdk-i386</home>
      <properties/>
    </jdk>
  </jdks>
  <viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
  <myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
  <clouds/>
  <quietPeriod>5</quietPeriod>
  <scmCheckoutRetryCount>0</scmCheckoutRetryCount>
  <views>
    <hudson.model.AllView>
      <owner class="hudson" reference="../../.."/>
      <name>All</name>
      <filterExecutors>false</filterExecutors>
      <filterQueue>false</filterQueue>
      <properties class="hudson.model.View$PropertyList"/>
    </hudson.model.AllView>
  </views>
  <primaryView>All</primaryView>
  <slaveAgentPort>0</slaveAgentPort>
  <label></label>
  <nodeProperties/>
  <globalNodeProperties/>
</hudson>

As you can see there are only two things I had to customize:

  • the JDK installation
  • the default view
I used the same JDK that run Jenkins without installing a new one. The JDK installation has a problem here. I had to hard wire the path of the JDK dir. In a subsequent post we'll see how to solve this problem.

And now we have to install Maven. We can let Jenkins install it for us putting the right file in the right position with:

template "#{node['jenkins']['master']['home']}/hudson.tasks.Maven.xml" do
 source 'hudson.tasks.Maven.xml.erb'
end

The hudson.tasks.Maven.xml.erb we should put in our templates dir is:

<?xml version='1.0' encoding='UTF-8'?>
<hudson.tasks.Maven_-DescriptorImpl>
  <installations>
    <hudson.tasks.Maven_-MavenInstallation>
      <name>Default Maven</name>
      <properties>
        <hudson.tools.InstallSourceProperty>
          <installers>
            <hudson.tasks.Maven_-MavenInstaller>
              <id>3.2.2</id>
            </hudson.tasks.Maven_-MavenInstaller>
          </installers>
        </hudson.tools.InstallSourceProperty>
      </properties>
    </hudson.tasks.Maven_-MavenInstallation>
  </installations>
</hudson.tasks.Maven_-DescriptorImpl>

To have a flexible cookbook we can substitute the Maven version with an attribute that can be customized using a Chef role or environment but now I want to keep the whole thing as simple as possible.

After the configuration files are copied in the right place Jenkins has to reload the configuration, so:

jenkins_command 'reload-configuration'


The last thing to do is job creation. In the Jenkins cookbook there is a resource for that but it needs a job configuration file that must exist on the target node.

As suggested by the Jenkins plugin documentation we can put it in the templates and then copy it in Chef's file cache path with:

job_config = File.join(Chef::Config[:file_cache_path], 'job-config.xml')

template job_config do
  source 'webapp-job-config.xml.erb'
end

The job config file should look like this:

<?xml version='1.0' encoding='UTF-8'?>
<maven2-moduleset plugin="maven-plugin@2.7.1">
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties/>
  <scm class="hudson.scm.SubversionSCM" plugin="subversion@1.54">
    <locations>
      <hudson.scm.SubversionSCM_-ModuleLocation>
        <remote>http://192.168.0.50/svn/repo/webapp/trunk</remote>
        <local>.</local>
        <depthOption>infinity</depthOption>
        <ignoreExternalsOption>false</ignoreExternalsOption>
      </hudson.scm.SubversionSCM_-ModuleLocation>
    </locations>
    <excludedRegions></excludedRegions>
    <includedRegions></includedRegions>
    <excludedUsers></excludedUsers>
    <excludedRevprop></excludedRevprop>
    <excludedCommitMessages></excludedCommitMessages>
    <workspaceUpdater class="hudson.scm.subversion.CheckoutUpdater"/>
    <ignoreDirPropChanges>false</ignoreDirPropChanges>
    <filterChangelog>false</filterChangelog>
  </scm>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <aggregatorStyleBuild>true</aggregatorStyleBuild>
  <incrementalBuild>false</incrementalBuild>
  <ignoreUpstremChanges>false</ignoreUpstremChanges>
  <archivingDisabled>false</archivingDisabled>
  <siteArchivingDisabled>false</siteArchivingDisabled>
  <fingerprintingDisabled>false</fingerprintingDisabled>
  <resolveDependencies>false</resolveDependencies>
  <processPlugins>false</processPlugins>
  <mavenValidationLevel>-1</mavenValidationLevel>
  <runHeadless>false</runHeadless>
  <disableTriggerDownstreamProjects>false</disableTriggerDownstreamProjects>
  <blockTriggerWhenBuilding>true</blockTriggerWhenBuilding>
  <settings class="jenkins.mvn.DefaultSettingsProvider"/>
  <globalSettings class="jenkins.mvn.DefaultGlobalSettingsProvider"/>
  <reporters/>
  <publishers/>
  <buildWrappers/>
  <prebuilders/>
  <postbuilders/>
  <runPostStepsIfResult>
    <name>FAILURE</name>
    <ordinal>2</ordinal>
    <color>RED</color>
    <completeBuild>true</completeBuild>
  </runPostStepsIfResult>
</maven2-moduleset>


Again, there are a lot of things we can do to have a flexible cookbook like the parametrization of the Subversion URL.

At last

jenkins_job 'Webapp' do
 config job_config
end

will create the job.

Now we only need to launch Chef to have a Jenkins machine configured and ready to launch the job.

In the next part we can launch Chef using Vagrant.

In the meanwhile, if you want to download the cookbook you can find it in this Github repo in the cookbooks/jenkins_server directory.

Nessun commento: