1
0
mirror of https://github.com/prometheus/alertmanager.git synced 2026-02-05 15:45:34 +01:00

Merge pull request #4355 from eyazici90/local/slack-timeout

feat: add timeout option for slack notifier
This commit is contained in:
Ben Kochie
2025-10-19 08:05:52 +01:00
committed by GitHub
4 changed files with 71 additions and 0 deletions

View File

@@ -512,6 +512,9 @@ type SlackConfig struct {
LinkNames bool `yaml:"link_names" json:"link_names,omitempty"`
MrkdwnIn []string `yaml:"mrkdwn_in,omitempty" json:"mrkdwn_in,omitempty"`
Actions []*SlackAction `yaml:"actions,omitempty" json:"actions,omitempty"`
// Timeout is the maximum time allowed to invoke the slack. Setting this to 0
// does not impose a timeout.
Timeout time.Duration `yaml:"timeout" json:"timeout"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.

View File

@@ -1460,6 +1460,12 @@ fields:
# The HTTP client's configuration.
[ http_config: <http_config> | default = global.http_config ]
# The maximum time to wait for a slack 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 ]
```
#### `<action_config>`

View File

@@ -204,8 +204,17 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
u = strings.TrimSpace(string(content))
}
if n.conf.Timeout > 0 {
postCtx, cancel := context.WithTimeoutCause(ctx, n.conf.Timeout, fmt.Errorf("configured slack timeout reached (%s)", n.conf.Timeout))
defer cancel()
ctx = postCtx
}
resp, err := n.postJSONFunc(ctx, n.client, u, &buf)
if err != nil {
if ctx.Err() != nil {
err = fmt.Errorf("%w: %w", err, context.Cause(ctx))
}
return true, notify.RedactURL(err)
}
defer notify.Drain(resp)

View File

@@ -234,3 +234,56 @@ func TestNotifier_Notify_WithReason(t *testing.T) {
})
}
}
func TestSlackTimeout(t *testing.T) {
tests := map[string]struct {
latency time.Duration
timeout time.Duration
wantErr bool
}{
"success": {latency: 100 * time.Millisecond, timeout: 120 * time.Millisecond, wantErr: false},
"error": {latency: 100 * time.Millisecond, timeout: 80 * time.Millisecond, wantErr: true},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
u, _ := url.Parse("https://slack.com/post.Message")
notifier, err := New(
&config.SlackConfig{
NotifierConfig: config.NotifierConfig{},
HTTPConfig: &commoncfg.HTTPClientConfig{},
APIURL: &config.SecretURL{URL: u},
Channel: "channelname",
Timeout: tt.timeout,
},
test.CreateTmpl(t),
promslog.NewNopLogger(),
)
require.NoError(t, err)
notifier.postJSONFunc = func(ctx context.Context, client *http.Client, url string, body io.Reader) (*http.Response, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(tt.latency):
resp := httptest.NewRecorder()
resp.Header().Set("Content-Type", "application/json; charset=utf-8")
resp.WriteHeader(http.StatusOK)
resp.WriteString(`{"ok":true}`)
return resp.Result(), nil
}
}
ctx := context.Background()
ctx = notify.WithGroupKey(ctx, "1")
alert := &types.Alert{
Alert: model.Alert{
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Hour),
},
}
_, err = notifier.Notify(ctx, alert)
require.Equal(t, tt.wantErr, err != nil)
})
}
}