Skip to content

User-facing Endpoints

This section documents the user-facing endpoints.

Those user-facing endpoints are used by tools and clients that interact with your orchestrator, such as opentf-ctl.

It can be used as a reference if you want to configure a reverse proxy in front of your orchestrator.

Info

Most services also expose an inbox endpoint, so that they can receive the publications they have subscribed to, but those inboxes are not user-facing endpoints and are hence not listed here (you can find them in each service configuration documentation). You do not have to include those in a reverse proxy configuration.

Services and plugins exposing user-facing endpoints

Note

All xxx_id below are UUIDs. They have the following form:

123e4567-e89b-12d3-a456-426655440000

Agent Channel plugin

This plugin exposes the following endpoints:

Event Bus service

This service exposes three endpoints:

  • /publications (POST)
  • /subscriptions (GET, POST)
  • /subscriptions/{subscription_id} (DELETE)

Insight Collector plugin

This plugin exposes one endpoint:

  • /workflows/{workflow_id}/insights (POST)

Killswitch service

This service exposes one endpoint:

  • /workflows/{workflow_id} (DELETE)

Localstore service

This service exposes one endpoint:

  • /workflows/{workflow_id}/files/{attachment_id} (GET, HEAD)

Observer service

This service exposes the following endpoints:

  • /channelhandlers (GET)
  • /channels (GET)
  • /namespaces (GET)
  • /version (GET)
  • /workflows (GET)
  • /workflows/status (GET)
  • /workflows/{workflow_id}/datasources/{datasource_kind} (GET)
  • /workflows/{workflow_id}/logs (GET)
  • /workflows/{workflow_id}/status (GET)
  • /workflows/{workflow_id}/workers (GET)

Quality Gate service

This plugin exposes two endpoints:

  • /workflows/{workflow_id}/qualitygate (GET, POST)

Receptionist service

This service exposes one endpoint:

Authentication

Whenever calling an endpoint, a signed token must be specified via the Authorization header.

This header will be of the form:

Authorization: Bearer xxxxxxxx

It must be signed by one of the trusted authorities known to the orchestrator.

If the token is invalid or missing, the endpoint will respond with an Unauthorized (401) error code.

If the token does not grant access to the required resource, the endpoint will respond with a Forbidden (403) error code.

Responses

Except when noted otherwise, the user-facing endpoints return status manifests.

Status manifests are JSON documents with the following format:

{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "...",
  "status": "Success", // if code is 2xx, else "Failure"
  "reason": "...",
  "code": 200
  // ...
}

Where code is a standard HTTP return code, reason an element of the following table which results from the return code, message a descriptive text, and details an optional part whose nature depends on the exchange.

Reason Code Description
OK 200 The request was successfully handled.
Created 201 The request was successfully handled, the corresponding resource was created.
NoContent 204 The request was successfully handled, no response was required.
BadRequest 400 The request was malformed.
Unauthorized 401 The token is missing, invalid, or not recognized.
Forbidden 403 The token is not allowed to perform the required action.
NotFound 404 The requested object does not exist.
AlreadyExists 409
Conflict 409
Invalid 422 The request was invalid.
InternalError 500 An internal error occurred.

If an error is returned (a code starting with 4 or 5), message describes the problem and there may be an error entry in the details object, providing further explanations.

Selectors

Several endpoints allow for optional field and label selectors in request:

Those selectors may be used to restrict the endpoint response to the items of interest.

To filter the response on fields, use the fieldSelector parameter. To filter the response on labels, use the labelSelector parameter. For example, this request will return only idle channels:

GET /channels?fieldSelector=status.phase=IDLE

Currently, label selectors may be used to filter /workflows/{workflow_id}/status endpoint request results. The following request will return only checkout category events of a workflow:

GET /workflows/3e1b7929-8a44-4196-a9b4-6e0b65d35a68/status?labelSelector=opentestfactory.org/category=checkout

Logical operators supported in selector conditions are =, !=, in and notin. Multiple conditions may be provided, separated by commas (,). The conditions are linked by a logical AND operator. For instance, this request will return only the channels that are not idle in the ns1 or ns2 namespace:

GET /channels?fieldSelector=status.phase!=IDLE,metadata.namespace in (ns1, ns2)

You can use fieldSelector and labelSelector together. The conditions will be linked by a logical AND operator.

Here are the possible selector formats:

key                                # the key or label exists
!key                               # the key or label does not exist
key==value                         # the key exists and has the specified value
key!=value                         # the key exists but has a different value
                                   # or the key does not exist
key in (value1, value2, ...)       # the key exists and its value is in the list
key notin (value1, value2, ...)    # the key exists and has a value not in the
                                   # list or the key does not exist
(value1, value2) in key            # the key contains all values (and possibly
                                   # others)
(value1, value2) notin key         # the key does not contains all the values
                                   # (but it may contain some) or the key does
                                   # not exist

For label selectors, the key is the label’s name. It may contain dots. For field selectors, the key is a series of field names separated by dots. The last field name may be surrounded by [ and ] to allow for dots in the field name.

Here are examples of fieldSelector keys:

apiVersion
metadata.name
spec.selector.matchLabels[example.org/label]

[apiVersion]                                    # Invalid
spec[selector][matchLabels][example.org/label]  # Invalid

Warning

You cannot use the [] notation for labelSelector.

Endpoints

Important

What is accessible and returned can be constrained by what the specified token is allowed to access. For example, if the specified token only has access to a specific namespace, resources not accessible from this namespace will not be shown.

/agents (GET)

This endpoint is exposed by the Agent Channel plugin. It lists the currently registered agents.

It accepts the following optional query parameters:

Query parameter Description
fieldSelector A field selector to filter the returned agent manifests.
labelSelector A label selector to filter the returned agent manifests.

If successful, returns an AgentRegistrationList manifest (a JSON document), not a status manifest.

This document has an apiVersion entry (v1), a kind entry (AgentRegistrationList), and an items entry which is a list of AgentRegistration objects.

This endpoint allows for field selectors to filter returned agent manifests.

Possible status responses

Please note that, as stated above, this endpoint does not return a status manifest in case of success.

Status Reason Code Description
Failure Invalid 422 The specified selectors are invalid.
Failure Forbidden 403 The token does not allow to list agents.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying the currently registered agents
GET /agents
{
  "apiVersion": "v1",
  "kind": "AgentRegistrationList",
  "items": [
    {
      "apiVersion": "opentestfactory.org/v1alpha1",
      "kind": "AgentRegistration",
      "metadata": {
        "agent_id": "7311820e-4497-4668-a007-e834dd5ee3b8",
        "creationTimestamp": "2023-03-10T07:58:25.860576",
        "name": "windows test agent",
        "namespaces": "default"
      },
      "spec": {
        "encoding": "utf-8",
        "script_path": "C:\\Users\\nobody\\work",
        "tags": ["windows", "robotframework"]
      },
      "status": {
        "communicationCount": 0,
        "communicationStatusSummary": {},
        "lastCommunicationTimestamp": "2023-03-10T08:55:09.547106"
      }
    }
  ]
}

/agents (POST)

This endpoint is exposed by the Agent Channel plugin. It allows agents to register to an agent channel plugin.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: Created
  • message: a summary of the agent registration including the agent’s name, ID, and tags (a string)
  • details: an object with two elements: uuid, the agent ID (a string), and version, the orchestrator version (also a string)

The agent ID can be used to de-register the agent.

Possible status responses
Status Reason Code Description
Success Created 201 The agent was successfully registered.
Failure BadRequest 400 The request was malformed.
Failure Invalid 422 The registration manifest is invalid.
Failure Forbidden 403 The token does not allow to register agents.
Failure Unauthorized 401 The token is invalid or missing.
Example: registering an agent
POST /agents
{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "AgentRegistration",
  "metadata": {
    "name": "test agent",
    "namespaces": "default",
    "version": "1.8.0"
  },
  "spec": {
    "tags": ["windows", "robotframework"],
    "encoding": "utf-8",
    "script_path": "C:\\Users\\nobody\\work"
  }
}
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Agent 'test agent' successfully registered (id=a9504b60-eb72-4f95-87b5-d146feeeb784, tags=windows).",
  "details": {
    "uuid": "a9504b60-eb72-4f95-87b5-d146feeeb784",
    "version": "0.48.0"
  },
  "status": "Success",
  "reason": "Created",
  "code": 201
}
Example: registering an agent without providing tags
POST /agents
{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "AgentRegistration",
  "metadata": {
    "name": "test agent",
    "namespaces": "default",
    "version": "1.8.0"
  },
  "spec": {
    "tags": [],
    "encoding": "utf-8",
    "script_path": "C:\\Users\\nobody\\work"
  }
}
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Not a valid AgentRegistration manifest.",
  "details": {
    "error": "[] is too short\n\nFailed validating 'minItems' in schema['properties']['spec']['properties']['tags']:\n    {'items': {'pattern': '^[a-zA-Z][a-zA-Z0-9-]*$', 'type': 'string'},\n     'minItems': 1,\n     'type': 'array'}\n\nOn instance['spec']['tags']:\n    []"
  },
  "status": "Failure",
  "reason": "Invalid",
  "code": 422
}

/agents (PATCH)

This endpoint is exposed by the Agent Channel plugin. It allows for refreshing specified agents status to prevent them from becoming unreachable.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Agents status refreshed. string

The payload is a JSON object, with keys being agent UUIDs. If the payload is invalid, a Failure status is returned instead with BadRequest reason and a 400 code.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure BadRequest 400 The request was malformed.

