Skip to content

Commit

Permalink
feat: implement webhook_url_file for discord and msteams (prometheus#…
Browse files Browse the repository at this point in the history
…3555)

* feat: implement webhook_url_file for discord

implements prometheus#3482

Signed-off-by: Philipp Born <git@pborn.eu>

* feat: implement webhook_url_file for msteams

implements prometheus#3536

Signed-off-by: Philipp Born <git@pborn.eu>

---------

Signed-off-by: Philipp Born <git@pborn.eu>
Signed-off-by: Gokhan Sari <gokhan@sari.me>
  • Loading branch information
tamcore authored and th0th committed Mar 23, 2024
1 parent 608c5a4 commit 525a555
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 6 deletions.
34 changes: 30 additions & 4 deletions config/notifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,9 @@ func (c *WebexConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
type DiscordConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`

HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"`
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"`
WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"`

Title string `yaml:"title,omitempty" json:"title,omitempty"`
Message string `yaml:"message,omitempty" json:"message,omitempty"`
Expand All @@ -227,7 +228,19 @@ type DiscordConfig struct {
func (c *DiscordConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultDiscordConfig
type plain DiscordConfig
return unmarshal((*plain)(c))
if err := unmarshal((*plain)(c)); err != nil {
return err
}

if c.WebhookURL == nil && c.WebhookURLFile == "" {
return fmt.Errorf("one of webhook_url or webhook_url_file must be configured")
}

if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 {
return fmt.Errorf("at most one of webhook_url & webhook_url_file must be configured")
}

return nil
}

// EmailConfig configures notifications via mail.
Expand Down Expand Up @@ -788,6 +801,7 @@ type MSTeamsConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
WebhookURL *SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"`
WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"`

Title string `yaml:"title,omitempty" json:"title,omitempty"`
Summary string `yaml:"summary,omitempty" json:"summary,omitempty"`
Expand All @@ -797,5 +811,17 @@ type MSTeamsConfig struct {
func (c *MSTeamsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultMSTeamsConfig
type plain MSTeamsConfig
return unmarshal((*plain)(c))
if err := unmarshal((*plain)(c)); err != nil {
return err
}

if c.WebhookURL == nil && c.WebhookURLFile == "" {
return fmt.Errorf("one of webhook_url or webhook_url_file must be configured")
}

if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 {
return fmt.Errorf("at most one of webhook_url & webhook_url_file must be configured")
}

return nil
}
4 changes: 4 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,9 @@ Discord notifications are sent via the [Discord webhook API](https://discord.com
[ send_resolved: <boolean> | default = true ]

# The Discord webhook URL.
# webhook_url and webhook_url_file are mutually exclusive.
webhook_url: <secret>
webhook_url_file: <filepath>

# Message title template.
[ title: <tmpl_string> | default = '{{ template "discord.default.title" . }}' ]
Expand Down Expand Up @@ -735,7 +737,9 @@ Microsoft Teams notifications are sent via the [Incoming Webhooks](https://learn
[ send_resolved: <boolean> | default = true ]

# The incoming webhook URL.
# webhook_url and webhook_url_file are mutually exclusive.
[ webhook_url: <secret> ]
[ webhook_url_file: <filepath> ]

# Message title template.
[ title: <tmpl_string> | default = '{{ template "msteams.default.title" . }}' ]
Expand Down
16 changes: 15 additions & 1 deletion notify/discord/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
Expand Down Expand Up @@ -120,6 +123,17 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
color = colorGreen
}

var url string
if n.conf.WebhookURL != nil {
url = n.conf.WebhookURL.String()
} else {
content, err := os.ReadFile(n.conf.WebhookURLFile)
if err != nil {
return false, fmt.Errorf("read webhook_url_file: %w", err)
}
url = strings.TrimSpace(string(content))
}

w := webhook{
Embeds: []webhookEmbed{{
Title: title,
Expand All @@ -133,7 +147,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
return false, err
}

resp, err := notify.PostJSON(ctx, n.client, n.webhookURL.String(), &payload)
resp, err := notify.PostJSON(ctx, n.client, url, &payload)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
41 changes: 41 additions & 0 deletions notify/discord/discord_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"time"

Expand Down Expand Up @@ -127,3 +128,43 @@ func TestDiscordTemplating(t *testing.T) {
})
}
}

func TestDiscordRedactedURL(t *testing.T) {
ctx, u, fn := test.GetContextWithCancelingURL()
defer fn()

secret := "secret"
notifier, err := New(
&config.DiscordConfig{
WebhookURL: &config.SecretURL{URL: u},
HTTPConfig: &commoncfg.HTTPClientConfig{},
},
test.CreateTmpl(t),
log.NewNopLogger(),
)
require.NoError(t, err)

test.AssertNotifyLeaksNoSecret(ctx, t, notifier, secret)
}

func TestDiscordReadingURLFromFile(t *testing.T) {
ctx, u, fn := test.GetContextWithCancelingURL()
defer fn()

f, err := os.CreateTemp("", "webhook_url")
require.NoError(t, err, "creating temp file failed")
_, err = f.WriteString(u.String() + "\n")
require.NoError(t, err, "writing to temp file failed")

notifier, err := New(
&config.DiscordConfig{
WebhookURLFile: f.Name(),
HTTPConfig: &commoncfg.HTTPClientConfig{},
},
test.CreateTmpl(t),
log.NewNopLogger(),
)
require.NoError(t, err)

test.AssertNotifyLeaksNoSecret(ctx, t, notifier, u.String())
}
16 changes: 15 additions & 1 deletion notify/msteams/msteams.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
Expand Down Expand Up @@ -113,6 +116,17 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
color = colorGreen
}

var url string
if n.conf.WebhookURL != nil {
url = n.conf.WebhookURL.String()
} else {
content, err := os.ReadFile(n.conf.WebhookURLFile)
if err != nil {
return false, fmt.Errorf("read webhook_url_file: %w", err)
}
url = strings.TrimSpace(string(content))
}

t := teamsMessage{
Context: "http://schema.org/extensions",
Type: "MessageCard",
Expand All @@ -127,7 +141,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
return false, err
}

resp, err := n.postJSONFunc(ctx, n.client, n.webhookURL.String(), &payload)
resp, err := n.postJSONFunc(ctx, n.client, url, &payload)
if err != nil {
return true, notify.RedactURL(err)
}
Expand Down
41 changes: 41 additions & 0 deletions notify/msteams/msteams_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"time"

Expand Down Expand Up @@ -192,3 +193,43 @@ func TestNotifier_Notify_WithReason(t *testing.T) {
})
}
}

