support loading webhook URL from a file (#3223)
* support loading webhook URL from a file /cc #2498 Signed-off-by: Simon Rozet <me@simonrozet.com> * notify/webhook: add test for reading url from file Signed-off-by: Simon Rozet <me@simonrozet.com> * notify/pushover: add tests for reading secrets from files Signed-off-by: Simon Rozet <me@simonrozet.com> --------- Signed-off-by: Simon Rozet <me@simonrozet.com>
This commit is contained in:
parent
44bb5c9bd3
commit
41eb1213bb
|
@ -473,7 +473,8 @@ type WebhookConfig struct {
|
|||
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
|
||||
|
||||
// URL to send POST request to.
|
||||
URL *SecretURL `yaml:"url" json:"url"`
|
||||
URL *SecretURL `yaml:"url" json:"url"`
|
||||
URLFile string `yaml:"url_file" json:"url_file"`
|
||||
|
||||
// MaxAlerts is the maximum number of alerts to be sent per webhook message.
|
||||
// Alerts exceeding this threshold will be truncated. Setting this to 0
|
||||
|
@ -488,11 +489,16 @@ func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.URL == nil {
|
||||
return fmt.Errorf("missing URL in webhook config")
|
||||
if c.URL == nil && c.URLFile == "" {
|
||||
return fmt.Errorf("one of url or url_file must be configured")
|
||||
}
|
||||
if c.URL.Scheme != "https" && c.URL.Scheme != "http" {
|
||||
return fmt.Errorf("scheme required for webhook url")
|
||||
if c.URL != nil && c.URLFile != "" {
|
||||
return fmt.Errorf("at most one of url & url_file must be configured")
|
||||
}
|
||||
if c.URL != nil {
|
||||
if c.URL.Scheme != "https" && c.URL.Scheme != "http" {
|
||||
return fmt.Errorf("scheme required for webhook url")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -228,7 +228,25 @@ func TestWebhookURLIsPresent(t *testing.T) {
|
|||
var cfg WebhookConfig
|
||||
err := yaml.UnmarshalStrict([]byte(in), &cfg)
|
||||
|
||||
expected := "missing URL in webhook config"
|
||||
expected := "one of url or url_file must be configured"
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned, expected:\n%v", expected)
|
||||
}
|
||||
if err.Error() != expected {
|
||||
t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookURLOrURLFile(t *testing.T) {
|
||||
in := `
|
||||
url: 'http://example.com'
|
||||
url_file: 'http://example.com'
|
||||
`
|
||||
var cfg WebhookConfig
|
||||
err := yaml.UnmarshalStrict([]byte(in), &cfg)
|
||||
|
||||
expected := "at most one of url & url_file must be configured"
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned, expected:\n%v", expected)
|
||||
|
|
|
@ -862,7 +862,7 @@ Pushover notifications are sent via the [Pushover API](https://pushover.net/api)
|
|||
# Whether to notify about resolved alerts.
|
||||
[ send_resolved: <boolean> | default = true ]
|
||||
|
||||
# The recipient user's key.
|
||||
# The recipient user's key.
|
||||
# user_key and user_key_file are mutually exclusive.
|
||||
user_key: <secret>
|
||||
user_key_file: <filepath>
|
||||
|
@ -1116,7 +1116,9 @@ The webhook receiver allows configuring a generic receiver.
|
|||
[ send_resolved: <boolean> | default = true ]
|
||||
|
||||
# The endpoint to send HTTP POST requests to.
|
||||
# url and url_file are mutually exclusive.
|
||||
url: <secret>
|
||||
url_file: <filepath>
|
||||
|
||||
# The HTTP client's configuration.
|
||||
[ http_config: <http_config> | default = global.http_config ]
|
||||
|
|
|
@ -15,6 +15,7 @@ package pushover
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
|
@ -59,3 +60,53 @@ func TestPushoverRedactedURL(t *testing.T) {
|
|||
|
||||
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, key, token)
|
||||
}
|
||||
|
||||
func TestPushoverReadingUserKeyFromFile(t *testing.T) {
|
||||
ctx, apiURL, fn := test.GetContextWithCancelingURL()
|
||||
defer fn()
|
||||
|
||||
const userKey = "user key"
|
||||
f, err := os.CreateTemp("", "pushover_user_key")
|
||||
require.NoError(t, err, "creating temp file failed")
|
||||
_, err = f.WriteString(userKey)
|
||||
require.NoError(t, err, "writing to temp file failed")
|
||||
|
||||
notifier, err := New(
|
||||
&config.PushoverConfig{
|
||||
UserKeyFile: f.Name(),
|
||||
Token: config.Secret("token"),
|
||||
HTTPConfig: &commoncfg.HTTPClientConfig{},
|
||||
},
|
||||
test.CreateTmpl(t),
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
notifier.apiURL = apiURL.String()
|
||||
require.NoError(t, err)
|
||||
|
||||
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, userKey)
|
||||
}
|
||||
|
||||
func TestPushoverReadingTokenFromFile(t *testing.T) {
|
||||
ctx, apiURL, fn := test.GetContextWithCancelingURL()
|
||||
defer fn()
|
||||
|
||||
const token = "token"
|
||||
f, err := os.CreateTemp("", "pushover_token")
|
||||
require.NoError(t, err, "creating temp file failed")
|
||||
_, err = f.WriteString(token)
|
||||
require.NoError(t, err, "writing to temp file failed")
|
||||
|
||||
notifier, err := New(
|
||||
&config.PushoverConfig{
|
||||
UserKey: config.Secret("user key"),
|
||||
TokenFile: f.Name(),
|
||||
HTTPConfig: &commoncfg.HTTPClientConfig{},
|
||||
},
|
||||
test.CreateTmpl(t),
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
notifier.apiURL = apiURL.String()
|
||||
require.NoError(t, err)
|
||||
|
||||
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, token)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
|
@ -101,7 +102,18 @@ func (n *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, er
|
|||
return false, err
|
||||
}
|
||||
|
||||
resp, err := notify.PostJSON(ctx, n.client, n.conf.URL.String(), &buf)
|
||||
var url string
|
||||
if n.conf.URL != nil {
|
||||
url = n.conf.URL.String()
|
||||
} else {
|
||||
content, err := os.ReadFile(n.conf.URLFile)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("read url_file: %w", err)
|
||||
}
|
||||
url = string(content)
|
||||
}
|
||||
|
||||
resp, err := notify.PostJSON(ctx, n.client, url, &buf)
|
||||
if err != nil {
|
||||
return true, notify.RedactURL(err)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
|
@ -116,3 +117,25 @@ func TestWebhookRedactedURL(t *testing.T) {
|
|||
|
||||
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, secret)
|
||||
}
|
||||
|
||||
func TestWebhookReadingURLFromFile(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())
|
||||
require.NoError(t, err, "writing to temp file failed")
|
||||
|
||||
notifier, err := New(
|
||||
&config.WebhookConfig{
|
||||
URLFile: f.Name(),
|
||||
HTTPConfig: &commoncfg.HTTPClientConfig{},
|
||||
},
|
||||
test.CreateTmpl(t),
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, u.String())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue