Event-Driven Automation with Project Harbor and Knative
Webhooks everywhere
Webhooks are very popular today. In a nutshell, webhooks are nothing but HTTP servers accepting HTTP
requests. These requests are typically events. An event is an immutable fact of something that has happened. Therefore, events are normally written in past tense. Most webhooks accept JSON-encoded events.
Harbor is a prominent open-source container registry. If you never heard of this project, check out my other posts (#harbor). Luckily, Harbor also supports the webhook standard.
Let’s take the example of someone deleting an image from the registry. Harbor generates the following DELETE_ARTIFACT
event:
|
|
Source: Harbor Documentation
Events like the above are related to a specific Harbor project.
A project in Harbor contains all repositories of an application. Images cannot be pushed to Harbor before a project is created. Role-Based Access Control (RBAC) is applied to projects, so that only users with the appropriate roles can perform certain operations.
Source: Harbor Documentation
Let’s stick with the example provided. The events sent are very detailed and thus useful. They can be used for ChatOps for example. This is where I came up with the idea to forward the Harbor event to a notification function running on the VMware Event Broker Appliance - VEBA.
VEBA Generic Webhook Endpoint and Webhook Functions
VEBA supports a generic webhook endpoint since version v0.7.0. The webhook endpoint can be enabled during the OVA deployment and is available on https://<veba-fqdn>/webhook
afterwards. It accepts incoming CloudEvents as payload.
The challenge is, and William tackled it in THIS POST, that not every solution supports the CloudEvents specification.
Although the idea behind CloudEvents is simple and powerful. If there’s no default description/definition for event data, event handling logics have to be developed again and again!
I hope this will change …
π― callout and request from @n3wscott to stop reinventing event integrations, eg alerts from your_thingy to Slack, S3, etc.
— Michael Gasch π©πͺπΊπ¦ (@embano1) May 19, 2022
Instead reuse transport-independent standards like @CloudEventsIO so routing becomes config, not code. pic.twitter.com/moZ96Q7bEq
Session from the tweet: “Thinking Cloud Native, CloudEvents Future - Scott Nichols, Chainguard” at KubeCon 2022 Europe 👇 Resources.
For my specific use case with Harbor, I checked the open issue’s on Github to see if someone had already raised the idea of adopting CloudEvents.
Luckily, issue #10146 brings the idea up. Give it a :thumbs_up: 😉
However, in a brainstorming session with Michael, I got him hooked and he wrote a complete new webhook function which transforms the incoming Harbor events into CloudEvents 💥.
We created a @KnativeProject web service as a target for @project_harbor webhooks. Harbor event notifications are automatically transformed to CloudEvents.
— Michael Gasch π©πͺπΊπ¦ (@embano1) June 27, 2022
So many great use cases! Thx to @vmw_rguske for the idea and collab!
Source: https://t.co/ix2pyfgaAJ
The Use Case with Harbor
The overall idea is to send the non-CloudEvent payload to the new kn-go-harbor-webhook
function, get it transformed into a CloudEvent and have the new kn-ps-harbor-slack function triggered.
For the used example above, the new event type
will be com.vmware.harbor.delete_artifact.v0
.
The following diagram depicts the end-to-end flow of the deployment.
🍒 = Existing VEBA Function Examples
In order to not only have a “transformation” function available but also a function which will be triggered on a new Harbor specific event, I simply took the existing Slack-Function example, adjusted the necessary fields
whithin the handler.ps1
accordingly in order to ultimately pick the wanted data from the incoming event.
VoilΓ , kn-ps-harbor-slack is ready to serve.
Deploying the new Harbor Functions
Let me guide you through the necessary steps in order to:
- have a properly prepared and configured environment (DNS, VEBA, Harbor) available
- have the kn-go-harbor-webhook function deployed
- have the kn-ps-harbor-slack function deployed
DNS Wildcard Configuration for VEBA
The first required step is to configure your DNS server with a wildcard entry for VEBA. This is necessary because when you’re going to deploy functions to VEBA, they’ll ultimately run as Pods on Kubernetes, or in Knative terminology as Knative Services (serving.knative.dev/service
).
Those Services are providing endpoints for which we have to make sure that they are reachable over the wire.
It is presented as follows: https://[function-name].[function-namespace].[veba-fqdn]
.
Since we are talking about Kubernetes Custom Resources, we can interact with such using kubectl
. In order to e.g. check your functions, run kubectl -n vmware-functions get ksvc
.
I’m using the Knative CLI for it because it’ll add an additional column to the output which is CONDITIONS
. It’s always good to check those.
|
|
Configuring a DNS wildcard entry on a Windows Server is made easy. Open the DNS Manager, right-click on your Forward Lookup Zone and select New Host (A or AAA). For Name enter *.<VEBA-Hostname>
. This should automatically create a FQDN like *.veba-dev.jarvis.tanzu
in my example. Last is the IP address of your VEBA.
William is explaining the necessary configurations on his blog when using Unbound on Ubuntu - LINK.
Deploy the kn-go-harbor-webhook Function
Deploying functions on VEBA is simple. Well documented guidance for each specific example function is available on .
If necessary, the first step is in most cases the creation of a Kubernetes secret in order to store sensible data (user credentials, etc.). Having said, we are going to create a new secret called webhook-auth
for our function in order to enforce basic authentication on the HTTP endpoint.
Create the webhook-auth
secret
|
|
Checking the secret to see if the given values have been passed.
|
|
Let’s quickly decrypt the value for password
:
|
|
Compliant :thumbs_up:
Create a SinkBinding
A Knative SinkBinding
decouples the event producer ("subject
", i.e. kn-go-harbor-webhook) from the receiver ("sink
", i.e. VEBA broker). With the following SinkBinding
, Knative injects the address of the VEBA broker to the function.
Create the SinkBinding
:
|
|
When checking the deployed object by executing kubectl -n vmware-functions get sinkbinding
, you’ll probably notice the info REASON: SubjectMissing
. This is normal since it is waiting for the actual function deployment.
Deploy the kn-go-harbor-webhook function
The final step now is the deployment of the kn-go-harbor-webhook
function itself.
|
|
Conditions and state of the deployment can be checked as already described using kubectl
or kn
.
|
|
Also, checking the logs tells you a lot.
|
|
Well, everything is in good condition and the function is ready to receive and transform Harbor notification events.
Configure the Webhook Feature in Harbor
In Harbor, open your project and go to Webhooks. Click on + NEW WEBHOOK and configure it to meet your requirements (e.g. in terms of event types). Important is the Endpoint URL as well as the Auth Header.
For Endpoint URL, enter the complete URL ending with /webhook
of the recently created kn-go-harbor-webhook function/Knative Service. For me it’s: https://kn-go-harbor-webhook.vmware-functions.veba-dev.jarvis.tanzu/webhook
For Auth Header, enter Basic
followed by a whitespace and the base64-encoded value of the <username>:<password>
string combination defined in the webhook-auth
secret step. Use a Basic Auth Header Generator like e.g. DebugBear.
The Webhook config should show up as shown in Firgue III below:
Verifying the Configuration
Verifying if everything works as expected can be done by e.g. push
ing an image to your project (repo) or e.g. delete
ing an existing one. For the sake of my example, I deleted an existing image (artifact
) and checked the logs of the user-container
, which runs inside the kn-ps-harbor-slack-00001-deployment-xxx-xxx
pod.
|
|
A beautified view is available in VEBA’s provided event viewer via https://<veba-fqdn>/events
.
Awesome! 🚀
🎥 kn-go-harbor-webhook function deployment 🎥
Deploy the kn-ps-harbor-slack Function
With the available CloudEvent payload, we can now configure and deploy other functions. As aforementioned (low hanging fruits), I wanted to get notified by actions happened related to my Harbor project (#ChatOps). The kn-ps-harbor-slack function will send a message to your Slack channel webhook.
There we go again… webhooks everywhere 😄
- Add your Slack channel webhook URL to the slack_secret.json file
- Create the Kubernetes secret
|
|
- Deploy the
function.yaml
By default, the function deployment will filter on the com.vmware.harbor.push_artifact.v0
Harbor Event. If you wish to change this, update the type
field within function.yaml
to the desired event type. A list of supported notification events is available on the official Harbor documentation under Configure Webhook Notifications. As mentioned, use the VEBA event viewer endpoint (https://<veba-fqdn>/events
) to display all incoming events.
|
|
When you are going to e.g. push
or delete
an artifact, a new message should show up in your Slack channel, like the provided example in Firgure V.
🎥 kn-ps-harbor-slack function deployment 🎥
As a side note: Harbor supports Slack as a configurable Notify Type OOB. But it sends the message in a raw JSON format. However, we also have a Microsoft Teams Function example available in PowerShell as well … 😉
Big THANKS to Michael for the great work on the kn-go-harbor-webhook function as well as for his review of this post.