Skip to content

Hooks for jobs and providers

Information

This reference documentation applies to all channel handlers and provider plugins part of OpenTestFactory. Externally provided channel handlers and provider plugins may or may not support hooks.

If, as a plugin writer you are using the opentf-toolkit package, your channel handlers and provider plugins support this configuration.

Hooks define steps that run before and/or after a specific event occurs.

Those steps are regular steps. For more information about step syntax, see “Workflow syntax for OpenTestFactory orchestrator.”

Events that trigger hooks are job setups, job teardowns, and function calls.

Events

Jobs setup events

A channel: setup event occurs before the workspace is prepared:

channel: setup

By default, each time a job initiates on an execution environment, an empty workspace is configured and reserved for the job. This workspace is then discarded when the job is completed.

The channel: setup event provides a mechanism to tailor the process of workspace preparation.

Hooks triggered by this event can force the use of a specific workspace and configure it.

Their before steps run before the workspace is prepared. Their after steps run after the workspace is prepared but before the job’s first step run.

A specific use-workspace: {path} step is allowed in the before steps. It sets the workspace path. If multiple use-workspace steps are defined, the last one is used. When the workspace path is explicitly set, no attempt is made to clean its content. It is up to the hook to ensure that the workspace is properly prepared.

Note

If the workspace path is explicitly set, its content will be preserved after the job ends.

Jobs teardown events

A channel: teardown event occurs after a job ends and before the workspace is deleted:

channel: teardown

Hooks triggered by this event can prevent the workspace deletion and can refine the cleanup operations.

Their before steps run before the workspace is cleaned up. Their after steps run after the workspace is cleaned up.

A specific keep-workspace: {bool} step is allowed in the before steps. It prevents the workspace from being deleted if the value evaluates to true. If multiple keep-workspace steps are defined, the last one is used.

Note

If the workspace path was explicitly set, its content will be preserved even if keep-workspace is not specified or set to false.

Functions events

Whenever a function prefix/name@version is called in a workflow the following event occurs:

categoryPrefix: prefix
category: name
categoryVersion: version

Hooks triggered by this event allow you to customize the function call.

Their before steps run before the function is called. Their after steps run after the function is called.

When defining the events that trigger a hook, you can omit the categoryVersion specifier. That way, all versions of the function will trigger the hook.

There must be at least one of category or categoryPrefix specified. It can be the _ placeholder (in which case all names or prefixes trigger the hook).

Scope

Hooks can be defined in a channel handler or provider plugin configuration file, or directly in a workflow.

Hooks defined in a channel handler or provider plugin configuration file or in a file referred to by the {handler}_CHANNEL_HOOKS or {provider_name}_PROVIDER_HOOKS environment variable only apply to jobs or functions handled by the plugin.

Hooks defined in a workflow only apply to jobs and functions defined or used in the workflow.

Sequence

The hooks sections define an ordered list of hooks. Whenever an event that allows for hooks occurs, relevant hooks are tested in order. All hooks that match are applied.

If two or more hooks match, the before blocks are added in order and the after blocks are added in reverse order:

before_steps from workflow-defined hooks
  before_steps from {provider or handler}-defined hook 1
    before_steps from {provider or handler}-defined hook 2
      steps from event
    after_steps from {provider or handler}-defined hook 2
  after_steps from {provider or handler}-defined hook 1
after_steps from workflow-defined hooks

Mandatory and optional sections

A hook must have at least a name and events section and at least a before or an after section (it may have both). It may have an if section. Those additional sections may be empty or omitted if not used.

hooks:
- name: mona
  events:
  - ...
  if:
  before:
  - ...
  after:
  - ...

Important

If the if conditional is omitted, if: success() is assumed. That is, the hook is applied if and only if the job is in success (that is, all previous steps were either successful or were carrying a continue-on-error: true annotation).

Examples

Job hooks

In the following examples, you will define a job hook that keeps the workspace used for a job if the job fails.

The first example defines job hooks at the channel handler level. This configuration should be done when you deploy the orchestrator. For more information about this configuration, see “Agent channel plugin.”

The second example defines the job hook in a workflow.

Defining job hooks using a definition file

Tip

If the content of the referred file changes, the hooks definitions used by the Agent channel plugin will change accordingly. You do not need to restart the orchestrator.

You can provide a separate file for the hooks definitions when you deploy the orchestrator. This file can have any name, as long as it is accessible from your plugin and the specified AGENTCHANNEL_CHANNEL_HOOKS environment variable refers to it:

AGENTCHANNEL_CHANNEL_HOOKS=/app/hooks/channel_hooks.yaml

The hooks definition file is as follows:

/app/hooks/channel_hooks.yaml
hooks:
- name: keep workspace on failure
  if: always()
  events:
  - channel: teardown
  before:
  - keep-workspace: true
    if: failure()
  - run: echo hook
    if: always()

Defining job hooks in a workflow

You can also define hooks directly in your workflow. In this case, they will apply only for the jobs defined or generated in the workflow.

metadata:
  name: job hooks demo
hooks:
- name: keep workspace on failure
  if: always()
  events:
  - channel: teardown
  before:
  - keep-workspace: true
    if: failure()
  - run: echo hook
    if: always()
jobs:
  my_job:
    runs-on: windows
    steps:
    - run: echo Hi there
    - run: ohno

Observable effects

