Skip to content

Commit a2da917

Browse files
[PM-17562] Update event integration documentation (#613)
* [PM-17562] Update event integration documentation * Added link to server documentation. Removed more redundant pieces
1 parent 0503bd3 commit a2da917

File tree

1 file changed

+36
-168
lines changed

1 file changed

+36
-168
lines changed

docs/getting-started/server/events.md

Lines changed: 36 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -59,84 +59,12 @@ To use database storage for events:
5959

6060
Events can be distributed via an AMQP messaging system. This messaging system enables new
6161
integrations to subscribe to the events. The system supports either RabbitMQ or Azure Service Bus.
62+
For a detailed look at the architecture and technical details, see
63+
[the documentation in the server repo](https://github.com/bitwarden/server/tree/6800bc57f3eb492222e128cffcd00e16b29cc155/src/Core/AdminConsole/Services/Implementations/EventIntegrations).
6264

63-
### Listener / Handler pattern
64-
65-
The goal of moving to distributed events is to build additional service integrations that consume
66-
events. To make it easy to support multiple AMQP services (RabbitMQ and Azure Service Bus), the act
67-
of listening to the stream of events is decoupled from the act of responding to an event.
68-
69-
**Listeners**
70-
71-
- One listener per communication platform (e.g. one for RabbitMQ, one for Azure Service Bus).
72-
- Multiple instances can be configured to run independently, each with its own handler and
73-
subscription / queue.
74-
- Perform all the aspects of setup / teardown, subscription, etc. for the messaging platform, but do
75-
not directly process any events themselves. Instead, they delegate to the handler with which they
76-
are configured.
77-
78-
**Handlers**
79-
80-
- One handler per integration (e.g. HTTP post or event database repository).
81-
- Completely isolated from and know nothing of the messaging platform in use. This allows them to be
82-
freely reused across different communication platforms.
83-
- Perform all aspects of handling an event.
84-
- Allows them to be highly testable as they are isolated and decoupled from the more complicated
85-
aspects of messaging.
86-
87-
This combination allows for a configuration inside of `Startup.cs` that pairs instances of the
88-
listener service for the currently running messaging platform with any number of handlers. It also
89-
allows for quick development of new handlers as they are focused only on the task of handling a
90-
specific event.
91-
92-
### RabbitMQ implementation
93-
94-
The RabbitMQ implementation adds a step that refactors the way events are handled when running
95-
locally or self-hosted. Instead of writing directly to the `Events` table via the
96-
`EventsRepository`, each event is broadcast to a RabbitMQ exchange. A new
97-
`RabbitMqEventListenerService` instance, configured with an `EventRepositoryHandler`, subscribes to
98-
the RabbitMQ exchange and writes to the `Events` table via the `EventsRepository`. The end result is
99-
the same (events are stored in the database), but the addition of the RabbitMQ exchange allows for
100-
other integrations to subscribe.
101-
102-
Additional handlers - each paired with their own `RabbitMqEventListenerService` and listening to
103-
their own queue - are available to be configured as well.
104-
105-
- `SlackEventHandler` posts messages to Slack channels or DMs.
106-
- `WebhookEventHandler` `POST`s each event to a configurable URL.
107-
108-
```kroki type=mermaid
109-
graph TD
110-
subgraph With RabbitMQ
111-
B1[EventService]
112-
B2[RabbitMQEventWriteService]
113-
B3[RabbitMQ exchange]
114-
B4[EventRepositoryHandler]
115-
B5[WebhookEventHandler]
116-
B6[Events Database Table]
117-
B7[HTTP Server]
118-
B8[SlackEventHandler]
119-
B9[Slack]
120-
121-
B1 -->|IEventWriteService| B2 --> B3
122-
B3-->|RabbitMqEventListenerService| B4 --> B6
123-
B3-->|RabbitMqEventListenerService| B5
124-
B5 -->|HTTP POST| B7
125-
B3-->|RabbitMqEventListenerService| B8
126-
B8 -->|HTTP POST| B9
127-
end
128-
129-
subgraph Without RabbitMQ
130-
A1[EventService]
131-
A2[RepositoryEventWriteService]
132-
A3[Events Database Table]
133-
134-
A1 -->|IEventWriteService| A2 --> A3
135-
136-
end
137-
```
65+
### RabbitMQ
13866

139-
#### Running the RabbitMQ container
67+
#### Running the RabbitMQ container (default using fixed-delay queues)
14068

14169
1. Verify that you've set a username and password in the `.env` file (see `.env.example` for an
14270
example)
@@ -154,6 +82,36 @@ end
15482
3. To verify this is running, open `http://localhost:15672` in a browser and login with the
15583
username and password in your `.env` file.
15684
85+
#### Running the RabbitMQ container (with optional delay plugin)
86+
87+
The standard installation of RabbitMQ does not support delaying message delivery. Our default option
88+
instead uses retry queues with a fixed amount of delay and checks the `DelayUntilDate` in each
89+
message to see if it is time for them to br processed. This provides the delay needed for retries
90+
(with backoff and jitter applied), but it does require more processing.
91+
92+
As an alternate approach, we have support for RabbitMQ's optional delay plugin - which does support
93+
adding a precise delay to a message and handles publishing at the specified time. It does require
94+
the plugin to be installed and enabled before running and is therefore not the default setup. To
95+
enable the plugin:
96+
97+
1. Download the latest version of the
98+
[RabbitMQ delay plugin GitHub repo](https://github.com/rabbitmq/rabbitmq-delayed-message-exchange).
99+
2. Add the plugin to your RabbitMQ instance. The easiest way to do this is to add the following
100+
line to Docker compose, under the `volumes:`. This puts the binary into the container that
101+
Docker spins up.
102+
103+
```
104+
- ./rabbitmq_delayed_message_exchange-4.1.0.ez:/opt/rabbitmq/plugins/rabbitmq_delayed_message_exchange-4.1.0.ez
105+
```
106+
107+
3. Build/rebuild your RabbitMQ instance with this in place to enable the ability to use the delay
108+
plugin.
109+
4. If you have previously created the Integration exchange, you have to delete it and let the
110+
server code recreate it (Rabbit will use the existing exchange without delay enabled if it
111+
already exists).
112+
5. Restart the servers and verify that the exchange was created successfully.
113+
6. Turn on the `useDelayPlugin` flag in secrets and push that out to the servers (see below)
114+
157115
#### Configuring the server to use RabbitMQ for events
158116
159117
1. Add the following to your `secrets.json` file, changing the defaults to match your `.env` file:
@@ -168,6 +126,7 @@ end
168126
"eventRepositoryQueueName": "events-write-queue",
169127
"slackQueueName": "events-slack-queue",
170128
"webhookQueueName": "events-webhook-queue",
129+
"useDelayPlugin": false
171130
}
172131
}
173132
```
@@ -191,50 +150,7 @@ With these changes in place, you should see the database events written as befor
191150
see in the RabbitMQ management interface that the messages are flowing through the configured
192151
exchange/queues.
193152
194-
### Azure Service Bus implementation
195-
196-
The Azure Service Bus implementation is a configurable replacement for Azure Queue. Instead of
197-
writing Events to the queue to be picked up, they are sent to the configured service bus topic. An
198-
instance of `AzureServiceBusEventListenerService` is then configured with the
199-
`AzureTableStorageEventHandler` to subscribe to that topic and write Events to Azure Table Storage.
200-
Similar to RabbitMQ above, the end result is the same (events are stored in Azure Table Storage),
201-
but the addition of the service bus topic allows for other integrations to subscribe.
202-
203-
As with the RabbitMQ implementation above, a `SlackEventHandler` and `WebhookEventHandler` can be
204-
configured to publish events to Slack and/or a webhook.
205-
206-
```kroki type=mermaid
207-
graph TD
208-
subgraph With Service Bus
209-
B1[EventService]
210-
B2[AzureServiceBusEventWriteService]
211-
B3[Azure Service Bus Topic]
212-
B4[AzureTableStorageEventHandler]
213-
B5[WebhookEventHandler]
214-
B6[Events in Azure Tables]
215-
B7[HTTP Server]
216-
B8[SlackEventHandler]
217-
B9[Slack]
218-
219-
B1 -->|IEventWriteService| B2 --> B3
220-
B3-->|AzureServiceBusEventListenerService| B4 --> B6
221-
B3-->|AzureServiceBusEventListenerService| B5
222-
B5 -->|HTTP POST| B7
223-
B3-->|AzureServiceBusEventListenerService| B8
224-
B8 -->|HTTP POST| B9
225-
end
226-
227-
subgraph With Storage Queue
228-
A1[EventService]
229-
A2[AzureQueueHostedService]
230-
A3[Azure Storage Queue]
231-
A4[AzureQueueHostedService]
232-
A5[Events in Azure Tables]
233-
234-
A1 -->|IEventWriteService| A2 --> A3 -->|RepositoryEventWriteService| A4 --> A5
235-
236-
end
237-
```
153+
### Azure Service Bus
238154
239155
#### Running the Azure Service Bus emulator
240156
@@ -286,51 +202,3 @@ end
286202
```
287203

