This guide explains how you can start to use ODS pipeline in a repository that has been created through an ODS quickstarter, and therefore uses Jenkins as CI/CD solution. It is assumed you have read the introduction and have aleady installed ODS pipeline in an OpenShift project.
The following assumes that you have selected the Go quickstarter and have a repository named foo-backend
in your Bitbucket project. Other quickstarters are converted following the same steps, simply adjust the instructions below.
On a high level, converting an ODS component consists of the following steps:
-
Replace Jenkinsfile with ods.yaml
-
Create Helm Chart
-
Configure Webhook
The Jenkinsfile
describes the Jenkins pipeline to run using stages provided by the ODS Jenkins Shared Library as well as custom Go tool instructions. The file might look like this:
@Library('ods-jenkins-shared-library@4.x') _
odsComponentPipeline(
imageStreamTag: 'ods/jenkins-agent-golang:4.x',
branchToEnvironmentMapping: [
'master': 'dev',
]
) { context ->
odsComponentStageImportOpenShiftImageOrElse(context) {
stageCheckFormat(context)
stageLint(context)
stageUnitTest(context)
stageBuild(context)
odsComponentStageScanWithSonar(context)
odsComponentStageBuildOpenShiftImage(context)
}
odsComponentStageRolloutOpenShiftDeployment(context)
}
def stageCheckFormat(def context) {
stage('Check Format') {
def unformatted = sh(script: 'gofmt -l .', returnStdout: true)
if (unformatted) {
println "Unformatted files:\n${unformatted}"
error 'All files need to be gofmt\'d. Please run: gofmt -w .'
}
}
}
def stageLint(def context) {
stage('Lint') {
withEnv(["CGO_ENABLED=0", "GOCACHE=${WORKSPACE}/.cache"]) {
sh "golangci-lint run"
}
}
}
def stageBuild(def context) {
stage('Build') {
def binary = "app_linux_amd64"
withEnv(["CGO_ENABLED=0", "GOCACHE=${WORKSPACE}/.cache"]) {
sh "go build -o docker/${binary}"
}
}
}
def stageUnitTest(def context) {
stage('Unit Test') {
withEnv(["CGO_ENABLED=0", "GOCACHE=${WORKSPACE}/.cache"]) {
sh 'mkdir -p build/test-results/test'
def gopkgs = sh(script: 'go list ./... | grep -v /vendor', returnStdout: true).trim()
withEnv(["GOPKGS=${gopkgs}"]) {
def status = sh(script: 'go test -v -coverprofile=coverage.out $GOPKGS 2>&1 > test-results.txt', returnStatus: true)
sh 'cat test-results.txt | go-junit-report > build/test-results/test/report.xml'
if (status != 0) {
error "go test failed!"
}
}
}
}
}
Compared to Jenkins, you cannot define any scripts directly in the ods.yaml
file describing your Tekton pipeline. You may only reference existing tasks and adjust their parameters. As a consequence, the build related stages (stageCheckFormat
, stageLint
, stageUnitTest
, stageBuild
) are provided by a task (named ods-pipeline-go-build
) instead.
An roughly equivalent ods.yaml
for the above Jenkinsfile
looks like this:
pipelines:
- tasks:
- name: build
taskRef:
resolver: git
params:
- { name: url, value: https://github.com/opendevstack/ods-pipeline-go.git }
- { name: revision, value: v0.1.2 }
- { name: pathInRepo, value: tasks/build.yaml }
workspaces:
- name: source
workspace: shared-workspace
- name: package
taskRef:
resolver: git
params:
- { name: url, value: https://github.com/opendevstack/ods-pipeline-image.git }
- { name: revision, value: v0.1.0 }
- { name: pathInRepo, value: tasks/package.yaml }
runAfter:
- build
workspaces:
- name: source
workspace: shared-workspace
- name: deploy
taskRef:
resolver: git
params:
- { name: url, value: https://github.com/opendevstack/ods-pipeline-helm.git }
- { name: revision, value: v0.1.0 }
- { name: pathInRepo, value: tasks/deploy.yaml }
runAfter:
- package
workspaces:
- name: source
workspace: shared-workspace
What has been done in Jenkins in stageCheckFormat
, stageLint
, stageUnitTest
, stageBuild
and odsComponentStageScanWithSonar
is now done by the ods-pipeline-go-build
task. If you have modified how the application is tested and built, or added further steps, you will need to create your own Tekton tasks reflecting those changes. See the authoring tasks guide.
Building the container image is now done in ods-pipeline-image-package
instead of in odsComponentStageBuildOpenShiftImage
. The task continues to use the existing docker/Dockerfile
file, which does not need to change much if at all. Consult the task reference in question for more information. In the case of Go, the ods-pipeline-go-build
task reference states that the resulting Go binary is named app
and placed into the docker
directory. Make sure that your docker/Dockerfile
copies app
, not e.g. app_linux_amd64
(as is the default for an ODS 4.x based Go quickstarter).
Finally, the application is deployed in ods-pipeline-helm-deploy
as opposed to odsComponentStageRolloutOpenShiftDeployment
.
Let’s look at this deployment piece in detail. The new Tekton task makes use of Helm to define and deploy the Kubernetes resources to use. Your existing repository might not define Kubernetes resources at all (this is the default), or they might be expressed as OpenShift templates (in a folder named openshift
) and applied with Tailor. ODS pipeline only supports Helm at the moment, and requires the Kubernetes resources (the Helm "chart") to be under version control. It is recommended to start with the sample chart provided in this repository. If the existing component controlled resources via Tailor, please see the ODS Quickstarter Migration Guide of Tailor as well.
The final step is to create a Bitbucket webhook pointing to the ODS pipeline installation. To do this, go to "Repository Settings > Webhooks" and create a new webhook:
-
"Name": choose any name you wish, e.g.
ods-pipeline
-
"URL": enter the URL of the route of your event listener, followed by
/bitbucket
, e.g.https://ods-pipeline.example.com/bitbucket
-
"Secret": enter the value of the
secret
field in the OpenShiftods-bitbucket-webhook
Secret.
Select the "Repository: Push" and "Pull request: Opened" events and save the configuration.
Any existing Jenkins webhook setting can be disabled now. After that, any push to the repository will trigger the pipeline described in ods.yaml
.
Once you have done your first steps, consult the ods.yaml
reference and the tasks reference for more information.
For an end-to-end example, have a look at the example project.