mirror of https://github.com/knative/docs.git
283 lines
7.3 KiB
Markdown
283 lines
7.3 KiB
Markdown
# Writing an event source using JavaScript
|
|
|
|
This tutorial provides instructions to build an event source in JavaScript and implement it with a ContainerSource or SinkBinding.
|
|
|
|
ContainerSource and SinkBinding both work by injecting environment variables to an application. Injected environment variables at minimum contain the URL of a sink that receives events.
|
|
|
|
## Bootstrapping
|
|
|
|
Create the project and add the dependencies:
|
|
|
|
```bash
|
|
npm init -y
|
|
npm install cloudevents got --save
|
|
```
|
|
|
|
## Using ContainerSource
|
|
|
|
A ContainerSource creates a container for your event source image and manages this container.
|
|
|
|
The sink URL where events are posted will be made available to the application through the `K_SINK` environment variable by the ContainerSource.
|
|
|
|
### Example
|
|
|
|
The following example event source emits an event to the sink every second:
|
|
|
|
```javascript
|
|
// File - index.js
|
|
const { CloudEvent, Emitter, emitterFor, httpTransport } = require('cloudevents');
|
|
|
|
const K_SINK = process.env['K_SINK'];
|
|
K_SINK || logExit('Error: K_SINK Environment variable is not defined');
|
|
console.log(`Sink URL is ${K_SINK}`);
|
|
|
|
const source = 'urn:event:from:heartbeat/example';
|
|
const type = 'heartbeat.example';
|
|
|
|
let eventIndex = 0;
|
|
setInterval(() => {
|
|
console.log(`Emitting event # ${++eventIndex}`);
|
|
|
|
// Create a new CloudEvent each second
|
|
const event = new CloudEvent({ source, type, data: {'hello': `World # ${eventIndex}`} });
|
|
|
|
// Emits the 'cloudevent' Node.js event application-wide
|
|
event.emit();
|
|
}, 1000);
|
|
|
|
// Create a function that can post an event
|
|
const emit = emitterFor(httpTransport(K_SINK));
|
|
|
|
// Send the CloudEvent any time a Node.js 'cloudevent' event is emitted
|
|
Emitter.on('cloudevent', emit);
|
|
|
|
registerGracefulExit();
|
|
|
|
function registerGracefulExit() {
|
|
process.on('exit', logExit);
|
|
//catches ctrl+c event
|
|
process.on('SIGINT', logExit);
|
|
process.on('SIGTERM', logExit);
|
|
// catches 'kill pid' (for example: nodemon restart)
|
|
process.on('SIGUSR1', logExit);
|
|
process.on('SIGUSR2', logExit);
|
|
}
|
|
|
|
function logExit(message = 'Exiting...') {
|
|
// Handle graceful exit
|
|
process.stdout.write(`${message}\n`);
|
|
process.exit();
|
|
}
|
|
```
|
|
|
|
```dockerfile
|
|
# File - Dockerfile
|
|
|
|
FROM node:16
|
|
WORKDIR /usr/src/app
|
|
COPY package*.json ./
|
|
RUN npm install
|
|
COPY . .
|
|
EXPOSE 8080
|
|
CMD [ "node", "index.js" ]
|
|
```
|
|
|
|
### Procedure
|
|
|
|
Before publishing the ContainerSource, you must build the application
|
|
image, and push it to a container registry that your cluster can access.
|
|
|
|
1. Build and push the image:
|
|
|
|
```bash
|
|
export REGISTRY=docker.io/myregistry
|
|
|
|
# Build and push the container on your local machine.
|
|
docker buildx build --platform linux/arm64,linux/amd64 -t "$REGISTRY/node-heartbeat-source:v1" --push .
|
|
```
|
|
|
|
1. Create the event display service which logs any CloudEvents posted to it:
|
|
|
|
```yaml
|
|
apiVersion: serving.knative.dev/v1
|
|
kind: Service
|
|
metadata:
|
|
name: event-display
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- image: gcr.io/knative-releases/knative.dev/eventing-contrib/cmd/event_display
|
|
```
|
|
|
|
1. Create the ContainerSource object:
|
|
|
|
```yaml
|
|
apiVersion: sources.knative.dev/v1
|
|
kind: ContainerSource
|
|
metadata:
|
|
name: heartbeat-source
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- image: docker.io/myregistry/node-heartbeat-source:v1
|
|
name: heartbeats
|
|
sink:
|
|
ref:
|
|
apiVersion: serving.knative.dev/v1
|
|
kind: Service
|
|
name: event-display
|
|
```
|
|
|
|
1. Check the logs of the `event-display` Service. You can observe that a new message is pushed every second:
|
|
|
|
```bash
|
|
$ kubectl logs -l serving.knative.dev/service=event-display -c user-container
|
|
```
|
|
|
|
```bash
|
|
☁️ cloudevents.Event
|
|
Validation: valid
|
|
Context Attributes,
|
|
specversion: 1.0
|
|
type: heartbeat.example
|
|
source: urn:event:from:heartbeat/example
|
|
id: 47e69d34-def7-449b-8382-3652495f9163
|
|
datacontenttype: application/json
|
|
Data,
|
|
{
|
|
"hello": "World 1"
|
|
}
|
|
```
|
|
|
|
1. Optional: If you are interested in seeing what is injected into the event source as a `K_SINK`, you can check the logs:
|
|
|
|
```bash
|
|
$ kubectl logs heartbeat-source-7575c888c7-85w5t
|
|
```
|
|
|
|
```bash
|
|
Sink URL is http://event-display.default.svc.cluster.local
|
|
Emitting event #1
|
|
Emitting event #2
|
|
Event posted successfully
|
|
Event posted successfully
|
|
```
|
|
|
|
## Using SinkBinding
|
|
|
|
SinkBinding does not create any containers. It injects the sink information to an already existing Kubernetes resources. This is a flexible approach as you can use any Kubernetes PodSpecable object as an event source, such as Deployment, Job, or Knative services.
|
|
|
|
### Procedure
|
|
|
|
1. Create the `event-display` service:
|
|
|
|
```yaml
|
|
apiVersion: serving.knative.dev/v1
|
|
kind: Service
|
|
metadata:
|
|
name: event-display
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- image: gcr.io/knative-releases/knative.dev/eventing-contrib/cmd/event_display
|
|
```
|
|
|
|
1. Create a Kubernetes Deployment that runs the event source:
|
|
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: node-heartbeats-deployment
|
|
labels:
|
|
app: node-heartbeats
|
|
spec:
|
|
replicas: 2
|
|
selector:
|
|
matchLabels:
|
|
app: node-heartbeats
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: node-heartbeats
|
|
spec:
|
|
containers:
|
|
- name: node-heartbeats
|
|
image: docker.io/myregistry/node-heartbeat-source:v1
|
|
ports:
|
|
- containerPort: 8080
|
|
```
|
|
|
|
1. Because the SinkBinding has not yet been created, you will see an error message, because the `K_SINK` environment variable is not yet injected:
|
|
|
|
```bash
|
|
$ kubectl logs node-heartbeats-deployment-9ffbb644b-llkzk
|
|
```
|
|
|
|
```bash
|
|
Sink URL is undefined
|
|
Emitting event #1
|
|
Error during event post
|
|
TypeError [ERR_INVALID_ARG_TYPE]: The "url" argument must be of type string. Received type undefined
|
|
```
|
|
|
|
1. Create the SinkBinding object:
|
|
|
|
```yaml
|
|
apiVersion: sources.knative.dev/v1
|
|
kind: SinkBinding
|
|
metadata:
|
|
name: bind-node-heartbeat
|
|
spec:
|
|
subject:
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
selector:
|
|
matchLabels:
|
|
app: node-heartbeats
|
|
sink:
|
|
ref:
|
|
apiVersion: serving.knative.dev/v1
|
|
kind: Service
|
|
name: event-display
|
|
```
|
|
|
|
Observe that the Pods are recreated and this time the `K_SINK` environment variable is injected.
|
|
|
|
Since the `replicas` is set to 2, there are 2 pods that are posting events to the sink:
|
|
|
|
```bash
|
|
$ kubectl logs event-display-dpplv-deployment-67c9949cf9-bvjvk -c user-container
|
|
```
|
|
|
|
```bash
|
|
☁️ cloudevents.Event
|
|
Validation: valid
|
|
Context Attributes,
|
|
specversion: 1.0
|
|
type: heartbeat.example
|
|
source: urn:event:from:heartbeat/example
|
|
id: 47e69d34-def7-449b-8382-3652495f9163
|
|
datacontenttype: application/json
|
|
Data,
|
|
{
|
|
"hello": "World 1"
|
|
}
|
|
|
|
☁️ cloudevents.Event
|
|
Validation: valid
|
|
Context Attributes,
|
|
specversion: 1.0
|
|
type: heartbeat.example
|
|
source: urn:event:from:heartbeat/example
|
|
id: 47e69d34-def7-449b-8382-3652495f9163
|
|
datacontenttype: application/json
|
|
Data,
|
|
{
|
|
"hello": "World 1"
|
|
}
|
|
```
|