ActionChain
ActionChain is a no-frills linear workflow. It is a simple chain of action invocations. The result of each action (success or failure) is checked and used to determine the next action to run. This provides for simple branching logic, based upon success or failure.
Data can be passed between actions, and results are published for each action.
From the perspective of StackStorm, an ActionChain is itself an action. So it has the same operations and features, such as definition, registration, execution from CLI, usage from Rules, etc. An ActionChain can also be called as an Action from another ActionChain, or from an Orquesta workflow.
Note
If you need more complex workflow logic, such as forks, joins, retries, delays and policies for error handling, use Orquesta instead.
Creating an ActionChain
ActionChains are defined in packs, under the /opt/stackstorm/packs/<pack>/actions/
directory.
An ActionChain needs two files: a YAML metadata file, the same as used for simple actions, and the
ActionChain definition itself. The metadata file lives in the <pack>/actions
directory, while
the ActionChain definition is placed in the <pack>/actions/chains
directory.
ActionChain Metadata
The ActionChain metadata is very similar to that used for any other action.
The key differences are that it specifies action-chain
as the runner_type
, and the
entry_point
points to the workflow definition file.
Here is an example showing the action definition metadata echochain.meta.yaml for an ActionChain echochain.yaml:
---
# Action definition metadata
name: "echochain"
description: "Simple Action Chain workflow"
# `runner_type` has value `action-chain` to identify that action is an ActionChain.
runner_type: "action-chain"
# `entry_point` path to the ActionChain definition file, relative to the pack's action directory.
entry_point: "chains/echochain.yaml"
enabled: true
parameters:
skip_notify:
default:
- c2
notify:
on-complete:
message: "\"@channel: Action succeeded.\""
routes:
- "slack"
ActionChain Definition
This echochain.yaml: is the corresponding ActionChain workflow definition referenced above:
---
chain:
-
name: "c1"
ref: "core.local"
parameters:
cmd: "echo c1"
on-success: "c2"
on-failure: "c4"
-
name: "c2"
ref: "core.local"
parameters:
cmd: "echo \"c2: parent exec is {{action_context.parent.execution_id}}.\""
on-success: "c3"
on-failure: "c4"
-
name: "c3"
ref: "core.local"
parameters:
cmd: "echo c3"
on-failure: "c4"
-
name: "c4"
ref: "core.local"
parameters:
cmd: "echo fail c4"
default: "c1"
Definition Details:
chain
is the array property that contains tasks, which encapsulate action invocation.Tasks are named action execution specifications provided in the form of a list. The name is scoped to an ActionChain and is used as a reference to a task.
The
ref
property of a task points to an Action registered in StackStorm. This could be in any pack.on-success
is the link to a task to invoke next upon successful action execution. If not provided, the ActionChain will terminate with status set tosuccess
.on-failure
is an optional link to a task to invoke next in case of a failed action execution. If not provided, the ActionChain will terminate with the status set toerror
.default
is an optional top level property that specifies the start of an ActionChain. Ifdefault
is not explicitly specified, the ActionChain starts from the first action.
Registering the ActionChain
Once action definition and metadata files are created, load the action:
# Register the action
st2 action create /opt/stackstorm/packs/examples/actions/echochain.meta.yaml
# Check it is available
st2 action list --pack=examples
# Run it
st2 run examples.echochain
Any changes in the ActionChain workflow definition are picked up automatically. However if you
change the action metadata (e.g. rename or add parameters), you will have to update the action with
st2 action update <action.ref> <action.metadata.file>`
. Alternatively, a full reload with
st2ctl reload --register-all
will pick up all the changes.
Providing Input
To provide input to an ActionChain, input parameters must be defined in the action metadata:
---
# Action definition metadata
name: "echochain-param"
description: "Action Chain workflow passing variables"
# `runner_type` identifies the runner
runner_type: "action-chain"
# `entry_point` path to the ActionChain definition file, relative to the pack's action directory.
entry_point: "chains/echochain_param.yaml"
enabled: true
parameters:
input1:
type: "string"
required: true
description: "Any string to pass down"
The input parameter input1
can now be referenced in the parameters field of a task in the
ActionChain definition:
---
# ...
chain:
-
name: "action1"
ref: "core.local"
parameters:
action1_input: "{{input1}}"
# ...
action1_input
has value {{input1}}
. This syntax is variable referencing as supported by
Jinja templating.
Similar constructs are also used in Rule criteria and action fields.
Variables
ActionChain offers the convenience of named variables. Global vars are set at the top of the
definition with the vars
keyword.
Tasks publish new variables with the publish
keyword. Variables are handy when you need to mash
up a reusable value from the input, globals, datastore values, and results of multiple action
executions.
All variables are referred to using Jinja syntax. The cumulative published variables are also
available in the result of an ActionChain execution under the published
property if the
display_published
property is supplied to the ActionChain Runner.
---
vars:
domain: "{{ st2kv.system.domain }}" # Global Var
port: 9101
chain:
-
name: get_service_data
ref: my_pack.get_services
publish:
url_1: http://"{{ get_service_data.result[0].host.name }}.{{ domain }}:{{ port }}"
The publish_data.yaml workflow in
the examples pack shows a complete working example of using vars
and publish
:
---
vars:
domain: "{{ st2kv.system.domain }}" # `system` references DataStore key-values. Null if not set.
api_port: 9101
webui_port: 8080
chain:
-
name: "get_host"
ref: "core.local"
parameters:
cmd: hostname | tr -d ' \n'
publish:
# Publish a variable to shortcut a long expression
api_url: "http://{{ get_host.stdout }}:{{ api_port }}"
webui_url: "http://{{ get_host.stdout }}:{{ webui_port }}"
host: "{{ get_host.stdout }}"
# Jinja woodoo used for basic logic
fqdn: "{{ get_host.stdout }}{% if domain %}.{{ domain }}{% endif %}"
# A complex object can be published
paths:
api: [ "v1/actions", "v1/triggers"]
webui: "/"
auth_port: "{{ api_port + 1 }}"
# This variable references "system_info_path" variable which is an action parameter
system_info: "{{ system_info_path }}"
on-success: say_the_names
-
name: say_the_names
These published variables could also be used to pass data between workflows. See below for more information about passing data between workflows.
Passing Data between Tasks
The output of previous tasks can be referenced in a similar manner to input to an ActionChain.
This example echochain_param.yaml shows input and data passing down the workflow:
---
chain:
-
name: "c1"
ref: "core.local"
parameters:
cmd: "echo c1, input {{input1}}" # Refers to action's input parameter
on-success: "c2"
on-failure: "c4"
-
name: "c2"
ref: "core.local"
parameters:
cmd: "echo c2 {{c1.stdout}}" # refers to previous action's output
on-success: "c3"
on-failure: "c4"
-
name: "c3"
ref: "core.local"
parameters:
cmd: "echo c3 {{c2.stdout}}"
on-failure: "c4"
-
name: "c4"
ref: "core.local"
parameters:
cmd: "echo fail c4"
default: "c1"
Details:
Output of a task is always prefixed by the task name. e.g. in
{"cmd":"echo c2 {{c1.stdout}}"}
,c1.stdout
refers to the output of ‘c1’ and further drills down into properties of the output. The reference point is theresult
field ofaction execution
object.A special
__results
key provides access to the entire result of the whole chain up to that point of execution.
Passing Data Between Different Workflows
In StackStorm, a workflow is just an action. This means you pass data from one workflow to another in exactly the same manner was you would pass data to an action - you use action parameters.
In the example below, we have two workflows - workflow1
and workflow2
. The task named
task2
inside workflow1
calls workflow2
and passes the variable date
to it as an
action parameter. workflow2
then uses this value and prints it to standard output.
workflow1.yaml
---
chain:
-
name: "task1"
ref: "core.local"
parameters:
cmd: "date"
on-success: "task2"
-
name: "task2"
ref: "mypack.workflow2"
parameters:
date: "{{ task1.stdout }}" # Here we pass result from "task1" as a "date" action parameter to the action "workflow2"
workflow2.meta.yaml
---
name: "workflow2"
description: "..."
runner_type: "action-chain"
entry_point: "workflow2.yaml"
enabled: true
parameters:
date:
type: "string"
description: "Date which show be printed to stdout"
required: True
workflow2.yaml
---
chain:
-
name: "task1"
ref: "core.local"
parameters:
cmd: "echo {{ date }}" # Here we echo the variable "date" which was passed to the workflow as an action parameter
The example above applies to a scenario where you have two related workflows and one calls another.
If you have two independent workflows and you want to pass data between them or use data from one workflow in another, the most common approach to that is using the built-in key-value datastore.
Inside the first workflow you store data in the datastore and inside the second workflow you retrieve this data from a datastore. This approach creates tighter coupling between two workflows and makes them less re-usable and harder to run independently of each other. Where possible, we encourage you to design the workflow in such a way that you can pass data using action parameters instead.
Using action parameters means the second workflow can still be re-used and run independently of the first one - you simply need to pass the required parameters to it.
Pausing and Resuming Action Chain Execution
An execution of an ActionChain can be paused by running st2 execution pause <execution-id>
. An
execution must be in a running state in order for pause to be successful. The execution will initially
go into a pausing
state, then will go into a paused
state when no more tasks are in an active
state such as running
, pausing
, or canceling
. When an Action Chain is paused, it can be
resumed by running st2 execution resume <execution-id>
.
Published variables are saved in the execution context on pause and restored on resume.
Note
In this version, the published variables are stored unencrypted in the execution context.
The pause
and resume
operation will cascade down to subworkflows, whether it’s another StackStorm
action that is an Orquesta workflow or ActionChain. If the pause
operation is performed from a
subworkflow or subchain, then the pause
will cascade up to the parent workflow or parent chain.
However, if the resume
operation is performed from a subworkflow or subchain, the resume
will not cascade up to the parent workflow or parent chain. This allows users to resume and
troubleshoot branches individually.
Gotchas
Using YAML and Jinja implies some constraints on how to name and reference variables:
Variable names can use letters, underscores, and numbers. No dashes! This applies to all variables: global vars, input parameters, DataStore keys, and published variables.
The same naming rules apply to task names:
this-task-name-is-wrong
! Usetask_names_with_underscores
.Always quote variable references
"{{ my_variable.or.expression }}"
(remember that{ }
is a YAML dictionary). The types are respected inside the Jinja template but converted to strings outside:"{{ 1 + 2 }} + 3"
resolves to"3 + 3"
.
Error Reporting
ActionChain errors are classified as:
Errors reported by a specific task in the chain. In this case the error is reported as per behavior of the particular action in the task.
Sample output:
{ "result": { "tasks": [ { "created_at": "2015-02-27T19:29:02.057885+00:00", "execution_id": "54f0c57e0640fd177f278052", "id": "c1", "name": "c1", "result": { "failed": true, "return_code": 127, "stderr": "bash: borg: command not found\n", "stdout": "", "succeeded": false }, "state": "failed", "updated_at": "2015-02-27T19:29:03.149547+00:00", "workflow": null } ] } }
Errors experienced by the ActionChain runtime while determining the flow. In this case the error is reported as the error property of the ActionChain result.
Sample output:
{ "result": { "error": "Failed to run task \"c2\". Parameter rendering failed: 's1' is undefined", "traceback": "Traceback (most recent call last):...", "tasks": [ { "created_at": "2015-02-27T19:19:34.536558+00:00", "execution_id": "54f0c3460640fd15a843957d", "id": "c1", "name": "c1", "result": { "failed": false, "return_code": 0, "stderr": "", "stdout": "Fri Feb 27 19:19:34 UTC 2015\n", "succeeded": true }, "state": "succeeded", "updated_at": "2015-02-27T19:19:35.591297+00:00", "workflow": null } ] } }