~xenrox/ntfy-alertmanager

f4483532f52ed264cf1b1ea9e6ce194190c8ea91 — Thorben Günther a month ago fa1a791
Add support for displaying the alert's GeneratorURL
5 files changed, 57 insertions(+), 27 deletions(-)

M README.md
M config.scfg
M config/config.go
M config/config_test.go
M main.go
M README.md => README.md +10 -10
@@ 19,23 19,23 @@ of this file is [scfg] and there is an [example configuration file] in this repo
Furthermore you can take a look at [my deployment].

ntfy-alertmanager has support for setting ntfy [priority], [tags], [icon], [action buttons]
(which can be used to create an Alertmanager silence), [email notifications] and [phone calls].
(which can be used to e.g. create an Alertmanager silence or open the alert's Prometheus URL), [email notifications] and [phone calls].
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. Settings for "resolved" alerts will take precedence.
-   Tags are added together.
- For priority and icon the first found value will be chosen. Settings for "resolved" alerts will take precedence.
- Tags are added together.

### Alertmanager config

```yaml
receivers:
    - name: "ntfy"
      webhook_configs:
          - url: "http://127.0.0.1:8080"
            http_config:
                basic_auth:
                    username: "webhookUser"
                    password: "webhookPass"
  - name: "ntfy"
    webhook_configs:
      - url: "http://127.0.0.1:8080"
        http_config:
          basic_auth:
            username: "webhookUser"
            password: "webhookPass"
```

## Contributing

M config.scfg => config.scfg +5 -1
@@ 87,8 87,12 @@ ntfy {
    # Default: ""
    email-address foo@example.com
    # Call the specified number for all alerts. Use `yes` to pick the first of your verified numbers.
    Default: ""
    # Default: ""
    call +123456789
    # Add a button that will open the alert's generator/Prometheus URL with the following label.
    # This only works for the "single" alert-mode.
    # Default: ""
    generator-url-label source
}

alertmanager {

M config/config.go => config/config.go +15 -7
@@ 37,13 37,14 @@ type Config struct {
}

type ntfyConfig struct {
	Topic           string
	User            string
	Password        string
	AccessToken     string
	CertFingerprint string
	EmailAddress    string
	Call            string
	Topic             string
	User              string
	Password          string
	AccessToken       string
	CertFingerprint   string
	EmailAddress      string
	Call              string
	GeneratorURLLabel string
}

type labels struct {


@@ 272,6 273,13 @@ func parseBlock(block scfg.Block, config *Config) error {
				return err
			}
		}

		d = ntfyDir.Children.Get("generator-url-label")
		if d != nil {
			if err := d.ParseParams(&config.Ntfy.GeneratorURLLabel); err != nil {
				return err
			}
		}
	}

	cacheDir := block.Get("cache")

M config/config_test.go => config/config_test.go +6 -4
@@ 50,6 50,7 @@ ntfy {
    certificate-fingerprint 13:6D:2B:88:9C:57:36:D0:81:B4:B2:9C:79:09:27:62:92:CF:B8:6A:6B:D3:AD:46:35:CB:70:17:EB:99:6E:28:08:2A:B8:C6:79:4B:F6:2E:81:79:41:98:1D:53:C8:07:B3:5C:24:5F:B1:8E:B6:FB:66:B5:DD:B4:D0:5C:29:91
    user user
    password pass
    generator-url-label source
}

alertmanager {


@@ 76,10 77,11 @@ cache {
		User:        "webhookUser",
		Password:    "webhookPass",
		Ntfy: ntfyConfig{
			Topic:           "https://ntfy.sh/alertmanager-alerts",
			User:            "user",
			Password:        "pass",
			CertFingerprint: "136d2b889c5736d081b4b29c7909276292cfb86a6bd3ad4635cb7017eb996e28082ab8c6794bf62e817941981d53c807b35c245fb18eb6fb66b5ddb4d05c2991",
			Topic:             "https://ntfy.sh/alertmanager-alerts",
			User:              "user",
			Password:          "pass",
			CertFingerprint:   "136d2b889c5736d081b4b29c7909276292cfb86a6bd3ad4635cb7017eb996e28082ab8c6794bf62e817941981d53c807b35c245fb18eb6fb66b5ddb4d05c2991",
			GeneratorURLLabel: "source",
		},
		Labels: labels{Order: []string{"severity", "instance"},
			Label: map[string]labelConfig{

M main.go => main.go +21 -5
@@ 50,10 50,11 @@ type payload struct {
}

type alert struct {
	Status      string            `json:"status"`
	Labels      map[string]string `json:"labels"`
	Annotations map[string]string `json:"annotations"`
	Fingerprint string            `json:"fingerprint"`
	Status       string            `json:"status"`
	Labels       map[string]string `json:"labels"`
	Annotations  map[string]string `json:"annotations"`
	GeneratorURL string            `json:"generatorURL"`
	Fingerprint  string            `json:"fingerprint"`
}

type notification struct {


@@ 67,6 68,7 @@ type notification struct {
	silenceBody  string
	fingerprint  string
	status       string
	generatorURL string
}

type ntfyError struct {


@@ 91,6 93,7 @@ func (br *bridge) singleAlertNotifications(p *payload) []*notification {
		n := new(notification)
		n.fingerprint = alert.Fingerprint
		n.status = alert.Status
		n.generatorURL = alert.GeneratorURL

		// create title
		n.title = fmt.Sprintf("[%s]", strings.ToUpper(alert.Status))


@@ 295,6 298,8 @@ func (br *bridge) publish(n *notification) error {
		return err
	}

	var actions []string

	// ntfy authentication
	if br.cfg.Ntfy.Password != "" && br.cfg.Ntfy.User != "" {
		req.SetBasicAuth(br.cfg.Ntfy.User, br.cfg.Ntfy.Password)


@@ 333,8 338,19 @@ func (br *bridge) publish(n *notification) error {
			authString = fmt.Sprintf(", headers.Authorization=Basic %s", auth)
		}

		req.Header.Set("Actions", fmt.Sprintf("http, Silence, %s, method=POST, body=%s%s", url, n.silenceBody, authString))
		actions = append(actions, fmt.Sprintf("http, Silence, %s, method=POST, body=%s%s", url, n.silenceBody, authString))
	}

	if br.cfg.Ntfy.GeneratorURLLabel != "" && n.generatorURL != "" {
		actions = append(actions, fmt.Sprintf("view, %s, %s", br.cfg.Ntfy.GeneratorURLLabel, n.generatorURL))
	}

	nActions := len(actions)
	if nActions > 3 {
		// TODO: Limit actions to three
		br.logger.Warn(fmt.Sprintf("Publish: Too many actions (%d), ntfy only supports up to three.", nActions))
	}
	req.Header.Set("Actions", strings.Join(actions, ";"))

	configFingerprint := br.cfg.Ntfy.CertFingerprint
	if configFingerprint != "" {