Examples

Example: refreshing agents status
PATCH /agents -H "Content-Type: application/json" \
-d '{"29b2838f-5a63-4112-9a1a-64f2c8aef8b4": {}, "3ad00acd-2441-4e7a-a356-12bc30f727b2": {}}'
{
  "apiVersion": "v1",
  "code": 200,
  "details": null,
  "kind": "Status",
  "message": "Agents status refreshed.",
  "metadata": {},
  "reason": "OK",
  "status": "Success",
}
Example: sending a malformed request
PATCH /agents -H "Content-Type: application/json" \
-d '{"29b2838f-5a63-4112-9a1a-64f2c8aef8b4": }'
{
  "apiVersion": "v1",
  "code": 400,
  "details": null,
  "kind": "Status",
  "message": "Not a valid JSON document: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.",
  "metadata": {},
  "reason": "BadRequest",
  "status": "Failure",
}

/agents/{agent_id} (DELETE)

This endpoint is exposed by the Agent Channel plugin. It de-registers the specified agent.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Agent {agent_id} de-registered. string

If the agent is not known, a Failure status is returned instead with NotFound reason and a 404 code.

Possible status responses

Please note that, as stated above, this endpoint does not return a Status manifest in case of success.

Status Reason Code Description
Success OK 200 The agent was successfully de-registered.
Failure NotFound 404 The agent is not known.
Failure Forbidden 403 The token does not allow to de-register the agent.
Failure Unauthorized 401 The token is invalid or missing.
Example: de-registering an agent
DELETE /agents/a9504b60-eb72-4f95-87b5-d146feeeb784
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Agent a9504b60-eb72-4f95-87b5-d146feeeb784 de-registered.",
  "details": null,
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: attempting to de-register an unknown agent
DELETE /agents/a9504b60-eb72-4f95-87b5-d146feeeb784
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Agent a9504b60-eb72-4f95-87b5-d146feeeb784 not known.",
  "details": null,
  "status": "Failure",
  "reason": "NotFound",
  "code": 404
}

/agents/{agent_id} (GET, POST)

This endpoint is exposed by the Agent Channel plugin. It allows agents to interact with the agent channel plugin.

The exchange format depends on agent/agent channel plugin versions and is out of this document’s scope.

/agents/{agent_id}/files/{file_id} (GET, POST, PUT)

This endpoint is exposed by the Agent Channel plugin. It allows agents to interact with the agent channel plugin.

The exchange format depends on agent/agent channel plugin versions and is out of this document’s scope.

/channelhandlers (GET)

This endpoint is exposed by the Observer service. It lists the known channel handlers.

It returns a status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Known channel handlers string
  • details: an object with an items element which is a possibly empty list of strings, the known channel handlers IDs.
Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure Forbidden 403 The token does not allow to list channel handlers.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying the currently known channel handlers
GET /channelhandlers
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Known channel handlers",
  "details": {
    "items": [
      "50c7e5d1-7bdc-46f0-9422-3cc6660d00c0",
      "dd43b0bb-b359-4e9d-9459-c42ee7dc16d9"
    ],
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}

/channels (GET)

This endpoint is exposed by the Observer service. It lists the known channels.

It accepts the following optional query parameters:

Query parameter Description
fieldSelector A field selector to filter the returned channel manifests.
labelSelector A label selector to filter the returned channel manifests.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Known channels string
  • details: an object with an items element which is a possibly empty list of objects, the known channel manifests.

Each channel manifest has the following structure:

{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "Channel",
  "metadata": {
    "name": string,
    "namespaces": string,
    "channelhandler_id": uuid
  },
  "spec": {
    "tags": [string, ...]
  },
  "status": {
    "lastCommunicationTimestamp": timestamp,
    "phase": string,
    "currentJobID": uuid or null
  }
}

namespaces is either *, a name (foo), or a comma-separated list of names (foo,bar,baz).

If namespaces is * the channel is accessible from all namespaces. Otherwise, it is accessible from the listed namespace(s).

phase is one of the following strings: IDLE, PENDING, BUSY, or UNREACHABLE.

This endpoint allows for field selectors to filter returned channel manifests.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure Invalid 422 The specified selectors are invalid.
Failure Forbidden 403 The token does not allow to list channels.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying the currently known channels
GET /channels
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Known channels",
  "details": {
    "items": [
      {
        "apiVersion": "opentestfactory.org/v1alpha1",
        "kind": "Channel",
        "metadata": {
          "name": "robotframework.agents",
          "namespaces": "default",
          "channelhandler_id": "2af7585e-73dc-4f63-855e-4072fc9aa635"
        },
        "spec": {
          "tags": ["ssh", "linux", "robotframework"]
        },
        "status": {
          "currentJobID": null,
          "lastCommunicationTimestamp": "2022-05-02T10:00:49.933028",
          "phase": "IDLE"
        }
      },
      {
        "apiVersion": "opentestfactory.org/v1alpha1",
        "kind": "Channel",
        "metadata": {
          "name": "cypress-agent.agents",
          "namespaces": "*",
          "channelhandler_id": "2af7585e-73dc-4f63-855e-4072fc9aa635"
        },
        "spec": {
          "tags": ["ssh", "linux", "cypress"]
        },
        "status": {
          "currentJobID": "2af7585e-73dc-4f63-855e-4072fc9aa635",
          "lastCommunicationTimestamp": "2022-05-02T10:00:49.933028",
          "phase": "PENDING"
        }
      }
    ]
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: querying active channels in namespace default
GET /channels?fieldSelector=metadata.namespaces=default,status.phase=BUSY
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Known channels",
  "details": {
    "items": [
      {
        "apiVersion": "opentestfactory.org/v1alpha1",
        "kind": "Channel",
        "metadata": {
          "name": "robotframework.agents",
          "namespaces": "default",
          "channelhandler_id": "2af7585e-73dc-4f63-855e-4072fc9aa635"
        },
        "spec": {
          "tags": ["ssh", "linux", "robotframework"]
        },
        "status": {
          "currentJobID": "2cf7575e-73dc-4f63-855e-4072fc9aa635",
          "lastCommunicationTimestamp": "2022-05-02T10:00:49.933028",
          "phase": "BUSY"
        }
      },
    ]
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}

/namespaces (GET)

This endpoint is exposed by the Observer service. It lists namespaces in which operations are allowed.

It accepts two optional query parameters:

Query parameter Description
resource The resource for which to list namespaces. The possible values are agents, channelhandlers, channels, qualitygates, status, subscriptions, and workflows.
verb The verb for which to list namespaces. The possible values are list, get, create, and delete.

They must be either both defined or undefined. If they are defined, the returned namespaces are those in which the requested operation is allowed. If they are not defined, the returned namespaces are those in which at least some operation is allowed.

The endpoint returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Accessible namespaces string
  • details: an object with an items element which is a possibly empty list of strings.

The items element may be ["*"], in which case all namespaces allow for the requested operation (or for some operations if no resource/verb query parameters were specified).

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure Invalid 422 The specified query parameters are invalid.
Failure Forbidden 403 The token does not allow to list namespaces.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying available namespaces on an non-RBAC-enabled orchestrator
GET /namespaces
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Accessible namespaces",
  "details": {
    "items": [
      "*"
    ]
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: filtering with a ‘verb’ but with no ‘resource’
GET /namespaces?verb=create
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "details": null,
  "message": "resource and verb must be both provided or not provided at all.",
  "status": "Failure",
  "reason": "Invalid",
  "code": 422
}

/publications (POST)

This endpoint is exposed by the Event Bus service. It allows services and plugins to publish messages.

The published content, the body of the request, must be a JSON object. It does not have to have any specific entries. It can be empty.

The endpoint returns a status manifest (a JSON document).

On successful publication, the content is dispatched to all matching subscriptions, asynchronously. If there is no matching subscription, the response code will be 200, but its message part will be Publication received, but no matching subscription.

If there are matching subscriptions, the publication will be posted to their inbox endpoints. There will be a X-Subscription-ID header containing the subscription ID and a X-Publication-ID header containing the publication ID, to help disambiguate duplicates:

X-Subscription-ID: uuid
X-Publication-ID: uuid
Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure BadRequest 400 The request was malformed (the body was not a valid JSON object).
Failure Unauthorized 401 The token is invalid or missing.
Example: publishing a plain JSON document
POST /publications
{
}
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Publication received, but no matching subscription.",
  "details": null,
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: publishing a consumed event
POST /publications
{
  "apiVersion": "opentestfactory.org/v1beta1",
  "kind": "Workflow"
}
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Publication received.",
  "details": null,
  "status": "Success",
  "reason": "OK",
  "code": 200
}

Note

Please note that this published event is not a valid workflow, and hence will be rejected by its consumers.

/subscriptions (GET)

This endpoint is exposed by the Event Bus service. It lists the current subscriptions.

It accepts two optional query parameters:

Query parameter Description
fieldSelector A field selector to filter the returned subscription manifests.
labelSelector A label selector to filter the returned subscription manifests.

It returns a SubscriptionList manifest (a JSON document) if successful, not a status manifest.

This document has an apiVersion entry (v1), a kind entry (SubscriptionList), and an items entry which is an object with one entry per subscription. The entry key is the subscription ID, and the entry value is a Subscription manifest.

Each subscription manifest has the following structure:

