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)/agents/{agent_id}
(DELETE, GET, POST)/agents/{agent_id}/files/{file_id}
(GET, POST, PUT)
Event Bus service¶
This service exposes three endpoints:
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)
Observer service¶
This service exposes the following endpoints:
/channelhandlers
(GET)/channels
(GET)/namespaces
(GET)/version
(GET)/workflows
(GET)/workflows/status
(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 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.
Endpoint allows for field selectors to filter returned agent manifests.
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 the agent channel plugin.
It returns a status manifest (a JSON document) with the following entries:
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 deregister the agent.
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/{agent_id}
(DELETE)¶
This endpoint is exposed by the Agent Channel plugin. It deregisters the specified agent.
It returns a status manifest (a JSON document) with the following entry:
message
: theAgent {agent_id} de-registered.
string
If the agent is not known, a Failure
status is returned instead, with a 404 code
.
Example: deregistering 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 deregister 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:
message
: theKnown channel handlers
stringdetails
: an object with anitems
element which is a possibly empty list of strings, the known channel handlers IDs.
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 returns a status manifest (a JSON document) with the following entries:
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": "IDLE" or "BUSY" or "PENDING",
"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).
Endpoint allows for field selectors to filter returned channel manifests.
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, resource
and verb
, which 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 possible values for resources
are agents
, channelhandlers
, channels
,
qualitygates
, status
, subscriptions
, and workflows
, and the possible values for
verb
are list
, get
, create
, and delete
.
The endpoint returns a status manifest (a JSON document) with the following entries:
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).
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 POST request, is a JSON object. It does not have to have any specific entries. It can be an empty.
The endpoint returns a status manifest (a JSON document).
On successful publication, the publication is dispatched to all
corresponding subscriptions, asynchronously. If there is no corresponding subscription, the
response code will be 200, but its message
part will be Publication received, but no matching
subscription
.
If there are corresponding 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
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 returns a SubscriptionList manifest (a JSON document), 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
}
}
Endpoint allows for field selectors to filter returned subscription manifests.
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 status manifest (a JSON document) with the following entries:
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.
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 status manifest (a JSON document) with the following entry:
message
: theSubscription subscription_id canceled.
string
If the subscription is not known, a Failure
status is returned instead, with a 404 code
.
Example: deregistering 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 deregister 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 status manifest (a JSON document) with the following entries:
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.
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 returns a status manifest (a JSON document) with the following entries:
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.
Endpoint allows for field selectors to filter returned workflow manifests.
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 one optional query parameter, namespace
. If it is defined, 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
.
It returns a status manifest (a JSON document). On successful invocation, it has the following entries:
message
: theWorkflow name accepted (workflow_id=uuid).
stringdetails
: an object with aworkflow_id
entry, a string, the started workflow IDstatus
: theSuccess
stringreason
: theCreated
stringcode
: 201
If the provided workflow manifest is incorrect, an Invalid
reason is returned (code 422),
and the message
entry describes the cause of the rejection.
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
curl.exe -X POST `
-H "Authorization: Bearer $Env:TOKEN" `
-H "Content-Type: application/json" `
--data-binary '@workflow.json' `
http://orchestrator.example.com/workflows
(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: starting 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
curl -X POST ^
-H "Authorization: Bearer %TOKEN%" ^
-F workflow=@workflow.yaml ^
-F report1=@report1.html ^
-F report2=@report2.xml ^
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' `
https://orchestrator.example.com/workflows
{
"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?namespace=mynamespace
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
}
/workflows/{workflow_id}
(DELETE)¶
This endpoint is exposed by the Killswitch service. It stops a running workflow.
It returns a status manifest (a JSON document) with the following entry:
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.
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: 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}/files/{attachment_id}
(GET)¶
This endpoint is exposed by the Localstore service. It returns a requested attachment content.
If the workflow or the attachment is not known, a Failure
status is returned
instead, with a 404 code
.
/workflows/{workflow_id}/status
(GET)¶
This endpoint is exposed by the Observer service. It lists the workflow events and status.
It returns a status manifest (a JSON document) with the following entries:
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
404 code
.
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.
Endpoint allows for field and label selectors
to filter returned workflow events. Selectors can be used with page
and per_page
parameters.
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 status manifest (a JSON document) with the following entries:
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
404 code
.
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/{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
parameter.
It returns a status manifest (a JSON document) with one of the following standard
HTTP response codes: OK
, Invalid
, Unauthorized
, or NotFound
.
OK
means the service knows the specified workflow. The details.status
contains 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.
Invalid
means the specified mode
is not found in the definition file or is not one
of the expected values (strict
or passing
).
NotFound
means the workflow is not known. 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).
Unauthorized
means the provided token is invalid or not recognized by the quality gate service.
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.
The quality gate is defined by the mode
parameter.
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, an error is returned.
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/status
(GET)¶
This endpoint is exposed by the Observer service. It queries the status of the orchestrator.
It returns a status manifest (a JSON document) with the following entries:
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.
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
}