Skip to content

Commit

Permalink
Add support for CDEvents custom events
Browse files Browse the repository at this point in the history
As previously discussed in https://hackmd.io/LftfRirGRbKuAcLg9pdOag,
this introduces support for custom events:

- add a schemaURI (cdevents#184)
- add dev.cdeventsx types in spec.md
- add a custom folder which includes
  - README.md with context about custom events
  - registry.md to register external specs
  - schema.json - a common schema for custom events

Fixes: cdevents#168

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
  • Loading branch information
afrittoli committed Feb 22, 2024
1 parent ab73ef7 commit 7aa7f37
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .spellcheck-en-custom.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ README
SBOM
SBOMs
SCM
SDLC
SDK
SIG
SRE
Tekton
Expand Down Expand Up @@ -52,6 +54,7 @@ img
interoperable
jenkins
json
jsonschema
lifecycle
markdownlint
md
Expand All @@ -75,6 +78,7 @@ rfc
rolledback
runtime
sbom
schemauri
somewherelse
specversion
src
Expand All @@ -92,6 +96,7 @@ testsuiterun
ticketURI
toc
typesystem
unparsed
uri
url
utf
Expand Down
4 changes: 2 additions & 2 deletions .spellcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ matrix:
escapes: '\\[\\`~]'
delimiters:
# Ignore multiline content between fences (fences can have 3 or more back ticks)
# ```
# ```language
# content
# ```
- open: '(?s)^(?P<open> *`{3,})$'
- open: '(?s)^(?P<open> *`{3,}).*$'
close: '^(?P=open)$'
# Ignore text between inline back ticks
- open: '(?P<open>`+)'
Expand Down
149 changes: 149 additions & 0 deletions custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Custom Event Types

## Introduction

CDEvents brings standardization for events consumed and produced by tools in the software development life-cycle (SDLC).
Some of the tools that are candidate for CDEvents adoption already produce events, in their own specific formats.
Some of these events exist or can be mapped to events available in the CDEvents specification.
Some events might be added to CDEvents, if they're considered for interoperability by the CDEvents community.
Some events however, are very specific to a tool or are not relevant from an interoperability point of view.

Custom event types exist as a mean to make it easier for tools to adopt CDEvents and provide event producers and consumer with a consistent way to produce and consume events aligned to the CDEvents specification.

To ensure interoperability, tools should use events available in the CDEvents specification as much as possible. Missing events can be proposed to the CDEvents community and included in future releases. Custom events are meant for events that are strictly tool specific and thus not good candidates for CDEvents.

Custom events can be used as an interim solution until new events are included in the CDEvents specification. When considering this option, please note that the migration from custom events to CDEvents may be disruptive for both event consumers and producers. Thus it is not recommended to use interim custom events at a large scale.

### Specification

The following features of the specification are related to custom events:

- **The `dev.cdeventsx` event-type namespace**: This namespace is reserved for events that are compliant with the CDEvents specification, whose subject structure and semantics are defined outside of CDEvents.
- **The [`schemaURI`](/spec.md#schemauri) property in the `context`**: events may supply their schema URI via this new `context` property. Events must **always** validate against the CDEvents official schema too.
- **The [`dev.cdeventsx` jsonschema](schema.json)**: any event of type `dev.cdeventsx.*` must respect this schema as well as any additional schema supplied via `schemaURI`
- **The `subject` format for `dev.cdeventsx` events**: subjects must be in the format `<tool-name>-<subject-name>` to avoid event-type conflicts across tools
- **The [registry](registry.md)**: maintainers of the specifications of CDEvents custom events are encouraged to add their specification to the shared [registry](registry.md)

### Versioning

Similar to regular CDEvents, custom CDEvents include two versions:

- The specification version: this indicates the base CDEvents-defined [schema](schema.json) that is adopted by the event.
- The event version: this must follow the [semantic versioning approach of CDEvents][cdevents-versions]. Changes of versions for custom CDEvents are decoupled from CDEvents releases. When a custom CDEvents adopts a new version of the CDEvents spec, the event version must be updated as the change in spec version corresponds to a change in the schema of the event.

### SDKs

When consuming (parsing) an event with `context.schemaURI`, SDKs MUST fetch the schema defined in `context.schemaURI` and **additionally** validate the event against the supplied schema.

When consuming (parsing) an event with `dev.cdeventsx` type, the SDKs will return a object that is identical in structure for all events, and which include an unparsed blob for the `subject.content` part. SDKs MAY provide a way for users to register a function to be used to parse the `subject.content` of these messages.

When producing events, the SDK MAY provide a way for users to register additional functions to be used to render `dev.cdeventsx` events as `JSON` and `CloudEvents`.

## Transitioning Custom CDEvents to Standard CDEvents

In certain cases, custom events may be implemented as a stopgap solution to allow for faster CDEvents adoption. The custom events may be eventually make its way into the CDEvents specification, with a structure that could be different from the original one defined in the custom events.

This situation is not dissimilar from the process of adopting a new, backwards incompatible version of an existing event. Once the new CDEvent is introduced in the spec, newer versions of the SDKs will be able to produce it and consume it.

### Promoting a Custom CDEvent to Core CDEvent

To create a new CDEvent, start with the [guide](https://cdevents.dev/docs/primer/#adding-new-event-types) available in the CDEvents primer. Use cases and experiences from using the custom event would be a great addition to the proposal:

- Start proposing the new CDEvent in a GitHub issue. Attach relevant use cases and context from the corresponding custom event. Highlight why the event would be beneficial from interoperability point of view
- (Optional) Join one of the CDEvents working groups to present your proposal. This can also happen asynchronously if the working group schedule is not convenient
- Create a PR that adds the new event. You may search in the recent PR history for other similar PRs to guide you in the process. Promote your PR at the working groups and/or on Slack to obtain feedback. If struggling with the CI jobs, ask on slack for help
- Once the PR is merged, the new event will be available in the spec and in the SDKs with their next release. The release of the SDKs may happen some time after the release of the specification

### Handling Heterogeneous Producers and Consumers

It is safe to assume that several producers and consumers of the event exist. At some point in time only a fraction of producers and consumers will have adopted the new SDK.

Consumers with the latest SDK will be able to parse the old custom event as well as the new CDEvent. This means that there is no pressure for producers to adopt the new version, even after all consumers have been updated.

Consumers with the old SDK however, won't be able to parse the new CDEvents.
This means that producers that adopt the new SDK may have to consider producing both the old and the new events, until all consumers have update to the latest SDK.
In this case, it is responsibility of the system architect to ensure that old events are only sent to legacy consumers, to avoid the case of consumers receiving duplicate events.


## CDEvents and Links

The CDEvents community is working on the introduction of [links](https://github.com/cdevents/spec/pull/139) to the specification. Links will let CDEvents producer connect one event to others with specific semantics, and will help consumers trace through events to understand complete workflows.

Custom CDEvents can be linked like any other CDEvent, so links may exist between custom CDEvents as well as between a custom CDEvent and a normal CDEvent.

## Example of Custom Event

The following example shows how an existing event can be adapted into a CDEvents custom event.
It's based on an operational event of the [Harbor registry][harbor-docs], which would not be a good fit for CDEvents.

Original Harbor event:

```jsonld=
{
"specversion": "1.0",
"id": "81f243ce-699c-44d6-9dbe-b2ee5f10237a",
"requestid": "4b9dcf9a-db23-460c-9b52-c9d994e362ee",
"source": "/projects/2/webhook/policies/15",
"type": "harbor.quota.exceeded",
"datacontenttype": "application/json",
"time": "2023-04-03T07:04:44Z",
"data": {
"resources": [
{
"digest": "sha256:402d21757a03a114d273bbe372fa4b9eca567e8b6c332fa7ebf982b902207242"
}
],
"repository": {
"name": "alpine",
"namespace": "harbor",
"repo_full_name": "harbor/alpine",
"repo_type": "private"
},
"custom_attributes": {
"Details": "adding 2.1 MiB of storage resource, which when updated to current usage of 8.3 MiB will exceed the configured upper limit of 10.0 MiB."
}
},
"operator": ""
}
```

Corresponding CDEvent using a `dev.cdeventsx.*` type:

```jsonld=
{
"context": {
"version": "0.1.0",
"id": "271069a8-fc18-44f1-b38f-9d70a1695819",
"source": "/harbor/alpine",
"type": "dev.cdeventsx.harbor-quota.exceeded.0.1.0",
"timestamp": "2023-03-20T14:27:05.315384Z",
"schemaURI": "https://goharbor.io/cdeventsx/schema/harbor-quota/exceeded/0_1_0"
},
"subject": {
"id": "/projects/2/webhook/policies/15",
"source": "/harbor/alpine",
"type": "quota",
"content": {
"operator": "",
"resources": [
{
"digest": "sha256:402d21757a03a114d273bbe372fa4b9eca567e8b6c332fa7ebf982b902207242"
}
],
"repository": {
"name": "alpine",
"namespace": "harbor",
"repo_full_name": "harbor/alpine",
"repo_type": "private"
},
}
},
"customData": {
"requestid": "4b9dcf9a-db23-460c-9b52-c9d994e362ee",
"details": "adding 2.1 MiB of storage resource, which when updated to current usage of 8.3 MiB will exceed the configured upper limit of 10.0 MiB."
}
}
```

[harbor-docs]: https://goharbor.io/docs/2.10.0/working-with-projects/project-configuration/configure-webhooks/
[cdevents-versions]: https://cdevents.dev/docs/primer/#versioning-of-cdevents
6 changes: 6 additions & 0 deletions custom/registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Custom Events Registry

This registry contains a list of known specification of `dev.cdeventsx` custom CDEvents.

| Name | URL | Description |
| ---- | --- | ----------- |
93 changes: 93 additions & 0 deletions custom/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cdevents.dev/0.4.0-draft/schema/custom",
"properties": {
"context": {
"properties": {
"version": {
"type": "string",
"minLength": 1
},
"id": {
"type": "string",
"minLength": 1
},
"source": {
"type": "string",
"minLength": 1,
"format": "uri-reference"
},
"type": {
"type": "string",
"pattern": "^dev.cdeventsx.[a-zA-Z0-9]+-[a-zA-Z]+.[a-zA-Z]+$"
},
"timestamp": {
"type": "string",
"format": "date-time"
},
"schemaURI": {
"type": "string",
"minLength": 1,
"format": "uri"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"version",
"id",
"source",
"type",
"timestamp"
]
},
"subject": {
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"source": {
"type": "string",
"minLength": 1,
"format": "uri-reference"
},
"type": {
"type": "string",
"pattern": "^[a-zA-Z0-9]+-[a-zA-Z]+$"
},
"content": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": false,
"type": "object",
"required": [
"id",
"type",
"content"
]
},
"customData": {
"oneOf": [
{
"type": "object"
},
{
"type": "string",
"contentEncoding": "base64"
}
]
},
"customDataContentType": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"context",
"subject"
]
}
13 changes: 12 additions & 1 deletion spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,18 +192,29 @@ defined in the [vocabulary](#vocabulary):
- Type: [`String`][typesystem]
- Description: defines the type of event, as combination of a *subject*,
*predicate* and *version*. Valid event types are defined in the [vocabulary](#vocabulary).

All event types should be prefixed with `dev.cdevents.`. One occurrence may
have multiple events associated, as long as they have different event types.
*Versions* are semantic in the *major.minor.patch* format. For more details about versions
see the the see [versioning](https://cdevents.dev/docs/primer/#versioning) documentation.

In addition to `dev.cdevents.`, event types prefixed with `dev.cdeventsx.` can be defined in
specifications outside of CDEvents. Events that use these event types can be partly produced
and validated by CDEvents SDKs and are known a ["custom events"](custom/README.md).
External specifications can be registered in this repository in the
[custom events registry](custom/registry.md).

- Constraints:
- REQUIRED
- MUST be defined in the [vocabulary](#vocabulary)
- `dev.cdevents.` types MUST be defined in the [vocabulary](#vocabulary)
- `dev.cdeventsx.` types SHOULD be defined in a third party specification

- Examples:
- `dev.cdevents.taskrun.started`
- `dev.cdevents.environment.created`
- `dev.cdevents.<subject>.<predicate>`
- `dev.cdevents.mytool-process.finished`
- `dev.cdeventsx.<tool>-<subject>.<predicate>`

#### source (context)

Expand Down

0 comments on commit 7aa7f37

Please sign in to comment.