Skip to content
Open
29 changes: 19 additions & 10 deletions http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,25 +106,25 @@ func ToHTTPClientOption(option ...ClientOption) []commoncfg.HTTPClientOption {
return result
}

func (ns *Client) SendWebhook(ctx context.Context, l log.Logger, webhook *receivers.SendWebhookSettings) error {
func (ns *Client) SendWebhook(ctx context.Context, l log.Logger, webhook *receivers.SendWebhookSettings) (*receivers.WebhookResponse, error) {
// This method was moved from https://github.com/grafana/grafana/blob/71d04a326be9578e2d678f23c1efa61768e0541f/pkg/services/notifications/webhook.go#L38
if webhook.HTTPMethod == "" {
webhook.HTTPMethod = http.MethodPost
}
level.Debug(l).Log("msg", "sending webhook", "url", webhook.URL, "http method", webhook.HTTPMethod)

if webhook.HTTPMethod != http.MethodPost && webhook.HTTPMethod != http.MethodPut {
return fmt.Errorf("%w: %s", ErrInvalidMethod, webhook.HTTPMethod)
return nil, fmt.Errorf("%w: %s", ErrInvalidMethod, webhook.HTTPMethod)
}

request, err := http.NewRequestWithContext(ctx, webhook.HTTPMethod, webhook.URL, bytes.NewReader([]byte(webhook.Body)))
if err != nil {
return err
return nil, err
}
url, err := url.Parse(webhook.URL)
if err != nil {
// Should not be possible - NewRequestWithContext should also err if the URL is bad.
return err
return nil, err
}

if webhook.ContentType == "" {
Expand Down Expand Up @@ -155,7 +155,7 @@ func (ns *Client) SendWebhook(ctx context.Context, l log.Logger, webhook *receiv
)
if err != nil {
level.Error(l).Log("msg", "Failed to add HMAC roundtripper to client", "err", err)
return err
return nil, err
}
}

Expand All @@ -166,7 +166,7 @@ func (ns *Client) SendWebhook(ctx context.Context, l log.Logger, webhook *receiv

resp, err := client.Do(request)
if err != nil {
return redactURL(err)
return nil, redactURL(err)
}
defer func() {
if err := resp.Body.Close(); err != nil {
Expand All @@ -176,24 +176,33 @@ func (ns *Client) SendWebhook(ctx context.Context, l log.Logger, webhook *receiv

body, err := io.ReadAll(resp.Body)
if err != nil {
return err
return nil, err
}

webhookResp := &receivers.WebhookResponse{
StatusCode: resp.StatusCode,
Body: body,
Headers: make(map[string][]string),
}
for k, v := range resp.Header {
webhookResp.Headers[k] = v
}

if webhook.Validation != nil {
err := webhook.Validation(body, resp.StatusCode)
if err != nil {
level.Debug(l).Log("msg", "Webhook failed validation", "url", url.Redacted(), "statuscode", resp.Status, "body", string(body), "err", err)
return fmt.Errorf("webhook failed validation: %w", err)
return webhookResp, fmt.Errorf("webhook failed validation: %w", err)
}
}

if resp.StatusCode/100 == 2 {
level.Debug(l).Log("msg", "Webhook succeeded", "url", url.Redacted(), "statuscode", resp.Status)
return nil
return webhookResp, nil
}

level.Debug(l).Log("msg", "Webhook failed", "url", url.Redacted(), "statuscode", resp.Status, "body", string(body))
return fmt.Errorf("webhook response status %v", resp.Status)
return webhookResp, fmt.Errorf("webhook response status %v", resp.Status)
}

func redactURL(err error) error {
Expand Down
25 changes: 17 additions & 8 deletions http/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ func NewForkedSender(cli *Client) *ForkedSender {
return &ForkedSender{cli: cli}
}

func (f ForkedSender) SendWebhook(ctx context.Context, l log.Logger, cmd *receivers.SendWebhookSettings) error {
func (f ForkedSender) SendWebhook(ctx context.Context, l log.Logger, cmd *receivers.SendWebhookSettings) (*receivers.WebhookResponse, error) {
if cmd.HTTPMethod != "GET" {
return f.cli.SendWebhook(ctx, l, cmd)
}

request, err := http.NewRequestWithContext(ctx, cmd.HTTPMethod, cmd.URL, nil)
if err != nil {
return err
return nil, err
}
_, err = url.Parse(cmd.URL)
if err != nil {
// Should not be possible - NewRequestWithContext should also err if the URL is bad.
return err
return nil, err
}

request.Header.Set("User-Agent", "Grafana")
Expand All @@ -47,27 +47,36 @@ func (f ForkedSender) SendWebhook(ctx context.Context, l log.Logger, cmd *receiv

resp, err := NewTLSClient(cmd.TLSConfig, f.cli.cfg.dialer.DialContext).Do(request)
if err != nil {
return redactURL(err)
return nil, redactURL(err)
}
defer func() {
_ = resp.Body.Close()
}()

body, err := io.ReadAll(resp.Body)
if err != nil {
return err
return nil, err
}

webhookResp := &receivers.WebhookResponse{
StatusCode: resp.StatusCode,
Body: body,
Headers: make(map[string][]string),
}
for k, v := range resp.Header {
webhookResp.Headers[k] = v
}

if cmd.Validation != nil {
err := cmd.Validation(body, resp.StatusCode)
if err != nil {
return fmt.Errorf("webhook failed validation: %w", err)
return webhookResp, fmt.Errorf("webhook failed validation: %w", err)
}
}

if resp.StatusCode/100 == 2 {
return nil
return webhookResp, nil
}

return fmt.Errorf("webhook response status %v", resp.Status)
return webhookResp, fmt.Errorf("webhook response status %v", resp.Status)
}
2 changes: 1 addition & 1 deletion receivers/dingding/v1/dingding.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (dd *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error

cmd := &receivers.SendWebhookSettings{URL: u, Body: b}

if err := dd.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := dd.ns.SendWebhook(ctx, l, cmd); err != nil {
return false, fmt.Errorf("send notification to dingding: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion receivers/discord/v1/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (d Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
}
return nil
}
if err := d.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := d.ns.SendWebhook(ctx, l, cmd); err != nil {
return false, err
}
return true, nil
Expand Down
2 changes: 1 addition & 1 deletion receivers/googlechat/v1/googlechat.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (gcn *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, erro
Body: string(body),
}

if err := gcn.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := gcn.ns.SendWebhook(ctx, l, cmd); err != nil {
level.Error(l).Log("msg", "failed to send Google Hangouts Chat alert", "err", err)
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion receivers/jira/v1/jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func (n *Notifier) doAPIRequest(ctx context.Context, method, path string, reques

var shouldRetry bool
var responseBody []byte
err = n.ns.SendWebhook(ctx, logger, &receivers.SendWebhookSettings{
_, err = n.ns.SendWebhook(ctx, logger, &receivers.SendWebhookSettings{
URL: n.conf.URL.JoinPath(path).String(),
User: n.conf.User,
Password: n.conf.Password,
Expand Down
4 changes: 2 additions & 2 deletions receivers/kafka/v1/kafka.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (kn *Notifier) notifyWithAPIV2(ctx context.Context, as ...*types.Alert) (bo
Password: kn.settings.Password,
}

if err := kn.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := kn.ns.SendWebhook(ctx, l, cmd); err != nil {
level.Error(l).Log("msg", "Failed to send notification to Kafka", "err", err, "body", body)
return false, err
}
Expand Down Expand Up @@ -150,7 +150,7 @@ func (kn *Notifier) notifyWithAPIV3(ctx context.Context, as ...*types.Alert) (bo
// Can be implemented nicely using receivers. The v3 API can be used in streaming mode
// by setting “Transfer-Encoding: chunked” header.
// For as long as the connection is kept open, the server will keep accepting records.
if err := kn.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := kn.ns.SendWebhook(ctx, l, cmd); err != nil {
level.Error(l).Log("msg", "Failed to send notification to Kafka", "err", err, "body", body)
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion receivers/line/v1/line.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (ln *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error
Body: form.Encode(),
}

if err := ln.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := ln.ns.SendWebhook(ctx, l, cmd); err != nil {
level.Error(l).Log("msg", "failed to send notification to LINE", "err", err, "body", body)
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion receivers/oncall/v1/oncall.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
HTTPHeader: headers,
}

if err := n.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := n.ns.SendWebhook(ctx, l, cmd); err != nil {
return false, err
}

Expand Down
2 changes: 1 addition & 1 deletion receivers/opsgenie/v1/opsgenie.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (on *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error
},
}

if err := on.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := on.ns.SendWebhook(ctx, l, cmd); err != nil {
return false, fmt.Errorf("send notification to Opsgenie: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion receivers/pagerduty/v1/pagerduty.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (pn *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error
"Content-Type": "application/json",
},
}
if err := pn.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := pn.ns.SendWebhook(ctx, l, cmd); err != nil {
return false, fmt.Errorf("send notification to Pagerduty: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion receivers/pushover/v1/pushover.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (pn *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error
Body: uploadBody.String(),
}

if err := pn.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := pn.ns.SendWebhook(ctx, l, cmd); err != nil {
level.Error(l).Log("msg", "failed to send pushover notification", "err", err)
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion receivers/sensugo/v1/sensugo.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (sn *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error
"Authorization": fmt.Sprintf("Key %s", sn.settings.APIKey),
},
}
if err := sn.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := sn.ns.SendWebhook(ctx, l, cmd); err != nil {
level.Error(l).Log("msg", "failed to send Sensu Go event", "err", err)
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion receivers/teams/v1/teams.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func (tn *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error
cmd.Validation = validateResponse(l)
}

if err := tn.ns.SendWebhook(ctx, l, cmd); err != nil {
if _, err := tn.ns.SendWebhook(ctx, l, cmd); err != nil {
return false, errors.Wrap(err, "send notification to Teams")
}

Expand Down
Loading