{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "Subscription",
  "metadata": {
    "name": string,
    "annotations": {
      "opentestfactory.org/fieldselector": string,
      "opentestfactory.org/labelselector": string
    },
    "creationTimestamp": timestamp,
    "subscription_id": uuid
  },
  "spec": {
    "selector": object,
    "subscriber": {
      "endpoint": string
    }
  },
  "status": {
    "lastPublicationTimestamp": timestamp or null,
    "publicationCount": integer,
    "publicationStatusSummary": object
  }
}

This endpoint allows for field selectors to filter returned subscription manifests.

Possible status responses

Please note that, as stated above, this endpoint does not return a status manifest in case of success.

Status Reason Code Description
Failure Invalid 422 The specified selectors are invalid.
Failure Forbidden 403 The token does not allow to list subscriptions.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying the currently active subscriptions
GET /subscriptions
{
  "apiVersion": "v1",
  "kind": "SubscriptionList",
  "items": {
    "007677e7-0705-4dbb-809b-8260fc02bad1": {
      "apiVersion": "opentestfactory.org/v1alpha1",
      "kind": "Subscription",
      "metadata": {
        "annotations": {
          "opentestfactory.org/fieldselector": "kind==ExecutionCommand,apiVersion==opentestfactory.org/v1beta1",
          "opentestfactory.org/labelselector": ""
        },
        "creationTimestamp": "2023-03-14T15:05:04.490989",
        "name": "agentchannel",
        "subscription_id": "007677e7-0705-4dbb-809b-8260fc02bad1"
      },
      "spec": {
        "selector": {
          "matchFields": {
            "apiVersion": "opentestfactory.org/v1beta1"
          },
          "matchKind": "ExecutionCommand"
        },
        "subscriber": {
          "endpoint": "http://127.0.0.1:24368/inbox"
        }
      },
      "status": {
        "lastPublicationTimestamp": null,
        "publicationCount": 0,
        "publicationStatusSummary": {}
      }
    },
    "01c32d94-6b6a-435f-bd7e-89d3aa6a38a3": {
      "apiVersion": "opentestfactory.org/v1alpha1",
      "kind": "Subscription",
      "metadata": {
        "annotations": {
          "opentestfactory.org/fieldselector": "kind==ProviderCommand,apiVersion==opentestfactory.org/v1beta1",
          "opentestfactory.org/labelselector": "opentestfactory.org/category==get-file,opentestfactory.org/categoryPrefix==actions"
        },
        "creationTimestamp": "2023-03-14T15:05:05.359665",
        "name": "actionprovider",
        "subscription_id": "01c32d94-6b6a-435f-bd7e-89d3aa6a38a3"
      },
      "spec": {
        "selector": {
          "matchFields": {
            "apiVersion": "opentestfactory.org/v1beta1"
          },
          "matchKind": "ProviderCommand",
          "matchLabels": {
            "opentestfactory.org/category": "get-file",
            "opentestfactory.org/categoryPrefix": "actions"
          }
        },
        "subscriber": {
          "endpoint": "http://127.0.0.1:7780/inbox"
        }
      },
      "status": {
        "lastPublicationTimestamp": null,
        "publicationCount": 0,
        "publicationStatusSummary": {}
      }
    }
  }
}
Example: querying the currently active subscriptions for WorkflowResult events
GET /subscriptions?fieldSelector=spec.selector.matchKind=WorkflowResult
{
  "apiVersion": "v1",
  "kind": "SubscriptionsList",
  "items": {
    "4bd41d4f-94dc-4499-ae98-83088bc0a8d3": {
      "apiVersion": "opentestfactory.org/v1alpha1",
      "kind": "Subscription",
      "metadata": {
        "annotations": {
          "opentestfactory.org/fieldselector": "kind==WorkflowResult,apiVersion==opentestfactory.org/v1alpha1",
          "opentestfactory.org/labelselector": ""
        },
        "creationTimestamp": "2023-08-11T16:08:13.876609",
        "name": "localcleaner",
        "subscription_id": "4bd41d4f-94dc-4499-ae98-83088bc0a8d3"
      },
      "spec": {
        "selector": {
          "matchFields": {
            "apiVersion": "opentestfactory.org/v1alpha1"
          },
          "matchKind": "WorkflowResult"
        },
        "subscriber": {
          "endpoint": "http://127.0.0.1:7777/inbox"
        }
      },
      "status": {
        "lastPublicationTimestamp": null,
        "publicationCount": 0,
        "publicationStatusSummary": {},
        "quarantine":0
      }
    },
    "b1364c01-320d-48aa-8d39-1a38b25f14f4": {
      "apiVersion": "opentestfactory.org/v1alpha1",
      "kind": "Subscription",
      "metadata": {
        "annotations": {
          "opentestfactory.org/fieldselector": "kind==WorkflowResult,apiVersion==opentestfactory.org/v1alpha1",
          "opentestfactory.org/labelselector": ""
        },
        "creationTimestamp": "2023-08-11T16:08:17.941666",
        "name": "s3publisher",
        "subscription_id": "b1364c01-320d-48aa-8d39-1a38b25f14f4"
      },
      "spec": {
        "selector": {
          "matchFields": {
            "apiVersion": "opentestfactory.org/v1alpha1"
          },
          "matchKind": "WorkflowResult"
        },
        "subscriber": {
          "endpoint":"http://127.0.0.1:7787//inbox"
        }
      },
      "status": {
        "lastPublicationTimestamp": null,
        "publicationCount": 0,
        "publicationStatusSummary": {},
        "quarantine":0
      }
    },
    "ee3d8943-9876-4a22-aa27-da3549cbb5fb": {
      "apiVersion": "opentestfactory.org/v1alpha1",
      "kind": "Subscription",
      "metadata": {
        "annotations": {
          "opentestfactory.org/fieldselector": "kind==WorkflowResult,apiVersion==opentestfactory.org/v1alpha1",
          "opentestfactory.org/labelselector": ""
        },
        "creationTimestamp": "2023-08-11T16:08:13.901678",
        "name": "observer",
        "subscription_id": "ee3d8943-9876-4a22-aa27-da3549cbb5fb"
      },
      "spec": {
        "selector": {
          "matchFields": {
            "apiVersion": "opentestfactory.org/v1alpha1"
          },
          "matchKind": "WorkflowResult"
        },
        "subscriber": {
          "endpoint":"http://127.0.0.1:7775/inbox"
        }
      },
      "status": {
        "lastPublicationTimestamp": null,
        "publicationCount": 0,
        "publicationStatusSummary": {},
        "quarantine": 0
      }
    }
  }
}

/subscriptions (POST)

This endpoint is exposed by the Event Bus service. It allows services and plugins to subscribe to publications.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: Created
  • message: the Subscription 'name' successfully registred (id=subscription_id).
  • details: an object with an uuid entry, the subscription ID

The subscription ID can be used to cancel the subscription.

If the subscription request is invalid, message will summarize the reason and there will be an error entry (a string) in the details object.

Possible status responses
Status Reason Code Description
Success Created 201 The subscription was successfully registered.
Failure BadRequest 400 The request was malformed (not a valid JSON document).
Failure Invalid 422 The request is not a valid Subscription manifest.
Failure Forbidden 403 The token does not allow to create subscriptions.
Failure Unauthorized 401 The token is invalid or missing.
Example: subscribing to opentestfactory.org/v1beta1/ExecutionCommand events
POST /subscriptions
{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "Subscription",
  "metadata": {
    "name": "example",
  },
  "spec": {
    "selector": {
      "matchFields": {
        "apiVersion": "opentestfactory.org/v1beta1"
      },
      "matchKind": "ExecutionCommand"
    },
    "subscriber": {
      "endpoint": "http://127.0.0.1:12345/inbox"
    }
  }
}
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Subscription 'example' successfully registered (id=683f6807-c41a-4eda-966e-7b664b65c56a).",
  "details": {
    "uuid": "683f6807-c41a-4eda-966e-7b664b65c56a"
  },
  "status": "Success",
  "reason": "Created",
  "code": 201
}
Example: attempting to subscribe, failing to provide a subscriber endpoint
POST /subscriptions
{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "Subscription",
  "metadata": {
    "name": "example",
  },
  "spec": {
    "selector": {
      "matchFields": {
        "apiVersion": "opentestfactory.org/v1beta1"
      },
      "matchKind": "ExecutionCommand"
    },
    "sub scribe r": {
      "endpoint": "http://127.0.0.1:12345/inbox"
    }
  }
}
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Not a valid Subscription manifest.",
  "details": {
    "error": "'subscriber' is a required property\n\nFailed validating 'required' in schema['properties']['spec']:\n    {'additionalProperties': False,\n     'properties': {'selector': {'additionalProperties': False,\n                                 'minProperties': 1,\n                                 'properties': {'matchFields': {'additionalProperties': False,\n                                                                'minProperties': 1,\n
                                                 'patternProperties': {'^[a-z]([a-zA-Z0-9.-]*[a-zA-Z0-9])?$': {'type': 'string'}},\n
                                        'type': 'object'},\n                                                'matchKind': {'type': 'string'},\n
                                  'matchLabels': {'additionalProperties': False,\n                                                                'minProperties': 1,\n                                                                'patternProperties': {'^([a-zA-Z0-9-.]+/)?[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$': {'type': 'string'}},\n                                                                'type': 'object'}},\n                                 'type': 'object'},\n                    'subscriber': {'additionalProperties': False,\n                                   'properties': {'endpoint': {'type': 'string'},\n                                                  'insecure-skip-tls-verify': {'type': 'boolean'}},\n                                   'required': ['endpoint'],\n                                   'type': 'object'}},\n     'required': ['subscriber'],\n     'type': 'object'}\n\nOn instance['spec']:\n    {'selector': {'matchFields': {'apiVersion': 'opentestfactory.org/v1beta1'},\n                  'matchKind': 'ExecutionCommand'},\n     'sub scribe r': {'endpoint': 'http://127.0.0.1:12345/inbox'}}"
  },
  "status": "Failure",
  "reason": "Invalid",
  "code": 422
}

