Skip to content

EventBus

The EventBus serves as an event broker. It receives subscriptions and publications from the OpenTestFactory orchestrator services and dispatches the received publications accordingly.

Its endpoints may be exposed, in which case new plugins may subscribe to and publish events at any time, or it may be unexposed, in which case it will not be possible for new plugins to subscribe to and publish events.

Communications are authenticated by signed JWT tokens.

Subscription

Subscriptions are not publications. It is not possible for a service to subscribe to new subscriptions: they are handled using their own endpoints and are not dispatched to subscribers.

Subscribing

For a service to subscribe to publications, it must post a Subscription event to the following endpoint:

POST /subscriptions

It may post more than one subscription message if it is interested in more than one sort of publication.

This request will return a status message. If the subscription is valid, the status response will contain a details.uuid unique subscription identifier that can be used to cancel the subscription.

The body of the subscription message is a JSON document that complies with the Subscription schema.

It must contain an apiVersion part, a kind part, a metadata.name part, and a spec.subscriber.endpoint part.

It may contain a spec.subscriber.insecure-skip-tls-verify part, in which case, if the associated value resolves to true, this means that the event bus will not check if the HTTPS link is using valid certificates. If this part is missing, or if it resolves to false, the event bus will check the certificates, and will not send messages to the endpoint if they are not valid.

It may or may not contain a spec.selector part. If it does not contain such a part, it will receive all well-formed events.

If the spec.selector part is present, it must contain at least one of the following entries: matchKind, matchLabels, matchExpressions, matchFields, or matchFieldExpressions

The subscription will match events that satisfy all specified selectors.

The matchKind value is a string. The subscription will match events whose kind value exactly matches.

The matchLabels value is a set of label:value pairs. The subscription will match events that have the corresponding labels with their associated values. If a label is missing in the event, or it has a different value, the event will not be matched.

The matchFields value is a set of path:values pairs. The subscription will match events that comply with the specified description.

The matchExpressions and matchFieldExpressions values are lists of expressions.

Expressions are of any of the following three forms:

  • Field or label value in (or not in) a set of values

    {
      "key": "field or label",
      "operator": "In|NotIn",
      "values": ["value1", "value2"]
    }
    
  • Field or label exists (or does not exist)

    {
      "key": "field or label",
      "operator": "Exists|DoesNotExist"
    }
    
  • Field (a list or an object) contains all (or does not contain all) values

    {
      "key": "field only",
      "operator": "ContainsAll|DoesNotContainAll",
      "values": ["value1", "value2"]
    }
    

    (Only applies to fields, as labels values are strings, not composite values)

A list of expressions can contain expressions of different forms.

Subscription examples

This first subscription example is for a service that will receive all ExecutionCommand events:

{
  "apiVersion": "opentestfactory.org/v1",
  "kind": "Subscription",
  "metadata": {
    "name": "subscription name"
  },
  "spec": {
    "subscriber": {
      "endpoint": "https://service.example.com/inbox"
    },
    "selector": {
      "matchKind": "ExecutionCommand"
    }
  }
}

This second example is for a service that will receive ProviderCommand for the myprefix/mytask@v1 function:

{
  "apiVersion": "opentestfactory.org/v1",
  "kind": "Subscription",
  "metadata": {
    "name": "subscription name"
  },
  "spec": {
    "subscriber": {
      "endpoint": "https://service.example.com/inbox"
    },
    "selector": {
      "matchKind": "ProviderCommand",
      "matchLabels": {
        "categoryPrefix": "myprefix",
        "category": "mytask",
        "categoryVersion": "v1"
      }
    }
  }
}

This last example is for a service that will receive ExecutionResult events that have an attachments field:

{
  "apiVersion": "opentestfactory.org/v1",
  "kind": "Subscription",
  "metadata": {
    "name": "postman interpreter",
  },
  "spec": {
    "subscriber": {
      "endpoint": "https://service.example/com/inbox"
    },
    "selector": {
      "matchKind": "ExecutionResult",
      "matchFieldExpressions": [
        {
          "key": "attachments",
          "operator": "Exists"
        }
      ]
    }
  }
}

Unsubscribing

If a subscription is no longer relevant, a service may cancel its subscription:

DELETE /subscriptions/{subscription_id}

The possible return codes are OK (200) and NotFound (404).

Listing active subscriptions

It is possible to list all currently active subscriptions using the following endpoint:

GET /subscriptions

The return code is OK (200). The body of the response is a JSON document with the following elements:

{
    "apiVersion": "v1",
    "kind": "SubscriptionsList",
    "items": [
        // ...
    ]
}

Each item is a subscription message as received by the POST /subscriptions endpoint, complemented by a status section, which contains the following three entries:

  • publicationCount: the number of publications dispatched to this subscriber
  • lastPublicationTimestamp: the last publication dispatch date, in ISO format
  • publicationStatusSummary: an object with one entry per received return code and the number of associated dispatches.

Example of Active subscription details

Here is an example of an item returned by the GET /subscriptions endpoint:

{
    "apiVersion": "opentestfactory.org/v1",
    "kind": "Subscription",
    "metadata": {
        "name": "subscription name",
        "creationTimestamp": "2021-09-01T10:48:49.582383",
        "annotations": {
            // ...
        }
    },
    "spec": {
        "subscriber": {
            "endpoint": "https://service.example.com/inbox"
        },
        "selector": {
            "matchKind": "ExecutionCommand"
        }
    },
    "status": {
        "publicationCount": 12,
        "lastPublicationTimestamp": "2021-09-10T13:28:19.812383",
        "publicationStatusSummary": {
            "200": 10,
            "503": 2
        }
    }
}

It matched 12 publications. 10 were successfully dispatched to the subscriber, and 2 were received with a 503 error code.

Publication

Publications must be valid JSON documents. They may not have a kind or an apiVersion entry.

The body of the request must be the JSON document to dispatch, and the entrypoint is the following:

POST /publications

The possible return codes are OK (200) and BadRequest (400).

If the return code is OK, the message part of the status message is either Publication received, but no matching subscription. or Publication received.

The EventBus does not modify the publication message (the body of the request).

Dispatching

If a subscriber is momentarily unavailable, the event bus may not dispatch the publication to it even if the subscriber comes back to life at a later time. But other available subscribers will receive it.

Due to the asynchronous and distributed nature of the event bus, some publications may be dispatched more than once to a subscriber. Each publication contains a unique X-Publication-ID header that can be used to detect duplicated publications.

Additionally, each publication dispatched to a service contains an X-Subscription-ID header which is the corresponding subscription ID.