Add ntfy email support

Implements: https://todo.xenrox.net/~xenrox/ntfy-alertmanager/15
This commit is contained in:
Thorben Günther 2023-03-09 17:02:05 +01:00
parent 3bdecb70da
commit 025ad78a60
No known key found for this signature in database
GPG key ID: 415CD778D8C5AFED
4 changed files with 63 additions and 19 deletions

View file

@ -16,9 +16,9 @@ You can specify the configuration file location with the `--config` flag. By def
the configuration file will be read from `/etc/ntfy-alertmanager/config`. The format the configuration file will be read from `/etc/ntfy-alertmanager/config`. The format
of this file is [scfg]. of this file is [scfg].
ntfy-alertmanager has support for setting ntfy [priority], [tags], [icon] and [action buttons] ntfy-alertmanager has support for setting ntfy [priority], [tags], [icon], [action buttons]
(which can be used to create an Alertmanager silence). (which can be used to create an Alertmanager silence) and [email notifications].
Define a decreasing order of labels in the config file and map those labels to tags, priority or an icon. Define a decreasing order of labels in the config file and map those labels to tags, priority, an icon or an email address.
- For priority and icon the first found value will be chosen. An icon for "resolved" alerts will take precedence. - For priority and icon the first found value will be chosen. An icon for "resolved" alerts will take precedence.
- Tags are added together. - Tags are added together.
@ -47,6 +47,8 @@ labels {
priority 5 priority 5
tags "rotating_light" tags "rotating_light"
icon "https://foo.com/critical.png" icon "https://foo.com/critical.png"
# Forward messages which severity "critical" to the specified email address.
email-address foo@bar.com
} }
severity "info" { severity "info" {
@ -73,6 +75,8 @@ ntfy {
# ntfy authentication via access tokens (https://docs.ntfy.sh/publish/#access-tokens) # ntfy authentication via access tokens (https://docs.ntfy.sh/publish/#access-tokens)
# Either access-token or a user/password combination can be used - not both. # Either access-token or a user/password combination can be used - not both.
access-token foobar access-token foobar
# Forward all messages to the specified email address.
email-address foo@bar.com
} }
alertmanager { alertmanager {
@ -135,6 +139,7 @@ or write to me directly on matrix [@xenrox:xenrox.net].
[tags]: https://ntfy.sh/docs/publish/#tags-emojis [tags]: https://ntfy.sh/docs/publish/#tags-emojis
[icon]: https://docs.ntfy.sh/publish/#icons [icon]: https://docs.ntfy.sh/publish/#icons
[action buttons]: https://docs.ntfy.sh/publish/#action-buttons [action buttons]: https://docs.ntfy.sh/publish/#action-buttons
[email notifications]: https://docs.ntfy.sh/publish/#e-mail-notifications
[issue tracker]: https://todo.xenrox.net/~xenrox/ntfy-alertmanager [issue tracker]: https://todo.xenrox.net/~xenrox/ntfy-alertmanager
[mailing list]: https://lists.xenrox.net/~xenrox/public-inbox [mailing list]: https://lists.xenrox.net/~xenrox/public-inbox
[@xenrox:xenrox.net]: https://matrix.to/#/@xenrox:xenrox.net [@xenrox:xenrox.net]: https://matrix.to/#/@xenrox:xenrox.net

View file

@ -38,10 +38,11 @@ type config struct {
} }
type ntfyConfig struct { type ntfyConfig struct {
Topic string Topic string
User string User string
Password string Password string
AccessToken string AccessToken string
emailAddress string
} }
type labels struct { type labels struct {
@ -50,9 +51,10 @@ type labels struct {
} }
type labelConfig struct { type labelConfig struct {
Priority string Priority string
Tags []string Tags []string
Icon string Icon string
emailAddress string
} }
type cacheConfig struct { type cacheConfig struct {
@ -201,6 +203,13 @@ func readConfig(path string) (*config, error) {
} }
} }
d = labelDir.Children.Get("email-address")
if d != nil {
if err := d.ParseParams(&labelConfig.emailAddress); err != nil {
return nil, err
}
}
labels[fmt.Sprintf("%s:%s", labelName, name)] = *labelConfig labels[fmt.Sprintf("%s:%s", labelName, name)] = *labelConfig
} }
} }
@ -251,6 +260,13 @@ func readConfig(path string) (*config, error) {
return nil, errors.New("ntfy: cannot use both an access-token and a user/password at the same time") return nil, errors.New("ntfy: cannot use both an access-token and a user/password at the same time")
} }
d = ntfyDir.Children.Get("email-address")
if d != nil {
if err := d.ParseParams(&config.ntfy.emailAddress); err != nil {
return nil, err
}
}
cacheDir := cfg.Get("cache") cacheDir := cfg.Get("cache")
if cacheDir != nil { if cacheDir != nil {

View file

@ -24,6 +24,7 @@ labels {
priority 5 priority 5
tags "rotating_light" tags "rotating_light"
icon "https://foo.com/critical.png" icon "https://foo.com/critical.png"
email-address foo@bar.com
} }
severity "info" { severity "info" {
@ -70,7 +71,12 @@ cache {
ntfy: ntfyConfig{Topic: "https://ntfy.sh/alertmanager-alerts", User: "user", Password: "pass"}, ntfy: ntfyConfig{Topic: "https://ntfy.sh/alertmanager-alerts", User: "user", Password: "pass"},
labels: labels{Order: []string{"severity", "instance"}, labels: labels{Order: []string{"severity", "instance"},
Label: map[string]labelConfig{ Label: map[string]labelConfig{
"severity:critical": {Priority: "5", Tags: []string{"rotating_light"}, Icon: "https://foo.com/critical.png"}, "severity:critical": {
Priority: "5",
Tags: []string{"rotating_light"},
Icon: "https://foo.com/critical.png",
emailAddress: "foo@bar.com",
},
"severity:info": {Priority: "1"}, "severity:info": {Priority: "1"},
"instance:example.com": {Tags: []string{"computer", "example"}}, "instance:example.com": {Tags: []string{"computer", "example"}},
}, },

33
main.go
View file

@ -46,14 +46,15 @@ type alert struct {
} }
type notification struct { type notification struct {
title string title string
body string body string
priority string priority string
tags string tags string
icon string icon string
silenceBody string emailAddress string
fingerprint string silenceBody string
status string fingerprint string
status string
} }
func (br *bridge) singleAlertNotifications(p *payload) []*notification { func (br *bridge) singleAlertNotifications(p *payload) []*notification {
@ -100,6 +101,8 @@ func (br *bridge) singleAlertNotifications(p *payload) []*notification {
n.icon = br.cfg.resolved.Icon n.icon = br.cfg.resolved.Icon
} }
n.emailAddress = br.cfg.ntfy.emailAddress
for _, labelName := range br.cfg.labels.Order { for _, labelName := range br.cfg.labels.Order {
val, ok := alert.Labels[labelName] val, ok := alert.Labels[labelName]
if !ok { if !ok {
@ -119,6 +122,10 @@ func (br *bridge) singleAlertNotifications(p *payload) []*notification {
n.icon = labelConfig.Icon n.icon = labelConfig.Icon
} }
if n.emailAddress == "" {
n.emailAddress = labelConfig.emailAddress
}
for _, val := range labelConfig.Tags { for _, val := range labelConfig.Tags {
if !sliceContains(tags, val) { if !sliceContains(tags, val) {
tags = append(tags, val) tags = append(tags, val)
@ -197,6 +204,8 @@ func (br *bridge) multiAlertNotification(p *payload) *notification {
n.icon = br.cfg.resolved.Icon n.icon = br.cfg.resolved.Icon
} }
n.emailAddress = br.cfg.ntfy.emailAddress
for _, labelName := range br.cfg.labels.Order { for _, labelName := range br.cfg.labels.Order {
val, ok := p.CommonLabels[labelName] val, ok := p.CommonLabels[labelName]
if !ok { if !ok {
@ -216,6 +225,10 @@ func (br *bridge) multiAlertNotification(p *payload) *notification {
n.icon = labelConfig.Icon n.icon = labelConfig.Icon
} }
if n.emailAddress == "" {
n.emailAddress = labelConfig.emailAddress
}
for _, val := range labelConfig.Tags { for _, val := range labelConfig.Tags {
if !sliceContains(tags, val) { if !sliceContains(tags, val) {
tags = append(tags, val) tags = append(tags, val)
@ -270,6 +283,10 @@ func (br *bridge) publish(n *notification) error {
req.Header.Set("X-Tags", n.tags) req.Header.Set("X-Tags", n.tags)
} }
if n.emailAddress != "" {
req.Header.Set("X-Email", n.emailAddress)
}
if n.silenceBody != "" { if n.silenceBody != "" {
url := br.cfg.BaseURL + "/silences" url := br.cfg.BaseURL + "/silences"