Skip to content

Commit f5477e7

Browse files
akuitybothairyhum
andauthored
chore(backport release-1.10): docs: Add documentation on using custom steps (#6103)
Signed-off-by: Daniil Fedotov <daniil.fedotov@akuity.io> Co-authored-by: Daniil Fedotov <daniil.fedotov@akuity.io>
1 parent e4aef09 commit f5477e7

2 files changed

Lines changed: 256 additions & 2 deletions

File tree

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
---
2+
sidebar_label: Custom steps
3+
description: Execute command in user-provided image
4+
---
5+
6+
<span class="tag professional"></span>
7+
<span class="tag beta"></span>
8+
9+
# Custom steps
10+
11+
:::info
12+
13+
This promotion step is only available in Kargo on the
14+
[Akuity Platform](https://akuity.io/akuity-platform), versions v1.10 and above.
15+
16+
Additionally, it requires enabling of the Promotion Controller and use of self-hosted agent
17+
to allow for Pod-based promotions.
18+
19+
:::
20+
21+
:::warning
22+
23+
This feature is alpha version, configurations and specifications may change in the future.
24+
Performance and stability have not been tested yet, using the feature for memory-heavy processes may cause issues.
25+
26+
:::
27+
28+
Custom steps allow operators to configure their own promotion step logic using OCI images and scripting.
29+
This can be used for tasks currently not provided by built-in steps to extend Kargo capabilites
30+
with bespoke or proprietary functionality.
31+
32+
## Registering a custom step
33+
34+
Custom steps have to be registered in a Kargo cluster in order to be used in promotion templates.
35+
To register a new step, a Kargo cluster admin needs to create a cluster-scoped `CustomPromotionStep` resource:
36+
37+
```yaml
38+
apiVersion: ee.kargo.akuity.io/v1alpha1
39+
kind: CustomPromotionStep
40+
metadata:
41+
name: hello-world
42+
spec:
43+
## REQUIRED: image to execute the command in
44+
image: ubuntu
45+
## REQUIRED: command to run
46+
command: ["sh", "-c", "sleep 5; echo 'hello=${HELLO}-${{ config.world }}' > $KARGO_OUTPUT"]
47+
## OPTIONAL: additional environment to provide to the command
48+
env:
49+
- name: HELLO
50+
value: ${{ config.hello }}
51+
## OPTIONAL: output capture configuration
52+
output:
53+
source:
54+
## One of Pipe | Stdout | Stderr | File, default is Pipe
55+
type: Pipe
56+
## required when type is File, should be omitted if it's not
57+
path: ""
58+
## One of JSON | YAML | KeyValue | Text
59+
## inferred from file extension when omitted, default is KeyValue
60+
format: KeyValue
61+
## Apply expressions to output to reformat with different keys
62+
## If omitted returns the parsed output as is
63+
transform:
64+
message: output.hello
65+
## OPTIONAL: error handling metadata
66+
defaultTimeout: 5m
67+
defaultErrorThreshold: 3
68+
## OPTIONAL: capabilities to provision into step container
69+
## Options: access-control-plane, access-argocd
70+
capabilities: []
71+
## OPTIONAL: container resources configuration
72+
resources:
73+
requests:
74+
memory: "64Mi"
75+
cpu: "250m"
76+
limits:
77+
memory: "128Mi"
78+
cpu: "500m"
79+
## OPTIONAL: secrets to pull the image from private repos
80+
imagePullSecrets: []
81+
```
82+
83+
:::warning
84+
85+
The `command` field does not specify the `command` of the step container in promotion pod definition.
86+
Kargo is running an executor binary which coordinates step execution (start, retry, abort) and will run the `command`.
87+
88+
:::
89+
90+
:::warning
91+
92+
If retry policy is set for the step, the `command` could be executed multiple times on failure.
93+
It's recommended to design commands to have some idempotency.
94+
95+
:::
96+
97+
98+
## Using a custom step
99+
100+
After a custom step is registered in the cluster, it can be used in promotion template:
101+
102+
```yaml
103+
vars:
104+
- name: exampleVar
105+
value: example
106+
steps:
107+
- as: my-custom-step
108+
uses: hello-world
109+
config:
110+
world: ${{ vars.exampleVar }}
111+
hello: bonjour
112+
something:
113+
else:
114+
- entirely
115+
```
116+
117+
### Passing input to steps
118+
119+
Command execution will run in the same workdir as other steps.
120+
121+
To access step config or step execution context, `command` and values in `env` can use templates like:
122+
123+
```yaml
124+
command:
125+
- "echo"
126+
- "Step ${{ ctx.meta.step.alias }} in promotion $PROM_VAR with ${{ config.my_config }} and ${{ config.nested.values[0] }}"
127+
env:
128+
- name: PROM_VAR
129+
value: ${{ ctx.promotion }}
130+
```
131+
And config:
132+
```yaml
133+
config:
134+
my_config: configvalue
135+
nested:
136+
values:
137+
- one
138+
```
139+
140+
:::info
141+
142+
There is no config validation at the moment and configuration structure and keys can be arbitrary.
143+
The step will error in runtime if any expressions require a missing config value, e.g. `${{ config.something.else }}`
144+
145+
:::
146+
147+
148+
Templates are using [expression language](../40-expressions.md), but only with `config` and `ctx` variables. The `ctx` variable is using [the ctx format](../40-expressions.md#context-ctx-object-structure).
149+
150+
In order to pass values from secrets, configmaps or other promotion context to the custom step, they should be used in the promotion template and passed as `config` variables.
151+
152+
#### Passing secrets to custom steps
153+
154+
Example using credential from a secret, assuming secret `db_credential` has keys `username` and `password`:
155+
156+
```yaml
157+
apiVersion: ee.kargo.akuity.io/v1alpha1
158+
kind: CustomPromotionStep
159+
metadata:
160+
name: access-db
161+
spec:
162+
image: my_image
163+
command: ["db_script.sh", "--username=${{ config.cred.username }}"]
164+
env:
165+
- name: DB_PASSWORD
166+
value: ${{ config.cred.password }}
167+
```
168+
169+
```yaml
170+
- as: custom-db-step
171+
uses: access-db
172+
config:
173+
cred: ${{ secret("db_credential") }}
174+
```
175+
176+
### Steps output
177+
178+
Output from a step execution is parsed according to the `output` configuration:
179+
```yaml
180+
output:
181+
source:
182+
type: Pipe | Stdout | Stderr | File
183+
path: <string>
184+
format: JSON | YAML | KeyValue | Text
185+
transform:
186+
<key>: <expression_string>
187+
```
188+
189+
If `output` is not specified, default configuration is:
190+
```yaml
191+
output:
192+
source:
193+
type: Pipe
194+
path: ""
195+
format: KeyValue
196+
transform: {}
197+
```
198+
199+
#### Output sources
200+
201+
| Type | Behavior |
202+
|------|----------|
203+
| `Pipe` | Reads from the `KARGO_OUTPUT` temp file injected by the executor. Default when `output` is omitted. |
204+
| `Stdout` | Reads from the command's standard output. |
205+
| `Stderr` | Reads from the command's standard error. |
206+
| `File` | Reads from a file at the specified `path`. Format inferred from file extension when omitted. |
207+
208+
#### Ouptut formats
209+
210+
| Format | Behavior |
211+
|--------|----------|
212+
| `KeyValue` | `key=value` lines (GitHub Actions–compatible). Quotes are part of the value, not stripped. Lines without `=` are ignored. Multiline values supported via heredoc syntax (`key<<EOF` / lines / `EOF`). |
213+
| `JSON` | Parsed as JSON. Objects used directly; arrays/scalars wrapped as `{"output": <value>}`. |
214+
| `YAML` | Same semantics as JSON. Format inferred from `.yaml`/`.yml` file extensions. |
215+
| `Text` | Stored as `{"output": "<raw string>"}`. |
216+
217+
#### Transform expressions
218+
219+
An optional `transform` map accepts per-key [expr-lang](https://expr-lang.org) expressions to reshape the parsed output before it is stored. The variable `output` holds the parsed value (map for JSON/YAML/KeyValue, string for Text). Each expression may return any value:
220+
221+
```yaml
222+
transform:
223+
hasCritical: output.summary.critical > 0
224+
total: output.summary.total
225+
failed: output contains "FAIL"
226+
message: output.message
227+
```
228+
229+
If `transform` map is used, it will completely replace the output produced by the `source`.
230+
To pass some values through they should map to their respective output key (like `message` in the example above)
231+
232+
#### Output size limits
233+
234+
- All formats: hard 256 KiB limit on the final output map (checked after transform). Returns an error if exceeded.
235+
- The size check happens after `transform` evaluation, so a transform that extracts a small subset of a large payload will not hit the limit.
236+
- Step result messages: stdout/stderr truncated to the last 16 KiB each to keep Kubernetes status conditions readable.
237+
238+
## Runtime limitations
239+
240+
- Supported container architectures:
241+
- linux-arm64
242+
- linux-amd64
243+
- Aborting the step will kill the process with `SIGKILL`. There is no graceful shutdown at the moment.
244+
- Currently step containers run with user `65532`, images requiring specific user are not supported yet.
245+
- Avoid using too many steps in the same promotion as they utilize the same pod and may exhaust pod/node resources.
246+
247+
## ADVANCED: step capabilities
248+
249+
Kargo steps can have the following capabilities configured:
250+
251+
- `access-control-plane` - allow access to Kargo controlplane via k8s API. `kubeconfig` or `token` (for local in-cluster access) will be provisioned to `coordination/kubernetes/kargo` directory in the container.
252+
- `access-argocd` - allow access to ArgoCD controlplane via k8s API. `kubeconfig` or `token` (for local in-cluster access) will be provisioned to `coordination/kubernetes/argocd` directory in the container.

docs/enterprise-features.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"snow-query-for-records",
1818
"snow-wait-for-condition",
1919
"toml-parse",
20-
"toml-update"
20+
"toml-update",
21+
"Custom steps"
2122
],
2223
"pro": [
2324
"jira",
@@ -33,6 +34,7 @@
3334
"snow-update",
3435
"snow-delete",
3536
"snow-query-for-records",
36-
"snow-wait-for-condition"
37+
"snow-wait-for-condition",
38+
"Custom steps"
3739
]
3840
}

0 commit comments

Comments
 (0)