blob: 66426f1edba30423ce4d44433a1ed93afec409f0 [file] [log] [blame] [view]
---
title: Common Step Properties
layout: website-normal
---
### Shorthand and Longhand Syntax
Where possible, and in most examples so far, we've used the "shorthand" syntax for steps
which uses a string template to make it easy to write workflow steps:
```
steps:
- ssh echo today is `DATE`
- http http://some-rest-api/
- container my/container command
- set-sensor the-output = ssh says ${1.stdout}, http ${2.data}, container ${3.stdout}
```
This always starts with the type name, and the remainder is parsed according to a template
defined by that step type.
Internally, steps are maps with multiple inputs and properties,
and the shorthand syntax makes it possible to set the most common ones.
For `ssh` this is the command to run; for `container` an image; for `http` a URL; and for `set-xxx`
an expression of the form `key=value`.
It is always possible to write the longhand map syntax, and if using inputs or properties
which aren't supported in the shorthand template (such as the `condition` property described below),
the longhand map syntax is required:
```
steps:
- type: ssh
input:
command: echo today is `DATE`
condition:
target: ${skip_date}
not: { equals: true }
- ...
```
Shorthand can be combined as part of a map by providing the step shorthand string in a key
`step` (or `s` or `shorthand`). The type must be included as the first word, and the `type` key must not be used.
```
steps:
- step: ssh echo today is `DATE`
condition:
target: ${scratch.skip_date}
not: { equals: true }
```
Care should be taken when using `:` in a step with shorthand. YAML will parse it as a map if it is not quoted in YAML.
However at runtime, if the step looks like it came from an accidental colon causing a map, it will be reverted to a
string with the colon re-introduced, so you can write steps with shorthand `- log Your name is: ${name}`.
All steps support a number of common properties, described below.
### Explicit IDs and Name
Steps can define an explicit ID for use with `next`, for correlation in the UI,
and to be able to reference the output or input from a specific step using
the [workflow expression syntax](variables.md).
They can also include a `name` used in the UI.
```
steps:
- type: no-op
next: skipping-ssh-date
condition:
target: ${scratch.skip_date}
equals: true
- step: ssh echo today is `date`
name: Doing SSH
next: end
- id: skipping-ssh-date
name: Not doing SSH
step: log skipping ssh date command
```
### Conditions
The previous example shows the use of conditions, as mentioned as one of the properties common to all steps.
This makes use of the recent [Predicate DSL](../yaml-reference.md#predicate-dsl) conditions framework.
It is normally necessary to supply a `target`, unless one of the entity-specific target keys (e.g. `sensor` or `config`)
is used. The target and arguments here can use the [workflow expression syntax](variables.md).
The condition is evaluated when the step is about to run, and if the condition is not satisfied,
the workflow moves to the following step in the sequence, or ends if that was the last step.
Apart from `name` and `id` above, if a step's `condition` is unmet,
the other properties set on a step are ignored.
The `if` step can be used for simple conditions, as shown in the next section.
### Jumping with "Next" or "Goto"
The common property `next` allows overriding the workflow sequencing,
indicating that a different step should be gone to next.
This does not apply if a step's condition is not satisfied, as noted at the end of the previous section.
The value of the `next` property should be the ID of the step to go to
or one of the following reserved words:
* `start`: return to the start of the workflow
* `end`: exit the workflow (or if in a block where this doesn't make sense, such as `retry`, go to the last executed
step)
* `exit`: if in an error handler, exit that error handler
The `goto` step type is equivalent to the `no-op` step with `next` set,
as a simpler idiom for controlling workflow flow.
While `goto` is "considered harmful" in many programming environments,
for declarative workflow it is fairly common, because it can simplify what
might otherwise involve multiple nested workflows.
That said, the confusion that `goto` can cause should be kept in mind,
and its availability not abused: in particular where a task can be better done
in a proper high-level programming language, consider putting that program
into a container and calling it from your workflow.
Thus the above workflow can be written more concisely as:
```
steps:
- if ${scratch.skip_date} then goto skipping-ssh-date
- step: ssh echo today is `date`
next: end
- id: skipping-ssh-date
name: Not doing SSH
step: log skipping ssh date command
```
### Input and Output
Most steps take input parameters and return output.
Many step-specific input parameters can be set in the shorthand, but not all.
All input parameters can be specified in an `input` block.
It is also possible to customize the output from a step using an `output` block.
For example:
```
- step: let target = aws
condition:
target: location
tag: aws
next: picked-target
- step: let target = azure
condition:
target: location
tag: azure
next: picked-target
- input:
location_name: ${entity.location.name}
step: log Unrecognized cloud ${location_name}, using default
output:
cloud: default
next: end
- id: picked-target
step: log Picked target ${target}
output:
cloud: ${target}
```
The above will return an output map containing a key `cloud` and a value of either `azure`, `aws`, or `default`.
In addition, a custom `input` variable is passed to the third step.
(This is not the simplest way to write this logic, but it illustrates the concepts.)
This example also shows the expression syntax. More on inputs, outputs, variables, and expressions
is covered [here](variables.md).
### Timeout
Any step and/or an entire workflow can define a `timeout: <duration>`,
where the `<duration>` is of the form `1 day` or `1h 30m`.
If the step or workflow where this is present takes longer than this duration,
it will be interrupted and will throw a `TimeoutException`.
### Error Handling with `on-error`
Errors on a step and/or a workflow can use the `on-error: <handler>` property to determine how
and error should be handled. The `<handler>` can be:
* a single step as a string, for instance `on-error: retry`, or to prevent infinite loops
and introduce exponential backoff `on-error: retry limit 4 backoff 5s increasing 2x`
* a single step as a map, possibly with a condition; if the condition is not met,
the error is rethrown; for example:
```
- step: ssh systemctl restart my-service
on-error:
step: goto my-service-restart-error
condition:
target: ${exit_code}
greater-than: 0
```
If the `ssh` command returns an `exit_code` greater than zero (which the `ssh` step treats as an error)
this will go to the step with `id: my-service-restart-error`.
Any other error, such as network connectivity, will be rethrown and could be addressed by a workflow-level
`on-error` or could cause the workflow to fail.
* a list of steps, some or all with conditions and some or all with `next` indicated, as follows:
The list of steps will run sequentially, applying conditions to each.
The target of conditions in an error handler is the error itself, so
the DSL `error-cause` predicate can be used, for example
`error-cause: { java-instance-of: TimeoutException }` or
`error-cause: { glob: "*server timeout*" }`.
The error handler will complete and be considered to have handled the error at the first step
where the condition is satisfied and which indicates a `next` step (either a `goto` or `retry` step, or `next`
property).
and subsequent steps in the error handler will not run.
If all steps have conditions and none are met, the error handler will rethrow the error,
but otherwise, if one or more steps run and none of them throw errors or indicate a `next`,
it will consider the error to be handled and go to the next step in the non-error-handler workflow.
Where the handler combines non-conditional statements (such as `log`) with conditions,
all expected terminal conditions should indicate a `next`; to avoid confusion it is not recommended that
the last step be a condition that might not apply. Consider adding a final step
`fail rethrow message None of the error handler conditions were met` to make sure the handler does not
accidentally succeed because a `log` step was run, when none of the "real" conditions applied.
The `next` target `exit` can be used in an error handler to indicate to go to the next step in the containing
workflow sequence. Nested error handlers are permitted, and `exit` will return to the containing error handler
without indicating that it should exit. Any other `next` target from a nested workflow jumps out of all nested
error handlers and goes to that target in the non-error-handler workflow.
Error handlers run in the same context as the original workflow, not a new context as nested workflow does,
but with some restrictions. This has some significant benefits but also some things in special cases which
might require care:
* You can read and write workflow variables within error handlers
* You can set the `output` for use in the outer workflow
* Error handlers are not persisted; any replay will revert to the outer step or earlier
* ID's are not permitted in error handlers; any `next` refers to the containing workflow
* The workflow UI does not show error handling steps; their activity can only be seen in the tasks view
and in the logs
### Workflow Settings
There are a few other settings allowed on all or some steps which configure workflow behavior.
These are `replayable`, `idempotent`, and `retention`,
and are described under [Workflow Settings](settings.md).