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

@ -42,6 +42,7 @@ type ntfyConfig struct {
User string User string
Password string Password string
AccessToken string AccessToken string
emailAddress string
} }
type labels struct { type labels struct {
@ -53,6 +54,7 @@ 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"}},
}, },

17
main.go
View file

@ -51,6 +51,7 @@ type notification struct {
priority string priority string
tags string tags string
icon string icon string
emailAddress string
silenceBody string silenceBody string
fingerprint string fingerprint string
status string status string
@ -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"