/subscriptions/{subscription_id} (DELETE)

This endpoint is exposed by the Event Bus service. It cancels the specified subscription.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Subscription subscription_id canceled. string

If the subscription is not known, a Failure status is returned instead with a NotFound reason and a 404 code.

Possible status responses
Status Reason Code Description
Success OK 200 The subscription was successfully canceled.
Failure NotFound 404 The specified subscription is not known.
Failure Forbidden 403 The token does not allow to cancel subscriptions.
Failure Unauthorized 401 The token is invalid or missing.
Example: de-registering an existing subscription
DELETE /subscriptions/683f6807-c41a-4eda-966e-7b664b65c56a
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Subscription 683f6807-c41a-4eda-966e-7b664b65c56a canceled.",
  "details": null,
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: attempting to de-register an unknown subscription
DELETE /subscriptions/683f6807-c41a-4eda-966e-7b664b65c56a
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Subscription 683f6807-c41a-4eda-966e-7b664b65c56a not known.",
  "details": null,
  "status": "Failure",
  "reason": "NotFound",
  "code": 404
}

/version (GET)

This endpoint is exposed by the Observer service. It returns the build version information.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the BOM string
  • details: an object with an items element which is a possibly empty BOM, i.e. a JSON file that is used by OpenTestFactory’s build infrastructure to generate Docker images.
Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying build information on a nightly release
GET /version
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "BOM",
  "details": {
    "items": {
      "name": "otf-images",
      "internal-components": [
        {
          "env_version_key": "ORCHESTRATOR_VERSION",
          "name": "opentf-orchestrator",
          "type": "python",
          "version": "0.53.0.dev1533+main.1708123a"
        },
        {
          "destination": "",
          "env_version_key": "JAVA_PLUGINS_VERSION",
          "name": "org.opentestfactory:java-plugins",
          "type": "java",
          "version": "1.6.0-SNAPSHOT"
        }
      ]
    }
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: querying build information on a third party nightly release
GET /version
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "BOM",
  "details": {
    "items": {
      "name": "squash-orchestrator",
      "base-images": [
        {
          "env_version_key": "OTF_ALLINONE_VERSION",
          "name": "allinone",
          "type": "docker",
          "version": "nightly"
        }
      ],
      "internal-components": [
        {
          "env_version_key": "TM_SQUASHFREEMIUM_VERSION",
          "name": "squash-freemium",
          "type": "python",
          "version": "0.7.0.dev76+main.6a970632"
        },
        {
          "env_version_key": "TM_CONNECTOR_VERSION",
          "name": "org.squashtest.tf2:squash-tf-2-squash-tm-plugins",
          "type": "java",
          "version": "3.11.0-SNAPSHOT"
        }
      ]
    }
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}

/workflows (GET)

This endpoint is exposed by the Observer service. It lists the currently running and recently completed workflows.

It accepts the following optional query parameters:

Query parameter Description
expand If specified as manifest, the response will include the full workflow manifests, not just their IDs.
fieldSelector A field selector to filter the returned workflow.
labelSelector A label selector to filter the returned workflow.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Running and recent workflows string
  • details: an object with an items element which is a possibly empty list of strings, the running and recent workflows IDs

The workflow IDs can be used to get detailed information on the corresponding workflow.

If the expand=manifest query parameter is specified, the details.items element will be a dictionary, the keys being workflow IDs and the values being the corresponding workflow manifests.

This endpoint allows for field selectors to filter returned workflow manifests.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure Invalid 422 The specified selectors are invalid.
Failure Forbidden 403 The token does not allow to list workflows.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying the currently known workflows
GET /workflows
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Running and recent workflows",
  "details": {
    "items": [
      "50c7e5d1-7bdc-46f0-9422-3cc6660d00c0",
      "dd43b0bb-b359-4e9d-9459-c42ee7dc16d9"
    ],
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}

/workflows (POST)

This endpoint is exposed by the Receptionist service. It starts a workflow execution.

It accepts three optional query parameters:

Query parameter Description
dryRun The workflow is not started, but the request is validated. The response will be a Success status with a 201 code. If the workflow is invalid, the response will be a Failure status with an Invalid reason and a 422 code.
namespace The namespace the workflow will run on will be the one specified by the query parameter. If it is not defined, the namespace the workflow will run on will be the one specified in its metadata.namespace section, if any, or default.
ping If specified, no validation or action is performed. The response will be a Success status with a Pong! message and a 200 code.

Tip

If ping is specified, the other query parameters are ignored. A token is still required.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: Created
  • message: the Workflow name accepted (workflow_id=uuid). string
  • details: an object with a workflow_id entry, a string, the started workflow ID

If the provided workflow manifest is incorrect, a Failure status is returned with an Invalid reason and a 422 code, and the message entry describes the cause of the rejection.

Possible status responses
Status Reason Code Description
Success OK 200 The service is operational (only if ping is specified).
Success Created 201 The workflow was successfully created.
Failure Invalid 422 The workflow is invalid.
Failure Conflict 409 The workflow could not be published.
Failure Forbidden 403 The token does not allow to create the workflow.
Failure Unauthorized 401 The token is invalid or missing.

Formatting the request

There are two ways a workflow can be provided.

The first way is by using a JSON or YAML request body containing the workflow definition.

The second way is by using a multipart/form-data request body. In this case, you must provide a workflow field or file. You may also provide a variables field or file as well as resource files.

Tip

The examples below use the curl command. If you intend to start a lot of workflows from the command line, it is recommended to use a tool such as opentf-ctl. Please refer to “Tools - Run workflows” for more information.

Variables

variables, if specified, are merged in the workflow variables. Variables already defined in the workflow are overwritten by the ones provided by a field or file.

You must use a multipart/form-data request if you want to add environment- specific variables to your workflow.

If provided by a field, variables must be separated by line feed. If provided by a file, there must be one variable per line.

Warning

If you are on Windows and cannot use PowerShell, use a file, as described below. It is not practical to pass line feeds using CMD.

By field:

curl ... -F variables=$'FOO=abc def\nBAR=123' ...
curl.exe ... -F variables="FOO=abc def`nBAR=123" ...

By file:

curl ... -F variables=@MyVariables ...

The MyVariables file being for example:

FOO=abc def
BAR=123

If a variable is defined more than once, its last definition is used.

Resource files

If your workflow contains a resources.files part, you must also provide one file per defined entry in resources.files. You cannot use the field format for files.

You must use a multipart/form-data request if your workflow contains a resources.files part.

Examples

Example: starting a simple YAML workflow

Assuming an existing workflow.yaml YAML workflow in the current directory, you can call the endpoint using a POST request as such:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-type: application/x-yaml" \
     --data-binary @workflow.yaml \
     http://orchestrator.example.com/workflows
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -H "Content-type: application/x-yaml" ^
     --data-binary @workflow.yaml ^
     http://orchestrator.example.com/workflows
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -H "Content-type: application/x-yaml" `
     --data-binary '@workflow.yaml' `
     http://orchestrator.example.com/workflows
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow YAML Example accepted (workflow_id=bd801408-442e-4a64-896a-e91a1551b38e).",
  "details": {
    "workflow_id":"bd801408-442e-4a64-896a-e91a1551b38e"
  },
  "status":"Success",
  "reason":"Created",
  "code":201
}
Example: starting a simple JSON workflow in a non-default namespace

If workflow.json is a JSON workflow, you can alternatively use the following command:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-Type: application/json" \
     --data-binary @workflow.json \
     http://orchestrator.example.com/workflows?namespace=mynamespace
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -H "Content-Type: application/json" ^
     --data-binary @workflow.json ^
     http://orchestrator.example.com/workflows?namespace=mynamespace
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -H "Content-Type: application/json" `
     --data-binary '@workflow.json' `
     http://orchestrator.example.com/workflows?namespace=mynamespace

(Strictly speaking, as YAML is a superset of JSON, using a -H "Content-type: x-yaml" header would work just as well.)

{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow JSON Example accepted (workflow_id=aeefb009-f793-41c0-8cf3-48a05ce9d2c5).",
  "details": {
    "workflow_id":"aeefb009-f793-41c0-8cf3-48a05ce9d2c5"
  },
  "status":"Success",
  "reason":"Created",
  "code":201
}
Example: starting a workflow using a multipart/form-data request

