Skip to content

Commit

Permalink
Increase details of Channels documentation
Browse files Browse the repository at this point in the history
With a release approaching, the Channels documentation got more
information about how the Channel system actually work and how to build
your own channels.

The internal/changes/examples JSON files were inlined to ease the
readability and updated, as they contained partially outdated
information.

For the pkg/plugin part, some API documentation was added as this might
get used by external users.
  • Loading branch information
oxzi committed Jul 15, 2024
1 parent 860ea0c commit f2f3ca7
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 101 deletions.
308 changes: 266 additions & 42 deletions doc/10-Channels.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,312 @@
# Channels

After Icinga Notifications decides to send a notification of any kind, it will be passed to a channel plugin.
Such a channel plugin submits the notification event to a channel, e.g., email or a chat client.
Such a plugin submits the notification event to a domain-specific channel, e.g., email or a chat client.

Icinga Notifications comes packed with channel plugins, but also enables you to develop your own plugins.
Icinga Notifications comes packed with channels, but also enables you to develop your own channels.

To make those plugins available to Icinga Notifications, they must be placed in the
To make those channels available to Icinga Notifications, they must be placed in the
[channels directory](03-Configuration.md#channels-directory),
which is being done automatically for package installations.
Afterwards they can be configured through Icinga Notifications Web.
At startup, Icinga Notifications scans this directory, starts each channel once to query its configuration options
and stores these options in the database.
Using this information, Icinga Notifications Web allows channels to be configured,
which are then started, configured, and lastly used to send notification events from Icinga Notifications.

## Technical Channel Description

Channel plugins are processes that run continuously and independently of each other. They receive many requests over
their lifetime. They receive JSON-formatted requests on stdin and reply with JSON-formatted responses on stdout. The
request and response structure is inspired by JSON-RPC.
!!! warning

As this is still an early preview version, things might change.
There may still be severe bugs and incompatible changes may happen without any notice.

Channel plugins are independent processes that run continuously, supervised by Icinga Notifications.
They receive JSON-formatted requests on `stdin` and reply with JSON-formatted responses on `stdout`.
For logging or debugging, channels can write to `stderr`, which will be logged by Icinga Notifications.

The request and response structure is inspired by JSON-RPC.
The anatomy of requests and responses is described below.
Note that fields with a type marked as optional must be omitted in the JSON object if they do not have a value.

This documentation uses beautified JSON for ease of reading.

### Request

The request must be in JSON format and should contain following keys:
A channel receives a request as a JSON object with the following fields:

- `method`: The request method to call.
- `params`: The params for request method.
- `id`: Unsigned int value. Required to assign the response to its request as responses can be sent out of order.
| Field | Description | Type |
|----------|-------------------------------|------------------------------------------------------------------------|
| `method` | Request method to call. | String |
| `params` | Params for the request method | Optional JSON object, mapping parameter keys (string) to values (any). |
| `id` | Unique identifier. | Unsigned integer |

Examples:

- Simple request without any `params`:
```json
{
"method": "Simple",
"id" : 1000
}
```
- Request with `params` of different types:
```json
{
"method": "WithParams",
"params": {
"foo": 23,
"bar": "hello"
},
"id": 1000
}
```

### Response

Each request must be answered by the channel with a response JSON object of the following fields:

| Field | Description | Type |
|----------|-------------------------------------|---------------------------|
| `result` | Output of a successful method call. | Optional JSON value (any) |
| `error` | Error message. | Optional string |
| `id` | Request id. | Unsigned integer |

The `result` field may be omitted if the method does not return a value, i.e., for setter calls.

In case of a present `error` value, the `result` field must be omitted.
Thus, a successful responses without a `result` contains just an `id` field.

The `id` field in a response must match those of its request, allowing Icinga Notifications to link those two.
This is necessary as responses may be created out of order.

Examples:

- Successful response without a `result` message:
```json
{
"id": 1000
}
```
- Successful response with a `result`:
```json
{
"result": "hello world",
"id": 1000
}
```
- Response with an error:
```json
{
"error": "unknown method: 'Foo'",
"id": 1000
}
```

### Methods

The following methods must be implemented by a channel.

#### GetInfo

The parameterless `GetInfo` method returns information about the channel.

Its `result` is expected to be a JSON object with the `json` fields defined in the
[`Info` type](https://pkg.go.dev/github.com/icinga/icinga-notifications/pkg/plugin#Info).
The `config_attrs` field must be an array of JSON objects according to the
[`ConfigOption` type](https://pkg.go.dev/github.com/icinga/icinga-notifications/pkg/plugin#ConfigOption).
Those attributes define configuration options for the channel to be set via the `SetConfig` method.
Furthermore, they are used for channel configuration in Icinga Notifications Web.

##### Example GetInfo Request

```json
{
"method": "Add",
"params": {
"num1": 5,
"num2": 3
"method": "GetInfo",
"id" : 1
}
```

##### Example GetInfo Response

```json
{
"result": {
"name": "Minified Webhook",
"version": "0.0.0-gf369a11-dirty",
"author": "Icinga GmbH",
"config_attrs": [
{
"name": "url_template",
"type": "string",
"label": {
"de_DE": "URL-Template",
"en_US": "URL Template"
},
"help": {
"de_DE": "URL, optional als Go-Template über das zu verarbeitende plugin.NotificationRequest.",
"en_US": "URL, optionally as a Go template over the current plugin.NotificationRequest."
},
"required": true,
"min": null,
"max": null
},
{
"name": "response_status_codes",
"type": "string",
"label": {
"de_DE": "Antwort-Status-Codes",
"en_US": "Response Status Codes"
},
"help": {
"de_DE": "Kommaseparierte Liste erwarteter Status-Code der HTTP-Antwort, z.B.: 200,201,202,208,418",
"en_US": "Comma separated list of expected HTTP response status code, e.g., 200,201,202,208,418"
},
"default": "200",
"min": null,
"max": null
}
]
},
"id": 2020
"id": 1
}
```

#### SetConfig

The `SetConfig` method configures the channel.

The Icinga Notifications daemon is going to call this method at least once before sending the first notifications
to initialize the channel plugin.

The passed JSON object in the request's `param` field reflects the objects from `GetInfo`'s `config_attrs`.
Each object within the `config_attrs` array must be configurable by using its `name` attribute as the key together with
the desired configuration value, being of the specified type in the `type` field.

To illustrate, the URL template from the above output is configurable with the following JSON object passed in `params`:

```json
{
"url_template": "http://localhost:8000/update/{{.Incident.Id}}"
}
```

If the channel plugin successfully configured itself, a response without an `result` must be returned.
Otherwise, an `error` must be returned.

##### Example SetConfig Request

```json
{
"method": "Foo",
"method": "SetConfig",
"params": {
"a": "value1",
"b": "value2"
"url_template": "http://localhost:8000/update/{{.Incident.Id}}",
"response_status_codes": "200"
},
"id": 3030
"id": 2
}
```

### Response
##### Example GetInfo Response

```json
{
"id": 2
}
```

The response is in JSON format and contains following keys:
#### SendNotification

- `result`: The result as JSON format. Omitted when the method does not return a value (e.g. setter calls) or an error
has occurred.
- `error`: The error message. Omitted when no error has occurred.
- `id`: The request id. When result value is empty and no error is occurred, the response will only contain the request
id.
The `SendNotification` method requests the channel to dispatch notifications.

Examples:
Within the request's `params`, a JSON object representing a
[`NotificationRequest`](https://pkg.go.dev/github.com/icinga/icinga-notifications/pkg/plugin#NotificationRequest)
is passed.

If the channel cannot dispatch a notification, an `error` must be returned.
This may be due to channel-specific reasons, such as an email channel where the SMTP server is unavailable.

#### Example SendNotification Request

```json
{
"result": 8,
"id": 2020
"method": "SendNotification",
"params": {
"contact": {
"full_name": "icingaadmin",
"addresses": [
{
"type": "email",
"address": "[email protected]"
}
]
},
"object": {
"name": "dummy-816!random fortune",
"url": "http://localhost/icingaweb2/icingadb/service?name=random%20fortune&host.name=dummy-816",
"tags": {
"host": "dummy-816",
"service": "random fortune"
},
"extra_tags": {
"hostgroup/app-mobile": "",
"hostgroup/department-dev": "",
"hostgroup/env-prod": "",
"hostgroup/location-rome": "",
"servicegroup/app-storage": "",
"servicegroup/department-ps": "",
"servicegroup/env-prod": "",
"servicegroup/location-rome": ""
}
},
"incident": {
"id": 1437,
"url": "http://localhost/icingaweb2/notifications/incident?id=1437",
"severity": "crit"
},
"event": {
"time": "2024-07-12T10:47:30.445439055Z",
"type": "state",
"username": "",
"message": "Q:\tWhat looks like a cat, flies like a bat, brays like a donkey, and\n\tplays like a monkey?\nA:\tNothing."
}
},
"id": 3
}
```

#### Example SendNotification Response

```json
{
"error": "unknown method: 'Foo'",
"id": 3030
"id": 3
}
```

### Methods
## Writing Channel Plugins in Go

!!! warning

As this is still an early preview version, things might change.
There may still be severe bugs and incompatible changes may happen without any notice.

!!! tip

Icinga Notifications comes with a Webhook channel plugin.
Consider using this channel if your transport uses HTTP instead of writing a custom channel.

!!! tip

When developing custom channels, consider naming them with a unique prefix,
as additional channels will get added to Icinga Notifications in the future.
For example, name your channel `x_irc` or `my_irc` instead of `irc`.

Currently, the channel plugin include following three methods:
Since Icinga Notifications and all of its channels are written in the Go programming language,
libraries already used internally can be reused.
In particular, the [`Plugin`](https://pkg.go.dev/github.com/icinga/icinga-notifications/pkg/plugin#Plugin)
interface must be implemented, requesting methods for all the RPC methods described above.
The channel plugin's `main` function should call
[`RunPlugin`](https://pkg.go.dev/github.com/icinga/icinga-notifications/pkg/plugin#RunPlugin),
taking care about calling the RPC method implementations.

- `SetConfig`: Initialize the channel plugin with specified config as `params` key. The config is plugin specific
therefore each plugin defines what is expected as config.
[(example)](../internal/channel/examples/set-config.json)
- `GetInfo`: Get the information about the channel e.g. Name. The `params` key has no effect and can be omitted.
[(example)](../internal/channel/examples/get-info.json)
- `SendNotification`: Send the notifications. The `params` key should contain the information about the contact to be
notified, corresponding object, the incident and the triggered event.
[(example)](../internal/channel/examples/send-notification.json)
For concrete examples, there are the implemented channels in the Icinga Notifications repository under
[`./cmd/channels`](https://github.com/Icinga/icinga-notifications/tree/main/cmd/channels).
2 changes: 1 addition & 1 deletion doc/20-HTTP-API.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ curl -v -u 'source-2:insecureinsecure' -d '@-' 'http://localhost:5680/process-ev
"type": "state",
"severity": "crit",
"username": "",
"message": "Something went somehwere very wrong."
"message": "Something went somewhere very wrong."
}
EOF
```
Expand Down
4 changes: 0 additions & 4 deletions internal/channel/examples/get-info.json

This file was deleted.

Loading

0 comments on commit f2f3ca7

Please sign in to comment.