Skip to content

Arcaflow Workflows (concept)

Tip

This document describes the concept of Arcaflow Workflows. We describe the process of writing a workflow in this section

Steps

Workflows are a way to describe a sequence or parallel execution of individual steps. The steps are provided exclusively by plugins. The simplest workflow looks like this:

stateDiagram-v2
  [*] --> Step
  Step --> [*]

However, this is only true if the step only has one output. Most steps will at least have two possible outputs, for success and error states:

stateDiagram-v2
  [*] --> Step
  Step --> [*]: yes
  Step --> [*]: no

Plugins can declare as many outputs as needed, with custom names. The workflow engine doesn’t make a distinction based on the names, all outputs are treated equal for execution.

An important rule is that one step must always end in exactly one output. No step must end without an output, and no step can end in more than one output. This provides a mechanism to direct the flow of the workflow execution.

Plugins must also explicitly declare what parameters they expect as input for the step, and the data types of these and what parameters they will produce as output.

Interconnecting steps

When two steps are connected, they will be executed after each other:

stateDiagram-v2
  Step1: Step 1
  Step2: Step 2
  [*] --> Step1
  Step1 --> Step2
  Step2 --> [*]

Similarly, when two steps are not directly connected, they may be executed in parallel:

stateDiagram-v2
  Step1: Step 1
  Step2: Step 2
  [*] --> Step1
  [*] --> Step2
  Step1 --> [*]
  Step2 --> [*]

You can use the interconnection to direct the flow of step outputs:

stateDiagram-v2
  Step1: Step 1
  Step2: Step 2
  Step3: Step 3
  [*] --> Step1
  Step1 --> Step2: success
  Step1 --> Step3: error
  Step2 --> [*]
  Step3 --> [*]

Passing data between steps

When two steps are connected, you have the ability to pass data between them. Emblematically described:

stateDiagram-v2
  Step1: Step 1
  Step2: Step 2
  [*] --> Step1
  Step1 --> Step2: input_1 = !expr $.steps.step1.outputs.success
  Step2 --> [*]

The data type of the input on Step 2 in this case must match the result of the expression. If the data type does not match, the workflow will not be executed.

Undefined inputs

Step inputs can either be required or optional. When a step input is required, it must be configured or the workflow will fail to execute. However, there are cases when the inputs cannot be determined from previous steps. In this case, the workflow start can be connected and the required inputs can be obtained from the user when running the workflow:

stateDiagram-v2
  Step1: Step 1
  Step2: Step 2
  [*] --> Step1
  [*] --> Step2: input_1 = !expr $.input.option_1
  Step1 --> Step2: input_2 = !expr $.steps.step1.outputs.success
  Step2 --> [*]

This is typically the case when credentials, such as database access, etc. are required.

Outputs

The output for each step is preserved for later inspection. However, the workflow can explicitly declare outputs. These outputs are usable in scripted environments as a direct output of the workflow:

stateDiagram-v2
  [*] --> Step
  Step --> [*]: output_1 = !expr $.steps.step1.outputs.success

Background processes

To allow background processes idiomatically, plugins can implement the cancellation signal to be stopped by the workflow.

Flow control (WIP)

The workflow contains several flow control operations. These flow control operations are not implemented by plugins, but are part of the workflow engine itself.

Foreach

The foreach flow control allows you to loop over a sub-workflow with a list of input objects.

stateDiagram-v2
  [*] --> ForEach
  state ForEach {
    [*] --> loop_list_input
    loop_list_input --> sub_workflow
    sub_workflow --> loop_list_input
    state sub_workflow {
      [*] --> Step1
      Step1 --> [*]
    }
    sub_workflow --> [*]: Sub Output
  }
  ForEach --> [*]: Output

This feature can be configured for parallel or sequential execution.

Condition

A condition is a flow control operation that controls whether or not a step can run. You can also create multiple branches with opposing logic to create a switch-case effect.

stateDiagram-v2
  classDef tag font-style:italic;
  classDef step font-weight:bold;

  input
  [*] --> Step1:::step
  input --> Step1: !expr $.input.enabled
  or_disabled:::tag: !ordisabled
  Step1 --> or_disabled: success
  Step1 --> or_disabled: Disabled
  or_disabled --> [*]
stateDiagram-v2
  classDef tag font-style:italic;
  classDef step font-weight:bold;

  state if_state <<choice>>
  Step1:::step: Step 1
  [*] --> Step1
  Step1 --> if_state
  Step2:::step: Step 2
  Step3:::step: Step 3
  if_state --> Step2: !expr $.step1.output_1 == true
  if_state --> Step3: !expr $.step1.output_1 == false
  oneof:::tag: !oneof
  Step2 --> oneof
  Step3 --> oneof
  oneof --> [*]

Abort

Warning

This feature is not yet implemented.

The abort flow control is a quick way to exit out of a workflow. This is useful when entering a terminal error state and the workflow output data would be useless anyway.

stateDiagram-v2
  [*] --> Step1
  Step1 --> Abort: Output 1
  Step1 --> Step2: Output 2
  Step2 --> [*]

However, this is only required if you want to abort the workflow immediately. If you want an error case to result in the workflow failing, but whatever steps can be finished being finished, you can leave error outputs unconnected.

Do-while

Warning

This feature is not yet implemented.

A do-while block will execute the steps in it as long as a certain condition is met. The condition is derived from the output of the step or steps executed inside the loop:

stateDiagram-v2
  [*] --> DoWhile
  state DoWhile {
    [*] --> Step1
    Step1 --> [*]: output_1_condition= !expr $.step1.output_1.finished == false
  }
  DoWhile --> [*]

If the step declares multiple outputs, multiple conditions are possible. The do-while block will also have multiple outputs:

stateDiagram-v2
  [*] --> DoWhile
  state DoWhile {
    [*] --> Step1
    Step1 --> [*]: Output 1 condition
    Step1 --> [*]: Output 2 condition
  }
  DoWhile --> [*]: Output 1
  DoWhile --> [*]: Output 2

You may decide to only allow exit from a loop if one of the two outputs is satisfied:

stateDiagram-v2
  [*] --> DoWhile
  state DoWhile {
    [*] --> Step1
    Step1 --> Step1: Output 1
    Step1 --> [*]: Output 2
  }
  DoWhile --> [*]: Output 1

Multiply

Warning

This feature is not yet implemented.

The multiply flow control operation is useful when you need to dynamically execute sub-workflows in parallel based on an input condition. You can, for example, use this to run a workflow step on multiple or all Kubernetes nodes.

stateDiagram-v2
  Lookup: Lookup Kubernetes hosts
  [*] --> Lookup
  Lookup --> Multiply
  state Multiply {
    [*] --> Stresstest
    Stresstest --> [*]
  }
  Multiply --> [*]

The output of a Multiply operation will be a map, keyed with a string that is configured from the input.

Tip

You can think of a Multiply step like a variation of a for-each loop geared towards specific parallelization cases.

Synchronize

Warning

This feature is not yet implemented.

The synchronize step attempts to synchronize the execution of subsequent steps for a specified key. The key must be a constant and cannot be obtained from an input expression.

stateDiagram-v2
  [*] --> Step1
  [*] --> Step2
  Synchronize1: Synchronize (key=a)
  Synchronize2: Synchronize (key=a)
  Step1 --> Synchronize1
  Step2 --> Synchronize2
  Synchronize1 --> Step3
  Synchronize2 --> Step4
  Step3 --> [*]
  Step4 --> [*]