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:
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):
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.