Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/prometheus/alertmanager/eventrecorder"
"github.com/prometheus/alertmanager/matcher/compat"
"github.com/prometheus/alertmanager/notify/discord"
"github.com/prometheus/alertmanager/notify/gotify"
"github.com/prometheus/alertmanager/notify/incidentio"
"github.com/prometheus/alertmanager/notify/jira"
"github.com/prometheus/alertmanager/notify/mattermost"
Expand Down Expand Up @@ -207,6 +208,9 @@ func resolveFilepaths(baseDir string, cfg *Config) {
for _, cfg := range receiver.DiscordConfigs {
cfg.HTTPConfig.SetDirectory(baseDir)
}
for _, cfg := range receiver.GotifyConfigs {
cfg.HTTPConfig.SetDirectory(baseDir)
}
for _, cfg := range receiver.WebexConfigs {
cfg.HTTPConfig.SetDirectory(baseDir)
}
Expand Down Expand Up @@ -569,6 +573,12 @@ func (c *Config) UnmarshalYAML(unmarshal func(any) error) error {
return errors.New("no discord webhook URL or URLFile provided")
}
}
for _, gotifyCfg := range rcv.GotifyConfigs {
if gotifyCfg == nil {
return errors.New("missing gotify config")
}
gotifyCfg.HTTPConfig = cmp.Or(gotifyCfg.HTTPConfig, c.Global.HTTPConfig)
}
for _, webex := range rcv.WebexConfigs {
if webex == nil {
return errors.New("missing webex config")
Expand Down Expand Up @@ -966,6 +976,7 @@ type Receiver struct {
Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"`

DiscordConfigs []*discord.DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"`
GotifyConfigs []*gotify.GotifyConfig `yaml:"gotify_configs,omitempty" json:"gotify_configs,omitempty"`
EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"`
IncidentioConfigs []*incidentio.IncidentioConfig `yaml:"incidentio_configs,omitempty" json:"incidentio_configs,omitempty"`
PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions config/receiver/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/notify/discord"
"github.com/prometheus/alertmanager/notify/email"
"github.com/prometheus/alertmanager/notify/gotify"
"github.com/prometheus/alertmanager/notify/incidentio"
"github.com/prometheus/alertmanager/notify/jira"
"github.com/prometheus/alertmanager/notify/mattermost"
Expand Down Expand Up @@ -96,6 +97,9 @@ func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logg
for i, c := range nc.DiscordConfigs {
add("discord", i, c, func(l *slog.Logger) (notify.Notifier, error) { return discord.New(c, tmpl, l, httpOpts...) })
}
for i, c := range nc.GotifyConfigs {
add("gotify", i, c, func(l *slog.Logger) (notify.Notifier, error) { return gotify.New(c, tmpl, l, httpOpts...) })
}
for i, c := range nc.WebexConfigs {
add("webex", i, c, func(l *slog.Logger) (notify.Notifier, error) { return webex.New(c, tmpl, l, httpOpts...) })
}
Expand Down
43 changes: 43 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,8 @@ labels:
# Configurations for several notification integrations.
discord_configs:
[ - <discord_config>, ... ]
gotify_configs:
[ - <gotify_config>, ... ]
email_configs:
[ - <email_config>, ... ]
mattermost_configs:
Expand Down Expand Up @@ -1613,6 +1615,47 @@ token_file: <filepath>
[ http_config: <http_config> | default = global.http_config ]
```

### `<gotify_config>`

Gotify notifications are sent via the [Gotify message API](https://gotify.net/docs/pushmsg).

```yaml
# Whether to notify about resolved alerts.
[ send_resolved: <boolean> | default = true ]

# Gotify server URL to send the request to.
# Typically: https://gotify.example.com/message
# url and url_file are mutually exclusive.
url: <string>
url_file: <filepath>

# The Gotify application token.
# token and token_file are mutually exclusive.
token: <secret>
token_file: <filepath>

# Notification title.
[ title: <tmpl_string> | default = '{{ template "gotify.default.title" . }}' ]

# Notification message.
[ message: <tmpl_string> | default = '{{ template "gotify.default.message" . }}' ]

# Priority.
[ priority: <tmpl_string> | default = '{{ if eq .Status "firing" }}5{{ else }}0{{ end }}' ]

# Message content type. Used to populate Gotify message extras.
[ content_type: <string> | default = 'text/plain' ]

# The HTTP client's configuration.
[ http_config: <http_config> | default = global.http_config ]

# The maximum time to wait for a gotify request to complete, before failing the
# request and allowing it to be retried. The default value of 0s indicates that
# no timeout should be applied.
# NOTE: This will have no effect if set higher than the group_interval.
[ timeout: <duration> | default = 0s ]
```

### `<rocketchat_config>`

Rocketchat notifications are sent via the [Rocketchat REST API](https://developer.rocket.chat/reference/api/rest-api/endpoints/messaging/chat-endpoints/postmessage).
Expand Down
19 changes: 19 additions & 0 deletions docs/notification_examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ sort_rank: 8
The following are all different examples of alerts and corresponding Alertmanager configuration file setups (alertmanager.yml).
Each use the [Go templating](http://golang.org/pkg/text/template/) system.

## Sending notifications to Gotify

In this example we configure a Gotify receiver using an application token.

```
route:
receiver: 'gotify-notifications'

receivers:
- name: 'gotify-notifications'
gotify_configs:
- url: 'http://localhost:8080/message'
token: '<gotify_application_token>'
title: '{{ template "gotify.default.title" . }}'
message: '{{ template "gotify.default.message" . }}'
priority: '{{ if eq .Status "firing" }}5{{ else }}0{{ end }}'
content_type: 'text/plain'
```

## Customizing Slack notifications

In this example we've customised our Slack notification to send a URL to our organisation's wiki on how to deal with the particular alert that's been sent.
Expand Down
76 changes: 76 additions & 0 deletions notify/gotify/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gotify

import (
"errors"
"time"

amcommoncfg "github.com/prometheus/alertmanager/config/common"

commoncfg "github.com/prometheus/common/config"
)

var defaultGotifyConfig = GotifyConfig{
NotifierConfig: amcommoncfg.NotifierConfig{
VSendResolved: true,
},
Title: `{{ template "gotify.default.title" . }}`,
Message: `{{ template "gotify.default.message" . }}`,
Priority: `{{ if eq .Status "firing" }}5{{ else }}0{{ end }}`,
ContentType: "text/plain",
}

type GotifyConfig struct {
amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"`
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
URL *amcommoncfg.URL `yaml:"url,omitempty" json:"url,omitempty"`
URLFile string `yaml:"url_file,omitempty" json:"url_file,omitempty"`

Token commoncfg.Secret `yaml:"token,omitempty" json:"token,omitempty"`
TokenFile string `yaml:"token_file,omitempty" json:"token_file,omitempty"`

Title string `yaml:"title,omitempty" json:"title,omitempty"`
Message string `yaml:"message,omitempty" json:"message,omitempty"`
Priority string `yaml:"priority,omitempty" json:"priority,omitempty"`
ContentType string `yaml:"content_type,omitempty" json:"content_type,omitempty"`

Timeout time.Duration `yaml:"timeout" json:"timeout"`
}

func (c *GotifyConfig) UnmarshalYAML(unmarshal func(any) error) error {
*c = defaultGotifyConfig
type plain GotifyConfig
if err := unmarshal((*plain)(c)); err != nil {
return err
}

if c.URL == nil && c.URLFile == "" {
return errors.New("one of url or url_file must be configured")
}
if c.URL != nil && c.URLFile != "" {
return errors.New("at most one of url & url_file must be configured")
}
if c.Token == "" && c.TokenFile == "" {
return errors.New("one of token or token_file must be configured")
}
if c.Token != "" && c.TokenFile != "" {
return errors.New("at most one of token & token_file must be configured")
}
if c.ContentType == "" {
c.ContentType = "text/plain"
}

return nil
}
98 changes: 98 additions & 0 deletions notify/gotify/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gotify

import (
"errors"
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

func TestGotifyConfig_UnmarshalYAML(t *testing.T) {
test := []struct {
name string
in string
expected error
}{
{
name: "with url and token - successful run",
in: `
url: http://localhost:3000
token: 00000000-0000-0000-0000-0000000000001
`,
}, {
name: "with url_file and token_file - successful run",
in: `
url_file: /path/to/file
token_file: /path/to/token
`,
}, {
name: "with url and token_file - successful run",
in: `
url: http://localhost:3000
token_file: /path/to/token
`,
}, {
name: "with url_file and token - successful run",
in: `
url_file: /path/to/file
token: 00000000-0000-0000-0000-0000000000001
`,
}, {
name: "missing url and url_file, token provided - expected error missing url or url_file",
in: `
token: 00000000-0000-0000-0000-0000000000001
`,
expected: errors.New("one of url or url_file must be configured"),
}, {
name: "missing token and token_file, url provided - expected error missing token or token_file",
in: `
url: http://localhost:3000
`,
expected: errors.New("one of token or token_file must be configured"),
}, {
name: "url and url_file provided - expected error at most one of url & url_file",
in: `
url: http://localhost:3000
url_file: /path/to/file
`,
expected: errors.New("at most one of url & url_file must be configured"),
}, {
name: "token and token_file provided - expected error at most one of token & token_file",
in: `
url: http://localhost:3000
token: 00000000-0000-0000-0000-0000000000001
token_file: /path/to/token
`,
expected: errors.New("at most one of token & token_file must be configured"),
}, {
name: "empty content type - should default to text/plain",
in: `
url: http://localhost:3000
token: 00000000-0000-0000-0000-0000000000001
content_type: ""
`,
},
}

for _, tt := range test {
t.Run(tt.name, func(t *testing.T) {
var cfg GotifyConfig
err := yaml.UnmarshalStrict([]byte(tt.in), &cfg)
require.Equal(t, tt.expected, err)
})
}
}
Loading
Loading