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:
/agents
(GET, POST, PATCH)/agents/{agent_id}
(DELETE, GET, POST)/agents/{agent_id}/files/{file_id}
(GET, POST, PUT)
Event Bus service¶
This service exposes three endpoints:
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:
Receptionist service¶
This service exposes one endpoint:
/workflows
(POST)
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:
/agents
(GET)/channels
(GET)/subscriptions
(GET)/workflows
(GET)/workflows/{workflow_id}/status
(GET)
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), andversion
, 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
: theAgents 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
: theAgent {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
: theKnown channel handlers
stringdetails
: an object with anitems
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
: theKnown channels
stringdetails
: an object with anitems
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
: theAccessible namespaces
stringdetails
: an object with anitems
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
: theSubscription 'name' successfully registred (id=subscription_id).
details
: an object with anuuid
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
: theSubscription 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
: theBOM
stringdetails
: an object with anitems
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
: theRunning and recent workflows
stringdetails
: an object with anitems
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
: theWorkflow name accepted (workflow_id=uuid).
stringdetails
: an object with aworkflow_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:
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:
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:
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:
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
: theWorkflow {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 anitems
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
}
}
}
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
}
}
}
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 theinsight
query parameter. Thedetails
dictionary contains two entries:request_id
: UUID of the POST request,expected
: list of tuples identifying generated insights, each tuple containing insightname
andkind
properties.
A
request_id
parameter is added to themetadata
section of theWorkflowResult
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 theinsight
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, andstatus
, one of the following strings:RUNNING
,DONE
, orFAILED
.
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, andstatus
, one of the following strings:BUSY
orIDLE
.
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, andstatus
, one of the following strings:IDLE
orBUSY
.
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
}