Assuming the following workflow (or the one given in the ‘Defining job hooks in a workflow’ section if you are trying hooks defined in a workflow):

metadata:
  name: job hooks demo
jobs:
  my_job:
    runs-on: windows
    steps:
    - run: echo Hi there
    - run: ohno

If there is no ohno command on the agent, the my_job job will fail. The keep workspace on failure hook will be triggered and will prevent the workspace from being deleted. The echo hook step will be executed and its output will be displayed in the job logs:

Workflow 1c46de26-25df-4a26-86dd-5e98d02569ad is running.
Workflow hooks demo
(running in namespace 'default')
[2023-06-22T16:46:50] [job ...] Requesting execution environment providing windows in namespace 'default' for job 'my_job'
[2023-06-22T16:46:50] [job ...] Running command: echo Hi there
[2023-06-22T16:46:53] [job ...] Running command: ohno
[2023-06-22T16:46:53] [job ...] Hi there
[2023-06-22T16:46:53] [job ...] 'ohno' n'est pas reconnu en tant que commande interne
[2023-06-22T16:46:53] [job ...] ou externe, un programme exécutable ou un fichier de commandes.
[2023-06-22T16:46:53] [job ...] Status code was: 1
[2023-06-22T16:46:53] [job ...] Running command: echo hook
[2023-06-22T16:46:53] [job ...] hook
[2023-06-22T16:46:53] [job ...] ERROR: At least one step in job 6ca11248-a555-4a77-a186-d89d9958df04 returned a non-zero value.
[2023-06-22T16:46:53] [job ...] Releasing execution environment for job 'my_job'
[2023-06-22T16:46:53] [job ...] Keeping workspace 'C:\Users\me\work\6ca11248-a555-4a77-a186-d89d9958df04' on execution environment
Workflow failed.

Provider hooks

In the following examples, you will define hooks for Cucumber functions.

The first example define hooks at the provider level. This configuration should be done when you deploy the orchestrator. Please see “Common provider settings” for more information.

The second example defines hooks in a workflow.

Defining provider hooks using a definition file

Note

This is the recommended way to define provider hooks.

Tip

If the content of the referred file changes, the hooks definitions used by the Cucumber provider plugin will change accordingly. You do not need to restart the provider plugin.

You can provide a separate file for the hooks definitions when you deploy the orchestrator. This file can have any name, as long as it is accessible from your plugin and the specified CUCUMBER_PROVIDER_HOOKS environment variable refers to it:

CUCUMBER_PROVIDER_HOOKS=/app/hooks/cucumber_hooks.yaml

The hooks definition is as follows:

/app/hooks/cucumber_hooks.yaml
hooks:
  - name: my hook
    events:
    - categoryPrefix: cucumber
      category: cucumber
    if: (contains(inputs.reporters, 'junit')) && (runner.os == 'windows')
    before:
    - run: echo hello windows
    - run: del foobar.html
    after:
    - run: echo ::attach::foobar.html
    - run: cleanup
      if: always()
  - name: my other hook
    events:
    - category: _
    after:
    - uses: actions/delete-file
      with:
        path: foo/bar

Defining functions hooks in a workflow

Finally, you can define hooks directly in your workflow:

metadata:
  name: hooks demo
hooks:
  - name: my hook
    events:
    - categoryPrefix: cucumber
      category: cucumber
    if: (contains(inputs.reporters, 'junit')) && (runner.os == 'windows')
    before:
    - run: echo hello windows
    - run: del foobar.html
    after:
    - run: echo ::attach::foobar.html
    - run: cleanup
      if: always()
  - name: my other hook
    events:
    - category: cucumber  # if we were using the '_' placeholder, the hook
                          # would be triggered by all functions called in the
                          # workflow
    after:
    - uses: actions/delete-file
      with:
        path: foo/bar
jobs:
  my_job:
    runs-on: windows
    steps:
    - run: echo Hi there
    - uses: cucumber/cucumber@v1
      with:
        reporters: [foo, junit]
    - run: echo Bye
  my_second_job:
    runs-on: windows
    steps:
    - uses: cucumber/params@v1

Observable effects

Assuming the following workflow (if the hooks are defined at the provider level):

Note

Use the workflow example given in the ‘Defining functions hooks in a workflow’ above if you want to define the hooks in the workflow itself.

metadata:
  name: hooks demo
jobs:
  my_job:
    runs-on: windows
    steps:
    - run: echo Hi there
    - uses: cucumber/cucumber@v1
      with:
        reporters: [foo, junit]
    - run: echo Bye
  my_second_job:
    runs-on: windows
    steps:
    - uses: cucumber/params@v1

The my_job job will execute the following steps, in order:

- run: echo Hi there
- run: hello windows                    # added by 'my hook'
- run: del foobar.html                  # added by 'my hook'
- run: <what cucumber/cucumber does>
- run: <what actions/delete-file does>  # added by 'my other hook'
- run: echo ::attach::foobar.html       # added by 'my hook'
- run: cleanup                          # added by 'my hook'

If Cucumber returns a non-zero status code, the actions/delete-file and echo ::attach:: steps will be skipped, but the cleanup one will run nonetheless, as it includes an if: always() clause.

The my_second_job job will execute the following steps, in order:

- run: <what cucumber/params does>
- run: <what actions/delete-file does>  # added by 'my other hook'