Tekton
This guide explains how to use btdt
in a Tekton pipeline.
It will use the Docker images, so that no changes to the images of your tasks are necessary.
Of course, you could also install btdt
within the respective task images which might simplify the integration a bit.
Provide a Persistent Volume Claim as workspace to the pipeline run
To use btdt
in a Tekton pipeline, you need to provide a Persistent Volume Claim (PVC) for the cache.
This PVC should be provided as actual persistentVolumeClaim
in the PipelineRun
, not volumeClaimTemplate
.
Otherwise, you will have a fresh volume on each pipeline run, making the cache useless.
An example PipelineRun
could look like this:
# PipelineRun template, e.g. as part of your trigger
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: my-pipeline-run-$(uid)
spec:
params:
# ...
pipelineRef:
name: my-tekton-pipeline
workspaces:
- name: cache
persistentVolumeClaim:
claimName: my-tekton-cache
With the default Tekton settings (at time of writing), only a single PVC can be mounted into a task. Thus, if you are already using a PVC for you task (likely to check out your source code repository), you will have to also store the cache on this PVC.
Alternatively, you can disable the affinity assistant to be
able to mount multiple PVCs into a task. Run kubectl edit configmap feature-flags
to edit the configuration.
In the following, we assume this second setup. If you are using a single PVC, you will have to adjust the paths
accordingly.
Provide the cache workspace to the task
To be able to use the cache in a task, the cache workspace needs to be provided:
# pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: my-tekton-pipeline
spec:
workspaces:
- name: cache
tasks:
- name: run-tests
taskRef:
name: run-tests
kind: Task
workspaces:
- name: git-sources
workspace: git-sources
- name: cache
workspace: cache
Use the cache in a task
You must declare the cache workspace in the task, so that it can be used by the individual steps:
# task_run-tests.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: run-tests
spec:
steps:
# ...
workspaces:
- name: git-sources
description: Provides the workspace with the cloned repository.
- name: cache
description: Provides the btdt cache.
Restore the cache
Now you can add a step to restore the cache at the beginning of the task.
Here, we try to restore a node_modules
directory:
# task_run-tests.yaml
spec:
# ...
steps:
- name: restore-cache
image: jgosmann/btdt:0.1
workingDir: $(workspaces.cache.path)
onError: continue
script: |
#!/bin/sh
CACHE_KEY=node-modules-$(btdt hash package-lock.json)
echo "Cache key: $CACHE_KEY"
btdt restore --cache $(workspaces.cache.path) --keys $CACHE_KEY node_modules
Install dependencies only on cache miss
Depending on what you are caching, you might want to run some commands only a cache miss to generate the files that would be cached. For example, to install NPM dependencies only if the cache could not be restored:
# task_run-tests.yaml
spec:
# ...
steps:
# try restore
- name: run-tests
image: node
workingDir: $(workspaces.git-sources.path)
script: |
#!/bin/sh
if [ $(cat $(steps.step-restore-cache.exitCode.path)) -eq 0 ]; then
echo "Cache restore succeeded, skipping npm ci"
else
npm ci
fi
# run tests, build, etc.
Note, if you are using fallback keys, you would always want to run
npm ci
to ensure that the dependencies are installed correctly.
Store the cache
For the cache to provide a benefit, we need to fill it if a cache miss occurred. This requires an additional step
after the files to cache have been generated (e.g. by running npm ci
):
# task_run-tests.yaml
spec:
# ...
steps:
# try restore
# install dependencies/generate files to cache
- name: store-cache
image: jgosmann/btdt:0.1
workingDir: $(workspaces.git-sources.path)
script: |
#!/bin/sh
if [ $(cat $(steps.step-restore-cache.exitCode.path)) -eq 0 ]; then
echo "Cache restore succeeded, skipping cache store"
exit 0
fi
CACHE_KEY=node-modules-$(btdt hash package-lock.json)
echo "Cache key: $CACHE_KEY"
btdt store --cache $(workspaces.cache.path) --keys $CACHE_KEY node_modules
Example of complete task
When putting all of this together, your task definition will look something like this:
# task_run-tests.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: run-tests
spec:
steps:
- name: restore-cache
image: jgosmann/btdt:0.1
workingDir: $(workspaces.git-sources.path)
onError: continue
script: |
#!/bin/sh
CACHE_KEY=node-modules-$(btdt hash package-lock.json)
echo "Cache key: $CACHE_KEY"
btdt restore --cache $(workspaces.cache.path) --keys $CACHE_KEY node_modules
- name: run-tests
image: node
workingDir: $(workspaces.git-sources.path)
script: |
#!/bin/sh
if [ $(cat $(steps.step-restore-cache.exitCode.path)) -eq 0 ]; then
echo "Cache restore succeeded, skipping npm ci"
else
npm ci
fi
# run tests etc.
- name: store-cache
image: jgosmann/btdt
workingDir: $(workspaces.git-sources.path)
script: |
#!/bin/sh
if [ $(cat $(steps.step-restore-cache.exitCode.path)) -eq 0 ]; then
echo "Cache restore succeeded, skipping cache store"
exit 0
fi
CACHE_KEY=node-modules-$(btdt hash package-lock.json)
echo "Cache key: $CACHE_KEY"
btdt store --cache $(workspaces.cache.path) --keys $CACHE_KEY node_modules
workspaces:
- name: git-sources
description: Provides the workspace with the cloned repository.
- name: cache
description: Provides btdt cache.
Cleanup
To prevent the cache from growing indefinitely, you should configure a regular cleanup:
Clean task
# task_cache-clean.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: cache-clean
spec:
steps:
- name: cache-clean
image: jgosmann/btdt:0.1
script: |
#!/bin/sh
btdt clean --cache $(workspaces.cache.path) --max-age 7d --max-size 10GiB
workspaces:
- name: cache
description: Provides the btdt cache.
Clean pipeline
# pipeline_cache-clean.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: cache-clean-pipeline
spec:
workspaces:
- name: cache
params:
- name: runid
type: string
tasks:
- name: cache-clean
taskRef:
name: cache-clean
kind: Task
workspaces:
- name: cache
workspace: cache
Cron trigger
# trigger_cache-clean.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: cache-clean-schedule
spec:
schedule: '@hourly'
jobTemplate:
spec:
template:
spec:
containers:
- name: cache-clean-trigger
image: curlimages/curl
command: [ '/bin/sh', '-c' ]
args: [ "curl --header \"Content-Type: application/json\" --data '{}' el-cache-clean-listener.default.svc.cluster.local:8080" ]
restartPolicy: Never
---
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: cache-clean-listener
spec:
triggers:
- name: cache-clean-trigger
interceptors: [ ]
template:
spec:
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: cache-clean-$(uid)
spec:
pipelineRef:
name: cache-clean-pipeline
params:
- name: runid
value: $(uid)
workspaces:
- name: cache
persistentVolumeClaim:
claimName: my-tekton-cache