Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions receivers/email/v1/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ func (en *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, e
level.Warn(l).Log("msg", "failed to get all images for email", "err", err)
}

// Augment extended Alert data with any extra data if provided.
receivers.ApplyExtraData(ctx, data.Alerts)
Comment thread
fayzal-g marked this conversation as resolved.

cmd := &receivers.SendEmailSettings{
Subject: subject,
Data: map[string]interface{}{
Expand Down
66 changes: 66 additions & 0 deletions receivers/email/v1/email_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1

import (
"context"
"encoding/json"
"net/url"
"testing"

Expand Down Expand Up @@ -236,3 +237,68 @@ func TestNotify(t *testing.T) {
})
})
}

func TestNotify_ExtraData(t *testing.T) {
tmpl := templates.ForTests(t)
externalURL, err := url.Parse("http://localhost/base")
require.NoError(t, err)
tmpl.ExternalURL = externalURL

settings := Config{
SingleEmail: false,
Addresses: []string{
"someops@example.com",
},
Message: "{{ template \"default.title\" . }}",
Subject: templates.DefaultMessageTitleEmbed,
}

emailSender := receivers.MockNotificationService()
emailNotifier := &Notifier{
Base: receivers.NewBase(receivers.Metadata{}, log.NewNopLogger()),
ns: emailSender,
tmpl: tmpl,
settings: settings,
images: &images.UnavailableProvider{},
}

// Create test alerts
alerts := []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{"ann1": "annv1"},
},
},
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert2", "lbl1": "val2"},
Annotations: model.LabelSet{"ann1": "annv2"},
},
},
}

// Create extra data that will be passed via context
extraData1 := json.RawMessage(`{"customField": "customValue1", "priority": "high"}`)
extraData2 := json.RawMessage(`{"customField": "customValue2", "priority": "medium"}`)
extraDataSlice := []json.RawMessage{extraData1, extraData2}

// Create context with extra data
ctx := context.WithValue(context.Background(), receivers.ExtraDataKey, extraDataSlice)

// Call Notify
ok, err := emailNotifier.Notify(ctx, alerts...)
require.NoError(t, err)
require.True(t, ok)

// Verify that extra data is present in the alerts
emailAlerts, ok := emailSender.EmailSync.Data["Alerts"].(templates.ExtendedAlerts)
require.True(t, ok, "Alerts should be of type templates.ExtendedAlerts")
require.Len(t, emailAlerts, 2)

// Check first alert's extra data
require.JSONEq(t, string(extraData1), string(emailAlerts[0].ExtraData))

// Check second alert's extra data
require.JSONEq(t, string(extraData2), string(emailAlerts[1].ExtraData))
}
4 changes: 4 additions & 0 deletions receivers/mqtt/v1/mqtt.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ func (n *Notifier) buildMessage(ctx context.Context, l log.Logger, as ...*types.

var tmplErr error
tmpl, data := templates.TmplText(ctx, n.tmpl, as, l, &tmplErr)

// Augment extended Alert data with any extra data if provided.
receivers.ApplyExtraData(ctx, data.Alerts)

messageText := tmpl(n.settings.Message)
if tmplErr != nil {
level.Warn(l).Log("msg", "Failed to template MQTT message", "err", tmplErr.Error())
Expand Down
77 changes: 77 additions & 0 deletions receivers/mqtt/v1/mqtt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"net/url"
"testing"

Expand Down Expand Up @@ -436,3 +437,79 @@ func TestNew(t *testing.T) {
})
}
}

func TestNotify_ExtraData(t *testing.T) {
tmpl := templates.ForTests(t)
require.NotNil(t, tmpl)

externalURL, err := url.Parse("http://localhost/base")
require.NoError(t, err)
tmpl.ExternalURL = externalURL

settings := Config{
Topic: "alert1",
Message: templates.DefaultMessageEmbed,
MessageFormat: MessageFormatJSON,
}

// Create test alerts
alerts := []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{"ann1": "annv1"},
},
},
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert2", "lbl1": "val2"},
Annotations: model.LabelSet{"ann1": "annv2"},
},
},
}

// Create extra data that will be passed via context
extraData1 := json.RawMessage(`{"customField": "customValue1", "priority": "high"}`)
extraData2 := json.RawMessage(`{"customField": "customValue2", "priority": "medium"}`)
extraDataSlice := []json.RawMessage{extraData1, extraData2}