You can call the endpoint using a multipart/form-data request too. In this case, you do not specify the Content-Type header:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -F workflow=@workflow.yaml \
     https://orchestrator.example.com/workflows
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -F workflow=@workflow.yaml ^
     https://orchestrator.example.com/workflows
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -F workflow='@workflow.yaml' `
     https://orchestrator.example.com/workflows
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow YAML Example accepted (workflow_id=132a859f-b35b-4cf1-8987-3cae5a7fd91d).",
  "details": {
    "workflow_id":"132a859f-b35b-4cf1-8987-3cae5a7fd91d"
  },
  "status":"Success",
  "reason":"Created",
  "code":201
}
Example: dryRun a workflow with attached resource files

Assuming your workflow declares two resources.files entries, as such:

workflow.yaml
metadata:
  name: Plenty
resources:
  files:
  - report1
  - report2
jobs:
  a-job:
    runs-on: [linux]
    steps:
    - run: echo "hello"

You must call the endpoint using the following multipart/form-data request:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -F workflow=@workflow.yaml \
     -F report1=@report1.html \
     -F report2=@report2.xml \
     https://orchestrator.example.com/workflows?dryRun
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -F workflow=@workflow.yaml ^
     -F report1=@report1.html ^
     -F report2=@report2.xml ^
     https://orchestrator.example.com/workflows?dryRun
curl.exe -X POST \
     -H "Authorization: Bearer $Env:TOKEN" `
     -F workflow='@workflow.yaml' `
     -F report1='@report1.html' `
     -F report2='@report2.xml' `
     https://orchestrator.example.com/workflows?dryRun

As the workflow is valid, the response will be as shown below, but, as the dryRun query parameter was provided, the workflow will not be started.

{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow Plenty accepted (workflow_id=d8eab90b-39ad-4f40-a0e7-b40cc09c20f8).",
  "details": {
    "workflow_id":"d8eab90b-39ad-4f40-a0e7-b40cc09c20f8"
  },
  "status":"Success",
  "reason":"Created",
  "code":201
}
Example: starting a workflow with attached resource files and variables

Assuming your workflow declares two resources.files entries, as such:

workflow.yaml
metadata:
  name: Plenty with variables
variables:
  SERVER: foo
  SQUASH_USER: foobar
resources:
  files:
  - report1
  - report2
jobs:
  a-job:
    runs-on: [linux]
    steps:
    - run: echo "$SQUASH_USER"

You must call the endpoint using the following multipart/form-data request:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -F workflow=@workflow.yaml \
     -F report1=@report1.html \
     -F report2=@report2.xml \
     -F variables=SQUASH_USER=${USER}$'\n'SQUASH_PASSWORD=${PASSWD} \
     https://orchestrator.example.com/workflows
curl.exe -X POST \
     -H "Authorization: Bearer $Env:TOKEN" `
     -F workflow='@workflow.yaml' `
     -F report1='@report1.html' `
     -F report2='@report2.xml' `
     -F variables="SQUASH_USER=$Env:USER`nSQUASH_PASSWORD=$Env:PASSWD" `
     https://orchestrator.example.com/workflows

Here, we also provide two variables, SQUASH_USER and SQUASH_PASSWORD.

The SQUASH_USER variable is specified in the workflow, but its value will be overwritten with the value set in the curl call.

The SQUASH_PASSWORD variable will be available for use in the workflow, with the value set in the curl call.

The SERVER variable specified in the workflow remains available and unchanged.

Example: failing to provide a resource file

Assuming your workflow declares two resources.files entries, as such:

workflow.yaml
metadata:
  name: Two files
resources:
  files:
  - report1
  - report2
jobs:
  a-job:
    runs-on: [linux]
    steps:
    - run: echo "hello"

If you call the endpoint using the following multipart/form-data request:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -F workflow=@workflow.yaml \
     -F report1=@report1.html \
     https://orchestrator.example.com/workflows
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -F workflow=@workflow.yaml ^
     -F report1=@report1.html ^
     https://orchestrator.example.com/workflows
