From 0892e1c842d37187f3e9922ab9d1b56c88399d53 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Mon, 18 Jun 2018 17:55:19 -0700 Subject: [PATCH] First pass at a primer Signed-off-by: Doug Davis --- README.md | 11 +- about/references.md | 200 ------------- about/use-cases.md | 120 -------- primer.md | 617 ++++++++++++++++++++++++++++++++++++++++ source-event-action.png | Bin 0 -> 14563 bytes spec.md | 167 +---------- 6 files changed, 622 insertions(+), 493 deletions(-) delete mode 100644 about/references.md delete mode 100644 about/use-cases.md create mode 100644 primer.md create mode 100644 source-event-action.png diff --git a/README.md b/README.md index ac03435fe..820996fb6 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,8 @@ The following specifications are available: | **AMQP Transport Binding** | - | [master](https://github.com/cloudevents/spec/blob/master/amqp-transport-binding.md) | There is also the [CloudEvents Extension Attributes](https://github.com/cloudevents/spec/blob/master/extensions.md) -document. - -Included within this repository are some documents that helped guide the -working group's design decisions. They include: -- [Use-Cases](about/use-cases.md) from the community -- [Existing events](about/references.md) from some popular implementations - in the community +document and a [CloudEvents Primer](primer.md) to help understand some of +the history and design goals of the working group. ## Community @@ -58,7 +53,7 @@ something to share about your use of CloudEvents, please submit a PR! ## Working Group process The CNCF Serverless WG is working to formalize the [specification](spec.md) -based on [design goals](spec.md#design-goals) which focus on interoperability +based on [design goals](primer.md#design-goals) which focus on interoperability between systems which generate and respond to events. In order to achieve these goals, the Serverless WG must describe: diff --git a/about/references.md b/about/references.md deleted file mode 100644 index 973e9cc71..000000000 --- a/about/references.md +++ /dev/null @@ -1,200 +0,0 @@ -# References - -Examples of current event formats that exist today. - -### Microsoft - Event Grid -``` -{ - "topic":"/subscriptions/{subscription-id}", - "subject":"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501", - "eventType":"Microsoft.Resources.ResourceWriteSuccess", - "eventTime":"2017-08-16T03:54:38.2696833Z", - "id":"25b3b0d0-d79b-44d5-9963-440d4e6a9bba", - "data": { - "authorization":"{azure_resource_manager_authorizations}", - "claims":"{azure_resource_manager_claims}", - "correlationId":"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6", - "httpRequest":"", - "resourceProvider":"Microsoft.EventGrid", - "resourceUri":"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501", - "operationName":"Microsoft.EventGrid/eventSubscriptions/write", - "status":"Succeeded", - "subscriptionId":"{subscription-id}", - "tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47" - } -} -``` -[Documentation](https://docs.microsoft.com/en-us/azure/event-grid/event-schema) - -### Google - Cloud Functions (potential future) -``` -{ - "data": { - "@type": "types.googleapis.com/google.pubsub.v1.PubsubMessage", - "attributes": { - "foo": "bar", - }, - "messageId": "12345", - "publishTime": "2017-06-05T12:00:00.000Z", - "data": "somebase64encodedmessage" - }, - "context": { - "eventId": "12345", - "timestamp": "2017-06-05T12:00:00.000Z", - "eventTypeId": "google.pubsub.topic.publish", - "resource": { - "name": "projects/myProject/topics/myTopic", - "service": "pubsub.googleapis.com" - } - } -} -``` - -### AWS - SNS -``` -{ - "Records": [ - { - "EventVersion": "1.0", - "EventSubscriptionArn": eventsubscriptionarn, - "EventSource": "aws:sns", - "Sns": { - "SignatureVersion": "1", - "Timestamp": "1970-01-01T00:00:00.000Z", - "Signature": "EXAMPLE", - "SigningCertUrl": "EXAMPLE", - "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", - "Message": "Hello from SNS!", - "MessageAttributes": { - "Test": { - "Type": "String", - "Value": "TestString" - }, - "TestBinary": { - "Type": "Binary", - "Value": "TestBinary" - } - }, - "Type": "Notification", - "UnsubscribeUrl": "EXAMPLE", - "TopicArn": topicarn, - "Subject": "TestInvoke" - } - } - ] -} -``` -[Documentation](http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html) - -### AWS - Kinesis -``` -{ - "Records": [ - { - "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", - "eventVersion": "1.0", - "kinesis": { - "partitionKey": "partitionKey-3", - "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", - "kinesisSchemaVersion": "1.0", - "sequenceNumber": "49545115243490985018280067714973144582180062593244200961" - }, - "invokeIdentityArn": identityarn, - "eventName": "aws:kinesis:record", - "eventSourceARN": eventsourcearn, - "eventSource": "aws:kinesis", - "awsRegion": "us-east-1" - } - ] -} -``` - -### IBM - OpenWhisk - Web Action Event -``` -{ - "__ow_method": "post", - "__ow_headers": { - "accept": "*/*", - "connection": "close", - "content-length": "4", - "content-type": "text/plain", - "host": "172.17.0.1", - "user-agent": "curl/7.43.0" - }, - "__ow_path": "", - "__ow_body": "Jane" -} -``` - -### OpenStack - Audit Middleware - Event -``` -{ - "typeURI": "http://schemas.dmtf.org/cloud/audit/1.0/event", - "id": "d8304637-3f63-5092-9ab3-18c9781871a2", - "eventTime": "2018-01-30T10:46:16.740253+00:00", - "action": "delete", - "eventType": "activity", - "outcome": "success", - "reason": { - "reasonType": "HTTP", - "reasonCode": "204" - }, - "initiator": { - "typeURI": "service/security/account/user", - "name": "user1", - "domain": "domain1", - "id": "52d28347f0b4cf9cc1717c00adf41c74cc764fe440b47aacb8404670a7cd5d22", - "host": { - "address": "127.0.0.1", - "agent": "python-novaclient" - }, - "project_id": "ae63ddf2076d4342a56eb049e37a7621" - }, - "target": { - "typeURI": "compute/server", - "id": "b1b475fc-ef0a-4899-87f3-674ac0d56855" - }, - "observer": { - "typeURI": "service/compute", - "name": "nova", - "id": "1b5dbef1-c2e8-5614-888d-bb56bcf65749" - }, - "requestPath": "/v2/ae63ddf2076d4342a56eb049e37a7621/servers/b1b475fc-ef0a-4899-87f3-674ac0d56855" -} -``` -[Documentation](https://github.com/openstack/pycadf/blob/master/doc/source/event_concept.rst) - - -### Adobe - I/O Events -``` -{ - "event_id": "639fd17a-d0bb-40ca-83a4-e78612bce5dc", - "event": { - "@id": "82235bac-2b81-4e70-90b5-2bd1f04b5c7b", - "@type": "xdmCreated", - "xdmEventEnvelope:objectType": "xdmAsset", - "activitystreams:to": { - "xdmImsUser:id": "D13A1E7053E46A220A4C86E1@AdobeID", - "@type": "xdmImsUser" - }, - "activitystreams:generator": { - "xdmContentRepository:root": "https://cc-api-storage.adobe.io/", - "@type": "xdmContentRepository" - }, - "activitystreams:actor": { - "xdmImsUser:id": "D13A1E7053E46A220A4C86E1@AdobeID", - "@type": "xdmImsUser" - }, - "activitystreams:object": { - "@type": "xdmAsset", - "xdmAsset:asset_id": "urn:aaid:sc:us:4123ba4c-93a8-4c5d-b979-ffbbe4318185", - "xdmAsset:asset_name": "example.jpg", - "xdmAsset:etag": "6fc55d0389d856ae7deccebba54f110e", - "xdmAsset:path": "/MyFolder/example.jpg", - "xdmAsset:format": "image/jpeg" - }, - "activitystreams:published": "2016-07-16T19:20:30+01:00" - } -} -``` -[Documentation](https://www.adobe.io/apis/cloudplatform/events/documentation.html) diff --git a/about/use-cases.md b/about/use-cases.md deleted file mode 100644 index e29ab2faa..000000000 --- a/about/use-cases.md +++ /dev/null @@ -1,120 +0,0 @@ -# CloudEvents - Use Cases - -[WIP] Use-case examples to help end users understand the value of CloudEvents. - -### Normalizing Events Across Services & Platforms - -Major event publishers (e.g. AWS, Microsoft, Google, etc.) all publish events -in different formats on their respective platforms. There are even a few cases -where services on the same provider publish events in different formats (e.g. -AWS). This forces event consumers to implement custom logic to read or munge -event data across platforms and occasionally across services on a single -platform. - -CloudEvents can offer a single experience for authoring consumers that handle -events across all platforms and services. - -### Facilitating Integrations Across Services & Platforms - -Event data being transported across environments is increasingly common. -However, without a common way of describing events, delivery of events across -environments is hindered. There is no single way of determining where an event -came from and where it might be going. This prevents tooling to facilitate -successful event delivery and consumers from knowing what to do with event -data. - -CloudEvents offers useful metadata which middleware and consumers can rely upon -to facilitate event routing, logging, delivery and receipt. - -### Increasing Portability of Functions-as-a-Service - -Functions-as-a-Service (also known as serverless computing) is one of the -fastest growing trends in IT and it is largely event-driven. However, a -primary concern of FaaS is vendor lock-in. This lock-in is partially caused -by differences in function APIs and signatures across providers, but the -lock-in is also caused by differences in the format of event data received -within functions. - -CloudEvents' common way of describing event data increases the portability of -Functions-as-a-Service. - -### Improving Development & Testing of Event-Driven/Serverless Architectures - -The lack of a common event format complicates development and testing of -event-driven and serverless architectures. There is no easy way to mock events -accurately for development and testing purposes, and help emulate event-driven -workflows in a development environment. - -CloudEvents can enable better developer tools for building, testing and -handling the end-to-end lifecycle of event-driven and serverless architectures. - -### Event Data Evolution - -Most platforms and services version the data model of their events differently -(if they do this at all). This creates an inconsistent experience for -publishing and consuming the data model of events as those data models evolve. - -CloudEvents can offer a common way to version and evolve event data. This will -help event publishers safely version their data models based on best practices, -and this help event consumers safely work with event data as it evolves. - -### Normalizing Webhooks - -Webhooks is a style of event publishing which does not use a common format. -Consumers of webhooks don’t have a consistent way to develop, test, identify, -validate, and overall process event data delivered via webhooks. - -CloudEvents can offer consistency in webhook publishing and consumption. - -### Policy Enforcement - -The transiting of events between systems may need to be filtered, transformed, -or blocked due to security and policy concerns. Examples may be to prevent -ingress or egress of the events such as event data containing sensitive -information or wanting to disallow the information flow between the sender and -receiver. - -A common event format would allow easier reasoning about the data being -transited and allow for better introspection of the data. - -### Event Tracing - -An event sent from a source may result in a sequence of additional events -sent from various middleware devices such as event brokers and gateways. -CloudEvents includes metadata in events to associate these events as being -part of an event sequence for the purpose of event tracing and -troubleshooting. - -An event sent from a source may result in a sequence of additional events -sent from various middleware devices such as event brokers and gateways. -CloudEvents includes metadata in events to associate these events as being -part of an event sequence for the purpose of event tracing and -troubleshooting. - -### Cloudbursting - -### IoT - -IoT devices send and receive events related to their functionality. -For example, a connected thermostat will send telemetry on the current -temperature and could receive events to change temperatures. -These devices typically have a constrained operating environment -(cpu, memory) requiring a well defined event message format. -In a lot of cases these messages are binary encoded instead of textual. -Whether directly from the device or transformed via a gateway, CloudEvents -would allow for a better description of the origin of the message and the -format of the data contained within the message. - -### Event Correlation - -A serverless application/workflow could be associated with multiple events from -different event sources/producers. For example, a burglary detection -application/workflow could involve both a motion event and a door/window open event. -A serverless platform could receive many instances of each type of events, -e.g. it could receive motion events and window open events from different houses. -The serverless platform needs to correlate one type of event instance correctly -with other types of event instances and map a received event instance to the -correct application/workflow instance. CloudEvents will provide a standard way -for any event consumer (eg. the serverless platform) to locate the event -correlation information/token in the event data and map a received event instance -to the correct application/workflow instance. diff --git a/primer.md b/primer.md new file mode 100644 index 000000000..08c0dc0f2 --- /dev/null +++ b/primer.md @@ -0,0 +1,617 @@ +# CloudEvents Primer + +## Abstract + +This non-normative document provides an overview of the CloudEvents +specification. It is meant to compliment the CloudEvent specification +to provide additional background and insight into the history and +design decisions made during the development of the specification. This +allows the specification itself to focus on the normative technical +details. + +## Status of this document + +This document is a working draft. + +## Table of Contents + +- [History](#history) +- [CloudEvents Concepts](#cloudevents-concepts) +- [Design Goals](#design-goals) +- [CloudEvent Attributes Extensions](#cloudevent-attribute-extensions) +- [Prior Art](#prior-art) +- [Roles](#roles) +- [Value Proposition](#value-proposition) +- [Existing Event Formats](#existing-event-formats) + +## History + +The [CNCF Serverless Working group](https://github.com/cncf/wg-serverless) +was originally created by the CNCF's +[Technical Oversight Committee](https://github.com/cncf/toc) to investigate +Serverless Technology and to recommend some possible next steps for some +CNCF related activities in this space. One of the recommendations was to +investigate the creation of a common event format to aid in the +portability of functions between Cloud providers and the interoperability of +processing of event streams. As a result, the CloudEvents specification +was created. + +While initially the work on CloudEvents was done as part of the Serverless +Working group, once the specification reached its v0.1 milestone, the TOC +approved the CloudEvents work as a new stand-alone CNCF sandbox project. + +## CloudEvents Concepts + +An [event](spec.md#event) includes context and data about an +[occurrence](spec.md#occurrence). Each *occurrence* is uniquely +identified by the data of the *event*. + +*Events* represent facts and therefore do not include a destination, whereas +messages convey intent, transporting data from a source to a given destination. + +### Eventing + +Events are commonly used in server-side code to connect disparate systems where +the change of state in one system causes code to execute in another. For +example, a source may generate an event when it receives an external signal +(e.g. HTTP or RPC) or observes a changing value (e.g. an IoT sensor or period of +inactivity). + +To illustrate how a system uses CloudEvents, the simplified diagram below shows +how an event from a [source](spec.md#source) triggers an action. + +![alt text](source-event-action.png "A box representing the source with +arrow pointing to a box representing the action. The arrow is annotated with +'e' for event and 'protocol'.") + +The source generates a message where the event is encapsulated in a protocol. +The event arrives to a destination, triggering an action which is provided with +the event data. + +A *source* is a specific instance of a source-type which +allows for staging and test instances. Open source software of a specific +*source-type* may be deployed by multiple companies or providers. + +Events can be delivered through various industry standard protocols (e.g. HTTP, +AMQP, MQTT, SMTP), open-source protocols (e.g. Kafka, NATS), or platform/vendor +specific protocols (AWS Kinesis, Azure Event Grid). + +An action processes an event defining a behavior or effect which was +triggered by a specific *occurrence* from a specific *source*. While outside +of the scope of the specification, the purpose of generating an *event* is +typcially to allow other systems to easily react to changes in a source that +they do not control. The *source* and action are typically built by different +developers. Often the *source* is a managed service and the *action* is custom +code in a serverless Function (such as AWS Lambda or Google Cloud Functions). + +## Design Goals + +CloudEvents are typically used in a distributed system to allow for services to +be loosely coupled during development, deployed independently, and later +can be connected to create new applications. + +The goal of the CloudEvents specification is to define interoperability of event +systems that allow services to produce or consume events, where the producer and +consumer can be developed and deployed independently. A producer can generate +events before a consumer is listening, and a consumer can express an interest in +an event or class of events that is not yet being produced. Note that the +specifications produced by this effort are focused on interoperability of the +event format and how it appears while being sent on various transports, +such as HTTP. The specifications will not focus on the processing model of +either the event producer or event consumer. + +CloudEvents, at its core, defines a set of metadata, called attributes, about +the event being transferred between systems, and how those pieces of metadata +should appear in that message. The metadata is not meant to duplicate any of +the application data of the event itself, rather it is just the minimal +information needed to route the request to the proper component that will +process the event. + +Along with the definition of these attributes, there will also be +specifications of how to serialize the event in different formats and +transports (e.g. JSON and HTTP). + +*Cathy* Insert additional text (examples, usecases...) about "properties" here. + +### Non-Goals +The following will not be part of the specification: +* Function build and invocation process +* Language-specific runtime APIs +* Selecting a single identity/access control system + +## CloudEvent Attribute Extensions + +In order to achieve the stated goals, the working group will attempt to +constrain the number of metadata attributes they define in CloudEvents. To +that end, attributes defined by this working group will fall into three + categories: +- required +- optional +- extensions + +As the category names imply, "required" attributes will be the ones that +the working group considers vital to all events in all use cases, while +"optional" ones will be used in a majority of the cases. Both of the attributes +in these cases will be defined within the specfication itself. + +When the working group determines that an attribute is not common enough to +fall into those two categories but would still benefit from the level of +interoperability that comes from being well-defined, then they will be placed +into the "extensions" category and put into the (extensions)[extensions.md] +document. The specification defines how these extension attributes will +appear within a CloudEvent. + +In determining which category a proposed attribute belongs, or even if it +will be included at all, the working group uses use-cases and +user-stories to explain the rationale and need for them. This supporting +information will be added to the [Prior Art](#prior-art) section of this +document. + +## Prior Art + +This section describes some of the input material used by the working group +during the development of the CloudEvent specification. + +### Roles + +The list below enumerates the various participants, and scenarios, that might +be involved in the producing, managing or consuming of events. + +In these the roles of event producer and event consumer are kept +distinct. A single application context can always take on multiple roles +concurrently, including being both a producer and a consumer of events. + +1) Applications produce events for consumption by other parties, for instance + for providing consumers with insights about end-user activities, state + changes or environment observations, or for allowing complementing the + application's capabilities with event-driven extensions. + + Events are typically produced related to a context or a producer-chosen + classification. For example, a temperature sensor in a room might be + context-qualified by mount position, room, floor, and building. A sports + result might be classified by league and team. + + The producer application could run anywhere, such as on a server or a device. + + The produced events might be rendered and emitted directly by the producer + or by an intermediary; as example for the latter, consider event data + transmitted by a device over payload-size-constrained networks such as + LoRaWAN or ModBus, and where events compliant to this + specification will be rendered by a network gateway on behalf of the + producer. + + For example, a weather station transmits a 12-byte, proprietary event + payload indicating weather conditions once every 5 minutes over LoRaWAN. A + LoRaWAN gateway is then used to publish the event to an Internet destination + in the CloudEvents format. The LoRaWAN gateway is the event producer, + publishing on behalf of the weather station, and will set event metadata + appropriately to reflect the source of the event. + +2) Applications consume events for the purposes such as display, archival, + analytics, workflow processing, monitoring the condition and/or providing + transparency into the operation of a business solution and its foundational + building blocks. + + The consumer application could run anywhere, such as on a server or a + device. + + A consuming application will typically be interested in: + - distinguishing events such that the exact same event is not + processed twice. + - identifying and selecting the origin context or the + producer-assigned classification. + - identifying the temporal order of the events relative to the + originating context and/or relative to a wall-clock. + - understanding the context-related detail information carried + in the event. + - correlating event instances from multiple event producers and send + them to the same consumer context. + + In some cases, the consuming application might be interested in: + - obtaining further details about the event's subject from the + originating context, like obtaining detail information about a + changed object that requires privileged access authorization. + For example, a HR solution might only publish very limited + information in events for privacy reasons, and any event consumer + needing more data will have to obtain details related to the event + from the HR system under their own authorization context. + - interact with the event's subject at the originating context, + for instance reading a storage blob after having been informed + that this blob has just been created. + + Consumer interests motivate requirements for which information + producers ought to include an event. + +3) Middleware routes events from producers to consumers, or onwards + to other middleware. Applications producing events might delegate + certain tasks arising from their consumers' requirements to + middleware: + + - Management of many concurrent interested consumers for one of + multiple classes or originating contexts of events + - Processing of filter conditions over a class or originating context + of events on behalf of consumers. + - Transcoding, like encoding in MsgPack after decoding from JSON + - Transformation that changes the event's structure, like mapping from + a proprietary format to CloudEvents, while preserving the + identity and semantic integrity of the event. + - Instant "push-style" delivery to interested consumers. + - Storing events for eventual delivery, either for pick-up initiated + by the consumer ("pull"), or initiated by the middleware ("push") + after a delay. + - Observing event content or event flow for monitoring or + diagnostics purposes. + + To satisfy these needs, middleware will be interested in: + - A metadata discriminator usable for classification or + contextualization of events so that consumers can express interest + in one or multiple such classes or contexts. + For instance, a consumer might be interested in all events related + to a specific directory inside a file storage account. + - A metadata discriminator that allows distinguishing the subject of + a particular event of that class or context. + For instance, a consumer might want to filter out all events related + to new files ending with ".jpg" (the file name being the "new file" + event's subject) for the context describing specific directory + inside a file storage account that it has registered interest on. + - An indicator for the encoding of the event and its data. + - An indicator for the structural layout (schema) for the event and + its data. + + Whether its events are available for consumption via a middleware is + a delegation choice of the producer. + + In practice, middleware can take on role of a producer when it changes + the semantic meaning of an event, a consumer when it takes action based + on an event, or middleware when it routes events without making semantic + changes. + +4) Frameworks and other abstractions make interactions with event platform + infrastructure simpler, and often provide common API surface areas + for multiple event platform infrastructures. + + Frameworks are often used for turning events into an object graph, + and to dispatch the event to some specific handling user-code or + user-rule that permits the consuming application to react to + a particular kind of occurrence in the originating context and + on a particular subject. + + Frameworks are most interested in semantic metadata commonality + across the platforms they abstract, so that similar activities can + be handled uniformly. + + For a sports application, a developer using the framework might be + interested in all events from today's game (subject) of a team in a + league (topic of interest), but wanting to handle reports + of "goal" differently than reports of "substitution". + For this, the framework will need a suitable metadata discriminator + that frees it from having to understand the event details. + +### Value Proposition + +This section describes some of the use-cases that explain the value +of CloudEvents. + +#### Normalizing Events Across Services & Platforms + +Major event publishers (e.g. AWS, Microsoft, Google, etc.) all publish events +in different formats on their respective platforms. There are even a few cases +where services on the same provider publish events in different formats (e.g. +AWS). This forces event consumers to implement custom logic to read or munge +event data across platforms and occasionally across services on a single +platform. + +CloudEvents can offer a single experience for authoring consumers that handle +events across all platforms and services. + +#### Facilitating Integrations Across Services & Platforms + +Event data being transported across environments is increasingly common. +However, without a common way of describing events, delivery of events across +environments is hindered. There is no single way of determining where an event +came from and where it might be going. This prevents tooling to facilitate +successful event delivery and consumers from knowing what to do with event +data. + +CloudEvents offers useful metadata which middleware and consumers can rely upon +to facilitate event routing, logging, delivery and receipt. + +#### Increasing Portability of Functions-as-a-Service + +Functions-as-a-Service (also known as serverless computing) is one of the +fastest growing trends in IT and it is largely event-driven. However, a +primary concern of FaaS is vendor lock-in. This lock-in is partially caused +by differences in function APIs and signatures across providers, but the +lock-in is also caused by differences in the format of event data received +within functions. + +CloudEvents' common way of describing event data increases the portability of +Functions-as-a-Service. + +#### Improving Development & Testing of Event-Driven/Serverless Architectures + +The lack of a common event format complicates development and testing of +event-driven and serverless architectures. There is no easy way to mock events +accurately for development and testing purposes, and help emulate event-driven +workflows in a development environment. + +CloudEvents can enable better developer tools for building, testing and +handling the end-to-end lifecycle of event-driven and serverless architectures. + +#### Event Data Evolution + +Most platforms and services version the data model of their events differently +(if they do this at all). This creates an inconsistent experience for +publishing and consuming the data model of events as those data models evolve. + +CloudEvents can offer a common way to version and evolve event data. This will +help event publishers safely version their data models based on best practices, +and this help event consumers safely work with event data as it evolves. + +#### Normalizing Webhooks + +Webhooks is a style of event publishing which does not use a common format. +Consumers of webhooks don’t have a consistent way to develop, test, identify, +validate, and overall process event data delivered via webhooks. + +CloudEvents can offer consistency in webhook publishing and consumption. + +#### Policy Enforcement + +The transiting of events between systems may need to be filtered, transformed, +or blocked due to security and policy concerns. Examples may be to prevent +ingress or egress of the events such as event data containing sensitive +information or wanting to disallow the information flow between the sender and +receiver. + +A common event format would allow easier reasoning about the data being +transited and allow for better introspection of the data. + +#### Event Tracing + +An event sent from a source may result in a sequence of additional events +sent from various middleware devices such as event brokers and gateways. +CloudEvents includes metadata in events to associate these events as being +part of an event sequence for the purpose of event tracing and +troubleshooting. + +An event sent from a source may result in a sequence of additional events +sent from various middleware devices such as event brokers and gateways. +CloudEvents includes metadata in events to associate these events as being +part of an event sequence for the purpose of event tracing and +troubleshooting. + +#### Cloudbursting + +TBD + +#### IoT + +IoT devices send and receive events related to their functionality. +For example, a connected thermostat will send telemetry on the current +temperature and could receive events to change temperatures. +These devices typically have a constrained operating environment +(cpu, memory) requiring a well defined event message format. +In a lot of cases these messages are binary encoded instead of textual. +Whether directly from the device or transformed via a gateway, CloudEvents +would allow for a better description of the origin of the message and the +format of the data contained within the message. + +#### Event Correlation + +A serverless application/workflow could be associated with multiple events from +different event sources/producers. For example, a burglary detection +application/workflow could involve both a motion event and a door/window open +event. A serverless platform could receive many instances of each type of +events, e.g. it could receive motion events and window open events from +different houses. + +The serverless platform needs to correlate one type of event instance correctly +with other types of event instances and map a received event instance to the +correct application/workflow instance. CloudEvents will provide a standard way +for any event consumer (eg. the serverless platform) to locate the event +correlation information/token in the event data and map a received event +instance to the correct application/workflow instance. + +### Existing Event Formats + +As with the previous section, the examination (and understanding) of the +current state of the world was very important to the working group. To that +end, a sampling of existing current event formats that are used in practice +today was gathered. + +#### Microsoft - Event Grid +``` +{ + "topic":"/subscriptions/{subscription-id}", + "subject":"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501", + "eventType":"Microsoft.Resources.ResourceWriteSuccess", + "eventTime":"2017-08-16T03:54:38.2696833Z", + "id":"25b3b0d0-d79b-44d5-9963-440d4e6a9bba", + "data": { + "authorization":"{azure_resource_manager_authorizations}", + "claims":"{azure_resource_manager_claims}", + "correlationId":"54ef1e39-6a82-44b3-abc1-bdeb6ce4d3c6", + "httpRequest":"", + "resourceProvider":"Microsoft.EventGrid", + "resourceUri":"/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.EventGrid/eventSubscriptions/LogicAppdd584bdf-8347-49c9-b9a9-d1f980783501", + "operationName":"Microsoft.EventGrid/eventSubscriptions/write", + "status":"Succeeded", + "subscriptionId":"{subscription-id}", + "tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47" + } +} +``` +[Documentation](https://docs.microsoft.com/en-us/azure/event-grid/event-schema) + +#### Google - Cloud Functions (potential future) +``` +{ + "data": { + "@type": "types.googleapis.com/google.pubsub.v1.PubsubMessage", + "attributes": { + "foo": "bar", + }, + "messageId": "12345", + "publishTime": "2017-06-05T12:00:00.000Z", + "data": "somebase64encodedmessage" + }, + "context": { + "eventId": "12345", + "timestamp": "2017-06-05T12:00:00.000Z", + "eventTypeId": "google.pubsub.topic.publish", + "resource": { + "name": "projects/myProject/topics/myTopic", + "service": "pubsub.googleapis.com" + } + } +} +``` + +#### AWS - SNS +``` +{ + "Records": [ + { + "EventVersion": "1.0", + "EventSubscriptionArn": eventsubscriptionarn, + "EventSource": "aws:sns", + "Sns": { + "SignatureVersion": "1", + "Timestamp": "1970-01-01T00:00:00.000Z", + "Signature": "EXAMPLE", + "SigningCertUrl": "EXAMPLE", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "Message": "Hello from SNS!", + "MessageAttributes": { + "Test": { + "Type": "String", + "Value": "TestString" + }, + "TestBinary": { + "Type": "Binary", + "Value": "TestBinary" + } + }, + "Type": "Notification", + "UnsubscribeUrl": "EXAMPLE", + "TopicArn": topicarn, + "Subject": "TestInvoke" + } + } + ] +} +``` +[Documentation](http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html) + +#### AWS - Kinesis +``` +{ + "Records": [ + { + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "eventVersion": "1.0", + "kinesis": { + "partitionKey": "partitionKey-3", + "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", + "kinesisSchemaVersion": "1.0", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961" + }, + "invokeIdentityArn": identityarn, + "eventName": "aws:kinesis:record", + "eventSourceARN": eventsourcearn, + "eventSource": "aws:kinesis", + "awsRegion": "us-east-1" + } + ] +} +``` + +#### IBM - OpenWhisk - Web Action Event +``` +{ + "__ow_method": "post", + "__ow_headers": { + "accept": "*/*", + "connection": "close", + "content-length": "4", + "content-type": "text/plain", + "host": "172.17.0.1", + "user-agent": "curl/7.43.0" + }, + "__ow_path": "", + "__ow_body": "Jane" +} +``` + +#### OpenStack - Audit Middleware - Event +``` +{ + "typeURI": "http://schemas.dmtf.org/cloud/audit/1.0/event", + "id": "d8304637-3f63-5092-9ab3-18c9781871a2", + "eventTime": "2018-01-30T10:46:16.740253+00:00", + "action": "delete", + "eventType": "activity", + "outcome": "success", + "reason": { + "reasonType": "HTTP", + "reasonCode": "204" + }, + "initiator": { + "typeURI": "service/security/account/user", + "name": "user1", + "domain": "domain1", + "id": "52d28347f0b4cf9cc1717c00adf41c74cc764fe440b47aacb8404670a7cd5d22", + "host": { + "address": "127.0.0.1", + "agent": "python-novaclient" + }, + "project_id": "ae63ddf2076d4342a56eb049e37a7621" + }, + "target": { + "typeURI": "compute/server", + "id": "b1b475fc-ef0a-4899-87f3-674ac0d56855" + }, + "observer": { + "typeURI": "service/compute", + "name": "nova", + "id": "1b5dbef1-c2e8-5614-888d-bb56bcf65749" + }, + "requestPath": "/v2/ae63ddf2076d4342a56eb049e37a7621/servers/b1b475fc-ef0a-4899-87f3-674ac0d56855" +} +``` +[Documentation](https://github.com/openstack/pycadf/blob/master/doc/source/event_concept.rst) + +#### Adobe - I/O Events +``` +{ + "event_id": "639fd17a-d0bb-40ca-83a4-e78612bce5dc", + "event": { + "@id": "82235bac-2b81-4e70-90b5-2bd1f04b5c7b", + "@type": "xdmCreated", + "xdmEventEnvelope:objectType": "xdmAsset", + "activitystreams:to": { + "xdmImsUser:id": "D13A1E7053E46A220A4C86E1@AdobeID", + "@type": "xdmImsUser" + }, + "activitystreams:generator": { + "xdmContentRepository:root": "https://cc-api-storage.adobe.io/", + "@type": "xdmContentRepository" + }, + "activitystreams:actor": { + "xdmImsUser:id": "D13A1E7053E46A220A4C86E1@AdobeID", + "@type": "xdmImsUser" + }, + "activitystreams:object": { + "@type": "xdmAsset", + "xdmAsset:asset_id": "urn:aaid:sc:us:4123ba4c-93a8-4c5d-b979-ffbbe4318185", + "xdmAsset:asset_name": "example.jpg", + "xdmAsset:etag": "6fc55d0389d856ae7deccebba54f110e", + "xdmAsset:path": "/MyFolder/example.jpg", + "xdmAsset:format": "image/jpeg" + }, + "activitystreams:published": "2016-07-16T19:20:30+01:00" + } +} +``` +[Documentation](https://www.adobe.io/apis/cloudplatform/events/documentation.html) diff --git a/source-event-action.png b/source-event-action.png new file mode 100644 index 0000000000000000000000000000000000000000..fe938f088f1ff67a99ad0b7fe70bffaefbde7046 GIT binary patch literal 14563 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