Skip to content

Commit 41eb121

Browse files
authored
support loading webhook URL from a file (#3223)
* support loading webhook URL from a file /cc #2498 Signed-off-by: Simon Rozet <[email protected]> * notify/webhook: add test for reading url from file Signed-off-by: Simon Rozet <[email protected]> * notify/pushover: add tests for reading secrets from files Signed-off-by: Simon Rozet <[email protected]> --------- Signed-off-by: Simon Rozet <[email protected]>
1 parent 44bb5c9 commit 41eb121

File tree

6 files changed

+120
-8
lines changed

6 files changed

+120
-8
lines changed

config/notifiers.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,8 @@ type WebhookConfig struct {
473473
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
474474

475475
// URL to send POST request to.
476-
URL *SecretURL `yaml:"url" json:"url"`
476+
URL *SecretURL `yaml:"url" json:"url"`
477+
URLFile string `yaml:"url_file" json:"url_file"`
477478

478479
// MaxAlerts is the maximum number of alerts to be sent per webhook message.
479480
// Alerts exceeding this threshold will be truncated. Setting this to 0
@@ -488,11 +489,16 @@ func (c *WebhookConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
488489
if err := unmarshal((*plain)(c)); err != nil {
489490
return err
490491
}
491-
if c.URL == nil {
492-
return fmt.Errorf("missing URL in webhook config")
492+
if c.URL == nil && c.URLFile == "" {
493+
return fmt.Errorf("one of url or url_file must be configured")
493494
}
494-
if c.URL.Scheme != "https" && c.URL.Scheme != "http" {
495-
return fmt.Errorf("scheme required for webhook url")
495+
if c.URL != nil && c.URLFile != "" {
496+
return fmt.Errorf("at most one of url & url_file must be configured")
497+
}
498+
if c.URL != nil {
499+
if c.URL.Scheme != "https" && c.URL.Scheme != "http" {
500+
return fmt.Errorf("scheme required for webhook url")
501+
}
496502
}
497503
return nil
498504
}

config/notifiers_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,25 @@ func TestWebhookURLIsPresent(t *testing.T) {
228228
var cfg WebhookConfig
229229
err := yaml.UnmarshalStrict([]byte(in), &cfg)
230230

231-
expected := "missing URL in webhook config"
231+
expected := "one of url or url_file must be configured"
232+
233+
if err == nil {
234+
t.Fatalf("no error returned, expected:\n%v", expected)
235+
}
236+
if err.Error() != expected {
237+
t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error())
238+
}
239+
}
240+
241+
func TestWebhookURLOrURLFile(t *testing.T) {
242+
in := `
243+
url: 'http://example.com'
244+
url_file: 'http://example.com'
245+
`
246+
var cfg WebhookConfig
247+
err := yaml.UnmarshalStrict([]byte(in), &cfg)
248+
249+
expected := "at most one of url & url_file must be configured"
232250

233251
if err == nil {
234252
t.Fatalf("no error returned, expected:\n%v", expected)

docs/configuration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ Pushover notifications are sent via the [Pushover API](https://pushover.net/api)
862862
# Whether to notify about resolved alerts.
863863
[ send_resolved: <boolean> | default = true ]
864864
865-
# The recipient user's key.
865+
# The recipient user's key.
866866
# user_key and user_key_file are mutually exclusive.
867867
user_key: <secret>
868868
user_key_file: <filepath>
@@ -1116,7 +1116,9 @@ The webhook receiver allows configuring a generic receiver.
11161116
[ send_resolved: <boolean> | default = true ]
11171117
11181118
# The endpoint to send HTTP POST requests to.
1119+
# url and url_file are mutually exclusive.
11191120
url: <secret>
1121+
url_file: <filepath>
11201122
11211123
# The HTTP client's configuration.
11221124
[ http_config: <http_config> | default = global.http_config ]

notify/pushover/pushover_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package pushover
1515

1616
import (
1717
"fmt"
18+
"os"
1819
"testing"
1920

2021
"github.com/go-kit/log"
@@ -59,3 +60,53 @@ func TestPushoverRedactedURL(t *testing.T) {
5960

6061
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, key, token)
6162
}
63+
64+
func TestPushoverReadingUserKeyFromFile(t *testing.T) {
65+
ctx, apiURL, fn := test.GetContextWithCancelingURL()
66+
defer fn()
67+
68+
const userKey = "user key"
69+
f, err := os.CreateTemp("", "pushover_user_key")
70+
require.NoError(t, err, "creating temp file failed")
71+
_, err = f.WriteString(userKey)
72+
require.NoError(t, err, "writing to temp file failed")
73+
74+
notifier, err := New(
75+
&config.PushoverConfig{
76+
UserKeyFile: f.Name(),
77+
Token: config.Secret("token"),
78+
HTTPConfig: &commoncfg.HTTPClientConfig{},
79+
},
80+
test.CreateTmpl(t),
81+
log.NewNopLogger(),
82+
)
83+
notifier.apiURL = apiURL.String()
84+
require.NoError(t, err)
85+
86+
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, userKey)
87+
}
88+
89+
func TestPushoverReadingTokenFromFile(t *testing.T) {
90+
ctx, apiURL, fn := test.GetContextWithCancelingURL()
91+
defer fn()
92+
93+
const token = "token"
94+
f, err := os.CreateTemp("", "pushover_token")
95+
require.NoError(t, err, "creating temp file failed")
96+
_, err = f.WriteString(token)
97+
require.NoError(t, err, "writing to temp file failed")
98+
99+
notifier, err := New(
100+
&config.PushoverConfig{
101+
UserKey: config.Secret("user key"),
102+
TokenFile: f.Name(),
103+
HTTPConfig: &commoncfg.HTTPClientConfig{},
104+
},
105+
test.CreateTmpl(t),
106+
log.NewNopLogger(),
107+
)
108+
notifier.apiURL = apiURL.String()
109+
require.NoError(t, err)
110+
111+
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, token)
112+
}

notify/webhook/webhook.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"io"
2222
"net/http"
23+
"os"
2324

2425
"github.com/go-kit/log"
2526
"github.com/go-kit/log/level"
@@ -101,7 +102,18 @@ func (n *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, er
101102
return false, err
102103
}
103104

104-
resp, err := notify.PostJSON(ctx, n.client, n.conf.URL.String(), &buf)
105+
var url string
106+
if n.conf.URL != nil {
107+
url = n.conf.URL.String()
108+
} else {
109+
content, err := os.ReadFile(n.conf.URLFile)
110+
if err != nil {
111+
return false, fmt.Errorf("read url_file: %w", err)
112+
}
113+
url = string(content)
114+
}
115+
116+
resp, err := notify.PostJSON(ctx, n.client, url, &buf)
105117
if err != nil {
106118
return true, notify.RedactURL(err)
107119
}

notify/webhook/webhook_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"io"
2020
"net/http"
2121
"net/url"
22+
"os"
2223
"testing"
2324

2425
"github.com/go-kit/log"
@@ -116,3 +117,25 @@ func TestWebhookRedactedURL(t *testing.T) {
116117

117118
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, secret)
118119
}
120+
121+
func TestWebhookReadingURLFromFile(t *testing.T) {
122+
ctx, u, fn := test.GetContextWithCancelingURL()
123+
defer fn()
124+
125+
f, err := os.CreateTemp("", "webhook_url")
126+
require.NoError(t, err, "creating temp file failed")
127+
_, err = f.WriteString(u.String())
128+
require.NoError(t, err, "writing to temp file failed")
129+
130+
notifier, err := New(
131+
&config.WebhookConfig{
132+
URLFile: f.Name(),
133+
HTTPConfig: &commoncfg.HTTPClientConfig{},
134+
},
135+
test.CreateTmpl(t),
136+
log.NewNopLogger(),
137+
)
138+
require.NoError(t, err)
139+
140+
test.AssertNotifyLeaksNoSecret(ctx, t, notifier, u.String())
141+
}

0 commit comments

Comments
 (0)