Template Hierarchies and Using Aux Models to Simplify Template Construction

The blog post will demonstrate how you can simplify your Jenkins Job Templates through creating re-usable models using Auxiliary Templates.

Concepts and Definitions

Before we dive into the detail, we will recap on some of the concepts of Templates. The Templates plugin, available with the CloudBees Jenkins Platform, captures the sameness of configuration in multiple places. Administrators define templates of jobs/build steps/publishers and replicate them while creating new jobs. Changes are made in one central location and are reflected in all dependent configurations. The Templates Plugin lets you define four types of template:

  • Job 
  • Folder 
  • Builder 
  • Auxiliary

This post focuses on Auxiliary and Job templates.
 

Templates are conceptually broken down into a few pieces:

Model

A model defines a set of attributes that constitutes a template. Roughly speaking, you can think of this as a class of an object-oriented programming language. For example, if you are creating a template for your organization’s standard process of running a code coverage, you might create a model that has attributes like “packages to obtain coverage”, “tests to run.”

Attribute

An attribute defines a variable, what kind of data it represents, and how it gets presented to the users. This is somewhat akin to a field definition in a class definition.

Instance

An instance is a use of a model. It supplies concrete values to the attributes defined in the template. Roughly speaking, the model-to-instance relationship in the template plugin is like the class-to-object relationship in a programming language. You can create a lot of instances from a single template.

Transformer

A transformer is a process of taking an instance and mapping it into the “standard” Jenkins configuration, so that the rest of Jenkins understands this and can execute it. This can be logically thought of as a function.
 
Structuring Templates
Typical implementations will consist of multiple templates. Templates can be created for each new requirement, but oftentimes, there is a degree of similarity between the required templates. As with all good system implementations, a little advance planning and ongoing refactoring will pay dividends in the long run.

The Templates plugin provides two mechanisms to reuse previously designed models:

  • Auxiliary Templates
  • Template Inheritance

Template Inheritance

Typically you may create a template that performs a typical activity - in this example we assume that the job will need to access a web server running on standard ports. The Model will include the hostname of the server.

We configure the Job Template with the required parameter.

The full config.xml for the job is shown below:

<job-template plugin="cloudbees-template@4.17”>
<actions/>
<:description>template-web-host</description>
<displayName> template-web-host</displayName>
<attributes>
<template-attribute>
<name> name</name>
<displayName> Name</displayName>
<control class="com.cloudbees.hudson.plugins.modeling.controls.TextFieldControl"/>
</template-attribute>
<template-attribute>
<name> hostname</name>
<displayName> hostname</displayName> <helpHtml> hostname</helpHtml>
<control class="com.cloudbees.hudson.plugins.modeling.controls.TextFieldControl"/>
</template-attribute>
</attributes>
<properties/>
<instantiable>true</instantiable>
<help> template-web-host</help>
<transformer class="com.cloudbees.workflow.template.WorkflowTransformer" plugin="cloudbees-workflow-template@1.3">
<template>
<flow-definition/>
</template>
<sandbox>false</sandbox>
<script>echo "The value of hostname is $hostname"</script>
<scriptsandbox>false</scriptsandbox>
</transformer>
</jobtemplate>

Explanation of the UI Modes Nested auxiliary models can have one of 4 different UI modes:

  • Single Value
  • Single Value (choice of all the subtypes of the specified model)
  • List of values
  • List of values, including all subtypes

Single value

The attribute will hold one and only one value of the specified aux model. The UI will show the configuration of the aux model inline, and the user will not really see that it is a part of another model. This is useful for splitting a common fragment into a separate model and reusing it.

The model attributes are accessed using this style:

apacheHost.hostname

For example, in workflow this looks like:

echo "apacheHost.hostname : $apacheHost.hostname"
echo "apacheHost.userid : $apacheHost.userid"
echo "apacheHost.httpPort : $apacheHost.httpPort"

Single value (choice of all the subtypes of the specified model)

The attribute will hold one and only one value, but the type can be any of the instantiable concrete subtypes of the specified aux model (including itself, unless it’s abstract.) The user will see a radio button to select one of the possible types.

The model attributes are accessed using this style: host.hostname

It is also possible to determine the Type of the model using attributeid.model.id

Below is a workflow example:

echo "host.hostname : $host.hostname"
echo "host.userid : $host.userid"
if (host.model.id.contains("aux-apache-server")) {
    echo "Type: $host.model.id"
    echo "host.httpPort : $host.httpPort"
}

List of values

The attribute will hold arbitrary number of the specific aux model. Example of this is the JDK/Ant/Maven configuration in Jenkins system config page. Users will use add/remove buttons to modify the list.

The model attributes are accessed by iterating through the array. Below is a workflow example:

apacheHosts.each {
    userid=it.userid
    host=it.hostname
    echo "Host: $host, Userid = $userid"
}

List of values, including all subtypes

Somewhat like “list of values” above, but you’ll specify the base model type (typically abstract one) in the nested model field above, and Jenkins will allow users to instantiate arbitrary concrete subtypes. The UI will show an add button with a drop-down menu, with which users will select the specific subtype to instantiate. An example of this is the tool installer configuration to JDK/Ant/Maven.

The model attributes are accessed by iterating through the array. Below is a workflow example:

hosts.each {
    userid=it.userid
    host=it.hostname
    echo "Host: $host, Userid = $userid"
}

Conclusion
The CloudBees Jenkins Platform provides a rich templating capability that allows job complexity to be abstracted away from users, only exposing the relevant fields that they are required to enter.

The ability to extract out a hierarchy of common Models enables the template developer to re-use the definitions efficiently across many individual Job Templates and enforces standardised naming and usage patterns.
References

Template plugin user manual

 

Nigel Harniman
​Senior Solutions Architect
CloudBees

 

Blog Categories: 

Add new comment