288204
3. Start or re-start all services, including `EventsProcessor`.
289-
290-
### Integrations and integration configurations
291-
292-
Organizations can configure integration configurations to send events to different endpoints -- each
293-
handler maps to a specific integration and checks for the configuration when it receives an event.
294-
Currently, there are integrations / handlers for Slack and webhooks (as mentioned above).
295-
296-
**`OrganizationIntegration`**
297-
298-
- The top level object that enables a specific integration for the organization.
299-
- Includes any properties that apply to the entire integration across all events.
300-
301-
- For Slack, it consists of the token:
302-
303-
```json
304-
{ "token": "xoxb-token-from-slack" }
305-
```
306-
307-
- For webhooks, it is `null`. However, even though there is no configuration, an organization must
308-
have a webhook `OrganizationIntegration` to enable configuration via
309-
`OrganizationIntegrationConfiguration`.
310-
311-
**`OrganizationIntegrationConfiguration`**
312-
313-
- This contains the configurations specific to each `EventType` for the integration.
314-
- `Configuration` contains the event-specific configuration.
315-
316-
- For Slack, this would contain what channel to send the message to:
317-
318-
```json
319-
{ "channelId": "C123456" }
320-
```
321-
322-
- For Webhook, this is the URL the request should be sent to:
323-
324-
```json
325-
{ "url": "https://api.example.com" }
326-
```
327-
328-
- `Template` contains a template string that is expected to be filled in with the contents of the
329-
actual event.
330-
- The tokens in the string are wrapped in `#` characters. For instance, the UserId would be
331-
`#UserId#`
332-
- The `IntegrationTemplateProcessor` does the actual work of replacing these tokens with
333-
introspected values from the provided `EventMessage`.
334-
- The template does not enforce any structure -- it could be a freeform text message to send via
335-
Slack, or a JSON body to send via webhook; it is simply stored and used as a string for the most
336-
flexibility.

0 commit comments

Comments
 (0)