Skip to content

Default Step Implementation

There are a large number of use cases where a step contributed by a library is going to do the same thing:

  • Check out the source code
  • Run some CLI/shell command
  • Archive some files that were generated by the command

To support fast development of steps that follow this pattern, we can use a default step implementation.

What is the Default Step Implementation?

The default step implementation allows you to define a step from the Pipeline Configuration that specifies a container image as the runtime environment for the step, a shell command or script to execute, and then a stash to be created in order to store files generated.

Benefits

  • The default step implementation allows a quick-and-dirty way to prototype a step on the fly without needing to create a library.
  • Using container images for pipeline runtime dependencies is an excellent way to build a DevSecOps pipeline that is loosely coupled to the underlying infrastructure.

Setbacks

  • The default step implementation can be a little too powerful if you're striving for a tightly governed DevSecOps pipeline. This feature should be exposed to end users with care, as it exposes a lot of the functionality to the teams that would be using the template.
  • This feature hard-codes a particular functionality from the Pipeline Configuration and doesn't have the same configuration flexibility that a library would have.

Create a Step

In the same Pipeline Job we were using during the JTE: Pipeline Primitives lab, let's add a new step called unit_test().

Update the Pipeline Configuration

In your single-job, append to your Pipeline Configuration from the keywords lab:

Pipeline Configuration
steps {
    unit_test {
        stage = "Unit Test"
        image = "maven"
        command = "mvn -v"
    }
}

Important

Steps implemented through the default step implementation are defined in the steps block of the Pipeline Configuration. Root level keys within the steps block become the name of the step that can be invoked from the Pipeline Template.

The step above creates a unit_test step that can be invoked from the Pipeline Template that executes the command mvn -v inside the maven:latest container image from Docker Hub.

It would also make sense to update the continuous_integration Stage that was created to include the unit_test step, so the full Pipeline Configuration in your single-job should look like (note we changed requiresApproval = false to run without manual intervention):

Pipeline Configuration
libraries {
    maven
    sonarqube
    ansible
}

stages {
    continuous_integration {
        unit_test
        build
        static_code_analysis
    }
}

application_environments {
    dev {
        ip_addresses = [ "0.0.0.1", "0.0.0.2" ]
    }
    prod {
        long_name = "Production" 
        ip_addresses = [ "0.0.1.1", "0.0.1.2", "0.0.1.3", "0.0.1.4" ]
    }
}

keywords {
    requiresApproval = false 
}

steps {
    unit_test {
        stage = "Unit Test"
        image = "maven"
        command = "mvn -v"
    }
}

Run the Pipeline

Run the pipeline. From the job's main page, click Build Now in the left-hand navigation menu.

View the build logs and you should see output similar to:

Started by user admin
[JTE] Pipeline Configuration Modifications (show)
[JTE] Obtained Pipeline Template from job configuration
[JTE] Loading Library maven (show)
[JTE] Loading Library sonarqube (show)
[JTE] Loading Library ansible (show)
[JTE] Creating step unit_test from the default step implementation.
[JTE] Template Primitives are overwriting Jenkins steps with the following names: (show)
[Pipeline] Start of Pipeline
[JTE][Stage - continuous_integration]
[JTE][Step - null/unit_test.call()]
[Pipeline] stage
[Pipeline] { (Unit Test)
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/single-job
[Pipeline] {
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker inspect -f . maven
.
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] withDockerContainer
Jenkins seems to be running inside container f8a61ccd04d1fd2e436dc0ccbc3f5ad59cd95b6a736420fb3ef808b9da5b7dec
$ docker run -t -d -u 0:0 -w /var/jenkins_home/workspace/single-job --volumes-from f8a61ccd04d1fd2e436dc0ccbc3f5ad59cd95b6a736420fb3ef808b9da5b7dec -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** maven cat
$ docker top e18bdb071ce2403ffa413da3c58bd1b8cb4711ba9a861b080e0727364aa62e62 -eo pid,comm
[Pipeline] {
[Pipeline] unstash
[Pipeline] sh
+ mvn -v
Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29)
Maven home: /usr/share/maven
Java version: 17.0.5, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "5.10.104-linuxkit", arch: "amd64", family: "unix"
[Pipeline] }
$ docker stop --time=1 e18bdb071ce2403ffa413da3c58bd1b8cb4711ba9a861b080e0727364aa62e62
$ docker rm -f --volumes e18bdb071ce2403ffa413da3c58bd1b8cb4711ba9a861b080e0727364aa62e62
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // stage
[JTE][Step - maven/build.call()]
[Pipeline] stage
[Pipeline] { (Maven: Build)
[Pipeline] echo
build from the maven library
[Pipeline] }
[Pipeline] // stage
[JTE][Step - sonarqube/static_code_analysis.call()]
[Pipeline] stage
[Pipeline] { (SonarQube: Static Code Analysis)
[Pipeline] echo
static code analysis from the sonarqube library
[Pipeline] }
[Pipeline] // stage
[JTE][Step - ansible/deploy_to.call(ApplicationEnvironment)]
[Pipeline] stage
[Pipeline] { (Deploy to: dev)
[Pipeline] echo
Performing a deployment through Ansible..
[Pipeline] echo
Deploying to 0.0.0.1
[Pipeline] echo
Deploying to 0.0.0.2
[Pipeline] }
[Pipeline] // stage
[JTE][Step - ansible/deploy_to.call(ApplicationEnvironment)]
[Pipeline] stage
[Pipeline] { (Deploy to: Production)
[Pipeline] echo
Performing a deployment through Ansible..
[Pipeline] echo
Deploying to 0.0.1.1
[Pipeline] echo
Deploying to 0.0.1.2
[Pipeline] echo
Deploying to 0.0.1.3
[Pipeline] echo
Deploying to 0.0.1.4
[Pipeline] }
[Pipeline] // stage
[Pipeline] End of Pipeline
Finished: SUCCESS

When reading the lines, notice:

[JTE] Creating step unit_test from the default step implementation.

at the beginning of the build.

JTE saw a step was defined in the Pipeline Configuration and constructed the unit_test step on the fly for use in the Pipeline Template.

The logs pertaining to the unit_test step were:

[JTE][Step - null/unit_test.call()]
[Pipeline] stage
[Pipeline] { (Unit Test)
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/single-job
[Pipeline] {
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker inspect -f . maven
.
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] withDockerContainer
Jenkins seems to be running inside container f8a61ccd04d1fd2e436dc0ccbc3f5ad59cd95b6a736420fb3ef808b9da5b7dec
$ docker run -t -d -u 0:0 -w /var/jenkins_home/workspace/single-job --volumes-from f8a61ccd04d1fd2e436dc0ccbc3f5ad59cd95b6a736420fb3ef808b9da5b7dec -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** maven cat
$ docker top e18bdb071ce2403ffa413da3c58bd1b8cb4711ba9a861b080e0727364aa62e62 -eo pid,comm
[Pipeline] {
[Pipeline] unstash
[Pipeline] sh
+ mvn -v
Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29)
Maven home: /usr/share/maven
Java version: 17.0.5, vendor: Eclipse Adoptium, runtime: /opt/java/openjdk
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "5.10.104-linuxkit", arch: "amd64", family: "unix"
[Pipeline] }
$ docker stop --time=1 e18bdb071ce2403ffa413da3c58bd1b8cb4711ba9a861b080e0727364aa62e62
$ docker rm -f --volumes e18bdb071ce2403ffa413da3c58bd1b8cb4711ba9a861b080e0727364aa62e62
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // stage

You can see JTE announcing it's about to execute a step called unit_test that was constructed via the default step implementation here: [JTE][Step - null/unit_test.call()].

When the step was executed, it checked if the maven step was available locally and pulled the image if not.

Within the container image, it then ran mvn -v and the Maven version was printed to the build log.

Back to top