func TestMSTeamsRedactedURL(t *testing.T) {
ctx, u, fn := test.GetContextWithCancelingURL()
defer fn()

secret := "secret"
notifier, err := New(
&config.MSTeamsConfig{
WebhookURL: &config.SecretURL{URL: u},
HTTPConfig: &commoncfg.HTTPClientConfig{},
},
test.CreateTmpl(t),
log.NewNopLogger(),
)
require.NoError(t, err)

test.AssertNotifyLeaksNoSecret(ctx, t, notifier, secret)
}

func TestMSTeamsReadingURLFromFile(t *testing.T) {
ctx, u, fn := test.GetContextWithCancelingURL()
defer fn()

f, err := os.CreateTemp("", "webhook_url")
require.NoError(t, err, "creating temp file failed")
_, err = f.WriteString(u.String() + "\n")
require.NoError(t, err, "writing to temp file failed")

notifier, err := New(
&config.MSTeamsConfig{
WebhookURLFile: f.Name(),
HTTPConfig: &commoncfg.HTTPClientConfig{},
},
test.CreateTmpl(t),
log.NewNopLogger(),
)
require.NoError(t, err)

test.AssertNotifyLeaksNoSecret(ctx, t, notifier, u.String())
}

0 comments on commit 525a555

Please sign in to comment.