Solutions Delivery Platform

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:

  1. Check out the source code

  2. Run some CLI command

  3. Archive some files that were generated by the command

To support fast development of steps that follow this pattern, the default step implementation is available.

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

  1. The default step implementation allows a quick and dirty way to quickly prototype a step on the fly without needing to create a library.

  2. 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

  1. 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.

  2. This feature hard codes a particular functionality from the Pipeline Configuration and does not 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

Append to the Pipeline Configuration:

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

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:

stages{
    continuous_integration{
        unit_test
        build
        static_code_analysis
    }
}

Run the Pipeline

Run the pipeline. From the job’s main page, click Build Now in the lefthand navigation menu.

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

Started by user admin
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[JTE] Pipeline Configuration Modifications (show)
[JTE] Loading Library maven (show)
[JTE] Library maven does not have a configuration file.
[JTE] Loading Library sonarqube (show)
[JTE] Library sonarqube does not have a configuration file.
[JTE] Loading Library ansible (show)
[JTE] Library ansible does not have a configuration file.
[JTE] Creating step unit_test from the default step implementation.
[JTE] Obtained Pipeline Template from job configuration
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/single-job
[Pipeline] {
[Pipeline] writeFile
[Pipeline] archiveArtifacts
Archiving artifacts
[Pipeline] }
[Pipeline] // node
[JTE] [Stage - continuous_integration]
[JTE] [Step - Default Step Implementation/unit_test.call()]
[Pipeline] stage
[Pipeline] { (Unit Test)
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/single-job
[Pipeline] {
[Pipeline] isUnix
[Pipeline] sh
+ docker inspect -f . maven
.
[Pipeline] withDockerContainer
Jenkins seems to be running inside container cc7140d4fb91bef940e2fabe7225dcbcc9b44a3a5e17ee703b8fcbe42e53a17c
$ docker run -t -d -u 0:0 -w /var/jenkins_home/workspace/single-job --volumes-from cc7140d4fb91bef940e2fabe7225dcbcc9b44a3a5e17ee703b8fcbe42e53a17c -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 e41cf335bb258d366d9c5dc2a91090184d0bc00b207ccf10b125e035a3a5ede5 -eo pid,comm
[Pipeline] {
[Pipeline] unstash
[Pipeline] sh
+ mvn -v
Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-27T15:06:16Z)
Maven home: /usr/share/maven
Java version: 11.0.5, vendor: Oracle Corporation, runtime: /usr/local/openjdk-11
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "4.9.125-linuxkit", arch: "amd64", family: "unix"
[Pipeline] }
$ docker stop --time=1 e41cf335bb258d366d9c5dc2a91090184d0bc00b207ccf10b125e035a3a5ede5
$ docker rm -f e41cf335bb258d366d9c5dc2a91090184d0bc00b207ccf10b125e035a3a5ede5
[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
[Pipeline] timeout
Timeout set to expire in 5 min 0 sec
[Pipeline] {
[Pipeline] input
Approve the deployment?
Proceed or Abort
Approved by admin
[Pipeline] }
[Pipeline] // timeout
[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 - Default Step Implementation/unit_test.call()]
[Pipeline] stage
[Pipeline] { (Unit Test)
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/single-job
[Pipeline] {
[Pipeline] isUnix
[Pipeline] sh
+ docker inspect -f . maven
.
[Pipeline] withDockerContainer
Jenkins seems to be running inside container cc7140d4fb91bef940e2fabe7225dcbcc9b44a3a5e17ee703b8fcbe42e53a17c
$ docker run -t -d -u 0:0 -w /var/jenkins_home/workspace/single-job --volumes-from cc7140d4fb91bef940e2fabe7225dcbcc9b44a3a5e17ee703b8fcbe42e53a17c -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 e41cf335bb258d366d9c5dc2a91090184d0bc00b207ccf10b125e035a3a5ede5 -eo pid,comm
[Pipeline] {
[Pipeline] unstash
[Pipeline] sh
+ mvn -v
Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-27T15:06:16Z)
Maven home: /usr/share/maven
Java version: 11.0.5, vendor: Oracle Corporation, runtime: /usr/local/openjdk-11
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "4.9.125-linuxkit", arch: "amd64", family: "unix"
[Pipeline] }
$ docker stop --time=1 e41cf335bb258d366d9c5dc2a91090184d0bc00b207ccf10b125e035a3a5ede5
$ docker rm -f e41cf335bb258d366d9c5dc2a91090184d0bc00b207ccf10b125e035a3a5ede5
[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 - Default Step Implementation/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.