mockMQTTClient := new(mockMQTTClient)
mockMQTTClient.On(
"Connect",
mock.Anything,
settings.BrokerURL,
settings.ClientID,
settings.Username,
settings.Password,
mock.Anything,
).Return(nil)
mockMQTTClient.On("Disconnect", mock.Anything).Return(nil)
mockMQTTClient.On("Publish", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)

n := &Notifier{
Base: receivers.NewBase(receivers.Metadata{}, log.NewNopLogger()),
tmpl: tmpl,
settings: settings,
client: mockMQTTClient,
}

// Create context with extra data
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ctx = context.WithValue(ctx, receivers.ExtraDataKey, extraDataSlice)

// Call Notify
ok, err := n.Notify(ctx, alerts...)
require.NoError(t, err)
require.True(t, ok)

// Verify that the extra data is present in the published message
require.Equal(t, 1, len(mockMQTTClient.publishedMessages))

payload := string(mockMQTTClient.publishedMessages[0].payload)

// Verify that extra data is present in the JSON message
require.Contains(t, payload, "customField")
require.Contains(t, payload, "customValue1")
require.Contains(t, payload, "customValue2")
}
11 changes: 2 additions & 9 deletions receivers/oncall/v1/oncall.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,8 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
},
as...)

// Augment extended Alert data with any extra data if provided
// If there is no extra data in the context or it is malformed,
// we simply continue without erroring
extraData, ok := receivers.GetExtraDataFromContext(ctx)
if ok && len(data.Alerts) == len(extraData) {
for i, ed := range extraData {
data.Alerts[i].ExtraData = ed
}
}
// Augment extended Alert data with any extra data if provided.
receivers.ApplyExtraData(ctx, data.Alerts)

msg := &oncallMessage{
Version: "1",
Expand Down
3 changes: 3 additions & 0 deletions receivers/opsgenie/v1/opsgenie.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ func (on *Notifier) buildOpsgenieMessage(ctx context.Context, alerts model.Alert
var tmplErr error
tmpl, data := templates.TmplText(ctx, on.tmpl, as, l, &tmplErr)

// Augment extended Alert data with any extra data if provided.
receivers.ApplyExtraData(ctx, data.Alerts)
Comment thread
yuri-tceretian marked this conversation as resolved.

message, truncated := receivers.TruncateInRunes(tmpl(on.settings.Message), opsGenieMaxMessageLenRunes)
if truncated {
level.Warn(l).Log("msg", "truncated message", "alert", key, "max_runes", opsGenieMaxMessageLenRunes)
Expand Down
65 changes: 65 additions & 0 deletions receivers/opsgenie/v1/opsgenie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1

import (
"context"
"encoding/json"
"fmt"
"net/url"
"testing"
Expand Down Expand Up @@ -537,3 +538,67 @@ func TestNotify(t *testing.T) {
})
}
}

func TestNotify_ExtraData(t *testing.T) {
tmpl := templates.ForTests(t)

externalURL, err := url.Parse("http://localhost")
require.NoError(t, err)
tmpl.ExternalURL = externalURL

settings := Config{
APIKey: "abcdefgh0123456789",
APIUrl: DefaultAlertsURL,
Message: `{{ range $i, $a := .Alerts }}Alert {{ $i }}: {{ printf "%s" $a.ExtraData }} {{ end }}`,
Description: "",
AutoClose: true,
OverridePriority: true,
SendTagsAs: SendTags,
}

// Create test alerts
alerts := []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{"ann1": "annv1"},
},
},
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert2", "lbl1": "val2"},
Annotations: model.LabelSet{"ann1": "annv2"},
},
},
}

// Create extra data that will be passed via context
extraData1 := json.RawMessage(`{"customField": "customValue1", "priority": "high"}`)
extraData2 := json.RawMessage(`{"customField": "customValue2", "priority": "medium"}`)
extraDataSlice := []json.RawMessage{extraData1, extraData2}

webhookSender := receivers.MockNotificationService()

pn := &Notifier{
Base: receivers.NewBase(receivers.Metadata{}, log.NewNopLogger()),
ns: webhookSender,
tmpl: tmpl,
settings: settings,
images: &images.UnavailableProvider{},
}