curl.exe -X POST \
     -H "Authorization: Bearer $Env:TOKEN" `
     -F workflow='@workflow.yaml' `
     -F report1='@report1.html' `
     https://orchestrator.example.com/workflows

(Note that report2 is not provided.)

{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Not all expected files were attached: {'report2'}.",
  "details":null,
  "status":"Failure",
  "reason":"Invalid",
  "code":422
}
Example: attempting to start a workflow with attached resource files via a Content-Type: application/x-yaml request

Assuming your workflow declares two resources.files entries, as such:

workflow.yaml
metadata:
  name: Plenty
resources:
  files:
  - report1
  - report2
jobs:
  a-job:
    runs-on: [linux]
    steps:
    - run: echo "hello"

Attempting a Content-type: application/x-yaml request to start this workflow will result in an error:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-Type: application/x-yaml" \
     --data-binary @workflow.yaml \
     http://orchestrator.example.com/workflows
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -H "Content-Type: application/x-yaml" ^
     --data-binary @workflow.yaml ^
     http://orchestrator.example.com/workflows
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -H "Content-Type: application/x-yaml" `
     --data-binary '@workflow.yaml' `
     http://orchestrator.example.com/workflows
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Expecting files, must use multipart/form-data.",
  "details":null,
  "status":"Failure",
  "reason":"Invalid",
  "code":422
}
Example: pinging the receptionist service
curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     http://orchestrator.example.com/workflows?ping
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     http://orchestrator.example.com/workflows?ping
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     http://orchestrator.example.com/workflows?ping
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Pong!",
  "details":null,
  "status":"Success",
  "reason":"OK",
  "code":200
}

/workflows/{workflow_id} (DELETE)

This endpoint is exposed by the Killswitch service. It stops a running workflow.

It accepts three optional query parameters:

Query parameter Description
dryRun The workflow is not stopped, but the request is validated. The response will be a Success status with a reason of ‘OK’ and a 200 code if the workflow is running. If the workflow is not running, the response will be a Failure status with a reason of NotFound and a 404 code. The other possible code (401, 403, and 422) could also occurs, as usual.
source A string, the source of the cancellation request. It can be used by reporting services for information purpose.
reason A string, a reason for the cancellation request. It can be used by reporting services for information purpose.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: the Workflow {workflow_id} canceled. string

Please note that the workflow may not stop immediately. Running steps and cleaning tasks may require some time to complete.

Stopping a completed workflow is allowed and does not produce an error.

If the provided workflow ID is incorrect, a Failure status is returned with an Invalid reason and a 422 code, and the message entry describes the cause of the rejection.

If the workflow is not known, a Failure status is returned with a NotFound reason and a 404 code.

Possible status responses
Status Reason Code Description
Success OK 200 The workflow was successfully stopped.
Failure NotFound 404 The workflow is not known.
Failure Invalid 422 The workflow ID format is incorrect.
Failure Forbidden 403 The token does not allow to stop the workflow.
Failure Unauthorized 401 The token is invalid or missing.
Example: stopping a running workflow
DELETE /workflows/c75c7853-2cfa-42ab-80c4-f84966a8c016
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow c75c7853-2cfa-42ab-80c4-f84966a8c016 canceled.",
  "details":null,
  "status":"Success",
  "reason":"OK",
  "code":200
}
Example: dryRun stopping a running workflow
DELETE /workflows/c75c7853-2cfa-42ab-80c4-f84966a8c016?dryRun
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow c75c7853-2cfa-42ab-80c4-f84966a8c016 canceled.",
  "details":null,
  "status":"Success",
  "reason":"OK",
  "code":200
}
Example: stopping a completed or canceled workflow
DELETE /workflows/c75c7853-2cfa-42ab-80c4-f84966a8c016
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow c75c7853-2cfa-42ab-80c4-f84966a8c016 canceled.",
  "details":null,
  "status":"Success",
  "reason":"OK",
  "code":200
}
Example: attempting to stop an unknown workflow
DELETE /workflows/00000000-0000-0000-0000-000000000000
{
  "apiVersion":"v1",
  "kind":"Status",
  "metadata":{},
  "message":"Workflow 00000000-0000-0000-0000-000000000000 not found.",
  "details":null,
  "status":"Failure",
  "reason":"NotFound",
  "code":404
}

/workflows/{workflow_id}/datasources/{kind} (GET)

This endpoint is exposed by the Observer service. It allows to extract a data source from a workflow.

It accepts three optional query parameters:

Query parameter Description
scope The scope of the data source.
page The page number to return.
per_page The number of items per page.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: a summary of the workflow status (a string)
  • details: an object with an items element, a list of data source items. If the workflow is still running, an empty list is returned.

If the requested data source cache is not yet created or caching is in progress, a Success status is returned with an Accepted reason and a 202 code.

This endpoint allows for pagination and optional scope query parameter.

If kind is not known, a Failure status is returned with an Invalid reason and a 422 code, and the message entry details the allowed kinds.

If there are scope errors, a Failure status is returned with an Invalid reason and a 422 code too, and the message entry details the scope errors.

If the workflow is not known, a Failure status is returned with a NotFound reason and a 404 code.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Success Accepted 202 The data source caching is in progress.
Failure NotFound 404 The specified workflow is not known.
Failure Invalid 422 The specified kind or query parameters are invalid.
Failure Forbidden 403 The token does not allow to read the workflow.
Failure Unauthorized 401 The token is invalid or missing.

Data sources

Three data source kinds are available:

Kind Description
testcases A list of workflow test cases with details.
jobs A list of workflow jobs with test execution summary, environment tags and variables for each job.
tags A list of execution tags with test execution summary for each tag.
The testcases data source model
{
  "apiVersion": "testing.opentestfactory.org/v1alpha1",
  "kind": "TestCase",
  "metadata": {
    "name": "<<<[Test suite#]Test case name>>>",
    "id": "<<<Test case uuid>>>",
    "job_id": "<<<Test case job uuid>>>",
    "execution_id": "<<<Test case execution step id>>>",
    "workflow_id": "<<<Test case workflow id>>>",
    "namespace": "<<<Test case namespace>>>",
    "creationTimestamp": "<<<Test case provider command creation timestamp"
  },
  "test": {
    "runs-on": [<<<Test case execution environment tags>>>],
    "uses": "<<<Provider function (i.e `cypress/execute`)>>>",
    "technology": "<<<Test technology>>>",
    "job": "<<<Job name>>>",
    "test": "<<<Test reference>>>",
    "suiteName": "<<<Test suite name>>>",
    "testCaseName": "<<<Test case name>>>",
    "outcome": "success|failure|skipped|error",
    "managed": boolean, True for test cases managed by a test referential
  },
  "status": "SUCCESS|FAILURE|SKIPPED|ERROR",
  "execution": {
    "startTime": "<<<Execution start time>>>",
    "endTime": "<<<Execution end time>>>",
    "duration": <<<Test case execution time in ms>>>,
    "(failure|warning|error)Details": {
      "message": "<<<Error message>>>",
      "type": "<<<Error type>>>",
      "text": "<<<Error trace>>>"
    },
    "errorsList": [
      {
        "message": "<<<Execution general error message>>>",
        "timestamp": "<<<Execution general error timestamp>>>"
      }
    ]
  }

In execution, failure details element is optional, it is added only for test cases having FAILURE or ERROR status. execution.errorsList optional element is currently specific to Robot Framework executions that contain general execution errors: the errorsList element is added to the first executed test case.

For test cases managed by a test referential (test.managed=True), test element may contain additional labels. See test referential documentation for details.

The jobs data source model

{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "Job",
  "metadata": {
    "name": "<<<Job name>>>",
    "id": "<<<Job uuid>>>",
    "namespace": "<<<Job namespace>>>",
    "workflow_id": "<<<Job workflow uuid>>>",
    "creationTimestamp": "<<<Job creation timestamp>>>"
    },
  "spec": {
    "runs-on": [<<<Execution environment tags list>>>],
    "variables": {
      "<<<Environment variable name>>>": "<<<Environment variable value>>>",
      ...
    },
  "status": {
    "phase": "SUCCEEDED",
    "requestTime": "<<<Channel request timestamp>>>",
    "startTime": "<<<Job start time>>>",
    "endTime": "<<<Job end time>>>",
    "duration": <<<Job duration (endTime - startTime) in ms>>>,
    "testCaseCount": <<<Job test case count>>>,
    "testCaseStatusSummary": { <<<Job test case counts by status>>>
      "success": N,
      "failure": N,
      "error": N,
      "skipped": N,
      "cancelled": N
    }
  }    
}
All counts are integers.

The tags data source model

{
  "apiVersion": "opentestfactory.org/v1alpha1",
  "kind": "Tag",
  "metadata": {
    "name": "<<<Tag name>>>",
    "workflow_id": "<<<Tag workflow uuid>>>",
    "namespace": "<<<Tag namespace>>>"
  },
  "status": {
    "jobCount": <<<Tag jobs count>>>,
    "testCaseCount": <<<Tag test case count>>>,
    "testCaseStatusSummary": { <<<Tag test case counts by status>>>
      "success": N,
      "failure": N,
      "error": N,
      "skipped": N,
      "cancelled": N
    }
  }
}
All counts are integers.

Pagination

The list of data source items may be paginated. The pagination is the same as for the /workflows/{workflow_id}/status endpoint.

Scope

An optional scope query parameter can be used to restrain data source scope. The data source scope uses workflow context syntax and is based on the test context. It contains information about executed test cases, but can also be applied to filter jobs and tags data sources.

For instance, this query returns tags list where all test counts by tags are provided for JUnit test cases only:

curl "127.0.0.1:7775/workflows/{workflow_id}/datasources/tags?scope=test.technology=='junit'"

Warning

Note that the scope query parameter applies to the test cases only. If no test case matches provided scope, tags and jobs data sources are still returned, but their test case counts equal to zero. However, if there are scope errors, an Invalid response is returned, containing errors details.

Examples

Example: Querying the first item of the jobs data source
curl "127.0.0.1:7775/workflows/2e133f2c-7a2a-4d2d-8734-920e50ab20c2/datasources/jobs?page=1&per_page=1"
{
 "apiVersion" : "v1",
 "code" : 200,
 "details" : {
    "items" : [
       {
          "apiVersion" : "opentestfactory.org/v1alpha1",
          "kind" : "Job",
          "metadata" : {
             "creationTimestamp" : "2024-05-15T10:04:09.648230",
             "id" : "460f15dc-879f-4bc0-8743-1d2d061a0c29",
             "name" : "cucumber-execute-test",
             "namespace" : "default",
             "workflow_id" : "764f33fc-49fd-479b-a6b8-92f5ab9cdfc5"
          },
          "spec" : {
             "runs-on" : [
                "cucumber",
                "linux"
             ],
             "variables" : {
                "VAR_1": "value1"
             }
          },
          "status" : {
             "duration" : 8212.488,
             "endTime" : "2024-05-15T10:05:02.283481",
             "phase" : "SUCCEEDED",
             "requestTime" : "2024-05-15T10:04:23.959978",
             "startTime" : "2024-05-15T10:04:54.070993",
             "testCaseCount" : 5,
             "testCaseStatusSummary" : {
                "cancelled" : 0,
                "error" : 0,
                "failure" : 2,
                "skipped" : 0,
                "success" : 3
             }
          },
          "uuid" : "cucumber-execute-test"
       }
    ]
 },
 "kind" : "Status",
 "message" : "",
 "metadata" : {},
 "reason" : "OK",
 "status" : "Success"
}
Example: Getting only failed JUnit test cases from the testcases data source

curl "127.0.0.1:7775/workflows/2e133f2c-7a2a-4d2d-8734-920e50ab20c2/datasources/testcases?scope=test.outcome=='failure'%26%26test.technology=='junit'"
{
 "apiVersion" : "v1",
 "code" : 200,
 "details" : {
    "items" : [
       {
          "apiVersion" : "testing.opentestfactory.org/v1alpha1",
          "execution" : {
             "duration" : 14,
             "endTime" : "2024-05-15T10:06:04.759998",
             "failureDetails" : {
                "text" : "org.opentest4j.AssertionFailedError: Le résultat du calcul est incorrect.",
                "type" : "org.opentest4j.AssertionFailedError"
             },
             "startTime" : "2024-05-15T10:06:01.529635"
          },
          "kind" : "TestCase",
          "metadata" : {
             "creationTimestamp" : "2024-05-15T10:06:01.507841",
             "execution_id" : "65501add-4bc1-4055-a433-24f6a7df8628",
             "id" : "03cd6332-b960-455e-b702-2b34a6fe96b8",
             "job_id" : "b4f7bbe9-5ae5-4e83-8c18-fd03e0af951f",
             "name" : "squash.tfauto.CalcNoDNTest#subDNFailure",
             "namespace" : "default",
             "workflow_id" : "764f33fc-49fd-479b-a6b8-92f5ab9cdfc5"
          },
          "status" : "FAILURE",
          "test" : {
             "job" : "junit-execute-test",
             "managed" : false,
             "outcome" : "failure",
             "runs-on" : [
                "junit",
                "linux"
             ],
             "suiteName" : "squash.tfauto.CalcNoDNTest",
             "technology" : "junit",
             "test" : "junit-calc/squash.tfauto.CalcNoDNTest",
             "testCaseName" : "subDNFailure",
             "uses" : "junit/execute@v1"
          }
       },
       {...}
    ]
 },
 "kind" : "Status",
 "message" : "",
 "metadata" : {},
 "reason" : "OK",
 "status" : "Success"
}

Example: Querying tags data source with incorrect scope

curl "127.0.0.1:7775/workflows/2e133f2c-7a2a-4d2d-8734-920e50ab20c2/datasources/tags?scope=test.outcome='success'"
{
 "apiVersion" : "v1",
 "code" : 422,
 "details" : {
    "scope_error" : "Scope error occurred."
 },
 "kind" : "Status",
 "message" : "[SCOPE ERROR] Invalid conditional test.outcome='success': Invalid token ='success'.",
 "metadata" : {},
 "reason" : "Invalid",
 "status" : "Failure"
}

Example: Providing an unknown data source kind

curl "127.0.0.1:7775/workflows/2e133f2c-7a2a-4d2d-8734-920e50ab20c2/datasources/clouds"
{
 "apiVersion" : "v1",
 "code" : 422,
 "details" : null,
 "kind" : "Status",
 "message" : "Invalid data source kind `clouds`, was expecting one of: jobs, tags, testcases.",
 "metadata" : {},
 "reason" : "Invalid",
 "status" : "Failure"
}

/workflows/{workflow_id}/files/{attachment_id} (GET, HEAD)

This endpoint is exposed by the Localstore service. It returns a requested attachment content or its headers only.

If the workflow or the attachment is not known, a Failure status is returned instead with a NotFound reason and a 404 code.

Possible status responses

Please note that, as stated above, this endpoint does not return a status manifest in case of success, but the attachment’s content or headers instead.

Status Reason Code Description
Failure NotFound 404 The specified workflow or attachment is not known.
Failure Forbidden 403 The token does not allow to read attachments.
Failure Unauthorized 401 The token is invalid or missing.

/workflows/{workflow_id}/insights (POST)

This endpoint is exposed by the Insight Collector plugin. It allows to generate a report using an user-provided insights definition file. The insight or report definition to apply is defined by the insight query parameter.

It requires the following query parameter:

Query parameter Description
insight A string, the name of the desired insight.

The following definition formats are accepted: JSON or YAML.

Info

The report generation is asynchronous: WorkflowResult events will be published if the report generation is successful.

The endpoint returns a status manifest (a JSON document) with one of the following standard HTTP response codes: OK (200) or Invalid (422).

  • OK means the definition file is valid and the Insight Collector plugin will try to generate the report specified by the insight query parameter. The details dictionary contains two entries:

    • request_id: UUID of the POST request,
    • expected: list of tuples identifying generated insights, each tuple containing insight name and kind properties.

    A request_id parameter is added to the metadata section of the WorkflowResult events containing requested attachments, allowing for their identification and retrieval.

  • Invalid means the generation request cannot be satisfied: configuration file cannot be parsed, it contains invalid insight definition(s), or the insight query parameter is missing.

If no insights matching the insight query parameter are found in the definition file, an OK response code will be returned, with an empty details.expected list and “0 expected insights” message.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure Invalid 422 The specified insight or definition is invalid.
Failure Forbidden 403 The token does not allow to read the workflow.
Failure Unauthorized 401 The token is invalid or missing.

Examples

Example: requesting to generate a report based on an insight from joined definition

Assuming an existing my_insights.yaml definition file with detailed-execution-report insight, the endpoint can be called using the following POST request:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-type: application/x-yaml" \
     --data-binary @my_insights.yaml \
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights?insight=detailed-execution-report
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -H "Content-type: application/x-yaml" ^
     --data-binary @my_insights.yaml ^
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights?insight=detailed-execution-report
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -H "Content-type: application/x-yaml" `
     --data-binary '@my_insights.yaml' `
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights?insight=detailed-execution-report
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "1 expected insight",
  "details": {
    "request_id": "95559340-a592-44f3-82bf-d5fc011e072b",
    "expected": [("detailed-execution-report", "SummaryReport")]
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: requesting to generate a report based on an insight from the file joined to request

You may also join a definition to the request as a file. In this case, use insights as a parameter name.

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -F insights=@my_insights.yaml \
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights?insight=detailed-execution-report
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -F insights=@my_insights.yaml ^
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights?insight=detailed-execution-report
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -F insights='@my_insights.yaml' `
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights?insight=detailed-execution-report
Example: requesting to generate a report without providing insight query parameter

