Skip to main content

CDEvents in Action – Leveraging the Spec for Pipeline Notifications and Approvals

By December 5, 2023Blog, Member, Project

Contributed by Nicholas Lotz, Harness

Platform engineering teams steadily adopted sets of common tools as the field matures. Still there remains a tension between using broad open source offerings and proprietary solutions. Amid this push and pull, the ecosystem has come to recognize a “modern continuous delivery stack” that includes the following:

  • Kubernetes and container orchestration
  • Semantic versioning
  • RESTful API’s
  • Oauth for access delegation
  • … and so on

We expect each of our vendors to interoperate with most of the above technologies. They’ve become standards we expect in our software toolchains.

In this blog, I discuss the value of adding CD events to our toolkit of adopted standards. I’ll then show a practical use case for the CDEvents specification in a modern CD pipeline. 

Why a common spec for CD events?

Platform teams increasingly use development methodologies like declarative GitOps to manage deployments. Event-driven architectures are also gaining importance in cloud native software. However most vendors today are proprietary in the way they record CD events like:

  • Artifact creation
  • Environment and service creation
  • New pull request, or a reverted commit 
  • Automated test outputs

Yet teams practicing continuous deployment heavily rely on event handling. For example, an “artifact is created/updated” event is a typical trigger between CI and CD.

Your continuous integration builds and tests your application, and then publishes the resulting artifact. If you’re practicing pure CD, the arrival of the new artifact might automatically trigger an updated deployment. Or, at least, the publish event might trigger a successful build notification.

It’s a problem though when organizations use disparate systems for build, test, and deploy. There isn’t inherent interoperability between the events offered by a package repository vendor and a CD vendor. 

The CD Foundation is effecting change in this area by developing a standard for CD events. The CDEvents specification seeks to solve two problems that plague platform engineers.

  1. Interoperability. CD tools should be able to send events to each other without layers of translation middleware. A single event payload can be passed to multiple sinks.
  2. Observability. Teams should be able to unify monitoring and reporting tools to gain visibility into the entire end-to-end CI/CD lifecycle. 

To this end, the CDEvents spec introduces a common vocabulary and grammar for CD events. It also defines events in each of six categories:

Event CategoryDescriptionExample Event
Core EventsEvents fundamental to pipeline orchestrationpipelineRun started
Source Code Control EventsEvents related to source code managementchange merged
Continuous Integration EventsEvents related to CI activities like build and testbuild finished
Testing EventsEvents related to manual and automated testingtestSuiteRun started
Continuous Deployment EventsEvents related to deploying services to target environmentsservice deployed
Continuous Operations EventsEvents related to operating deployed services in their environmentsincident reported

For example, starting a pipeline run might trigger an event with the following JSON payload:

{
  "context": {
    "version": "0.3.0",
    "id": "271069a8-fc18-44f1-b38f-9d70a1695819",
    "source": "/event/source/123",
    "type": "dev.cdevents.pipelinerun.started.0.1.1",
    "timestamp": "2023-03-20T14:27:05.315384Z"
  },
  "subject": {
    "id": "mySubject123",
    "source": "/event/source/123",
    "type": "pipelineRun",
    "content": {
      "pipelineName": "myPipeline",
      "url": "https://www.example.com/mySubject123"
    }
  }
}

In the CDEvents spec, each event record includes unique context metadata along with a subject documenting the event entity itself. 

Let’s look at an example of mapping a CD tool’s events to the CDEvents spec.

Example: CDEvents in a CD Pipeline

Consider a pipeline that deploys a simple Kubernetes service called Guestbook. Don’t worry about the details of the application and deployment mechanics in this example. Along with the deployment stage, the pipeline also sends up to two notification webhooks to Slack:

  • When a user triggers the pipeline, prompting for an admin’s approval, and
  • Details on the pipeline run’s success or failure

In each relevant pipeline stage – Approval Request, CD Failure notification, CD Success notification – we send an event per the CDEvents spec to a handling function. Where the function is hosted doesn’t particularly matter; a cloud lambda would be a good candidate.  The handling function parses the event JSON, and then sends a human-readable message via Slack webhook. 

How do we then map to the CDEvents spec? While many CD platforms do not yet natively support the CDEvents spec, we can use variables to represent most CDEvent context and subject attributes in our event payload. 

For example, the first stage in our pipeline, Approval Request, takes place when a user first tries triggering the pipeline. This is the core pipelineRun started CDEvent. Take a look at the attributes in the CD Foundation’s example. Then in a pipeline configuration, most of the attribute values can be built-in variables native to your CD platform. The example below is a pipeline config and variables in Harness CD.

- step:

         type: Http

         name: Approval Request

         identifier: Approval_Request

         spec:

             url: <URL of handling function>

             method: POST

             headers:

                 - key: Content-type

                   value: application/json

             requestBody:

                 {

                     "context": {

                       "version": "0.3.0",

                       "id": "<+pipeline.executionId>",

                       "source": "<+pipeline.repo>”

                       "type": "dev.cdevents.pipelinerun.started.0.1.1",

                       "timestamp": "<+pipeline.startTs>"

                     },

                     "subject": {

                       "id": "<+pipeline.identifier>/<+pipeline.executionId>",

                       "source": "<+pipeline.repo>",

                       "type": "pipelineRun",

                       "content": {

                         "pipelineName": "<+pipeline.name>",

                         "url": "<+pipeline.executionUrl>" }}}

Our handling function parses the JSON payload and uses a couple of the event attributes in a bot-sent approval request to a Slack channel. 

Similarly, the end of our pipeline runs one of two conditional events: a success Slack notification if the pipeline succeeds, and a failure Slack notification if the pipeline fails. This maps well to the pipelineRun finished CDEvent. The event payload in our pipeline config can also include reference variables for its attributes.

   requestBody:
                 {
                     "context": {
                       "version": "0.3.0",
                       "id": "<+pipeline.executionId>",
                       "source": "<+pipeline.repo>”
                       "type": "dev.cdevents.pipelinerun.finished.0.1.1",
                       "timestamp": "<+pipeline.stage.slack_notify_cd_success.startTS>"
                     },
                     "subject": {
                       "id": "<+pipeline.identifier>/<+pipeline.executionId>",
                       "source": "<+pipeline.repo>",
                       "type": "pipelineRun",
                       "content": {
                         "pipelineName": "<+pipeline.name>",
                         "url": "<+pipeline.executionUrl>",
                         "outcome": "<+pipeline.stages.deploy_guestbook.status>", 
                         "errors": "<+pipeline.stages.deploy_guestbook.errorSummary>"}}}

The pipeline step passes the payload to be parsed by our handling function. The function then notifies of the pipeline run’s success or failure in Slack. 

Looking ahead: Toward greater CD Events adoption

Just the two events from the example show the utility of adopting a CD event standard. While this article only showcases a single integration (Slack), we can use the same handlers to pass our events to an arbitrary number of sinks, such as monitoring or logging platforms.

The CDEvents specification ensures our CD workflow passes consistent, useful information for any handlers or event sinks. We encourage you to continue exploring CDEvents as well as other CDF-hosted projects

We also encourage you to read the CDEvents Whitepaper (PDF).