Add Mastodon alert provider - #1372
Conversation
Add the mastodon Provider type for posting Flux events as statuses on a Mastodon account. Events are published with a plain HTTP POST to the /api/v1/statuses endpoint of the server given in the address, using an OAuth access token with the write:statuses scope as a bearer token. The status text carries a severity emoji, the involved object, the event message and the event metadata as key-value lines in sorted key order, truncated to the 500-character default server limit. An Idempotency-Key header derived from the event guards against duplicate statuses when the HTTP client retries a request whose response was lost. An optional visibility query parameter on the address maps to the status visibility field, since the Provider API has no dedicated field for it. Assisted-by: Claude Code/claude-fable-5 Signed-off-by: Chose Carreras <xchose@gmail.com>
a2c1576 to
f8ab539
Compare
|
Rebased onto latest |
|
CI is green now — thanks for approving the run. The branch is rebased on main and still merges cleanly. Locally Could a maintainer take a look when there's time? |
|
It feels so strange to send a Flux alert to a social network 🤔 🤔 🤔 |
Mastodon can be used like Slack, but self-hosted and private. |
| Mastodon server limit) are truncated. An `Idempotency-Key` header derived from | ||
| the event is sent to prevent duplicate statuses on retried requests. |
There was a problem hiding this comment.
I wonder if we really need this since we already do this on our side, or if it would make sense to reuse the same key:
notification-controller/internal/server/event_server.go
Lines 213 to 254 in 75dc464
There was a problem hiding this comment.
Since we could do retries it feels like the key can be useful. In this case I wonder if reusing the same key we already have would make sense.
| // The visibility is carried as a query parameter of the address | ||
| // because the Provider API has no dedicated field for it. | ||
| q := u.Query() | ||
| visibility := q.Get("visibility") | ||
| q.Del("visibility") | ||
| u.RawQuery = q.Encode() |
There was a problem hiding this comment.
Could someone mistakenly understand that the mastodon server accepts this query parameter? This solution feels a bit hacky... I can see other providers that don't exactly use a URL also benefiting from more inputs... Can we choose a sensible default here and only expose this input later, after putting more thought into how we can do this properly for all providers?
Fixes #447
Adds a
mastodonProvider type for posting Flux events as statuses on a Mastodon account, via a plain HTTPPOSTto the/api/v1/statusesendpoint — no SDK dependency (the issue suggestsmattn/go-mastodon, but like every other notifier this only needs the sharedpostMessageclient).Implementation
addressis the server root URL (e.g.https://mastodon.social); the/api/v1/statusespath is appended automatically (and preserved if already present).write:statusesscope (Secret keytoken), sent as a bearer token.💫/🚨, same convention as the Telegram provider), involved objectkind/name.namespace, event message, and event metadata as key-value lines. Truncated to 500 characters — the default Mastodon server limit, which cannot be discovered without an extra API call, and exceeding it fails the post with a 422.Idempotency-Keyheader (SHA-256 over object UID, reason, timestamp and status text) guards against duplicate statuses when the retrying HTTP client loses a response.visibilityquery parameter on the address (public/unlisted/private) maps to the payload'svisibilityfield and is stripped from the request URL — the Provider API has no dedicated field for it, and alert floods on the public timeline are likely unwanted.Sample rendered payload:
{ "status": "💫 gitrepository/podinfo.flux-system\nstored artifact for commit 'master@sha1:eec06d1...'\n\nrevision: master@sha1:eec06d1...", "visibility": "unlisted" }Testing
internal/notifier/mastodon_test.go: asserts endpoint path, bearer auth header, Idempotency-Key presence, payload shape, severity emoji, visibility query-param mapping (including URL stripping), 500-char truncation, path preservation, and constructor validation against anhttptestserver.make generate manifests tidy fmt vetleave a clean tree.Manual e2e testing
kind cluster, controller image + CRD from this PR, real mastodon.social account. Secret:
address: https://mastodon.social?visibility=unlisted,tokenwithwrite:statuses. Info/error/recovery events from a podinfoGitRepository→ 3× HTTP 2xx, statuses posted as unlisted:🤖 Generated with Claude Code
Full MD with testing:
e2e-comment.md