// Create context with extra data
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ctx = context.WithValue(ctx, receivers.ExtraDataKey, extraDataSlice)

// Call Notify
ok, err := pn.Notify(ctx, alerts...)
require.NoError(t, err)
require.True(t, ok)

// Verify that extra data is present in the request body (message field)
require.Contains(t, webhookSender.Webhook.Body, "customField")
require.Contains(t, webhookSender.Webhook.Body, "customValue1")
require.Contains(t, webhookSender.Webhook.Body, "customValue2")
}
3 changes: 3 additions & 0 deletions receivers/pagerduty/v1/pagerduty.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ func (pn *Notifier) buildPagerdutyMessage(ctx context.Context, alerts model.Aler
var tmplErr error
tmpl, data := templates.TmplText(ctx, pn.tmpl, as, l, &tmplErr)

// Augment extended Alert data with any extra data if provided.
receivers.ApplyExtraData(ctx, data.Alerts)

details := make(map[string]string, len(pn.settings.Details))
for k, v := range pn.settings.Details {
detail, err := pn.tmpl.ExecuteTextString(v, data)
Expand Down
85 changes: 85 additions & 0 deletions receivers/pagerduty/v1/pagerduty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

"github.com/go-kit/log"

"github.com/grafana/alerting/images"
"github.com/grafana/alerting/receivers"
"github.com/grafana/alerting/templates"
)
Expand Down Expand Up @@ -510,3 +511,87 @@ func TestNotify(t *testing.T) {
})
}
}

func TestNotify_ExtraData(t *testing.T) {
tmpl := templates.ForTests(t)

externalURL, err := url.Parse("http://localhost")
require.NoError(t, err)
tmpl.ExternalURL = externalURL

hostname, err := os.Hostname()
require.NoError(t, err)

settings := Config{
Key: "abcdefgh0123456789",
Severity: DefaultSeverity,
Details: map[string]string{
"extra_data_0": `{{ printf "%s" (index .Alerts 0).ExtraData }}`,
"extra_data_1": `{{ printf "%s" (index .Alerts 1).ExtraData }}`,
},
Class: DefaultClass,
Component: "Grafana",
Group: DefaultGroup,
Summary: templates.DefaultMessageTitleEmbed,
Source: hostname,
Client: DefaultClient,
ClientURL: "{{ .ExternalURL }}",
URL: DefaultURL,
}

// Create test alerts
alerts := []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{"ann1": "annv1"},
},
},
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert2", "lbl1": "val2"},
Annotations: model.LabelSet{"ann1": "annv2"},
},
},
}

// Create extra data that will be passed via context
extraData1 := json.RawMessage(`{"customField": "customValue1", "priority": "high"}`)
extraData2 := json.RawMessage(`{"customField": "customValue2", "priority": "medium"}`)
extraDataSlice := []json.RawMessage{extraData1, extraData2}

webhookSender := receivers.MockNotificationService()

pn := &Notifier{
Base: receivers.NewBase(receivers.Metadata{}, log.NewNopLogger()),
ns: webhookSender,
tmpl: tmpl,
settings: settings,
images: &images.UnavailableProvider{},
}

// Create context with extra data
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ctx = context.WithValue(ctx, receivers.ExtraDataKey, extraDataSlice)

// Call Notify
ok, err := pn.Notify(ctx, alerts...)
require.NoError(t, err)
require.True(t, ok)

// Parse the webhook body to verify extra data was included
var pagerDutyMsg pagerDutyMessage
err = json.Unmarshal([]byte(webhookSender.Webhook.Body), &pagerDutyMsg)
require.NoError(t, err)

// Verify that extra data is present in the custom details (2 extra_data fields)
require.Contains(t, pagerDutyMsg.Payload.CustomDetails, "extra_data_0")
require.Contains(t, pagerDutyMsg.Payload.CustomDetails, "extra_data_1")

// Check first alert's extra data
require.JSONEq(t, string(extraData1), pagerDutyMsg.Payload.CustomDetails["extra_data_0"])

// Check second alert's extra data
require.JSONEq(t, string(extraData2), pagerDutyMsg.Payload.CustomDetails["extra_data_1"])
}
Loading