Calling the endpoint with the following request will return a failure:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-type: application/x-yaml" \
     --data-binary @my_insights.yaml \
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -H "Content-type: application/x-yaml" ^
     --data-binary @my_insights.yaml ^
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -H "Content-type: application/x-yaml" `
     --data-binary '@my_insights.yaml' `
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/insights
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Missing insight name pattern.",
  "status": "Failure",
  "reason": "Invalid",
  "code": 422
}

/workflows/{workflow_id}/logs (GET)

This endpoint is exposed by the Observer service. It returns a specified workflow execution log.

The endpoint supports Range headers and streaming options.

The execution log returned by this endpoint has the following parameters: step_depth=1, job_depth=1, max_command_length=0 (only top-level jobs and steps are displayed).

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Success Partial Content 206 The requested range was successfully sent.
Failure Range Not Satisfiable 416 The requested range is not satisfiable.
Failure Forbidden 403 The token does not allow to read the workflow.

Examples

Example: requesting workflow execution log
GET /workflows/282cad72-005a-4268-b605-8cb05291f92e/logs
Workflow Example Robot Test
(running in namespace 'default')
[2024-10-30T09:45:54] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Requesting execution environment providing ['linux', 'robotoframework'] in namespace 'default' for job 'robot-2-execute-test'
[2024-10-30T09:45:54] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Running function robotframeworkexecutev1 
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] ==============================================================================
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Test Example Test
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] ==============================================================================
[...]
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] 7 tests, 7 passed, 0 failed
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] ==============================================================================
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Output:  /home/user/run_agent/output.xml
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Log:     /home/user/run_agent/log.html
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Report:  /home/user/run_agent/report.html
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Releasing execution environment for job 'robot-2-execute-test'
Example: requesting workflow execution log within a specified range
GET /workflows/282cad72-005a-4268-b605-8cb05291f92e/logs -H "Range: bytes=128-700"
[2024-10-30T09:45:54] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Running function robotframeworkexecutev1 
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] ==============================================================================
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] Test Example Test
[2024-10-30T09:45:56] [job 29b2838f-5a63-4112-9a1a-64f2c8aef8b4] ==========================================================================

/workflows/{workflow_id}/qualitygate (GET)

This endpoint is exposed by the Quality Gate plugin. It checks whether a workflow satisfies a quality gate defined by the mode query parameter.

It accepts the following optional query parameters:

Query parameter Description
mode The quality gate mode to apply (strict if not specified).
timeout The test cases caching timeout (8 seconds if not specified).

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: an empty string
  • details.status: the quality gate status for the workflow. It can be one of the following:

    • SUCCESS: the workflow has succeeded and passed the quality gate.
    • NOTEST: the workflow has succeeded, but it produced no test result, or it contains no test results matching the quality gate rules scope.
    • FAILURE: the workflow has failed, or failed the quality gate.
    • RUNNING: the workflow execution is still ongoing.

If test cases caching times out, a Success status is returned with an Accepted reason and a 202 code.

If the specified mode query parameter is not found in the definition file, a Failure status is returned with an Invalid reason and a 422 code.

If the specified workflow ID is not known, a Failure status is returned with a NotFound reason and a 404 code.

Info

It may be because the workflow is not yet known to the quality gate service, or that the workflow is no longer known to the quality gate service. Quality gate results for a given workflow are kept for a limited period after workflow completion (typically 60 minutes, but this can be configured).

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Success Accepted 202 The test cases caching is still in progress.
Failure NotFound 404 The specified workflow is not known.
Failure Invalid 422 The specified query parameters are invalid.
Failure Forbidden 403 The token does not allow to read the workflow.
Failure Unauthorized 401 The token is invalid or missing.

Modes

This endpoint allows to use a mode (i.e. a quality gate) defined in the definition file loaded at the quality gate service launch. Two default modes are always available: strict and passing. If no mode is specified, strict is used.

  • strict means a workflow will pass the quality gate if and only if it has been completed successfully and all test cases were OK.

  • passing means a workflow will pass the quality gate if it has been completed successfully, even if some test cases were not OK.

Examples

Example: checking the quality gate status of a workflow with several tests

This is a status manifest that contains test evaluation results and the general workflow execution status (in details.status). The JUnit tests execution result is evaluated as SUCCESS even if some tests have failed, but the general execution status is FAILURE, because Cypress tests did not pass the threshold.

GET /workflows/5dcc66dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=junit.cypress.quality.gate
{
  "apiVersion": "v1",
  "code": 200,
  "details": {
      "rules": {
          "Cypress tests": {
              "result": "FAILURE",
              "scope": "test.technology == 'cypress'",
              "success_ratio": "50.0%",
              "tests_failed": 10,
              "tests_in_scope": 20,
              "tests_passed": 10
          },
          "JUnit tests": {
              "result": "SUCCESS",
              "scope": "test.technology == 'junit'",
              "success_ratio": "80.0%",
              "tests_failed": 10,
              "tests_in_scope": 50,
              "tests_passed": 40
          },
      },
      "status": "FAILURE"
  },
  "kind": "Status",
  "message": "",
  "metadata": {},
  "reason": "OK",
  "status": "Success"
}
Example: checking the quality gate status on passing mode of a workflow with a failed test

This is a status manifest for a workflow with at least one failed test, using the passing mode. As long as the workflow has been completed successfully, it will pass the quality gate:

GET /workflows/5dcc66dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=passing
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "",
  "details": {
    "status": "SUCCESS"
  },
  "status": "Success",
  "reason": "OK"
  "code": 200,
}
Example: checking the quality gate status of a workflow with one failed test on strict mode

This is a status manifest for the same workflow, using the default (strict) mode. As it has at least one failed test, it fails that quality gate:

