StepActions
Overview
🌱
StepActions
is an beta feature. Theenable-step-actions
feature flag must be set to"true"
to specify aStepAction
in aStep
.
A StepAction
is the reusable and scriptable unit of work that is performed by a Step
.
A Step
is not reusable, the work it performs is reusable and referenceable. Steps
are in-lined in the Task
definition and either perform work directly or perform a StepAction
. A StepAction
cannot be run stand-alone (unlike a TaskRun
or a PipelineRun
). It has to be referenced by a Step
. Another way to think about this is that a Step
is not composed of StepActions
(unlike a Task
being composed of Steps
and Sidecars
). Instead, a Step
is an actionable component, meaning that it has the ability to refer to a StepAction
. The author of the StepAction
must be able to compose a Step
using a StepAction
and provide all the necessary context (or orchestration) to it.
Configuring a StepAction
A StepAction
definition supports the following fields:
- Required
apiVersion
- Specifies the API version. For example,tekton.dev/v1alpha1
.kind
- Identifies this resource object as aStepAction
object.metadata
- Specifies metadata that uniquely identifies theStepAction
resource object. For example, aname
.spec
- Specifies the configuration information for thisStepAction
resource object.image
- Specifies the image to use for theStep
.- The container image must abide by the container contract.
- Optional
command
- cannot be used at the same time as using
script
.
- cannot be used at the same time as using
args
script
- cannot be used at the same time as using
command
.
- cannot be used at the same time as using
env
params
results
workingDir
securityContext
volumeMounts
The example below demonstrates the use of most of the above-mentioned fields:
apiVersion: tekton.dev/v1beta1
kind: StepAction
metadata:
name: example-stepaction-name
spec:
env:
- name: HOME
value: /home
image: ubuntu
command: ["ls"]
args: ["-lh"]
Declaring Parameters
Like with Tasks
, a StepAction
must declare all the parameters that it uses. The same rules for Parameter
name, type (including object, array and string) apply as when declaring them in Tasks
. A StepAction
can also provide default value to a Parameter
.
Parameters
are passed to the StepAction
from its corresponding Step
referencing it.
apiVersion: tekton.dev/v1beta1
kind: StepAction
metadata:
name: stepaction-using-params
spec:
params:
- name: gitrepo
type: object
properties:
url:
type: string
commit:
type: string
- name: flags
type: array
- name: outputPath
type: string
default: "/workspace"
image: some-git-image
args: [
"-url=$(params.gitrepo.url)",
"-revision=$(params.gitrepo.commit)",
"-output=$(params.outputPath)",
"$(params.flags[*])",
]
🌱
params
cannot be directly used in ascript
inStepActions
. Directly substitutingparams
inscripts
makes the workload prone to shell attacks. Therefore, we do not allow direct usage ofparams
inscripts
inStepActions
. Instead, rely on passingparams
toenv
variables and reference them inscripts
. We cannot do the same forinlined-steps
because it breaksv1 API
compatibility for existing users.
Passing Params to StepAction
A StepAction
may require params. In this case, a Task
needs to ensure that the StepAction
has access to all the required params
.
When referencing a StepAction
, a Step
can also provide it with params
, just like how a TaskRun
provides params to the underlying Task
.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: step-action
spec:
params:
- name: param-for-step-action
description: "this is a param that the step action needs."
steps:
- name: action-runner
ref:
name: step-action
params:
- name: step-action-param
value: $(params.param-for-step-action)
Note: If a Step
declares params
for an inlined Step
, it will also lead to a validation error. This is because an inlined Step
gets its params
from the TaskRun
.
Emitting Results
A StepAction
also declares the results that it will emit.
apiVersion: tekton.dev/v1alpha1
kind: StepAction
metadata:
name: stepaction-declaring-results
spec:
results:
- name: current-date-unix-timestamp
description: The current date in unix timestamp format
- name: current-date-human-readable
description: The current date in human readable format
image: bash:latest
script: |
#!/usr/bin/env bash
date +%s | tee $(results.current-date-unix-timestamp.path)
date | tee $(results.current-date-human-readable.path)
It is possible that a StepAction
with Results
is used multiple times in the same Task
or multiple StepActions
in the same Task
produce Results
with the same name. Resolving the Result
names becomes critical otherwise there could be unexpected outcomes. The Task
needs to be able to resolve these Result
names clashes by mapping it to a different Result
name. For this reason, we introduce the capability to store results on a Step
level.
StepActions
can also emit Results
to $(step.results.<resultName>.path)
.
apiVersion: tekton.dev/v1alpha1
kind: StepAction
metadata:
name: stepaction-declaring-results
spec:
results:
- name: current-date-unix-timestamp
description: The current date in unix timestamp format
- name: current-date-human-readable
description: The current date in human readable format
image: bash:latest
script: |
#!/usr/bin/env bash
date +%s | tee $(step.results.current-date-unix-timestamp.path)
date | tee $(step.results.current-date-human-readable.path)
Results
from the above StepAction
can be fetched by the Task
or in another Step/StepAction
via $(steps.<stepName>.results.<resultName>)
.
Fetching Emitted Results from StepActions
A Task
can fetch Results
produced by the StepActions
(i.e. only Results
emitted to $(step.results.<resultName>.path)
, NOT $(results.<resultName>.path)
) using variable replacement syntax. We introduce a field to Task Results
called Value
whose value can be set to the variable $(steps.<stepName>.results.<resultName>)
.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: task-fetching-results
spec:
results:
- name: git-url
description: "url of git repo"
value: $(steps.git-clone.results.url)
- name: registry-url
description: "url of docker registry"
value: $(steps.kaniko.results.url)
steps:
- name: git-clone
ref:
name: clone-step-action
- name: kaniko
ref:
name: kaniko-step-action
Results
emitted to $(step.results.<resultName>.path)
are not automatically available as TaskRun Results
. The Task
must explicitly fetch it from the underlying Step
referencing StepActions
.
For example, lets assume that in the previous example, the “kaniko” StepAction
also produced a Result
named “digest”. In that case, the Task
should also fetch the “digest” from “kaniko” Step
.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: task-fetching-results
spec:
results:
- name: git-url
description: "url of git repo"
value: $(steps.git-clone.results.url)
- name: registry-url
description: "url of docker registry"
value: $(steps.kaniko.results.url)
- name: digest
description: "digest of the image"
value: $(steps.kaniko.results.digest)
steps:
- name: git-clone
ref:
name: clone-step-action
- name: kaniko
ref:
name: kaniko-step-action
Passing Results between Steps
StepResults
(i.e. results written to $(step.results.<result-name>.path)
, NOT $(results.<result-name>.path)
) can be shared with following steps via replacement variable $(steps.<step-name>.results.<result-name>)
.
Pipeline supports two new types of results and parameters: array []string
and object map[string]string
.
Result Type | Parameter Type | Specification | enable-api-fields |
---|---|---|---|
string | string | $(steps.<step-name>.results.<result-name>) |
stable |
array | array | $(steps.<step-name>.results.<result-name>[*]) |
alpha or beta |
array | string | $(steps.<step-name>.results.<result-name>[i]) |
alpha or beta |
object | string | $(tasks.<task-name>.results.<result-name>.key) |
alpha or beta |
Note: Whole Array Results
(using star notation) cannot be referred in script
and env
.
The example below shows how you could pass step results
from a step
into following steps, in this case, into a StepAction
.
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: step-action-run
spec:
TaskSpec:
steps:
- name: inline-step
results:
- name: result1
type: array
- name: result2
type: string
- name: result3
type: object
properties:
IMAGE_URL:
type: string
IMAGE_DIGEST:
type: string
image: alpine
script: |
echo -n "[\"image1\", \"image2\", \"image3\"]" | tee $(step.results.result1.path)
echo -n "foo" | tee $(step.results.result2.path)
echo -n "{\"IMAGE_URL\":\"ar.com\", \"IMAGE_DIGEST\":\"sha234\"}" | tee $(step.results.result3.path)
- name: action-runner
ref:
name: step-action
params:
- name: param1
value: $(steps.inline-step.results.result1[*])
- name: param2
value: $(steps.inline-step.results.result2)
- name: param3
value: $(steps.inline-step.results.result3[*])
Note: Step Results
can only be referenced in a Step's/StepAction's
env
, command
and args
. Referencing in any other field will throw an error.
Declaring WorkingDir
You can declare workingDir
in a StepAction
:
apiVersion: tekton.dev/v1alpha1
kind: StepAction
metadata:
name: example-stepaction-name
spec:
image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:latest
workingDir: /workspace
script: |
# clone the repo
...
The Task
using the StepAction
has more context about how the Steps
have been orchestrated. As such, the Task
should be able to update the workingDir
of the StepAction
so that the StepAction
is executed from the correct location.
The StepAction
can parametrize the workingDir
and work relative to it. This way, the Task
does not really need control over the workingDir, it just needs to pass the path as a parameter.
apiVersion: tekton.dev/v1alpha1
kind: StepAction
metadata:
name: example-stepaction-name
spec:
image: ubuntu
params:
- name: source
description: "The path to the source code."
workingDir: $(params.source)
Declaring SecurityContext
You can declare securityContext
in a StepAction
:
apiVersion: tekton.dev/v1alpha1
kind: StepAction
metadata:
name: example-stepaction-name
spec:
image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:latest
securityContext:
runAsUser: 0
script: |
# clone the repo
...
Note that the securityContext
from StepAction
will overwrite the securityContext
from TaskRun
.
Declaring VolumeMounts
You can define VolumeMounts
in StepActions
. The name
of the VolumeMount
MUST be a single reference to a string Parameter
. For example, $(params.registryConfig)
is valid while $(params.registryConfig)-foo
and "unparametrized-name"
are invalid. This is to ensure reusability of StepActions
such that Task
authors have control of which Volumes
they bind to the VolumeMounts
.
apiVersion: tekton.dev/v1alpha1
kind: StepAction
metadata:
name: myStep
spec:
params:
- name: registryConfig
- name: otherConfig
volumeMounts:
- name: $(params.registryConfig)
mountPath: /registry-config
- name: $(params.otherConfig)
mountPath: /other-config
image: ...
script: ...
Referencing a StepAction
StepActions
can be referenced from the Step
using the ref
field, as follows:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: step-action-run
spec:
taskSpec:
steps:
- name: action-runner
ref:
name: step-action
Upon resolution and execution of the TaskRun
, the Status
will look something like:
status:
completionTime: "2023-10-24T20:28:42Z"
conditions:
- lastTransitionTime: "2023-10-24T20:28:42Z"
message: All Steps have completed executing
reason: Succeeded
status: "True"
type: Succeeded
podName: step-action-run-pod
provenance:
featureFlags:
EnableStepActions: true
...
startTime: "2023-10-24T20:28:32Z"
steps:
- container: step-action-runner
imageID: docker.io/library/alpine@sha256:eece025e432126ce23f223450a0326fbebde39cdf496a85d8c016293fc851978
name: action-runner
terminationReason: Completed
terminated:
containerID: containerd://46a836588967202c05b594696077b147a0eb0621976534765478925bb7ce57f6
exitCode: 0
finishedAt: "2023-10-24T20:28:42Z"
reason: Completed
startedAt: "2023-10-24T20:28:42Z"
taskSpec:
steps:
- computeResources: {}
image: alpine
name: action-runner
If a Step
is referencing a StepAction
, it cannot contain the fields supported by StepActions
. This includes:
image
command
args
script
env
volumeMounts
Using any of the above fields and referencing a StepAction
in the same Step
is not allowed and will cause a validation error.
# This is not allowed and will result in a validation error
# because the image is expected to be provided by the StepAction
# and not inlined.
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: step-action-run
spec:
taskSpec:
steps:
- name: action-runner
ref:
name: step-action
image: ubuntu
Executing the above TaskRun
will result in an error that looks like:
Error from server (BadRequest): error when creating "STDIN": admission webhook "validation.webhook.pipeline.tekton.dev" denied the request: validation failed: image cannot be used with Ref: spec.taskSpec.steps[0].image
When a Step
is referencing a StepAction
, it can contain the following fields:
computeResources
workspaces
(Isolated workspaces)volumeDevices
imagePullPolicy
onError
stdoutConfig
stderrConfig
securityContext
envFrom
timeout
ref
params
Using any of the above fields and referencing a StepAction
is allowed and will not cause an error. For example, the TaskRun
below will execute without any errors:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: step-action-run
spec:
taskSpec:
steps:
- name: action-runner
ref:
name: step-action
params:
- name: step-action-param
value: hello
computeResources:
requests:
memory: 1Gi
cpu: 500m
timeout: 1h
onError: continue
Specifying Remote StepActions
A ref
field may specify a StepAction
in a remote location such as git.
Support for specific types of remote will depend on the Resolvers
your
cluster’s operator has installed. For more information including a tutorial, please check resolution docs. The below example demonstrates referencing a StepAction
in git:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
generateName: step-action-run-
spec:
taskSpec:
steps:
- name: action-runner
ref:
resolver: git
params:
- name: url
value: https://github.com/repo/repo.git
- name: revision
value: main
- name: pathInRepo
value: remote_step.yaml
The default resolver type can be configured by the default-resolver-type
field in the config-defaults
ConfigMap (alpha
feature). See additional-configs.md for details.
Controlling Step Execution with when Expressions
You can define when
in a step
to control its execution.
The components of when
expressions are input
, operator
, values
, cel
:
Component | Description | Syntax |
---|---|---|
input |
Input for the when expression, defaults to an empty string if not provided. |
* Static values e.g. "ubuntu" * Variables (parameters or results) e.g. "$(params.image)" or "$(tasks.task1.results.image)" or "$(tasks.task1.results.array-results[1])" |
operator |
operator represents an input ’s relationship to a set of values , a valid operator must be provided. |
in or notin |
values |
An array of string values, the values array must be provided and has to be non-empty. |
* An array param e.g. ["$(params.images[*])"] * An array result of a task ["$(tasks.task1.results.array-results[*])"] * An array result of a step ["(steps.step1.results.array-results[*])"] * values can contain static values e.g. "ubuntu" * values can contain variables (parameters or results) or a Workspaces’s bound state e.g. ["$(params.image)"] or ["$(steps.step1.results.image)"] or ["$(tasks.task1.results.array-results[1])"] or ["$(steps.step1.results.array-results[1])"] |
cel |
The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate. This is an alpha feature, enable-cel-in-whenexpression needs to be set to true to use this feature. |
cel-syntax |
The below example shows how to use when expressions to control step executions:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc-2
spec:
resources:
requests:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
---
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
generateName: step-when-example
spec:
workspaces:
- name: custom
persistentVolumeClaim:
claimName: my-pvc-2
taskSpec:
description: |
A simple task that shows how to use when determine if a step should be executed
steps:
- name: should-execute
image: bash:latest
script: |
#!/usr/bin/env bash
echo "executed..."
when:
- input: "$(workspaces.custom.bound)"
operator: in
values: [ "true" ]
- name: should-skip
image: bash:latest
script: |
#!/usr/bin/env bash
echo skipskipskip
when:
- input: "$(workspaces.custom2.bound)"
operator: in
values: [ "true" ]
- name: should-continue
image: bash:latest
script: |
#!/usr/bin/env bash
echo blabalbaba
- name: produce-step
image: alpine
results:
- name: result2
type: string
script: |
echo -n "foo" | tee $(step.results.result2.path)
- name: run-based-on-step-results
image: alpine
script: |
echo "wooooooo"
when:
- input: "$(steps.produce-step.results.result2)"
operator: in
values: [ "bar" ]
workspaces:
- name: custom
The StepState for a skipped step looks like something similar to the below:
{
"container": "step-run-based-on-step-results",
"imageID": "docker.io/library/alpine@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b",
"name": "run-based-on-step-results",
"terminated": {
"containerID": "containerd://bf81162e79cf66a2bbc03e3654942d3464db06ff368c0be263a8a70f363a899b",
"exitCode": 0,
"finishedAt": "2024-03-26T03:57:47Z",
"reason": "Completed",
"startedAt": "2024-03-26T03:57:47Z"
},
"terminationReason": "Skipped"
}
Where terminated.exitCode
is 0
and terminationReason
is Skipped
to indicate the Step exited successfully and was skipped.
Feedback
Was this page helpful?