Technical Documentation: serverless/gateway
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`README.md`)
๐ฆ Archived - This repository is archived and preserved for reference only. No updates, issues, or pull requests will be accepted. If you have questions, please reach out to our support team.
We're Hiring! โข Website โข Slack โข Newsletter โข Forum โข Meetups โข Twitter
The Event Gateway combines both API Gateway and Pub/Sub functionality into a single event-driven experience. It's
dataflow for event-driven, serverless architectures. It routes Events (data) to Functions (serverless compute).
Everything it cares about is an event! Even calling a function. It makes it easy to share events across different
systems, teams and organizations!
Use the Event Gateway right now, by running the Event Gateway Getting Started Application with the Serverless Framework.
Features:
- Platform agnostic - All your cloud services are now compatible with one another: share cross-cloud functions and events with AWS Lambda, Microsoft Azure, IBM Cloud and Google Cloud Platform.
- Send events from any cloud - Data streams in your application become events. Centralize events from any cloud provider to get a birdโs eye view of all the data flowing through your cloud.
- React to cross-cloud events - You arenโt locked in to events and functions being on the same provider: Any event, on any cloud, can trigger any function. Set events and functions up like dominoes and watch them fall.
- First-class support for CloudEvents - Emit and react to events in CloudEvents standard.
- Expose events to your team - Share events and functions to other parts of the application. Your teammates can find them and utilize them in their own services.
- Extendable through middleware - Perform data transforms, authorizations, serializations, and other custom computes straight from the Event Gateway.
The Event Gateway is a L7 proxy and realtime dataflow engine, intended for use with Functions-as-a-Service on AWS,
Azure, Google & IBM.
The project is under heavy development. The APIs will continue to change until we release a 1.0.0 version. It's not
yet ready for production applications.
Contents
- Quick Start
- Running the Event Gateway
- Motivation
- Components
- CloudEvents Support
- SDKs
- Versioning
- FAQ
- Background
- Community
Reference
- API
- Event Types
- Subscription Types
- Architecture
- Clustering
- System Events and Plugin System
- System Metrics
- Reliability Guarantees
Quick Start
Getting Started
Looking for an example to get started? The easiest way to use the Event Gateway is with the serverless-event-gateway-plugin with the Serverless Framework. Check out the Getting Started Example to deploy your first service to the Event Gateway.
Running the Event Gateway
Hosted version
If you don't want to run the Event Gateway yourself, you can use the hosted version provided by the Serverless team. Sign up here!
via Docker
There is an official Docker image.
docker run -p 4000:4000 -p 4001:4001 serverless/event-gateway --devBinary
On macOS or Linux run the following to download the binary:
curl -sfL https://raw.githubusercontent.com/serverless/event-gateway/master/install.sh | shOn Windows download binary.
Then run the binary in development mode with:
$ event-gateway --devKubernetes
The repo contains helm charts for a quick deploy to an existing cluster using native nginx Ingress. To deploy
a development cluster you can follow the minikube instructions.
If you want more detailed information on running and developing with the Event Gateway,
please check Running Locally and Developing guides.
Motivation
- It is cumbersome to plug things into each other. This should be easy! Why do I need to set up a queue system to
keep track of new user registrations or failed logins? - Introspection is terrible. There is no performant way to emit logs and metrics from a function. How do I know
a new piece of code is actually working? How do I feed metrics to my existing monitoring system? How do I
plug this function into to my existing analytics system? - Using new functions is risky without the ability to incrementally deploy them.
- The AWS API Gateway is frequently cited as a performance and cost-prohibitive factor for using AWS Lambda.
Components
Event Registry
Event Registry is a single source of truth about events occuring in the space. Every event emitted to a space has to have event
type registered beforehand. Event Registry also provides a way to authorize incoming events. Please check
Event Types reference for more information.
Function Discovery
Discover and call serverless functions from anything that can reach the Event Gateway. Function Discovery supports the
following function types:
- FaaS functions (AWS Lambda, Google Cloud Functions, Azure Functions, IBM Cloud Functions)
- Connectors (AWS Kinesis, AWS Kinesis Firehose, AWS SQS)
- HTTP endpoints/Webhook (e.g. POST http://example.com/function)
Function Discovery stores information about functions allowing the Event Gateway to call them as a reaction to received
event.
Example: Register An AWS Lambda Function
curl example
curl --request POST \
--url http://localhost:4001/v1/spaces/default/functions \
--header 'content-type: application/json' \
--data '{
"functionId": "hello",
"type": "awslambda",
"provider":{
"arn": "arn:aws:lambda:us-east-1:377024778620:function:bluegreen-dev-hello",
"region": "us-east-1"
}
}'Node.js SDK example
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.registerFunction({
functionId: 'sendEmail',
type: 'awslambda',
provider: {
arn: 'xxx',
region: 'us-west-2'
}
})Subscriptions
Lightweight pub/sub system. Allows functions to asynchronously receive custom events. Instead of rewriting your
functions every time you want to send data to another place, this can be handled entirely in configuration
using the Event Gateway. This completely decouples functions from one another, reducing communication costs across
teams, eliminates effort spent redeploying functions, and allows you to easily share events across functions,
HTTP services, even different cloud providers. Functions may be registered as subscribers to a custom event.
When an event occurs, all subscribers are called asynchronously with the event as its argument.
Creating a subscription requires providing ID of registered function, an event type, an HTTP method (POST by default),
and a path (/ by default). The method and path properties defines HTTP endpoint which Events API will be listening on.
Event Gateway supports two subscription types: async and sync. Please check Subscription Types
reference for more information.
Example: Subscribe to an Event
curl example
curl --request POST \
--url http://localhost:4001/v1/spaces/default/subscriptions \
--header 'content-type: application/json' \
--data '{
"type": "async",
"eventType": "user.created",
"functionId": "sendEmail",
"path": "/myteam"
}'Node.js SDK example
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.subscribe({
type: 'async',
eventType: 'user.created',
functionId: 'sendEmail',
path: '/myteam'
})sendEmail function will be invoked for every user.created event to <Events API>/myteam endpoint.
Example: Emit an Event
curl example
curl --request POST \
--url http://localhost:4000/ \
--header 'content-type: application/json' \
--data '{
"eventType": "myapp.user.created",
"eventID": "66dfc31d-6844-42fd-b1a7-a489a49f65f3",
"cloudEventsVersion": "0.1",
"source": "/myapp/services/users",
"eventTime": "1990-12-31T23:59:60Z",
"data": { "userID": "123" },
"contentType": "application/json"
}'Node.js SDK example
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.emit({
"eventType": "myapp.user.created",
"eventID": "66dfc31d-6844-42fd-b1a7-a489a49f65f3",
"cloudEventsVersion": "0.1",
"source": "/myapp/services/users",
"eventTime": "1990-12-31T23:59:60Z",
"data": { "userID": "123" },
"contentType": "application/json"
})Example: Subscribe to an `http.request` Event
Not all data are events that's why Event Gateway has a special, built-in http.request event type that enables subscribing to
raw HTTP requests.
curl example
curl --request POST \
--url http://localhost:4001/v1/spaces/default/subscriptions \
--header 'content-type: application/json' \
--data '{
"type": "sync",
"eventType": "http.request",
"functionId": "listUsers",
"method": "GET",
"path": "/users"
}'Node.js SDK example
const eventGateway = new EventGateway({ url: 'http://localhost' })
eventGateway.subscribe({
type: 'sync',
eventType: 'http.request',
functionId: 'listUsers',
method: 'GET',
path: '/users'
})listUsers function will be invoked for every HTTP GET request to <Events API>/users endpoint.
Spaces
One additional concept in the Event Gateway are Spaces. Spaces provide isolation between resources. Space is a
coarse-grained sandbox in which entities (Functions and Subscriptions) can interact freely. All actions are
possible within a space: publishing, subscribing and invoking.
Space is not about access control/authentication/authorization. It's only about isolation. It doesn't enforce any
specific subscription path.
This is how Spaces fit different needs depending on use-case:
- single user - single user uses default space for registering function and creating subscriptions.
- multiple teams/departments - different teams/departments use different spaces for isolation and for hiding internal
implementation and architecture.
Technically speaking Space is a mandatory field ("default" by default) on Function or Subscription object that user has
to provide during function registration or subscription creation. Space is a first class concept in Config API. Config
API can register function in specific space or list all functions or subscriptions from a space.
CloudEvents Support
Event Gateway has the first-class support for CloudEvents. It means few things.
First of all, if the event emitted to the Event Gateway is in CloudEvents format, the Event Gateway is able to recognize it and trigger proper subscriptions based on event type specified in the event. Event Gateway supports both Structured Content and Binary Content modes described in HTTP Transport Binding spec.
Secondly, there is a special, built-in HTTP Request Event type allowing reacting to raw HTTP requests that are not formatted according to CloudEvents spec. This event type can be especially helpful for building REST APIs.
Currently, Event Gateway supports CloudEvents v0.1 schema specification.
SDKs
Versioning
This project uses Semantic Versioning 2.0.0. We are in initial development phase
right now (v0.X.Y). The public APIs should not be considered stable. Every breaking change will be listed in the
release changelog.
FAQ
What The Event Gateway is NOT
- it's not a replacement for message queues (no message ordering, currently weak durability guarantees only)
- it's not a replacement for streaming platforms (no processing capability and consumers group)
- it's not a replacement for existing service discovery solutions from the microservices world
Event Gateway vs FaaS Providers
The Event Gateway is NOT a FaaS platform. It integrates with existing FaaS providers (AWS Lambda, Google Cloud Functions,
Azure Functions, OpenWhisk Actions). The Event Gateway enables building large serverless architectures in a unified way
across different providers.
Background
SOA came along with a new set of challenges. In monolithic architectures, it was simple to call a built-in library or
rarely-changing external service. In SOA it involves much more network communication which is not reliable. The main problems to solve include:
- Where is the service deployed? How many instances are there? Which instance is the closest to me? (service discovery)
- Requests to the service should be balanced between all service instances (load balancing)
- If a remote service call failed I want to retry it (retries)
- If the service instance failed I want to stop sending requests there (circuit breaking)
- Services are written in multiple languages, I want to communicate between them using the best language for the particular task (sidecar)
- Calling remote service should not require setting up new connection every time as it increases request time (persistent connections)
The following systems are solutions those problems:
The main goal of those tools is to manage the inconveniences of network communication.
Microservices Challenges & FaaS
The greatest benefit of serverless/FaaS is that it solves almost all of above problems:
- service discovery: I don't care! I have a function name, that's all I need.
- load balancing: I don't care! I know that there will be a function to handle my request (blue/green deployments still an issue though)
- retries: It's highly unusual that my request will not proceed as function instances are ephemeral and failing function is immediately replaced with a new instance. If it happens I can easily send another request. In case of failure, it's easy to understand what is the cause.
- circuit breaking: Functions are ephemeral and auto-scaled, low possibility of flooding/DoS & cascading failures.
- sidecar: calling function is as simple as calling method from cloud provider SDK.
- in FaaS setting up persistent connection between two functions defeats the purpose as functions instances are ephemeral.
Tools like Envoy/Linkerd solve different domain of technical problems that doesn't occur in serverless space. They have a lot of features that are unnecessary in the context of serverless computing.
Service Discovery in FaaS = Function Discovery
Service discovery problems may be relevant to serverless architectures, especially when we have a multi-cloud setup or we want to call a serverless function from a legacy system (microservices, etc...). There is a need for some proxy that will know where the function is actually deployed and have retry logic built-in. Mapping from function name to serverless function calling metadata is a different problem from tracking the availability of a changing number of service instances. That's why there is a room for new tools that solves function discovery problem rather than the service discovery problem. Those problems are fundamentally different.
Overview (`contrib/README.md`)
Event Gateway Contributions
This folder contains services and contributions which complement the Event Gateway.
- Install Event Gateway on Kubernetes using Helm
- Provision the Event Gateway on AWS ECS Fargate using Terraform
- Grafana Prometheus Dashboard template for Event Gateway
- Grafana InfluxDB Dashboard template for Event Gateway
Overview (`libkv/README.md`)
`libkv` package
This package implements function.Service and subscription.Service using libkv (etcd) as a backend store.
Overview (`tests/README.md`)
`tests` package
This package contains high-level integration tests. Unit tests are placed in the respective packages.
Overview (`contrib/helm/README.md`)
Event Gateway on Kubernetes
This chart deploys the Event Gateway with etcd onto a Kubernetes cluster. Please note, the default instructions expect
an existing kubernetes cluster that supports ingress, such as GKE. If your environment doesn't have ingress support
set up, please follow the minikube instructions to set this up for your development environment.
Contents
Quickstart
Make sure you have helm installed on your machine and run helm init on your k8s cluster. This will set up thehelm and tiller functions required for easy deployment of config files to your cluster. You can follow
instructions here if you have not set this up previously.
Once installed, navigate to the event-gateway/contrib/helm folder and install the following components:
etcd-operator
helm install stable/etcd-operator --name ego [--namespace <namespace>]event-gateway
helm install event-gateway --name eg [--namespace <namespace>]This will install each of the etcd-operator and event-gateway into the default namespace in kubernetes. Please note,
this namespace has no bearing on your Event Gateway spaces as outlined in the docs. If you'd like
to install etcd-operator and event-gateway in another namespace, add the --namespace <namespace> option to
both helm install commands above.
Next we'll need to collect the Event Gateway IP to use on the CLI. Since the Ingress was configured to receive all
connections from the eventgateway.minikube host, you'll need to pass this as a header value to the request.
export EVENT_GATEWAY_URL=$(kubectl get ingress event-gateway-ingress -o jsonpath={.status.loadBalancer.ingress[0].ip})
curl --request GET \
--url http://{EVENT_GATEWAY_URL}/v1/metrics \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube'With your environment set up, you can now jump to the examples section to put your event-gateway to use!
Examples
Once you've set the EVENT_GATEWAY_URL environment variable, you're set to start interacting with the event-gateway!
NOTE: the events and configuration API ports are abstracted away from us via the kubernetes Ingress. The path-based
routing will ensure the request goes to the proper service managed by the cluster.
DOUBLENOTE: the examples below all assume the default namespace for the event-gateway. If you've updated or changed
this on your end, please don't forget to update the queries accordingly.
Register a function
Define the function registration payload, using AWS as an example:
cat > function.json <<EOF
{
"functionId": "echo",
"type": "awslambda",
"provider": {
"arn": "arn:aws:lambda:us-east-1:123456789012:function:event-gateway-tests-dev-echo",
"region": "us-east-1",
"awsAccessKeyID": "AAAAAAAAAAAAAAAAAAAA",
"awsSecretAccessKey": "AAAAaBcDeFgHiJqLmNoPqRsTuVwXyz0123456789"
}
}
EOFThen call the registration endpoint with your json payload:
curl --request POST \
--url http://${EVENT_GATEWAY_URL}/v1/spaces/default/functions \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube' \
--data @function.jsonAnd the corresponsing reply (if successful) should read something like the following:
{
"space": "default",
"functionId": "echo",
"type": "awslambda",
"provider": {
"arn": "arn:aws:lambda:us-east-1:123456789012:function:event-gateway-tests-dev-echo",
"region": "us-east-1",
"awsAccessKeyId": "AAAAAAAAAAAAAAAAAAAA",
"awsSecretAccessKey": "AAAAaBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789"
}
}NOTE: if you try to overwrite an existing function, you will receive an error! To replace an existing function
you will have to delete it first, then register the function once more. For example, trying to re-register the echo
function will yield:
curl --request POST \
--url http://${EVENT_GATEWAY_URL}/v1/spaces/default/functions \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube' \
--data @function.json
{
"errors": [{
"message": "Function \"echo\" already registered."
}]
}Query all functions
To check for registered functions, query the config API with the GET request:
curl --request GET \
--url http://${EVENT_GATEWAY_URL}/v1/spaces/default/functions \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube' | jqYou should see the functions list return your defined set of functions across all vendors.
{
"functions": [
{
"space": "default",
"functionId": "echo",
"type": "awslambda",
"provider": {
"arn": "arn:aws:lambda:us-east-1:123456789012:function:event-gateway-tests-dev-echo",
"region": "us-east-1",
"awsAccessKeyId": "AAAAAAAAAAAAAAAAAAAA",
"awsSecretAccessKey": "AAAAaBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789"
}
}
]
}Register an event type
To register an event, make sure to POST the event name to the event-gateway.
curl --request POST \
--url http://${EVENT_GATEWAY_URL}/v1/spaces/default/eventtypes \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube' \
--data '{ "name": "eventgateway.function.invoked" }'The reply should look something like the following:
{
"space": "default",
"name": "eventgateway.function.invoked"
}Query all event types
curl --request GET \
--url http://${EVENT_GATEWAY_URL}/v1/spaces/default/eventtypes \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube'Your registered events reply should look as follows:
{
"eventTypes": [
{
"space": "default",
"name": "eventgateway.function.invoked"
}
]
}Register a subscription
To register subscriptions to one of your registered event types, make sure to specify the eventType in
the JSON POST payload.
curl --request POST \
--url http://${EVENT_GATEWAY_URL}/v1/spaces/default/subscriptions \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube' \
--data '{
"type": "async",
"eventType": "eventgateway.function.invoked",
"functionId": "echo",
"path": "/",
"method": "POST"
}'Your reply payload should include the subscriptionId for your new subscription:
{
"space": "default",
"subscriptionId": "YXN5bmMsZXZlbnRnYXRld2F5LmZ1bmN0aW9uLmludm9rZWQsZWNobywlMkYsUE9TVA",
"type": "async",
"eventType": "eventgateway.function.invoked",
"functionId": "echo",
"path": "/",
"method": "POST"
}Query all subscriptions
To list our your current subscrptions, you can do the following:
curl --request GET \
--url http://${EVENT_GATEWAY_URL}/v1/spaces/default/subscriptions \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube'The output should list each of the registered subscriptions:
{
"subscriptions": [
{
"space": "default",
"subscriptionId": "YXN5bmMsZXZlbnRnYXRld2F5LmZ1bmN0aW9uLmludm9rZWQsZWNobywlMkYsUE9TVA",
"type": "async",
"eventType": "eventgateway.function.invoked",
"functionId": "echo",
"path": "/",
"method": "POST"
}
]
}Trigger an event
In order to trigger a registered function, call the event-gateway URL with the proper functionId. Following
our example from earlier, we've registered a GET function with functionId set to echo. To trigger this function,
we would:
curl --request GET \
--url http://${EVENT_GATEWAY_URL}/echo \
--header 'content-type: application/json' \
--header 'host: eventgateway.minikube'NOTE: as mentioned earlier, the events service is handled by the path-routing service of the kubernetes Ingress. Any path
that's prepended with /v1 will ultimately route to the config service, while other paths default to the events service.
Configuration
| Parameter | Description | Default |
|---|---|---|
images.repository |
Event Gateway image | serverless/event-gateway |
images.tag |
Event Gateway image tag | 0.9.0 |
replicaCount |
Number of containers | 3 |
service.type |
Type of Kubernetes service | LoadBalancer |
service.annotations |
Custom annotations for the service | [] |
service.config.port |
Config API port number | 4001 |
service.events.port |
Events API port number | 4000 |
resources.limits.cpu |
CPU resource limits | 200m |
resources.limits.memory |
Memory resource limits | 256Mi |
resources.requests.cpu |
CPU resource requests | 200m |
resources.requests.memory |
Memory resource requests | 256Mi |
command |
Options to pass to event-gateway command |
[--db-hosts=eg-etcd-cluster-client:2379, --log-level=debug] |
etcd_cluster_name |
Name of the etcd cluster. Passed to the --db-hosts option as <etcd-cluster-name>-client |
eg-etcd-cluster |
The service annotations can be used to set any annotations required by your platform, for example, if
you update your values.yml with:
- annotations: []
+ annotations:
+ - "service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0"
+ - "foo: bar"then the service will be annotated as shown:
$ helm install event-gateway --debug --dry-run | grep "kind: Service" -A5
kind: Service
metadata:
name: rafting-umbrellabird-event-gateway
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
foo: barCleanup
When you'd like to clean up the deployments, it's easy to remove services using helm:
helm delete --purge eg
helm delete --purge egoOverview (`contrib/terraform/README.md`)
Event Gateway Terraform module
This module creates Event Gateway running on ECS Fargate with a standalone etcd cluster.
The module is an extract form the Tectonic Installer repository.
Usage
module "event-gateway" {
source = "github.com/serverless/event-gateway//contrib/terraform/modules/event-gateway"
aws_region = "us-east-1"
command_list = ["-db-hosts", "event-gateway-etcd-0.etcd:2379,event-gateway-etcd-1.etcd:2379,event-gateway-etcd-2.etcd:2379", "-log-level", "debug"]
tags = {
Application = "event-gateway"
}
}
output "config_url" {
value = "${module.event-gateway.config_url}"
}
output "events_url" {
value = "${module.event-gateway.events_url}"
}Debugging etcd
It's possible to enable SSH access via bastion instance, by adding parameters:
bastion_enabled = true
ssh_key = "eg-key"Bastion IP can be distplayed by adding output:
output "bastion_ip" {
value = "${module.event-gateway.bastion_ip}"
}To connect to one of the etcd cluster hosts, run:
ssh -J ec2-user@<bastion_ip> core@<etcd_host_private_ip>Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| aws_region | AWS region for the stack | string | - | yes |
| bastion_enabled | Set to true enables SSH access to etcd nodes in the private subnet | string | false |
no |
| command_list | List of parameters for the event-gateway command |
list | ["-log-level", "debug"] |
no |
| config_alb_name | Config ALB name | string | alb-config |
no |
| config_port | Port number of the Event Gateway Config API | string | 4001 |
no |
| eg_image | Event Gateway docker image | string | serverless/event-gateway:latest |
no |
| eg_vpc_name | Event Gateway VPC name | string | eg-vpc |
no |
| etcd_base_domain | Name of the base domain for the etcd cluster | string | etcd |
no |
| etcd_image | etcd Docker image | string | quay.io/coreos/etcd:v3.1.8 |
no |
| etcd_instance_count | Number of nodes in the etcd cluster | string | 3 |
no |
| etcd_instance_type | Etcd node type | string | t2.micro |
no |
| etcd_root_volume_iops | Number of IOPS of the etcd cluster volumes | string | 100 |
no |
| etcd_root_volume_size | Size of the etcd cluster volumes (in GiB) | string | 30 |
no |
| etcd_root_volume_type | Type of the etcd cluster volumes | string | gp2 |
no |
| etcd_ssh_key | (optional) Name of the preexisting SSH key | string | `` | no |
| etcd_tls_enabled | Enable TLS for the etcd cluster | string | false |
no |
| events_alb_name | Events ALB name | string | alb-events |
no |
| events_port | Event Gateway Events API port number | string | 4000 |
no |
| fargate_cpu | Fargate instance CPU units | string | 256 |
no |
| fargate_memory | Fargate instance memory | string | 512 |
no |
| tags | Additional tags | map | <map> |
no |
| task_count | Number of Event Gateway Fargate tasks | string | 3 |
no |
Outputs
| Name | Description |
|---|---|
| bastion_ip | Public IP of etcd bastion instance |
| config_url | Event Gateway Config API URL |
| events_url | Event Gateway Events API URL |
Overview (`examples/aws-node-crm-service/README.md`)
Using Custom Events
Background
In a microservices architecture, you'll often have events in one service that are relevant to other services. In this example, we'll show you how to emit events from one service and subscribe to event from other services.
We'll use a common example -- the user creation flow. Your users may be created in the Users service, but the event when a user is created is important to multiple services. Perhaps you want to trigger a welcome email in your email service, post a celebratory message to Slack, or insert the user into your marketing team's CRM.
Let's focus on the last use case here. We'll emulate a CRM service that listens to events across our architecture and updates our CRM accordingly. We'll set up a subscription on the user.created event that's emitted from our Users service and insert the new user into our CRM.
Deploying & Testing the CRM Service
First, let's set up our CRM service. I like to start with my function logic first, as my business logic is where I should be focusing. Take a look at index.js, which includes the logic:
module.exports.handler = (event, context, cb) => {
const user = event.data;
console.log('Saving user to CRM: ');
console.dir(user);
// Add your CRM logic here
// saveUserToCRM(user)
cb(null, { message: 'success' })
}This function is pretty basic as you'll need to implement the logic specific to your CRM. It accepts the event and creates a user object from the event data. Then it saves that user to my CRM. We'll just log the user object here rather than actually save it to a CRM.
Next, let's see how we configure that function to be triggered by our event. In the serverless.yml, look at the functions block:
functions:
addUserToCRM:
handler: index.handler
events:
- eventgateway:
event: user.createdWe've registered our function logic from index.js, and we've created an eventgateway subscription to the user.created event. Whenever that event is emitted into our Event Gateway space, this function will be triggered.
Finally, there are a few other relevant configuration sections of serverless.yml:
custom:
eventgateway:
url: <your URL> # e.g. tenant-myapp.slsgateway.com
accessKey: <your Access Key>
plugins:
- "@serverless/serverless-event-gateway-plugin"This serverless-event-gateway-plugin adds additional functionality to register functions and subscriptions with the Event Gateway. The eventgateway section of the custom block configures the plugin with your application URL and access key. Sign up for both here.
Once you've entered your Application URL and Access Key, install your dependencies and deploy:
$ npm install
$ sls deployWith your function deployed and subscription configured, it's time to test it out. You can use the plugin command sls gateway emit to emit a test event into your Event Gateway space. There's a sample event body in event.json to represent a test user.created event:
$ sls gateway emit --event "user.created" --data "$(cat event.json)"
Event emitted: user.created
Run `serverless logs -f <functionName>` to verify your subscribed function was triggered.Note that we only get a message that an event was emitted. Custom events are processed asynchronously, and multiple functions can be subscribed to a custom event. This decouples producers and consumers, and it means a producer won't be able to tell who receives an event when it publishes it.
You can check your function logs to make sure everything is working properly:
$ sls logs -f addUserToCRM
START RequestId: 7b8800f5-2d34-11e8-8165-171c6f09ec0e Version: $LATEST
2018-03-21 18:20:07.365 (+00:00) 7b8800f5-2d34-11e8-8165-171c6f09ec0e Saving user to CRM:
{ company: 'Test Corp, Inc.',
email: '[email protected]',
firstname: 'Test',
lastname: 'Williams',
username: 'test-user' }
END RequestId: 7b8800f5-2d34-11e8-8165-171c6f09ec0e
REPORT RequestId: 7b8800f5-2d34-11e8-8165-171c6f09ec0e Duration: 5.77 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 19 MBAs you can see, our function was triggered from our event and logged out the message as desired. Success!
Emitting Events in your Services
In the example above, we saw how to set up a consumer of events as well as how to publish test events via the command line. However, most events won't come from the command line -- they'll come from your application code, or webhooks, or database events.
In this example, let's complete the story by emitting user.created events from our Users service. The easiest way to emit an event is to use the event-gateway-sdk for Node.
To use the SDK, you'll create an Event Gateway client configured for your application, then emit an event with the event name and payload to send.
The emit.js file shows how you could use the SDK in your application:
// emit.js
const SDK = require('@serverless/event-gateway-sdk');
const URL = 'tenant-myapp.slsgateway.com'
const eventGateway = new SDK({
url: URL
})
function createUser(user) {
// Save your user to database
// saveUserToDB(user)
// Then emit your event
eventGateway
.emit({
event: 'user.created',
data: user
})
.then(() => console.log('Emitted user.created event!'))
}
const user = {
"username": "sls-fan",
"firstname": "Bill",
"lastname": "Jones",
"company": "Big Corp, Inc.",
"email": "[email protected]"
}
createUser(user);The createUser function is similar to one you would have in your application -- save the new user to your database, then emit a user.created event that other services can subscribe to.
You can run this file as a test if you like. You'll need to edit the APP variable to match the application you used in your serverless.yml. Then just execute the file and check your function logs:
$ node emit.js
Emitted user.created event!
$ sls logs -f addUserToCRM
START RequestId: 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda Version: $LATEST
2018-03-21 18:33:29.796 (+00:00) 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda Saving user to CRM:
{ company: 'Big Corp, Inc.',
email: '[email protected]',
firstname: 'Bill',
lastname: 'Jones',
username: 'sls-fan' }
END RequestId: 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda
REPORT RequestId: 5a11a5d8-2d36-11e8-ac4f-eb85ef6fadda Duration: 1.64 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 21 MBSuccess!
Overview (`examples/aws-node-users-service/README.md`)
Using Event Gateway as an API Gateway
Background
With the explosion of FaaS and Serverless architectures, applications are getting split up into smaller and smaller pieces. Rather than have a single bundle of code that handles all of your HTTP endpoints, you can create individual functions that handle a very specific task -- create a User, update my Shopping Cart, delete my Tweet.
This separation makes it easier for teams to move independently and ship more quickly, but it also presents a management problem. How do you make it easy for clients to access these hundreds of isolated functions?
Using the Event Gateway as an API Gateway solves this problem. You can have collections of functions split into services via the Serverless Framework. These services can be deployed independently to the same domain in a way that's easy for clients to access.
In this example, we'll deploy an example Users service with a few different HTTP endpoints. We'll see how we can interact with those endpoints in a familiar, RESTful way.
Deploying & Testing the Users Service
For our example Users service, let's say we want endpoints to create a new user, to get a specific user, and to delete a single user. We'll implement each of these endpoints in separate functions. Let's take a look at index.js where they're implemented:
const faker = require('faker')
module.exports.get = (event, context, cb) => {
console.log(event)
cb(null, {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: event.data.params.id,
name: faker.name.findName(),
email: faker.internet.email()
})
})
}
module.exports.post = (event, context, cb) => {
console.log(event)
cb(null, {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: faker.random.number(),
name: faker.name.findName(),
email: faker.internet.email()
})
})
}
module.exports.delete = (event, context, cb) => {
console.log(event)
cb(null, {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: `Deleted user with id ${event.data.params.id}`
})
})
}We're exporting three functions -- get, post, and delete -- to handle our three endpoints. The logic is using the faker library to send out pretend data, but you would use some sort of data store in your actual application.
Next, let's see how we configure the functions to be triggered by our Event Gateway. In the serverless.yml, look at the functions block:
functions:
getUser:
handler: index.get
events:
- eventgateway:
event: http
method: GET
path: /users/:id
createUser:
handler: index.post
events:
- eventgateway:
event: http
path: /users
method: POST
deleteUser:
handler: index.delete
events:
- eventgateway:
event: http
path: /users/:id
method: DELETEFor each function we've registered an eventgateway event of type http, indicating that it's an API Gateway event. We include the path and HTTP method to match on as well.
Finally, there are a few other relevant configuration sections of serverless.yml:
custom:
eventgateway:
url: <your URL> # e.g. tenant-myapp.slsgateway.com
accessKey: <your Access Key>
plugins:
- "@serverless/serverless-event-gateway-plugin"This serverless-event-gateway-plugin adds additional functionality to register functions and subscriptions with the Event Gateway. The eventgateway section of the custom block configures the plugin with your Application URL and Access Key. You may get this by signing up here.
Once you've entered your Application URL and Access Key, install your dependencies and deploy:
$ npm install
$ sls deployNow let's give it a try! Using curl or in your browser, navigate to the getUser endpoint at https://<applicationURL>/users/15:
$ curl -X GET https://tenant-myapp.slsgateway.com/users/15 | jq "."
{
"id": "15",
"name": "Ariel Dach",
"email": "[email protected]"
}It worked! You can try your POST and DELETE endpoints as well.
You can reuse this same Application URL across multiple services using different endpoint paths to allow for agility across your team.
Api (`docs/api.md`)
API
The Event Gateway has two APIs: the Configuration API for registering functions and subscriptions, and the runtime Events API for sending events into the Event Gateway.
This document contains the API documentation for both Events and Configuration APIs. You can also find links to OpenAPI specs for these APIs.
Contents
Events API
The Event Gateway exposes an API for emitting events. By default Events API runs on :4000 port.
Event Definition
All data that passes through the Event Gateway is formatted as a CloudEvent, based on CloudEvents v0.1 schema.
Example:
{
"eventType": "myapp.user.created",
"eventID": "66dfc31d-6844-42fd-b1a7-a489a49f65f3",
"cloudEventsVersion": "0.1",
"source": "https://serverless.com/event-gateway/#transformationVersion=0.1",
"eventTime": "1990-12-31T23:59:60Z",
"data": { "foo": "bar" },
"contentType": "application/json"
}How To Emit an Event
Creating a subscription requires path (default: /), method (default: POST) and eventType. path indicates path under which you can send the event.
Endpoint
POST <Events API URL>/<Subscription Path>
Request
CloudEvents payload
Response
Status code:
202 Accepted- this status code is returned if there is nosyncsubscription defined. Otherwise, status code is controlled by function synchronously subscribed on this endpoint.
HTTP Request Event
Not all data are events that's why Event Gateway has a special, built-in http.request event type that enables subscribing to
raw HTTP requests. It's especially helpful for building REST APIs or supporting legacy payloads. http.request event is a
CloudEvent created by Event Gateway where data field has the following structure:
path-string- request pathmethod-string- request methodheaders-object- request headershost-string- request hostquery-object- query parametersparams-object- matched path parametersbody- depends onContent-Typeheader - request payload
CORS
By default cross-origin resource sharing (CORS) is disabled. CORS is configured per-method/path basis using
CORS Configuration API.
Event Gateway handles preflight OPTIONS requests for you. You don't need to setup subscription for OPTIONS method
because the Event Gateway will respond with all appropriate headers.
Legacy Mode
Legacy mode is deprecated and will be removed in upcoming releases.
In legacy mode, Event Gateway is able to recognize event type based on Event header. If the event is not formatted according to CloudEvents specification Event Gateway looks for this header and creates CloudEvent internally. In this case, whole request body is put into data field.
Event Data Type
The MIME type of the data block can be specified using the Content-Type header (by default it'sapplication/octet-stream). This allows the Event Gateway to understand how to deserialize the data block if it needs
to. In case of application/json type the Event Gateway passes JSON payload to the target functions. In any other case
the data block is base64 encoded.
Configuration API
The Event Gateway exposes a RESTful JSON configuration API. By default Configuration API runs on :4001 port.
Event Types
Create Event Type
Endpoint
POST <Configuration API URL>/v1/spaces/<space>/eventtypes
Request
JSON object:
name-string- required, event type nameauthorizerId-string- authorizer function IDmetadata-object- arbitrary metadata
Response
Status code:
201 Createdon success400 Bad Requeston validation error409 Conflictif event type already exists
JSON object:
space-string- space namename-string- event type nameauthorizerId-string- authorizer function IDmetadata-object- arbitrary metadata
Update Event Type
Endpoint
PUT <Configuration API URL>/v1/spaces/<space>/eventtypes/<event type name>
Request
JSON object:
authorizerId-string- authorizer function IDmetadata-object- arbitrary metadata
Response
Status code:
200 OKon success400 Bad Requeston validation error or if the authorizer function doesn't exist404 Not Foundif event type doesn't exist
JSON object:
space-string- space namename-string- event type nameauthorizerId-string- authorizer function IDmetadata-object- arbitrary metadata
Delete Event Type
Delete event type. This operation fails if there is at least one subscription using the event type.
Endpoint
DELETE <Configuration API URL>/v1/spaces/<space>/eventtypes/<event type name>
Response
Status code:
204 No Contenton success400 Bad Requestif there are subscriptions using the event type404 Not Foundif event type doesn't exist
List Event Types
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/eventtypes
Query Parameters
Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.
Response
Status code:
200 OKon success
JSON object:
eventTypes-arrayofobject- event types:space-string- space namename-string- event type nameauthorizerId-string- authorizer function IDmetadata-object- arbitrary metadata
Get Event Type
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/eventtypes/<event type name>
Response
Status code:
200 OKon success404 Not Foundif event type doesn't exist
JSON object:
space-string- space namename-string- event type nameauthorizerId-string- authorizer function IDmetadata-object- arbitrary metadata
Functions
Register Function
Endpoint
POST <Configuration API URL>/v1/spaces/<space>/functions
Request
JSON object:
functionId-string- required, function IDtype-string- required, provider type:awslambdaorhttpprovider-object- required, provider specific information about a function, depends on type:- for AWS Lambda:
arn-string- required, AWS ARN identifierregion-string- required, region nameawsAccessKeyId-string- optional, AWS API key ID. By default credentials from the environment are used.awsSecretAccessKey-string- optional, AWS API access key. By default credentials from the environment are used.awsSessionToken-string- optional, AWS session token
- for HTTP function:
url-string- required, the URL of an http or https remote endpoint
- for AWS Kinesis connector:
streamName-string- required, AWS Kinesis Stream Nameregion-string- required, region nameawsAccessKeyId-string- optional, AWS API key ID. By default credentials from the environment are used.awsSecretAccessKey-string- optional, AWS API access key. By default credentials from the environment are used.awsSessionToken-string- optional, AWS session token
- for AWS Firehose connector:
deliveryStreamName-string- required, AWS Firehose Delivery Stream Nameregion-string- required, region nameawsAccessKeyId-string- optional, AWS API key ID. By default credentials from the environment are used.awsSecretAccessKey-string- optional, AWS API access key. By default credentials from the environment are used.awsSessionToken-string- optional, AWS session token
- for AWS SQS connector:
queueUrl-string- required, AWS SQS Queue URLregion-string- required, region nameawsAccessKeyId-string- optional, AWS API key ID. By default credentials from the environment are used.awsSecretAccessKey-string- optional, AWS API access key. By default credentials from the environment are used.awsSessionToken-string- optional, AWS session token
- for AWS Lambda:
metadata-object- arbitrary metadata
Response
Status code:
201 Createdon success400 Bad Requeston validation error409 Conflictif function already exists
JSON object:
space-string- space namefunctionId-string- function IDprovider-object- provider specific information about a functionmetadata-object- arbitrary metadata
Update Function
Endpoint
PUT <Configuration API URL>/v1/spaces/<space>/functions/<function ID>
Request
JSON object:
type-string- required, provider type:awslambdaorhttpprovider-object- required, provider specific information about a function, depends on type:- for AWS Lambda:
arn-string- required, AWS ARN identifierregion-string- required, region nameawsAccessKeyId-string- optional, AWS API key IDawsSecretAccessKey-string- optional, AWS API keyawsSessionToken-string- optional, AWS session token
- for HTTP function:
url-string- required, the URL of an http or https remote endpoint
- for AWS Lambda:
metadata-object- arbitrary metadata
Response
Status code:
200 OKon success400 Bad Requeston validation error404 Not Foundif function doesn't exist
JSON object:
space-string- space namefunctionId-string- function IDprovider-object- provider specific information about a functionmetadata-object- arbitrary metadata
Delete Function
Delete all types of functions. This operation fails if the function is currently in-use by a subscription.
Endpoint
DELETE <Configuration API URL>/v1/spaces/<space>/functions/<function ID>
Response
Status code:
204 No Contenton success404 Not Foundif function doesn't exist
List Functions
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/functions
Query Parameters
Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.
Response
Status code:
200 OKon success
JSON object:
functions-arrayofobject- functions:space-string- space namefunctionId-string- function IDprovider-object- provider specific information about a functionmetadata-object- arbitrary metadata
Get Function
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/functions/<function ID>
Response
Status code:
200 OKon success404 Not Foundif function doesn't exist
JSON object:
space-string- space namefunctionId-string- function IDprovider-object- provider specific information about a functionmetadata-object- arbitrary metadata
Subscriptions
Create Subscription
Endpoint
POST <Configuration API URL>/v1/spaces/<space>/subscriptions
Request
type-string- subscription type,syncorasynceventType-string- event typefunctionId-string- ID of function to receive eventspath-string- optional, URL path under which events (HTTP requests) are accepted, default:/method-string- optional, HTTP method that accepts requests, default:POSTmetadata-object- arbitrary metadata
Response
Status code:
201 Createdon success400 Bad Requeston validation error409 Conflictif subscription already exists
JSON object:
space-string- space namesubscriptionId-string- subscription IDtype-string- subscription typeeventType-string- event typefunctionId- function IDmethod-string- HTTP method that accepts requestspath-string- path that accepts requests, starts with/metadata-object- arbitrary metadata
Update Subscription
Endpoint
PUT <Configuration API URL>/v1/spaces/<space>/subscriptions/<subscription ID>
Request
Note that type, eventType, functionId, path, and method may not be updated in an UpdateSubscription call.
type-string- subscription type,syncorasynceventType-string- event typefunctionId-string- ID of function to receive eventspath-string- optional, URL path under which events (HTTP requests) are accepted, default:/method-string- optional, HTTP method that accepts requests, default:POSTmetadata-object- arbitrary metadata
Response
Status code:
200 Createdon success400 Bad Requeston validation error404 Not Foundif subscription doesn't exist
JSON object:
space-string- space namesubscriptionId-string- subscription IDtype-string- subscription typeeventType-string- event typefunctionId- function IDmethod-string- HTTP method that accepts requestspath-string- path that accepts requests, starts with/metadata-object- arbitrary metadata
Delete Subscription
Endpoint
DELETE <Configuration API URL>/v1/spaces/<space>/subscriptions/<subscription ID>
Response
Status code:
204 No Contenton success404 Not Foundif subscription doesn't exist
List Subscriptions
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/subscriptions
Query Parameters
Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.
Response
Status code:
200 OKon success
JSON object:
subscriptions-arrayofobject- subscriptionsspace-string- space namesubscriptionId-string- subscription IDtype-string- subscription typeeventType-string- event typefunctionId- function IDmethod-string- HTTP method that accepts requestspath-string- path that accepts requests, starts with/metadata-object- arbitrary metadata
Get Subscription
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/subscriptions/<subscription ID>
Response
Status code:
200 OKon success404 NotFoundif subscription doesn't exist
JSON object:
space-string- space namesubscriptionId-string- subscription IDtype-string- subscription typeeventType-string- event typefunctionId- function IDmethod-string- HTTP method that accepts requestspath-string- path that accepts requests, starts with/metadata-object- arbitrary metadata
CORS
Create CORS Configuration
Endpoint
POST <Configuration API URL>/v1/spaces/<space>/cors
Request
method-string- endpoint methodpath-string- endpoint pathallowedOrigins-arrayofstring- list of allowed origins. An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com), default:*allowedMethods-arrayofstring- list of allowed methods, default:HEAD,GET,POSTallowedHeaders-arrayofstring- list of allowed headers, default:Origin,Accept,Content-TypeallowCredentials-bool- allow credentials, default: falsemetadata-object- arbitrary metadata
Response
Status code:
201 Createdon success400 Bad Requeston validation error409 Conflictif CORS configuration already exists
JSON object:
space-string- space namecorsId-string- CORS configuration IDmethod-string- endpoint methodpath-string- endpoint pathallowedOrigins-arrayofstring- list of allowed originsallowedMethods-arrayofstring- list of allowed methodsallowedHeaders-arrayofstring- list of allowed headersallowCredentials-boolean- allow credentialsmetadata-object- arbitrary metadata
Update CORS Configuration
Endpoint
PUT <Configuration API URL>/v1/spaces/<space>/cors/<CORS ID>
Request
Note that method, and path may not be updated in an UpdateCORS call.
method-string- endpoint methodpath-string- endpoint pathallowedOrigins-arrayofstring- list of allowed originsallowedMethods-arrayofstring- list of allowed methodsallowedHeaders-arrayofstring- list of allowed headersallowCredentials-boolean- allow credentialsmetadata-object- arbitrary metadata
Response
Status code:
200 Createdon success400 Bad Requeston validation error404 Not Foundif CORS configuration doesn't exist
JSON object:
space-string- space namecorsId-string- CORS configuration IDmethod-string- endpoint methodpath-string- endpoint pathallowedOrigins-arrayofstring- allowed originsallowedMethods-arrayofstring- allowed methodsallowedHeaders-arrayofstring- allowed headersallowCredentials-boolean- allow credentialsmetadata-object- arbitrary metadata
Delete CORS Configuration
Endpoint
DELETE <Configuration API URL>/v1/spaces/<space>/cors/<CORS ID>
Response
Status code:
204 No Contenton success404 Not Foundif CORS configuration doesn't exist
List CORS Configurations
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/cors
Query Parameters
Endpoint allows filtering list of returned object with filters passed as query parameters. Currently, filters can only use metadata properties e.g. metadata.service=usersService.
Response
Status code:
200 OKon success
JSON object:
cors-arrayofobject- CORS configurationsspace-string- space namecorsId-string- CORS configuration IDmethod-string- endpoint methodpath-string- endpoint pathallowedOrigins-arrayofstring- allowed originsallowedMethods-arrayofstring- allowed methodsallowedHeaders-arrayofstring- allowed headersallowCredentials-boolean- allow credentialsmetadata-object- arbitrary metadata
Get CORS Configuration
Endpoint
GET <Configuration API URL>/v1/spaces/<space>/cors/<CORS ID>
Response
Status code:
200 OKon success404 NotFoundif CORS configuration doesn't exist
JSON object:
space-string- space namecorsId-string- CORS configuration IDmethod-string- endpoint methodpath-string- endpoint pathallowedOrigins-arrayofstring- allowed originsallowedMethods-arrayofstring- allowed methodsallowedHeaders-arrayofstring- allowed headersallowCredentials-boolean- allow credentialsmetadata-object- arbitrary metadata
Prometheus Metrics
Endpoint exposing Prometheus metrics.
Endpoint
GET <Configuration API URL>/metrics
Status
Dummy endpoint (always returning 200 OK status code) for checking if the event gateway instance is running.
Endpoint
GET <Configuration API URL>/v1/status
Architecture (`docs/architecture.md`)
Architecture
/* Detailed source-code truncated for AI context efficiency. */Clustering (`docs/clustering.md`)
Clustering
The Event Gateway is a horizontally scalable system. It can be scaled by adding instances to the cluster. A cluster is
a group of instances sharing the same database. A cluster can be created in one cloud region, across multiple regions,
across multiple cloud provider or even in both cloud and on-premise data centers.
The Event Gateway instances use a strongly consistent, subscribable DB (initially etcd,
with support for Consul, and Zookeeper planned) to store and broadcast configuration. The instances locally
cache configuration used to drive low-latency event routing. The instance local cache is built asynchronously based on
events from backing DB.
The Event Gateway is a stateless service and there is no direct communication between different instances. All
configuration data is shared using backing DB. If the instance from region 1 needs to call a function from region 2 the
invocation is not routed through the instance in region 2. The instance from region 1 invokes the function from region 2
directly.
/* Detailed source-code truncated for AI context efficiency. */Developing (`docs/developing.md`)
Developing
Clone GitHub repo
mkdir -p $GOPATH/src/github.com/serverless
cd $GOPATH/src/github.com/serverless
git clone [email protected]:serverless/event-gateway.git
cd event-gatewayInstall [`dep`](https://github.com/golang/dep) package manager
On macOS you can install or upgrade to the latest released version with Homebrew:
$ brew install dep
$ brew upgrade depOr you can install via go get:
go get -u github.com/golang/dep/cmd/depInstall dependencies
go get -u github.com/hashicorp/go-plugin
go get -u github.com/hashicorp/go-hclog
go get -u golang.org/x/net/{context,http2,trace}
dep ensureRun locally
go run cmd/event-gateway/main.go -devEvent Types (`docs/event-types.md`)
Event Types
Event Registry is a single source of truth about events occuring in the space. Every event emitted to a space has to have event
type registered beforehand. Event type can be registered using Event Types Configuration API.
Authorization
Event Type can define authorizer function that will be called before calling a subscribed function. Authorizer function is a function registered in Event Gateway beforehand.
Invocation Payload
The authorizer function is invoked with a special payload. The function has access to the whole request object because different parts of the request can be required for running authorization logic (e.g. API key can be stored in different headers or query params). The invocation payload is a JSON object with the following structure:
event-object- event received and parsed by EGrequest-object- original HTTP request to the EG, this field is exactly the same as HTTP event, including body, which in case of CloudEvent will be exactly the same as event field
Invocation Result
The authorize function is expected to return authorization response JSON object with the following structure:
authorization-object- object containing authorization data, required if authorization is successful. Fields:principalId-string- required if authorization is successful, the principal user identification associated with the token sent in the requestcontext-map[string]string- arbitrary data that will be accessible by the downstream function
error-object- error information, required if authorization is unsuccessful. Fields:message-string- authorization error message
If the authorization is successful error field has to be null or not defined otherwise Event Gateway treats authorization process as unsuccessful and 403 Forbidden error is returned to the client assuming that there was sync subscription defined. authorization object is attached to CloudEvent Extensions field under eventgateway.authorization key.
If Event Gateway will not be able to parse the response from authorizer function or there will be error during invocation authorization process is considered as unsuccessful.
Metrics (`docs/metrics.md`)
Collecting and Viewing Event Gateway Metrics
This guide details how to collect and analyze metrics from the Event Gateway.
Introduction
The Event Gateway exposes a number of Prometheus-based metrics + counters via the configuration
API to help monitor the health of the gateway.
Contents
Collecting Metrics
Event Gateway metrics are exposed via a Prometheus text-based metric exporter and as such are
queryable by a variety of modern toolsets. While you can scrape data directly from your prometheus database, the following (non-exhaustive) list of sources can also
be configured to scrape Event Gateway metrics:
Visualizing Data
Included in the contrib folder are identical dashboards to visualize Event Gateway metrics with different
data sources. Depending on your setup you'll find a Prometheus and InfluxDB version
of the dashboard.
Importing a Grafana dashboard is relatively straightforward provided your datasources are configured
properly within your Grafana instance. Each of our dashboards lists respective datasources as a template variable, assuming the default datasource is set
to the type for your datasource; in other words, if you import the Prometheus dashboard the template will expect your default data source to be set
to Prometheus.
If your default datasource is not either of the Prometheus or InfluxDB sources, fear not! The template variable is set as the datasource for each
panel within the dashboard, so updating the datasource is as easy as choosing another option from the template dropdown.
NOTE: There are two dashboards included for each datasource: an aggregated dashboard for all spaces in a given Event Gateway deployment, and a second
dashboard that can drill down to individual spaces.
Prometheus
You can find the source for the Prometheus dashboard here.
InfluxDB
You can find the source for the InfluxDB dashboard here.
List of Metrics
Both Events and Configuration API exposes Prometheus metrics. The metrics are accesible via /v1/metrics endpoint of Configuration API. The table below outlines the specific metrics available to query from the endpoint:
Events API
| Metric | Type | Labels | Description |
|---|---|---|---|
eventgateway_events_received_total |
counter | space, type |
total of events received |
eventgateway_events_processed_total |
counter | space, type |
total of processed events |
eventgateway_events_dropped_total |
counter | space, type |
total of events dropped due to insufficient processing power |
eventgateway_events_backlog |
gauge | gauge of asynchronous events count waiting to be processed | |
eventgateway_events_custom_processing_seconds |
histogram | bucketed histogram of processing duration of an event (from receiving the async custom event to calling a function) |
Labels
space- space nametype- event type name
Configuration API
| Metric | Type | Labels | Description |
|---|---|---|---|
eventgateway_eventtypes_total |
gauge | space |
gauge of registered event types count |
eventgateway_functions_total |
gauge | space |
gauge of registered functions count |
eventgateway_subscriptions_total |
gauge | space |
gauge of created subscriptions count |
eventgateway_config_requests_total |
counter | space, resource, operation |
total of Config API requests |
eventgateway_config_request_duration_seconds |
histogram | bucketed histogram of request duration of Config API requests |
Labels
space- space nameresource- Configuration API resource, possible values:eventtype,functionorsubscriptionoperation- Configuration API operation, possible values:create,get,delete,list,update
Reliability Guarantees (`docs/reliability-guarantees.md`)
Reliability Guarantees
Events are not durable
The event received by Event Gateway is stored only in memory, it's not persisted to disk before processing. This means that in case of hardware failure or software crash the event may not be delivered to the subscriber. For a synchronous subscription (http) it can manifest as error message returned to the requester. For asynchronous custom event with multiple subscribers it means that the event may not be delivered to all of the subscribers.
Events are delivered _at most once_
Event Gateway attempts delivery fulfillment for an event only once and consequently any event received successfully by the Event Gateway is guaranteed to be received by the subscriber at most once. That said, the nature of Event Gateway provider implementation could result in retries under specific circumstances, but these should not cause delivering the same event multiple times. For example, Providers for AWS Services that use the AWS SDK are subject to auto retry logic that's built into the SDK (AWS documentation on API retries).
AWS Lambda provider uses RequestResponse invocation type which means that retry logic for asynchronous AWS events doesn't apply here. Among others it means, that failed deliveries of custom events are not sent to DLQ. Please find more information in Understanding Retry Behavior, "Synchronous invocation" section.
Running Locally (`docs/running-locally.md`)
Running Locally
Installation (via GitHub releases)
On macOS or Linux run the following:
curl -sfL https://raw.githubusercontent.com/serverless/event-gateway/master/install.sh | shOn Windows download binary.
Installation (via Docker)
There is a official Docker image.
docker pull serverless/event-gatewayRunning Locally
Run event-gateway in dev mode:
event-gateway -devRunning in Docker
docker run -p 4000:4000 -p 4001:4001 serverless/event-gateway -devPass in your AWS credentials
Mounts the ~/.aws folder from the host to the /home/.aws folder inside the container. Event Gateway can then read the credentials from within the container.
docker run -p 4000:4000 -p 4001:4001 -e "HOME=/home" -v ~/.aws:/home/.aws serverless/event-gateway -devPreserve state of etcd
While testing if you restart the container running Event Gateway and want to preserve the data in etcd, you can specify a data dir with the -embed-data-dir "/home/data" flag specifying a destination folder. Then you can mount the folder ~/.event-gateway/data from your host into the container at /home/data. Event Gateway will read the data from there.
docker run -p 4000:4000 -p 4001:4001 -v ~/.event-gateway/data:/home/data serverless/event-gateway -embed-data-dir "/home/data" -devOperations
Register a Function
Register an AWS Lambda function in the Function Discovery.
curl --request POST \
--url http://127.0.0.1:4001/v1/spaces/default/functions \
--header 'content-type: application/json' \
--data '{"functionId": "hello", "provider":{"type": "awslambda", "arn": "<Function AWS ARN>", "region": "<Region>", "accessKeyId": "<Access Key ID>", "secretAccessKey": "<Secret Access Key>"}}'Subscribe to an Event
Once the function is register you can subscribe it to you custom event.
curl --request POST \
--url http://127.0.0.1:4001/v1/spaces/default/subscriptions \
--header 'content-type: application/json' \
--data '{"functionId": "hello", "event": "pageVisited"}'Emit an Event
An event can be emitted using Events API.
curl --request POST \
--url http://127.0.0.1:4000/ \
--header 'content-type: application/json' \
--header 'event: pageVisited' \
--data '{"userId": "123"}'After emitting the event subscribed function is called asynchronously.
Storage (`docs/storage.md`)
Implications of Storage Semantics
For configuration, this system has this desired behavior:
- something wants to configure something
- everything that needs to know about that configuration reacts to it
We benefit from strongly consistent atomic writes because we want all configuration to
reach everything that needs to be configured. Weakly consistent systems
may not observe all updates, and we will end up with broken or half-working
features. We also benefit from efficient notification primitives for
keeping the overall system performant, such as watches. Without watches,
we have to continuously scan ranges of keys in the database until we
find new data, which is enormously expensive, slow, and hard to scale.
Applied to security, where we want to revoke a user's privileges completely,
if we are using a database without the ability to perform atomic writes,
even if it is strongly consistent,
then one component may try updating a user's account by first reading
their data, modifying it locally, then trying to update it in the database.
If we don't have atomic updates, the ordering could end up being this:
node A reads user Bob's data
node A locally updates Bob's data to add admin privileges
node B deletes Bob's data to remove all privileges
node B receives "write successful" from database
node A writes Bob's new data to the system with admin privileges
Node B then tells the system that was trying to delete Bob "Bob sucessfully deleted"
Bob steals all of the company's secretsThis is not desirable. However, some strongly consistent databases let us
perform compare-and-swap operations that let us atomically update Bob, without
losing any intermediate updates.
node A reads user Bob's data
node A locally updates Bob's data to add admin privileges
node B deletes Bob's data to remove all privileges
node B receives "write successful" from database
node A tries to do "update Bob unless changed since read" which fails
Node B then tells the system that was trying to delete Bob "Bob sucessfully deleted"
Bob is locked out and steals nothingAtomic updates generally significantly reduce the cognitive burden for
building a system which must store and react to state changes in different
components. Watches are another significant help for this, as they
notify interested systems in relevant changes.
Watches are how we apply the event-driven model to our database.
When interesting changes happen, interested parties react to them.
This decouples the emitter from the reactor, greatly simplifying
interactions in the system. Without watches, we need to
have some way of detecting changes in a database. If we can scan through
all keys, we can do an O(N) traversal of the entire database, which
does not scale very high, but could work alright. If we don't have
the ability to scan through all data, we may actually have no way of
learning what changes are unless there is a top-level key that is set
that holds everything. This does not scale beyond a couple kilobytes.
Storage Options
stateless gateway services backed by zk/etcd/consul cluster, abstracted by docker/libkv
pros
- flexible support for the three most popular configuration databases
- very clear operational characteristics, does not confuse anyone about what's happening
- the gateway is fully stateless, easy to autoscale, clear semantics for operators
- lowest amount of work for us
- write once, run anywhere in the same way
- allows users to easily take advantage of existing database skills, tools, backup tools, monitoring, etc...
cons
- requires users to run their own cluster (they are already running things though, so this isn't a high marginal cost)
embedded etcd in gateway, cluster of 3 or 5 active as "leaders", rest of gateways are stateless
pros
- single binary
cons
- unclear operational characteristics, when the cluster gets wedged it may be extremely hard to debug
- harder than running your own cluster, because you can't reuse existing database skills
- very hard on operators when things go wrong
- harder on operators to get things safely set up
- still effectively have a separate cluster if you want to autoscale without accidentally losing leaders
- creating a reliable "autopilot" etcd deployment system took tyler 4 months in the past
embedded etcd in gateway, single node configured as "leader", rest of gateways are stateless
pros
- easy to set up
- can be used in combination with a separate etcd cluster for the best of both worlds
- does not require setting up a cluster to try out, demo, or run in small deployments
- clear operational characteristics, everyone knows it's not reliable
cons
- single point of failure on the single leader node
dynamo + spanner + cosmosdb + on prem other databases
pros
- single binary
- easy deployment for users on cloud providers
- one fewer piece to keep running
cons
- dramatically increases the complexity for building the system for us
- we need to target multiple consistency models (hard+++)
- need to target systems without watch semantics (hard+++)
- need to target systems without atomic write semantics (hard+++)
- we need to spend much more effort on testing
- we need to spend much more effort on fixing bugs
- we need to spend much more effort on writing monitoring code
- we need to master all of these databases in order to program against them
eventually consistent gossip-based config sharing with CRDTs
pros
- single binary
- no external dependencies, even on databases
cons
- if all instances go away, the configuration is gone
- we're basically building our own distributed database (hard+++++)
- need to create backup tooling
- unclear operational characteristics for people
- huge extra effort needs to go into correctness testing
just config files, reloaded on file change
pros
- single binary
- flexible
- can be used with any of the other approaches to decouple functionality
cons
- users don't get an API for the gateway
Recommendation
Keep the system stateless to make it easy to operate. Store state
in a database that supports atomic updates and watches, such as
etcd, zookeeper, or consul. Use the docker/libkv library to
support all 3 backing databases. This lets us treat every
deployment environment the same way, regardless of whether
it's in a cloud provider or on-prem. This combination of
operational clarity and database semantics will significantly
lower the amount of effort we need to put into engineering over time.
Purely for demo and trial purposes, allow a gateway to be started
with an --embedded-master flag which will start an embedded etcd instance
that other gateways can use as a shared backing store. This allows people to
try out the system without standing up an etcd cluster first.
The main downside, having to run a separate cluster in production, is probably
not a big deal for people who are interested in running this themselves.
Assumptions relating to this cost, and it not seeming very high:
- most users will fall into these camps:
- small/medium orgs interested in trying out locally but using the SaaS offering
- orgs with more significant engineering resources who already run zk/etcd/consul
- orgs with more significant engineering resources who are not averse to running zk/etcd/consul
- orgs who are comfortable paying compose.com $30/mo for hosted etcd
- users who are not interested in paying for SaaS but still want to run the gateway
themselves WITHOUT running a database are unlikely to be very upset when they occasionally
need to reconfigure the gateway when the single master goes away.
Subscription Types (`docs/subscription-types.md`)
Subscription Types
Event Gateway supports two subscription types: async and sync.
`async` subscription
async subscription implements lightweight pub/sub system. There can be many async subscriptions listening to the same event type, on the same path and HTTP method. The function is asynchronously invoked by the Event Gateway.
`sync` subscription
In case of sync subscription invoked function can control the HTTP response returned by the Event Gateway. Because of that, there can be only one sync subscription for a path, HTTP method, and event type tuple.
Status code, headers and response body can be controlled by returning JSON object with the following fields:
statusCode-int- response status code, default: 200headers-object- response headersbody-string- response body. Currently, the Event Gateway supports only string responses.
If the function invocation failed or the backing function didn't return JSON object in the above format Event Gateway returns 500 Internal Server Error.
Path parameters
The Event Gateway allows creating sync subscription with parameterized paths. Every path segment prefixed with : is
treated as a parameter, e.g. /users/:id.
The Event Gateway prevents from creating subscriptions in following conflicting situations:
- registering static path when there is parameterized path registered already (
/users/:idvs./users/foo) - registering parameterized path with different parameter name (
/users/:idvs./users/:name)
Key and value of matched parameters are passed to a function in an HTTP Request Event under params field.
Wildcard parameters
Special type of path parameter is wildcard parameter. It's a path segment prefixed with *. Wildcard parameter can only
be specified at the end of the path and will match every character till the end of the path. For examples
parameter /users/*userpath for request path /users/group1/user1 will match group1/user1 as a userpath parameter.
System Events And Plugin System (`docs/system-events-and-plugin-system.md`)
System Events and Plugin System
System Events
System Events are special type of events emitted by the Event Gateway instance internally. They are emitted on each stage
of event processing flow starting from receiving event to function invocation end. Those events are:
eventgateway.event.received- the event is emitted when an event was received by Events API. Data fields:event- event payloadpath- Events API pathheaders- HTTP request headers
eventgateway.function.invoking- the event emitted before invoking a function. Data fields:space- space nameevent- event payloadfunctionId- registered function ID
eventgateway.function.invoked- the event emitted after successful function invocation. Data fields:space- space nameevent- event payloadfunctionId- registered function IDresult- function response
eventgateway.function.invocationFailed- the event emitted after failed function invocation. Data fields:space- space nameevent- event payloadfunctionId- registered function IDerror- invocation error
Plugin System
The Event Gateway is built with extensibility in mind. Built-in plugin system allows reacting on system events and
manipulate how an event is processed through the Event Gateway.
Current implementation supports plugins written only in Golang. We plan to support other languages in the future.
Plugin system is based on go-plugin. A plugin needs to implement the following
interface:
type Reacter interface {
Subscriptions() []Subscription
React(event event.Event) error
}Subscription model indicates the event that plugin subscribes to and the subscription type. A subscription can be either
sync or async. Sync (blocking) subscription means that in case of error returned from React method the event won't be
further processed by the Event Gateway.
React method is called for every system event that plugin subscribed to.
For more details, see the example plugin.
Openapi Openapi Config Api.Yaml (`docs/openapi/openapi-config-api.yaml`)
openapi: "3.0.1"
info:
title: Event Gateway - Configuration API
version: "0.8.1"
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
contact:
name: Serverless, Inc.
url: http://www.serverless.com
servers:
- url: http://localhost:4001/{basePath}
variables:
basePath:
default: v1
tags:
name: "event type"
name: "function"
name: "subscription"
name: "cors"
paths:
/spaces/{spaceName}/eventtypes:
summary: "Operations about event types"
get:
summary: "List event types"
tags:
- "event type"
operationId: "ListEventTypes"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/Filters"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/EventTypes"
500:
$ref: '#/components/responses/Error'
post:
summary: "Create event type"
tags:
- "event type"
operationId: "CreateEventType"
parameters:
- $ref: "#/components/parameters/Space"
requestBody:
$ref: "#/components/requestBodies/CreateEventType"
responses:
201:
content:
application/json:
schema:
$ref: "#/components/schemas/EventType"
400:
$ref: '#/components/responses/ValidationError'
500:
$ref: '#/components/responses/Error'/spaces/{spaceName}/eventtypes/{eventTypeName}:
summary: "Operations about single event type"
get:
summary: "Get event type"
tags:
- "event type"
operationId: "GetEventType"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/EventTypeName"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/EventType"
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
put:
summary: "Update event type"
tags:
- "event type"
operationId: "UpdateEventType"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/EventTypeName"
requestBody:
$ref: "#/components/requestBodies/UpdateEventType"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/EventType"
400:
$ref: '#/components/responses/ValidationError'
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
delete:
summary: "Delete event type"
tags:
- "event type"
operationId: "DeleteEventType"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/EventTypeName"
responses:
204:
400:
$ref: '#/components/responses/EventTypeHasSubscriptionsError'
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'/spaces/{spaceName}/functions:
summary: "Operations about functions"
get:
summary: "List functions"
tags:
- "function"
operationId: "ListFunctions"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/Filters"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Functions"
500:
$ref: '#/components/responses/Error'
post:
summary: "Register function"
tags:
- "function"
operationId: "CreateFunction"
parameters:
- $ref: "#/components/parameters/Space"
requestBody:
$ref: "#/components/requestBodies/CreateFunction"
responses:
201:
content:
application/json:
schema:
$ref: "#/components/schemas/Function"
400:
$ref: '#/components/responses/ValidationError'
500:
$ref: '#/components/responses/Error'/spaces/{spaceName}/functions/{functionId}:
summary: "Operations about single function"
get:
summary: "Get function"
tags:
- "function"
operationId: "GetFunction"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/FunctionID"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Function"
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
put:
summary: "Update function"
tags:
- "function"
operationId: "UpdateFunction"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/FunctionID"
requestBody:
$ref: "#/components/requestBodies/UpdateFunction"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Function"
400:
$ref: '#/components/responses/ValidationError'
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
delete:
summary: "Delete function"
tags:
- "function"
operationId: "DeleteFunction"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/FunctionID"
responses:
204:
400:
$ref: '#/components/responses/FunctionHasSubscriptionsError'
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'/spaces/{spaceName}/subscriptions:
summary: "Operations about subscriptions"
get:
summary: "List subscriptions"
tags:
- "subscription"
operationId: "ListSubscriptions"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/Filters"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Subscriptions"
500:
$ref: '#/components/responses/Error'
post:
summary: "Create subscription"
tags:
- "subscription"
operationId: "CreateSubscription"
parameters:
- $ref: "#/components/parameters/Space"
requestBody:
$ref: "#/components/requestBodies/CreateSubscription"
responses:
201:
content:
application/json:
schema:
$ref: "#/components/schemas/Subscription"
400:
$ref: '#/components/responses/ValidationError'
500:
$ref: '#/components/responses/Error'/spaces/{spaceName}/subscription/{subscriptionId}:
summary: "Operations about single subscription"
get:
summary: "Get subscription"
tags:
- "subscription"
operationId: "GetSubscription"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/SubscriptionID"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Subscription"
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
put:
summary: "Update subscription"
tags:
- "subscription"
operationId: "UpdateSubscription"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/SubscriptionID"
requestBody:
$ref: "#/components/requestBodies/UpdateSubscription"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/Subscription"
400:
$ref: '#/components/responses/ValidationError'
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
delete:
summary: "Delete subscription"
tags:
- "subscription"
operationId: "DeleteSubscription"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/SubscriptionID"
responses:
204:
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'/spaces/{spaceName}/cors:
summary: "Operations about CORS configuration"
get:
summary: "List CORS configurations"
tags:
- "cors"
operationId: "ListCORS"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/Filters"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/CORSes"
500:
$ref: '#/components/responses/Error'
post:
summary: "Create CORS configuration"
tags:
- "cors"
operationId: "CreateCORS"
parameters:
- $ref: "#/components/parameters/Space"
requestBody:
$ref: "#/components/requestBodies/CreateCORS"
responses:
201:
content:
application/json:
schema:
$ref: "#/components/schemas/CORS"
400:
$ref: '#/components/responses/ValidationError'
500:
$ref: '#/components/responses/Error'/spaces/{spaceName}/cors/{corsId}:
summary: "Operations about single CORS configuration"
get:
summary: "Get CORS configuration"
tags:
- "cors"
operationId: "GetCORS"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/CORSID"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/CORS"
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
put:
summary: "Update CORS configuration"
tags:
- "cors"
operationId: "UpdateCORS"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/CORSID"
requestBody:
$ref: "#/components/requestBodies/UpdateCORS"
responses:
200:
content:
application/json:
schema:
$ref: "#/components/schemas/CORS"
400:
$ref: '#/components/responses/ValidationError'
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
delete:
summary: "Delete CORS configuration"
tags:
- "cors"
operationId: "DeleteCORS"
parameters:
- $ref: "#/components/parameters/Space"
- $ref: "#/components/parameters/CORSID"
responses:
204:
404:
$ref: '#/components/responses/NotFoundError'
500:
$ref: '#/components/responses/Error'
components:
schemas:
SpaceName:
type: string
FunctionID:
type: string
SubscriptionID:
type: string
CORSID:
type: string
SubscriptionType:
type: string
enum:
- async
- sync
ProviderType:
type: string
enum:
- awsfirehose
- awskinesis
- awslambda
- awssqs
- http
Provider:
type: object
oneOf:
- $ref: '#/components/schemas/AWSFirehose'
- $ref: '#/components/schemas/AWSKinesis'
- $ref: '#/components/schemas/AWSLambda'
- $ref: '#/components/schemas/AWSSQS'
- $ref: '#/components/schemas/HTTP'
EventType:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
name:
$ref: '#/components/schemas/EventTypeName'
authorizerId:
$ref: '#/components/schemas/FunctionID'
Function:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
functionId:
$ref: '#/components/schemas/FunctionID'
type:
$ref: '#/components/schemas/ProviderType'
provider:
$ref: '#/components/schemas/Provider'
EventTypes:
type: object
properties:
eventTypes:
type: array
items:
$ref: '#/components/schemas/EventType'
Functions:
type: object
properties:
functions:
type: array
items:
$ref: '#/components/schemas/Function'
Subscription:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
subscriptionId:
$ref: '#/components/schemas/SubscriptionID'
type:
$ref: '#/components/schemas/SubscriptionType'
eventType:
$ref: '#/components/schemas/EventType'
functionId:
$ref: '#/components/schemas/FunctionID'
path:
$ref: '#/components/schemas/Path'
method:
$ref: '#/components/schemas/Method'
Subscriptions:
type: object
properties:
subscriptions:
type: array
items:
$ref: '#/components/schemas/Subscription'
CORS:
type: object
properties:
space:
$ref: '#/components/schemas/SpaceName'
corsId:
$ref: '#/components/schemas/CORSID'
method:
$ref: '#/components/schemas/Method'
path:
$ref: '#/components/schemas/Path'
allowedOrigins:
$ref: '#/components/schemas/AllowedOrigins'
allowedMethods:
$ref: '#/components/schemas/AllowedMethods'
allowedHeaders:
$ref: '#/components/schemas/AllowedHeaders'
allowCredentials:
$ref: '#/components/schemas/AllowCredentials'
CORSes:
type: object
properties:
cors:
type: array
items:
$ref: '#/components/schemas/CORS'
AWSFirehose:
type: object
properties:
deliveryStreamName:
$ref: '#/components/schemas/DeliveryStreamName'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
AWSKinesis:
type: object
properties:
streamName:
$ref: '#/components/schemas/StreamName'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
AWSLambda:
type: object
properties:
arn:
$ref: '#/components/schemas/ARN'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
AWSSQS:
type: object
properties:
queueUrl:
$ref: '#/components/schemas/QueueURL'
region:
$ref: '#/components/schemas/Region'
awsAccessKeyId:
$ref: '#/components/schemas/AWSAccessKeyId'
awsSecretAccessKey:
$ref: '#/components/schemas/AWSSecretAccessKey'
awsSessionToken:
$ref: '#/components/schemas/AWSSessionToken'
HTTP:
type: object
properties:
url:
$ref: '#/components/schemas/URL'
ARN:
type: string
Region:
type: string
AWSAccessKeyId:
type: string
AWSSecretAccessKey:
type: string
AWSSessionToken:
type: string
StreamName:
type: string
DeliveryStreamName:
type: string
QueueURL:
type: string
URL:
type: string
format: url
EventTypeName:
type: string
Method:
type: string
Path:
type: string
default: "/"
AllowedOrigins:
type: array
items:
type: string
AllowedMethods:
type: array
items:
type: string
AllowedHeaders:
type: array
items:
type: string
AllowCredentials:
type: boolean
Error:
type: object
properties:
message:
type: string
Errors:
type: object
properties:
errors:
type: array
items:
$ref: "#/components/schemas/Error"
parameters:
Space:
in: "path"
name: "spaceName"
required: true
schema:
$ref: "#/components/schemas/SpaceName"
EventTypeName:
in: "path"
name: "eventTypeName"
required: true
schema:
$ref: "#/components/schemas/EventTypeName"
FunctionID:
in: "path"
name: "functionId"
required: true
schema:
$ref: "#/components/schemas/FunctionID"
SubscriptionID:
in: "path"
name: "subscriptionId"
required: true
schema:
$ref: "#/components/schemas/SubscriptionID"
CORSID:
in: "path"
name: "corsId"
required: true
schema:
$ref: "#/components/schemas/CORSID"
Filters:
in: "query"
name: "filters"
style: form
example:
"metadata.service": usersService
schema:
type: object
additionalProperties:
type: string
requestBodies:
CreateEventType:
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
$ref: '#/components/schemas/EventTypeName'
authorizerId:
$ref: '#/components/schemas/FunctionID'
UpdateEventType:
content:
application/json:
schema:
type: object
properties:
authorizerId:
$ref: '#/components/schemas/FunctionID'
CreateFunction:
content:
application/json:
schema:
type: object
required:
- functionId
- type
- provider
properties:
functionId:
$ref: '#/components/schemas/FunctionID'
type:
$ref: '#/components/schemas/ProviderType'
provider:
$ref: '#/components/schemas/Provider'
UpdateFunction:
content:
application/json:
schema:
type: object
properties:
type:
$ref: '#/components/schemas/ProviderType'
provider:
$ref: '#/components/schemas/Provider'
CreateSubscription:
content:
application/json:
schema:
type: object
required:
- type
- eventType
- functionId
properties:
type:
$ref: '#/components/schemas/SubscriptionType'
eventType:
$ref: '#/components/schemas/EventType'
functionId:
$ref: '#/components/schemas/FunctionID'
path:
$ref: '#/components/schemas/Path'
method:
$ref: '#/components/schemas/Method'
UpdateSubscription:
content:
application/json:
schema:
type: object
properties:
type:
$ref: '#/components/schemas/SubscriptionType'
eventType:
$ref: '#/components/schemas/EventType'
functionId:
$ref: '#/components/schemas/FunctionID'
path:
$ref: '#/components/schemas/Path'
method:
$ref: '#/components/schemas/Method'
CreateCORS:
content:
application/json:
schema:
type: object
properties:
method:
$ref: '#/components/schemas/Method'
path:
$ref: '#/components/schemas/Path'
allowedOrigins:
$ref: '#/components/schemas/AllowedOrigins'
allowedMethods:
$ref: '#/components/schemas/AllowedMethods'
allowedHeaders:
$ref: '#/components/schemas/AllowedHeaders'
allowCredentials:
$ref: '#/components/schemas/AllowCredentials'
UpdateCORS:
content:
application/json:
schema:
type: object
properties:
method:
$ref: '#/components/schemas/Method'
path:
$ref: '#/components/schemas/Path'
allowedOrigins:
$ref: '#/components/schemas/AllowedOrigins'
allowedMethods:
$ref: '#/components/schemas/AllowedMethods'
allowedHeaders:
$ref: '#/components/schemas/AllowedHeaders'
allowCredentials:
$ref: '#/components/schemas/AllowCredentials'
responses:
Error:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
NotFoundError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
ValidationError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
FunctionHasSubscriptionsError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
EventTypeHasSubscriptionsError:
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
--- METRICS ---
- Files Extracted: 20
- Estimated Token Budget: ~29637 tokens
- Canonical Reference: https://codewiki.google/github.com/serverless/gateway