GET /workflows/5dcc66dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "",
  "details": {
    "status": "FAILURE"
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: checking the quality gate status of a workflow where no test matches the quality gate rules

This is a status manifest for a workflow that contains no tests matching user-defined my.quality.gate quality gate rules scope:

GET /workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=my.quality.gate
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "",
  "details": {
    "status": "NOTEST"
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: attempting to check the quality gate status of an unknown workflow
GET /workflows/00000000-0000-0000-0000-000000000000/qualitygate
{
  "apiVersion": "v1",
  "kind": "Status",
  "message": "Workflow 00000000-0000-0000-0000-000000000000 not found.",
  "metadata": {},
  "status": "Failure",
  "reason": "NotFound",
  "code": 404
}

/workflows/{workflow_id}/qualitygate (POST)

This endpoint is the second endpoint exposed by the Quality Gate plugin. It allows to check whether a workflow satisfies a quality gate from the quality gate definition joined to the request.

It requires the following query parameters:

Query parameter Description
mode The quality gate mode to apply.
timeout The test cases caching timeout (8 seconds if not specified).

The following quality gate definition formats are accepted: JSON or YAML.

It returns a status manifest (a JSON document). The response codes and the details.status are the same as for the GET endpoint.

This endpoint does not have any default quality gate. If no mode is specified or mode is not found in the quality gate definition, a Failure status is returned with an Invalid reason and a 422 code.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Success Accepted 202 The test cases caching is still in progress.
Failure NotFound 404 The specified workflow is not known.
Failure Invalid 422 The specified definition or query parameters are invalid.
Failure Forbidden 403 The token does not allow to read the workflow.
Failure Unauthorized 401 The token is invalid or missing.

Examples

Example: checking that a workflow passes the quality gate from the quality gate definition joined to request

Assuming an existing my_qualitygates.yaml quality gate definition with the junit quality gate, the endpoint can be called using the following POST request. The status manifest indicates that all the tests in a workflow satisfying to the quality gate junit rules scope are in success:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-type: application/x-yaml" \
     --data-binary @my_qualitygates.yaml \
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=junit
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -H "Content-type: application/x-yaml" ^
     --data-binary @my_qualitygates.yaml ^
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=junit
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -H "Content-type: application/x-yaml" `
     --data-binary '@my_qualitygates.yaml' `
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=junit
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "",
  "details": {
    "status": "SUCCESS"
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: checking that a workflow passes the quality gate from the definition file joined to request

You may also join a quality gate definition to the request as a file. In this case, use qualitygates as a parameter name.

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -F qualitygates=@my_qualitygates.yaml \
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=junit
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -F qualitygates=@my_qualitygates.yaml ^
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=junit
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -F qualitygates='@my_qualitygates.yaml' `
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=junit
Example: checking that a workflow passes the quality gate with mode not in quality gate definition

Assuming that my_qualitygates.yaml does not contain a quality gate named cypress, calling the endpoint with the following request will return a failure:

curl -X POST \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-type: application/x-yaml" \
     --data-binary @my_qualitygates.yaml \
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=cypress
curl -X POST ^
     -H "Authorization: Bearer %TOKEN%" ^
     -H "Content-type: application/x-yaml" ^
     --data-binary @my_qualitygates.yaml ^
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=cypress
curl.exe -X POST `
     -H "Authorization: Bearer $Env:TOKEN" `
     -H "Content-type: application/x-yaml" `
     --data-binary '@my_qualitygates.yaml' `
     http://orchestrator.example.com/workflows/5dcc44dd-5d0d-4dcf-9b18-f39eafc0f279/qualitygate?mode=cypress
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Quality gate cypress not found in definition file.",
  "status": "Failure",
  "reason": "Invalid",
  "code": 422
}

/workflows/{workflow_id}/status (GET)

This endpoint is exposed by the Observer service. It lists the workflow events and status.

It accepts the following optional query parameters:

Query parameter Description
fieldSelector The field selector to filter the returned events.
labelSelector The label selector to filter the returned events.
page The page number to return.
per_page The number of items per page.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: a summary of the workflow status (a string)
  • details: an object with two elements: items, a list of all events about the workflow, and status, one of the following strings: RUNNING, DONE, or FAILED.

If the workflow is not known, a Failure status is returned instead with a NotFound reason and a 404 code.

This endpoint allows for field and label selectors to filter returned workflow events. Selectors can be used with page and per_page parameters.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure NotFound 404 The specified workflow is not known.
Failure Invalid 422 The specified selectors or query parameters are invalid.
Failure Forbidden 403 The token does not allow to read the workflow.
Failure Unauthorized 401 The token is invalid or missing.

Pagination

The list of events, items, may be paginated. The pagination, if used, is as per RFC8288 and relies on the Link header attribute:

import requests

base_url = '...'
response = requests.get(f'{base_url}/workflows/4420a8ad-1880-45dd-af07-9162583efcff/status')
events = response.json()['details']['items']
while 'next' in response.links:
    response = requests.get(response.links['next']['url'])
    events += response.json()['details']['items']

# All events have been collected

By default, the page size is determined by the observer service, but this can be changed by using the per_page parameter. There may be a maximum value allowed for per_page. (In the current implementation the default value is 100 and the maximum value is 1000.)

If a specific per_page value is set and assuming it is within the allowed limits, the links will use this value.

A specific page can be specified using the page parameter. page starts at 1, as per RFC8288.

Even if items is paginated, the status is for the whole workflow.

Examples

Example: querying the status of a completed workflow
GET /workflows/4420a8ad-1880-45dd-af07-9162583efcff/status
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "status": "Success",
  "message": "Workflow completed",
  "details": {
    "items": [...],
    "status": "DONE"
  },
  "code": 200,
  "reason": "OK"
}
Example: querying the status of a failed workflow
GET /workflows/4420a8ad-1880-45dd-af07-9162583efcff/status
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "status": "Success",
  "message": "Workflow failed",
  "details": {
    "items": [...],
    "status": "FAILED"
  },
  "code": 200,
  "reason": "OK"
}
Example: querying only notification events of a workflow
GET /workflows/7e9fd692-d8d5-4928-8329-230cc43c6b9f/status?fieldSelector=kind=Notification
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "status": "Success",
  "message": "Workflow failed",
  "details": {
    "items": [
      {
        [...]
      }
      {
        "apiVersion": "v1",
        "kind": "Notification",
        "metadata": {
          "job_id": "7e9fd692-d8d5-4928-8329-230cc43c6b9f"
          [...]
        }
        "spec": {
          "logs": [
            "[DEBUG] Received execution result d3e6e34a-77ed-4872-a846-516d40d60e44 for job 7e9fd692-d8d5-4928-8329-230cc43c6b9f"
          ]
        }
      },
      {
        "apiVersion": "v1",
        "kind": "Notification",
        "metadata": {
          "job_id": "7e9fd692-d8d5-4928-8329-230cc43c6b9f"
          [...]
        }
        "spec": {
          "logs": [
            "[DEBUG] Received execution result aa7d872f-d110-43ac-aec6-abdc0597db5a for job 7e9fd692-d8d5-4928-8329-230cc43c6b9f"
          ]
        }
      }
      {
        [...]
      }
    ]
    "status": "DONE"
  },
  "code": 200,
  "reason": "OK"
}
Example: querying ExecutionResult events of a workflow with pagination
GET /workflows/4420a8ad-1880-45dd-af07-9162583efcff/status?fieldSelector=kind=ExecutionResult&per_page=2&page=5
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "status": "Success",
  "message": "Workflow completed",
  "details": {
    "items": [
      {
        "apiVersion": "v1",
        "kind": "ExecutionResult",
        "metadata": {
          [...]
        }
        "status": 0
      },
      {
        "apiVersion": "v1",
        "kind": "ExecutionResult",
        "metadata": {
          [...]
        }
        "status": 1
      }
    ]
    "status": "DONE"
  },
  "code": 200,
  "reason": "OK"
}
Example: attempting to query the status of an unknown workflow
GET /workflows/00000000-0000-0000-0000-000000000000/status
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Workflow 00000000-0000-0000-0000-000000000000 not found.",
  "details": null,
  "status": "Failure",
  "reason": "NotFound",
  "code": 404
}

/workflows/{workflow_id}/workers (GET)

This endpoint is exposed by the Observer service. It lists the workers currently working on the workflow.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: a summary of the workflow status (a string)
  • details: an object with two elements: items, a list of active workers on the workflow, and status, one of the following strings: BUSY or IDLE.

If the workflow is not known, a Failure status is returned instead with a NotFound reason and a 404 code.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure NotFound 404 The specified workflow is not known.
Failure Forbidden 403 The token does not allow to read the workflow.
Failure Unauthorized 401 The token is invalid or missing.
Example: checking if some workers are processing the specified workflow (2 still busy)
GET /workflows/4420a8ad-1880-45dd-af07-9162583efcff/workers
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "2 active workers on workflow",
  "details": {
    "items": [
      "3137d9e2-0873-4cba-b5d3-f36dff8d9b3f",
      "e276b403-6d27-4d63-b3f1-1415c258736c"
    ],
    "status": "BUSY"
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: checking if some workers are processing the specified workflow (not anymore)
GET /workflows/4420a8ad-1880-45dd-af07-9162583efcff/workers
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "0 active workers on workflow",
  "details": {
    "items": [
    ],
    "status": "IDLE"
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: attempting to check whether some workers are processing an unknown workflow
GET /workflows/00000000-0000-0000-0000-000000000000/workers
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "Workflow 00000000-0000-0000-0000-000000000000 not found.",
  "details": null,
  "status": "Failure",
  "reason": "NotFound",
  "code": 404
}

/workflows/status (GET)

This endpoint is exposed by the Observer service. It queries the status of the orchestrator.

It returns a Success status manifest (a JSON document) with the following entries:

  • reason: OK
  • message: a summary of the orchestrator status (a string)
  • details: an object with two elements: items, a list of all still running workflows, and status, one of the following strings: IDLE or BUSY.

If there are no running workflows, and if there are no active workers on any workflow, details.status is IDLE. It is BUSY otherwise.

Possible status responses
Status Reason Code Description
Success OK 200 The request was successfully handled.
Failure Forbidden 403 The token does not allow to read the orchestrator status.
Failure Unauthorized 401 The token is invalid or missing.
Example: querying the status of a busy orchestrator

In the following example, a workflow is still running (or has at least one active worker):

GET /workflows/status
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "1 workflows in progress",
  "details": {
    "items": ["50c7e5d1-7bdc-46f0-9422-3cc6660d00c0"],
    "status": "BUSY"
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}
Example: querying the status of an idle orchestrator

In this example, all workflows have been completed, and no worker is still active. It is safe to stop the orchestrator service.

GET /workflows/status
{
  "apiVersion": "v1",
  "kind": "Status",
  "metadata": {},
  "message": "No workflow in progress",
  "details": {
    "items": [],
    "status": "IDLE"
  },
  "status": "Success",
  "reason": "OK",
  "code": 200
}