Files
grafana/pkg/tests/api/alerting/api_notification_channel_test.go
T
George Robinson 5e1d628f21 Alerting: Use Adaptive Cards in Teams notifications (#53532)
This commit changes the cards in Teams notifications from Office 365
Connector cards to Adaptive Cards to fix an issue where images were not
shown in Teams for desktop and web. Since Office 365 Connector cards
are deprecated, it made sense to move to Adapative Cards and fix this
bug at the same time.

The Adaptive Card messages maintain the design of the Office 365
Connector Card with a number of minor differences:

- In Adaptive Card messages the color of the title is red or green
  depending on the status of the alerts, where as with Office 365
  connector cards there was a colored border at the top of the title

- In Adaptive Card messages the title is bold to make it easier to read
  when the color is red or green

- In Adaptive Card messages the thumbnails for images are medium size
  if there are more than two images, otherwise large size
2022-08-10 19:51:20 +01:00

2432 lines
72 KiB
Go

package alerting
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net"
"net/http"
"regexp"
"strings"
"sync"
"testing"
"time"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/tests/testinfra"
)
func TestTestReceivers(t *testing.T) {
t.Run("assert no receivers returns 400 Bad Request", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
resp := postRequest(t, testReceiversURL, `{
"receivers": []
}`, http.StatusBadRequest)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
res := Response{}
err = json.Unmarshal(b, &res)
require.NoError(t, err)
require.NotEmpty(t, res.TraceID)
})
t.Run("assert working receiver returns OK", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
mockEmails := &mockEmailHandler{}
env.NotificationService.EmailHandlerSync = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
resp := postRequest(t, testReceiversURL, `{
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "receiver-1",
"type": "email",
"disableResolveMessage": false,
"settings": {
"addresses":"example@email.com"
},
"secureFields": {}
}
]
}]
}`, http.StatusOK)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var result apimodels.TestReceiversResult
require.NoError(t, json.Unmarshal(b, &result))
require.Len(t, result.Receivers, 1)
require.Len(t, result.Receivers[0].Configs, 1)
expectedJSON := fmt.Sprintf(`{
"alert": {
"annotations": {
"summary": "Notification test",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"alertname": "TestAlert",
"instance": "Grafana"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"name": "receiver-1",
"uid": "%s",
"status": "ok"
}
]
}],
"notified_at": "%s"
}`,
result.Receivers[0].Configs[0].UID,
result.NotifiedAt.Format(time.RFC3339Nano))
require.JSONEq(t, expectedJSON, string(b))
require.Len(t, mockEmails.emails, 1)
require.Equal(t, []string{"example@email.com"}, mockEmails.emails[0].To)
})
t.Run("assert invalid receiver returns 400 Bad Request", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
mockEmails := &mockEmailHandler{}
env.NotificationService.EmailHandlerSync = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
resp := postRequest(t, testReceiversURL, `{
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "receiver-1",
"type": "email",
"disableResolveMessage": false,
"settings": {},
"secureFields": {}
}
]
}]
}`, http.StatusBadRequest)
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var result apimodels.TestReceiversResult
require.NoError(t, json.Unmarshal(b, &result))
require.Len(t, result.Receivers, 1)
require.Len(t, result.Receivers[0].Configs, 1)
expectedJSON := fmt.Sprintf(`{
"alert": {
"annotations": {
"summary": "Notification test",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"alertname": "TestAlert",
"instance": "Grafana"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"name": "receiver-1",
"uid": "%s",
"status": "failed",
"error": "the receiver is invalid: failed to validate receiver \"receiver-1\" of type \"email\": could not find addresses in settings"
}
]
}],
"notified_at": "%s"
}`,
result.Receivers[0].Configs[0].UID,
result.NotifiedAt.Format(time.RFC3339Nano))
require.JSONEq(t, expectedJSON, string(b))
})
t.Run("assert timed out receiver returns 408 Request Timeout", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
mockEmails := &mockEmailHandlerWithTimeout{
timeout: 5 * time.Second,
}
env.NotificationService.EmailHandlerSync = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
req, err := http.NewRequest(http.MethodPost, testReceiversURL, strings.NewReader(`{
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "receiver-1",
"type": "email",
"disableResolveMessage": false,
"settings": {
"addresses":"example@email.com"
},
"secureFields": {}
}
]
}]
}`))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Request-Timeout", "1")
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.Equal(t, http.StatusRequestTimeout, resp.StatusCode)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var result apimodels.TestReceiversResult
require.NoError(t, json.Unmarshal(b, &result))
require.Len(t, result.Receivers, 1)
require.Len(t, result.Receivers[0].Configs, 1)
expectedJSON := fmt.Sprintf(`{
"alert": {
"annotations": {
"summary": "Notification test",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"alertname": "TestAlert",
"instance": "Grafana"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"name": "receiver-1",
"uid": "%s",
"status": "failed",
"error": "the receiver timed out: context deadline exceeded"
}
]
}],
"notified_at": "%s"
}`,
result.Receivers[0].Configs[0].UID,
result.NotifiedAt.Format(time.RFC3339Nano))
require.JSONEq(t, expectedJSON, string(b))
})
t.Run("assert multiple different errors returns 207 Multi Status", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
mockEmails := &mockEmailHandlerWithTimeout{
timeout: 5 * time.Second,
}
env.NotificationService.EmailHandlerSync = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
req, err := http.NewRequest(http.MethodPost, testReceiversURL, strings.NewReader(`{
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "receiver-1",
"type": "email",
"disableResolveMessage": false,
"settings": {},
"secureFields": {}
}
]
}, {
"name":"receiver-2",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "receiver-2",
"type": "email",
"disableResolveMessage": false,
"settings": {
"addresses":"example@email.com"
},
"secureFields": {}
}
]
}]
}`))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Request-Timeout", "1")
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, resp.Body.Close())
})
require.Equal(t, http.StatusMultiStatus, resp.StatusCode)
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var result apimodels.TestReceiversResult
require.NoError(t, json.Unmarshal(b, &result))
require.Len(t, result.Receivers, 2)
require.Len(t, result.Receivers[0].Configs, 1)
require.Len(t, result.Receivers[1].Configs, 1)
expectedJSON := fmt.Sprintf(`{
"alert": {
"annotations": {
"summary": "Notification test",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"alertname": "TestAlert",
"instance": "Grafana"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"name": "receiver-1",
"uid": "%s",
"status": "failed",
"error": "the receiver is invalid: failed to validate receiver \"receiver-1\" of type \"email\": could not find addresses in settings"
}
]
}, {
"name":"receiver-2",
"grafana_managed_receiver_configs": [
{
"name": "receiver-2",
"uid": "%s",
"status": "failed",
"error": "the receiver timed out: context deadline exceeded"
}
]
}],
"notified_at": "%s"
}`,
result.Receivers[0].Configs[0].UID,
result.Receivers[1].Configs[0].UID,
result.NotifiedAt.Format(time.RFC3339Nano))
require.JSONEq(t, expectedJSON, string(b))
})
}
func TestTestReceiversAlertCustomization(t *testing.T) {
t.Run("assert custom annotations and labels are sent", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
mockEmails := &mockEmailHandler{}
env.NotificationService.EmailHandlerSync = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
resp := postRequest(t, testReceiversURL, `{
"alert": {
"annotations": {
"annotation1": "value1",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"label1": "value1"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"uid":"",
"name":"receiver-1",
"type":"email",
"disableResolveMessage":false,
"settings":{
"addresses":"example@email.com"
},
"secureFields":{}
}
]
}]
}`, http.StatusOK)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var result apimodels.TestReceiversResult
require.NoError(t, json.Unmarshal(b, &result))
require.Len(t, result.Receivers, 1)
require.Len(t, result.Receivers[0].Configs, 1)
expectedJSON := fmt.Sprintf(`{
"alert": {
"annotations": {
"annotation1": "value1",
"summary": "Notification test",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"alertname": "TestAlert",
"instance": "Grafana",
"label1": "value1"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"name": "receiver-1",
"uid": "%s",
"status": "ok"
}
]
}],
"notified_at": "%s"
}`,
result.Receivers[0].Configs[0].UID,
result.NotifiedAt.Format(time.RFC3339Nano))
require.JSONEq(t, expectedJSON, string(b))
require.Len(t, mockEmails.emails, 1)
require.Equal(t, []string{"example@email.com"}, mockEmails.emails[0].To)
})
t.Run("assert custom annotations can replace default annotations", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
mockEmails := &mockEmailHandler{}
env.NotificationService.EmailHandlerSync = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
resp := postRequest(t, testReceiversURL, `{
"alert": {
"annotations": {
"summary": "This is a custom annotation",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"uid":"",
"name":"receiver-1",
"type":"email",
"disableResolveMessage":false,
"settings":{
"addresses":"example@email.com"
},
"secureFields":{}
}
]
}]
}`, http.StatusOK)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var result apimodels.TestReceiversResult
require.NoError(t, json.Unmarshal(b, &result))
require.Len(t, result.Receivers, 1)
require.Len(t, result.Receivers[0].Configs, 1)
expectedJSON := fmt.Sprintf(`{
"alert": {
"annotations": {
"summary": "This is a custom annotation",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"alertname": "TestAlert",
"instance": "Grafana"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"name": "receiver-1",
"uid": "%s",
"status": "ok"
}
]
}],
"notified_at": "%s"
}`,
result.Receivers[0].Configs[0].UID,
result.NotifiedAt.Format(time.RFC3339Nano))
require.JSONEq(t, expectedJSON, string(b))
require.Len(t, mockEmails.emails, 1)
require.Equal(t, []string{"example@email.com"}, mockEmails.emails[0].To)
})
t.Run("assert custom labels can replace default label", func(t *testing.T) {
// Setup Grafana and its Database
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Login: "grafana",
Password: "password",
})
mockEmails := &mockEmailHandler{}
env.NotificationService.EmailHandlerSync = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
resp := postRequest(t, testReceiversURL, `{
"alert": {
"labels": {
"alertname": "This is a custom label"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"uid":"",
"name":"receiver-1",
"type":"email",
"disableResolveMessage":false,
"settings":{
"addresses":"example@email.com"
},
"secureFields":{}
}
]
}]
}`, http.StatusOK)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var result apimodels.TestReceiversResult
require.NoError(t, json.Unmarshal(b, &result))
require.Len(t, result.Receivers, 1)
require.Len(t, result.Receivers[0].Configs, 1)
expectedJSON := fmt.Sprintf(`{
"alert": {
"annotations": {
"summary": "Notification test",
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
},
"labels": {
"alertname": "This is a custom label",
"instance": "Grafana"
}
},
"receivers": [{
"name":"receiver-1",
"grafana_managed_receiver_configs": [
{
"name": "receiver-1",
"uid": "%s",
"status": "ok"
}
]
}],
"notified_at": "%s"
}`,
result.Receivers[0].Configs[0].UID,
result.NotifiedAt.Format(time.RFC3339Nano))
require.JSONEq(t, expectedJSON, string(b))
require.Len(t, mockEmails.emails, 1)
require.Equal(t, []string{"example@email.com"}, mockEmails.emails[0].To)
})
}
func TestNotificationChannels(t *testing.T) {
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
DisableLegacyAlerting: true,
EnableUnifiedAlerting: true,
DisableAnonymous: true,
AppModeProduction: true,
})
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
mockChannel := newMockNotificationChannel(t, grafanaListedAddr)
amConfig := getAlertmanagerConfig(mockChannel.server.Addr)
mockEmail := &mockEmailHandler{}
// Overriding some URLs to send to the mock channel.
os, opa, ot, opu, ogb, ol, oth := channels.SlackAPIEndpoint, channels.PagerdutyEventAPIURL,
channels.TelegramAPIURL, channels.PushoverEndpoint, channels.GetBoundary,
channels.LineNotifyURL, channels.ThreemaGwBaseURL
originalTemplate := channels.DefaultTemplateString
t.Cleanup(func() {
channels.SlackAPIEndpoint, channels.PagerdutyEventAPIURL,
channels.TelegramAPIURL, channels.PushoverEndpoint, channels.GetBoundary,
channels.LineNotifyURL, channels.ThreemaGwBaseURL = os, opa, ot, opu, ogb, ol, oth
channels.DefaultTemplateString = originalTemplate
})
channels.DefaultTemplateString = channels.TemplateForTestsString
channels.SlackAPIEndpoint = fmt.Sprintf("http://%s/slack_recvX/slack_testX", mockChannel.server.Addr)
channels.PagerdutyEventAPIURL = fmt.Sprintf("http://%s/pagerduty_recvX/pagerduty_testX", mockChannel.server.Addr)
channels.TelegramAPIURL = fmt.Sprintf("http://%s/telegram_recv/bot%%s/%%s", mockChannel.server.Addr)
channels.PushoverEndpoint = fmt.Sprintf("http://%s/pushover_recv/pushover_test", mockChannel.server.Addr)
channels.LineNotifyURL = fmt.Sprintf("http://%s/line_recv/line_test", mockChannel.server.Addr)
channels.ThreemaGwBaseURL = fmt.Sprintf("http://%s/threema_recv/threema_test", mockChannel.server.Addr)
channels.GetBoundary = func() string { return "abcd" }
env.NotificationService.EmailHandlerSync = mockEmail.sendEmailCommandHandlerSync
// As we are using a NotificationService mock here, but he test expects real NotificationService -
// we try to issue a real POST request here
env.NotificationService.WebhookHandler = func(_ context.Context, cmd *models.SendWebhookSync) error {
if res, err := http.Post(cmd.Url, "", strings.NewReader(cmd.Body)); err == nil {
_ = res.Body.Close()
}
return nil
}
// Create a user to make authenticated requests
createUser(t, env.SQLStore, user.CreateUserCommand{
DefaultOrgRole: string(org.RoleEditor),
Password: "password",
Login: "grafana",
})
apiClient := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
{
// There are no notification channel config initially - so it returns the default configuration.
alertsURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
resp := getRequest(t, alertsURL, http.StatusOK) // nolint
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.JSONEq(t, defaultAlertmanagerConfigJSON, string(b))
}
{
// Create the namespace we'll save our alerts to.
apiClient.CreateFolder(t, "default", "default")
// Post the alertmanager config.
u := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
_ = postRequest(t, u, amConfig, http.StatusAccepted) // nolint
// Verifying that all the receivers and routes have been registered.
alertsURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
resp := getRequest(t, alertsURL, http.StatusOK) // nolint
b := getBody(t, resp.Body)
re := regexp.MustCompile(`"uid":"([\w|-]*)"`)
e := getExpAlertmanagerConfigFromAPI(mockChannel.server.Addr)
require.JSONEq(t, e, string(re.ReplaceAll([]byte(b), []byte(`"uid":""`))))
}
{
// Create rules that will fire as quickly as possible
originalFunction := store.GenerateNewAlertRuleUID
t.Cleanup(func() {
store.GenerateNewAlertRuleUID = originalFunction
})
store.GenerateNewAlertRuleUID = func(_ *sqlstore.DBSession, _ int64, ruleTitle string) (string, error) {
return "UID_" + ruleTitle, nil
}
rulesConfig := getRulesConfig(t)
u := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules/default", grafanaListedAddr)
_ = postRequest(t, u, rulesConfig, http.StatusAccepted) // nolint
}
// Eventually, we'll get all the desired alerts.
// nolint:gosec
require.Eventually(t, func() bool {
return mockChannel.totalNotifications() >= len(nonEmailAlertNames) && len(mockEmail.emails) >= 1
}, 30*time.Second, 1*time.Second)
mockChannel.matchesExpNotifications(t, expNonEmailNotifications)
require.Equal(t, expEmailNotifications, mockEmail.emails)
require.NoError(t, mockChannel.Close())
{
// Delete the configuration; so it returns the default configuration.
u := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
req, err := http.NewRequest(http.MethodDelete, u, nil)
require.NoError(t, err)
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
t.Cleanup(func() {
err := resp.Body.Close()
require.NoError(t, err)
})
b, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, 202, resp.StatusCode)
require.JSONEq(t, `{"message":"configuration deleted; the default is applied"}`, string(b))
alertsURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
resp = getRequest(t, alertsURL, http.StatusOK) // nolint
b, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.JSONEq(t, defaultAlertmanagerConfigJSON, string(b))
}
}
func getAlertmanagerConfig(channelAddr string) string {
return strings.ReplaceAll(alertmanagerConfig, "CHANNEL_ADDR", channelAddr)
}
func getExpAlertmanagerConfigFromAPI(channelAddr string) string {
return strings.ReplaceAll(expAlertmanagerConfigFromAPI, "CHANNEL_ADDR", channelAddr)
}
// nonEmailAlertNames are name of alerts to be sent for non-email channels. This should be in sync with
// the routes that we define in Alertmanager config.
var nonEmailAlertNames = []string{
"AlertmanagerAlert",
"OpsGenieAlert",
"VictorOpsAlert",
"ThreemaAlert",
"LineAlert",
"DiscordAlert",
"KafkaAlert",
"GoogleChatAlert",
"PushoverAlert",
"SensuGoAlert",
"TelegramAlert",
"DingDingAlert",
"SlackAlert1",
"SlackAlert2",
"PagerdutyAlert",
"TeamsAlert",
"WebhookAlert",
}
// emailAlertNames are name of alerts to be sent via email. This should be in sync with
// the routes that we define in Alertmanager config.
var emailAlertNames = []string{
"EmailAlert",
}
func getRulesConfig(t *testing.T) string {
t.Helper()
interval, err := model.ParseDuration("10s")
require.NoError(t, err)
rules := apimodels.PostableRuleGroupConfig{
Name: "arulegroup",
Interval: interval,
}
// Create rules that will fire as quickly as possible for all the routes.
for _, alertName := range append(nonEmailAlertNames, emailAlertNames...) {
rules.Rules = append(rules.Rules, apimodels.PostableExtendedRuleNode{
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
Title: alertName,
Condition: "A",
Data: []ngmodels.AlertQuery{
{
RefID: "A",
RelativeTimeRange: ngmodels.RelativeTimeRange{
From: ngmodels.Duration(time.Duration(5) * time.Hour),
To: ngmodels.Duration(time.Duration(3) * time.Hour),
},
DatasourceUID: "-100",
Model: json.RawMessage(`{
"type": "math",
"expression": "2 + 3 > 1"
}`),
},
},
},
})
}
b, err := json.Marshal(rules)
require.NoError(t, err)
return string(b)
}
type mockNotificationChannel struct {
t *testing.T
server *http.Server
receivedNotifications map[string][]string
receivedNotificationsMtx sync.Mutex
}
func newMockNotificationChannel(t *testing.T, grafanaListedAddr string) *mockNotificationChannel {
// Spin up a separate webserver to receive notifications emitted by Grafana.
listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
nc := &mockNotificationChannel{
server: &http.Server{
Addr: listener.Addr().String(),
},
receivedNotifications: make(map[string][]string),
t: t,
}
nc.server.Handler = nc
go func() {
require.EqualError(t, nc.server.Serve(listener), http.ErrServerClosed.Error())
}()
return nc
}
func (nc *mockNotificationChannel) ServeHTTP(res http.ResponseWriter, req *http.Request) {
nc.t.Helper()
nc.receivedNotificationsMtx.Lock()
defer nc.receivedNotificationsMtx.Unlock()
paths := strings.Split(req.URL.Path[1:], "/")
key := strings.Join(paths[0:2], "/")
body := getBody(nc.t, req.Body)
nc.receivedNotifications[key] = append(nc.receivedNotifications[key], body)
res.WriteHeader(http.StatusOK)
}
func (nc *mockNotificationChannel) totalNotifications() int {
total := 0
nc.receivedNotificationsMtx.Lock()
defer nc.receivedNotificationsMtx.Unlock()
for _, v := range nc.receivedNotifications {
total += len(v)
}
return total
}
func (nc *mockNotificationChannel) matchesExpNotifications(t *testing.T, exp map[string][]string) {
t.Helper()
nc.receivedNotificationsMtx.Lock()
defer nc.receivedNotificationsMtx.Unlock()
require.Len(t, nc.receivedNotifications, len(exp))
for expKey, expVals := range exp {
actVals, ok := nc.receivedNotifications[expKey]
require.True(t, ok)
require.Len(t, actVals, len(expVals))
for i := range expVals {
expVal := expVals[i]
var r1, r2 *regexp.Regexp
switch expKey {
case "webhook_recv/webhook_test":
// It has a time component "startsAt".
r1 = regexp.MustCompile(`.*"startsAt"\s*:\s*"([^"]+)"`)
case "slack_recvX/slack_testX":
fallthrough
case "slack_recv1/slack_test_without_token":
// It has a time component "ts".
r1 = regexp.MustCompile(`.*"ts"\s*:\s*([0-9]+)`)
case "sensugo_recv/sensugo_test":
// It has a time component "ts".
r1 = regexp.MustCompile(`.*"issued"\s*:\s*([0-9]+)`)
case "pagerduty_recvX/pagerduty_testX":
// It has a changing "source".
r1 = regexp.MustCompile(`.*"source"\s*:\s*"([^"]+)"`)
case "googlechat_recv/googlechat_test":
// "Grafana v | 25 May 21 17:44 IST"
r1 = regexp.MustCompile(`.*"text"\s*:\s*"(Grafana v[^"]+)"`)
case "victorops_recv/victorops_test":
// It has a time component "timestamp".
r1 = regexp.MustCompile(`.*"timestamp"\s*:\s*([0-9]+)`)
case "alertmanager_recv/alertmanager_test":
// It has a changing time fields.
r1 = regexp.MustCompile(`.*"startsAt"\s*:\s*"([^"]+)"`)
r2 = regexp.MustCompile(`.*"UpdatedAt"\s*:\s*"([^"]+)"`)
}
if r1 != nil {
parts := r1.FindStringSubmatch(actVals[i])
require.Len(t, parts, 2)
if expKey == "alertmanager_recv/alertmanager_test" {
// 2 fields for Prometheus Alertmanager.
parts2 := r2.FindStringSubmatch(actVals[i])
require.Len(t, parts2, 2)
expVal = fmt.Sprintf(expVal, parts[1], parts2[1])
} else {
expVal = fmt.Sprintf(expVal, parts[1])
}
}
switch expKey {
case "line_recv/line_test", "threema_recv/threema_test":
// POST parameters.
require.Equal(t, expVal, actVals[i])
case "pushover_recv/pushover_test", "telegram_recv/bot6sh027hs034h":
// Multipart data.
multipartEqual(t, expVal, actVals[i])
default:
require.JSONEq(t, expVal, actVals[i])
}
}
}
}
func multipartEqual(t *testing.T, exp, act string) {
t.Helper()
fillMap := func(r *multipart.Reader, m map[string]string) {
for {
part, err := r.NextPart()
if part == nil || errors.Is(err, io.EOF) {
break
}
require.NoError(t, err)
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(part)
require.NoError(t, err)
m[part.FormName()] = buf.String()
}
}
expReader := multipart.NewReader(strings.NewReader(exp), channels.GetBoundary())
actReader := multipart.NewReader(strings.NewReader(act), channels.GetBoundary())
expMap, actMap := make(map[string]string), make(map[string]string)
fillMap(expReader, expMap)
fillMap(actReader, actMap)
require.Equal(t, expMap, actMap)
}
func (nc *mockNotificationChannel) Close() error {
return nc.server.Close()
}
type mockEmailHandler struct {
emails []*models.SendEmailCommandSync
}
func (e *mockEmailHandler) sendEmailCommandHandlerSync(_ context.Context, cmd *models.SendEmailCommandSync) error {
// We 0 out the start time since that is a variable that we cannot predict.
alerts := cmd.Data["Alerts"].(channels.ExtendedAlerts)
for i := range alerts {
alerts[i].StartsAt = time.Time{}
}
e.emails = append(e.emails, cmd)
return nil
}
// mockEmailHandlerWithTimeout blocks until the timeout has expired.
type mockEmailHandlerWithTimeout struct {
mockEmailHandler
timeout time.Duration
}
func (e *mockEmailHandlerWithTimeout) sendEmailCommandHandlerSync(ctx context.Context, cmd *models.SendEmailCommandSync) error {
select {
case <-time.After(e.timeout):
return e.mockEmailHandler.sendEmailCommandHandlerSync(ctx, cmd)
case <-ctx.Done():
return ctx.Err()
}
}
// alertmanagerConfig has the config for all the notification channels
// that we want to test. It is recommended to use different URL for each
// channel and have 1 route per channel.
// group_wait 0s means the notification is sent as soon as it is received.
const alertmanagerConfig = `
{
"alertmanager_config": {
"route": {
"receiver": "slack_recv1",
"group_wait": "0s",
"group_by": [
"alertname"
],
"routes": [
{
"receiver": "email_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"EmailAlert\""
]
},
{
"receiver": "slack_recv1",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"SlackAlert1\""
]
},
{
"receiver": "slack_recv2",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"SlackAlert2\""
]
},
{
"receiver": "pagerduty_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"PagerdutyAlert\""
]
},
{
"receiver": "dingding_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"DingDingAlert\""
]
},
{
"receiver": "discord_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"DiscordAlert\""
]
},
{
"receiver": "sensugo_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"SensuGoAlert\""
]
},
{
"receiver": "pushover_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"PushoverAlert\""
]
},
{
"receiver": "googlechat_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"GoogleChatAlert\""
]
},
{
"receiver": "kafka_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"KafkaAlert\""
]
},
{
"receiver": "line_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"LineAlert\""
]
},
{
"receiver": "threema_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"ThreemaAlert\""
]
},
{
"receiver": "opsgenie_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"OpsGenieAlert\""
]
},
{
"receiver": "alertmanager_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"AlertmanagerAlert\""
]
},
{
"receiver": "victorops_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"VictorOpsAlert\""
]
},
{
"receiver": "teams_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"TeamsAlert\""
]
},
{
"receiver": "webhook_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"WebhookAlert\""
]
},
{
"receiver": "telegram_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"TelegramAlert\""
]
}
]
},
"receivers": [
{
"name": "email_recv",
"grafana_managed_receiver_configs": [
{
"name": "email_test",
"type": "email",
"settings": {
"addresses": "test@email.com",
"singleEmail": true
}
}
]
},
{
"name": "dingding_recv",
"grafana_managed_receiver_configs": [
{
"name": "dingding_test",
"type": "dingding",
"settings": {
"url": "http://CHANNEL_ADDR/dingding_recv/dingding_test"
}
}
]
},
{
"name": "discord_recv",
"grafana_managed_receiver_configs": [
{
"name": "discord_test",
"type": "discord",
"settings": {
"url": "http://CHANNEL_ADDR/discord_recv/discord_test"
}
}
]
},
{
"name": "googlechat_recv",
"grafana_managed_receiver_configs": [
{
"name": "googlechat_test",
"type": "googlechat",
"settings": {
"url": "http://CHANNEL_ADDR/googlechat_recv/googlechat_test"
}
}
]
},
{
"name": "kafka_recv",
"grafana_managed_receiver_configs": [
{
"name": "kafka_test",
"type": "kafka",
"settings": {
"kafkaRestProxy": "http://CHANNEL_ADDR",
"kafkaTopic": "my_kafka_topic"
}
}
]
},
{
"name": "victorops_recv",
"grafana_managed_receiver_configs": [
{
"name": "victorops_test",
"type": "victorops",
"settings": {
"url": "http://CHANNEL_ADDR/victorops_recv/victorops_test"
}
}
]
},
{
"name": "teams_recv",
"grafana_managed_receiver_configs": [
{
"name": "teams_test",
"type": "teams",
"settings": {
"url": "http://CHANNEL_ADDR/teams_recv/teams_test"
}
}
]
},
{
"name": "webhook_recv",
"grafana_managed_receiver_configs": [
{
"name": "webhook_test",
"type": "webhook",
"settings": {
"url": "http://CHANNEL_ADDR/webhook_recv/webhook_test",
"username": "my_username",
"httpMethod": "POST",
"maxAlerts": "5"
},
"secureSettings": {
"password": "mysecretpassword"
}
}
]
},
{
"name": "sensugo_recv",
"grafana_managed_receiver_configs": [
{
"name": "sensugo_test",
"type": "sensugo",
"settings": {
"url": "http://CHANNEL_ADDR/sensugo_recv/sensugo_test",
"namespace": "sensugo"
},
"secureSettings": {
"apikey": "mysecretkey"
}
}
]
},
{
"name": "pushover_recv",
"grafana_managed_receiver_configs": [
{
"name": "pushover_test",
"type": "pushover",
"settings": {},
"secureSettings": {
"userKey": "mysecretkey",
"apiToken": "mysecrettoken"
}
}
]
},
{
"name": "line_recv",
"grafana_managed_receiver_configs": [
{
"name": "line_test",
"type": "LINE",
"settings": {},
"secureSettings": {
"token": "mysecrettoken"
}
}
]
},
{
"name": "threema_recv",
"grafana_managed_receiver_configs": [
{
"name": "threema_test",
"type": "threema",
"settings": {
"gateway_id": "*1234567",
"recipient_id": "abcdefgh"
},
"secureSettings": {
"api_secret": "myapisecret"
}
}
]
},
{
"name": "opsgenie_recv",
"grafana_managed_receiver_configs": [
{
"name": "opsgenie_test",
"type": "opsgenie",
"settings": {
"apiUrl": "http://CHANNEL_ADDR/opsgenie_recv/opsgenie_test"
},
"secureSettings": {
"apiKey": "mysecretkey"
}
}
]
},
{
"name": "alertmanager_recv",
"grafana_managed_receiver_configs": [
{
"name": "alertmanager_test",
"type": "prometheus-alertmanager",
"settings": {
"url": "http://CHANNEL_ADDR/alertmanager_recv/alertmanager_test"
},
"secureSettings": {}
}
]
},
{
"name": "telegram_recv",
"grafana_managed_receiver_configs": [
{
"name": "telegram_test",
"type": "telegram",
"settings": {
"chatid": "telegram_chat_id"
},
"secureSettings": {
"bottoken": "6sh027hs034h"
}
}
]
},
{
"name": "slack_recv1",
"grafana_managed_receiver_configs": [
{
"name": "slack_test_without_token",
"type": "slack",
"settings": {
"recipient": "#test-channel",
"mentionChannel": "here",
"mentionUsers": "user1, user2",
"mentionGroups": "group1, group2",
"username": "Integration Test",
"icon_emoji": "🚀",
"icon_url": "https://awesomeemoji.com/rocket",
"text": "Integration Test {{ template \"slack.default.text\" . }}",
"title": "Integration Test {{ template \"slack.default.title\" . }}",
"fallback": "Integration Test {{ template \"slack.default.title\" . }}"
},
"secureSettings": {
"url": "http://CHANNEL_ADDR/slack_recv1/slack_test_without_token"
}
}
]
},
{
"name": "slack_recv2",
"grafana_managed_receiver_configs": [
{
"name": "slack_test_with_token",
"type": "slack",
"settings": {
"recipient": "#test-channel",
"mentionUsers": "user1, user2",
"username": "Integration Test"
},
"secureSettings": {
"token": "myfullysecrettoken"
}
}
]
},
{
"name": "pagerduty_recv",
"grafana_managed_receiver_configs": [
{
"name": "pagerduty_test",
"type": "pagerduty",
"settings": {
"severity": "warning",
"class": "testclass",
"component": "Integration Test",
"group": "testgroup",
"summary": "Integration Test {{ template \"pagerduty.default.description\" . }}"
},
"secureSettings": {
"integrationKey": "pagerduty_recv/pagerduty_test"
}
}
]
}
]
}
}
`
var expAlertmanagerConfigFromAPI = `
{
"template_files": null,
"alertmanager_config": {
"route": {
"receiver": "slack_recv1",
"group_wait": "0s",
"group_by": [
"alertname"
],
"routes": [
{
"receiver": "email_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"EmailAlert\""
]
},
{
"receiver": "slack_recv1",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"SlackAlert1\""
]
},
{
"receiver": "slack_recv2",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"SlackAlert2\""
]
},
{
"receiver": "pagerduty_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"PagerdutyAlert\""
]
},
{
"receiver": "dingding_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"DingDingAlert\""
]
},
{
"receiver": "discord_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"DiscordAlert\""
]
},
{
"receiver": "sensugo_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"SensuGoAlert\""
]
},
{
"receiver": "pushover_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"PushoverAlert\""
]
},
{
"receiver": "googlechat_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"GoogleChatAlert\""
]
},
{
"receiver": "kafka_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"KafkaAlert\""
]
},
{
"receiver": "line_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"LineAlert\""
]
},
{
"receiver": "threema_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"ThreemaAlert\""
]
},
{
"receiver": "opsgenie_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"OpsGenieAlert\""
]
},
{
"receiver": "alertmanager_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"AlertmanagerAlert\""
]
},
{
"receiver": "victorops_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"VictorOpsAlert\""
]
},
{
"receiver": "teams_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"TeamsAlert\""
]
},
{
"receiver": "webhook_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"WebhookAlert\""
]
},
{
"receiver": "telegram_recv",
"group_wait": "0s",
"group_by": [
"alertname"
],
"matchers": [
"alertname=\"TelegramAlert\""
]
}
]
},
"templates": null,
"receivers": [
{
"name": "email_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "email_test",
"type": "email",
"disableResolveMessage": false,
"settings": {
"addresses": "test@email.com",
"singleEmail": true
},
"secureFields": {}
}
]
},
{
"name": "dingding_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "dingding_test",
"type": "dingding",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/dingding_recv/dingding_test"
},
"secureFields": {}
}
]
},
{
"name": "discord_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "discord_test",
"type": "discord",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/discord_recv/discord_test"
},
"secureFields": {}
}
]
},
{
"name": "googlechat_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "googlechat_test",
"type": "googlechat",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/googlechat_recv/googlechat_test"
},
"secureFields": {}
}
]
},
{
"name": "kafka_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "kafka_test",
"type": "kafka",
"disableResolveMessage": false,
"settings": {
"kafkaRestProxy": "http://CHANNEL_ADDR",
"kafkaTopic": "my_kafka_topic"
},
"secureFields": {}
}
]
},
{
"name": "victorops_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "victorops_test",
"type": "victorops",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/victorops_recv/victorops_test"
},
"secureFields": {}
}
]
},
{
"name": "teams_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "teams_test",
"type": "teams",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/teams_recv/teams_test"
},
"secureFields": {}
}
]
},
{
"name": "webhook_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "webhook_test",
"type": "webhook",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/webhook_recv/webhook_test",
"username": "my_username",
"httpMethod": "POST",
"maxAlerts": "5"
},
"secureFields": {
"password": true
}
}
]
},
{
"name": "sensugo_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "sensugo_test",
"type": "sensugo",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/sensugo_recv/sensugo_test",
"namespace": "sensugo"
},
"secureFields": {
"apikey": true
}
}
]
},
{
"name": "pushover_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "pushover_test",
"type": "pushover",
"disableResolveMessage": false,
"settings": {},
"secureFields": {
"userKey": true,
"apiToken": true
}
}
]
},
{
"name": "line_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "line_test",
"type": "LINE",
"disableResolveMessage": false,
"settings": {},
"secureFields": {
"token": true
}
}
]
},
{
"name": "threema_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "threema_test",
"type": "threema",
"disableResolveMessage": false,
"settings": {
"gateway_id": "*1234567",
"recipient_id": "abcdefgh"
},
"secureFields": {
"api_secret": true
}
}
]
},
{
"name": "opsgenie_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "opsgenie_test",
"type": "opsgenie",
"disableResolveMessage": false,
"settings": {
"apiUrl": "http://CHANNEL_ADDR/opsgenie_recv/opsgenie_test"
},
"secureFields": {
"apiKey": true
}
}
]
},
{
"name": "alertmanager_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "alertmanager_test",
"type": "prometheus-alertmanager",
"disableResolveMessage": false,
"settings": {
"url": "http://CHANNEL_ADDR/alertmanager_recv/alertmanager_test"
},
"secureFields": {}
}
]
},
{
"name": "telegram_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "telegram_test",
"type": "telegram",
"disableResolveMessage": false,
"settings": {
"chatid": "telegram_chat_id"
},
"secureFields": {
"bottoken": true
}
}
]
},
{
"name": "slack_recv1",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "slack_test_without_token",
"type": "slack",
"disableResolveMessage": false,
"settings": {
"fallback": "Integration Test {{ template \"slack.default.title\" . }}",
"icon_emoji": "🚀",
"icon_url": "https://awesomeemoji.com/rocket",
"mentionChannel": "here",
"mentionGroups": "group1, group2",
"mentionUsers": "user1, user2",
"recipient": "#test-channel",
"text": "Integration Test {{ template \"slack.default.text\" . }}",
"title": "Integration Test {{ template \"slack.default.title\" . }}",
"username": "Integration Test"
},
"secureFields": {
"url": true
}
}
]
},
{
"name": "slack_recv2",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "slack_test_with_token",
"type": "slack",
"disableResolveMessage": false,
"settings": {
"mentionUsers": "user1, user2",
"recipient": "#test-channel",
"username": "Integration Test"
},
"secureFields": {
"token": true
}
}
]
},
{
"name": "pagerduty_recv",
"grafana_managed_receiver_configs": [
{
"uid": "",
"name": "pagerduty_test",
"type": "pagerduty",
"disableResolveMessage": false,
"settings": {
"class": "testclass",
"component": "Integration Test",
"group": "testgroup",
"severity": "warning",
"summary": "Integration Test {{ template \"pagerduty.default.description\" . }}"
},
"secureFields": {
"integrationKey": true
}
}
]
}
]
}
}
`
var expEmailNotifications = []*models.SendEmailCommandSync{
{
SendEmailCommand: models.SendEmailCommand{
To: []string{"test@email.com"},
SingleEmail: true,
Template: "ng_alert_notification",
Subject: "[FIRING:1] EmailAlert (default)",
Data: map[string]interface{}{
"Title": "[FIRING:1] EmailAlert (default)",
"Message": "",
"Status": "firing",
"Alerts": channels.ExtendedAlerts{
channels.ExtendedAlert{
Status: "firing",
Labels: template.KV{"alertname": "EmailAlert", "grafana_folder": "default"},
Annotations: template.KV{},
StartsAt: time.Time{},
EndsAt: time.Time{},
GeneratorURL: "http://localhost:3000/alerting/grafana/UID_EmailAlert/view",
Fingerprint: "1e8f5e886dc14813",
SilenceURL: "http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DEmailAlert&matcher=grafana_folder%3Ddefault",
DashboardURL: "",
PanelURL: "",
ValueString: "[ var='A' labels={} value=1 ]",
},
},
"GroupLabels": template.KV{"alertname": "EmailAlert"},
"CommonLabels": template.KV{"alertname": "EmailAlert", "grafana_folder": "default"},
"CommonAnnotations": template.KV{},
"ExternalURL": "http://localhost:3000/",
"RuleUrl": "http://localhost:3000/alerting/list",
"AlertPageUrl": "http://localhost:3000/alerting/list?alertState=firing&view=state",
},
},
},
}
// expNonEmailNotifications is all the expected notifications (except email).
// The key for the map is taken from the URL. The last 2 components of URL
// split with "/" forms the key for that route.
var expNonEmailNotifications = map[string][]string{
"slack_recv1/slack_test_without_token": {
`{
"channel": "#test-channel",
"text": "Integration Test [FIRING:1] SlackAlert1 (default)",
"username": "Integration Test",
"icon_emoji": "🚀",
"icon_url": "https://awesomeemoji.com/rocket",
"attachments": [
{
"title": "Integration Test [FIRING:1] SlackAlert1 (default)",
"title_link": "http://localhost:3000/alerting/list",
"text": "Integration Test ",
"fallback": "Integration Test [FIRING:1] SlackAlert1 (default)",
"footer": "Grafana v",
"footer_icon": "https://grafana.com/assets/img/fav32.png",
"color": "#D63232",
"ts": %s
}
],
"blocks": [
{
"text": {
"text": "<!here|here> <!subteam^group1><!subteam^group2> <@user1><@user2>",
"type": "mrkdwn"
},
"type": "section"
}
]
}`,
},
"slack_recvX/slack_testX": {
`{
"channel": "#test-channel",
"text": "[FIRING:1] SlackAlert2 (default)",
"username": "Integration Test",
"attachments": [
{
"title": "[FIRING:1] SlackAlert2 (default)",
"title_link": "http://localhost:3000/alerting/list",
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = SlackAlert2\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_SlackAlert2/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DSlackAlert2&matcher=grafana_folder%%3Ddefault\n",
"fallback": "[FIRING:1] SlackAlert2 (default)",
"footer": "Grafana v",
"footer_icon": "https://grafana.com/assets/img/fav32.png",
"color": "#D63232",
"ts": %s
}
],
"blocks": [
{
"text": {
"text": "<@user1><@user2>",
"type": "mrkdwn"
},
"type": "section"
}
]
}`,
},
"pagerduty_recvX/pagerduty_testX": {
`{
"routing_key": "pagerduty_recv/pagerduty_test",
"dedup_key": "234edb34441f942f713f3c2ccf58b1d719d921b4cbe34e57a1630f1dee847e3b",
"description": "[FIRING:1] PagerdutyAlert (default)",
"event_action": "trigger",
"payload": {
"summary": "Integration Test [FIRING:1] PagerdutyAlert (default)",
"source": "%s",
"severity": "warning",
"class": "testclass",
"component": "Integration Test",
"group": "testgroup",
"custom_details": {
"firing": "\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = PagerdutyAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_PagerdutyAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DPagerdutyAlert&matcher=grafana_folder%%3Ddefault\n",
"num_firing": "1",
"num_resolved": "0",
"resolved": ""
}
},
"client": "Grafana",
"client_url": "http://localhost:3000/",
"links": [
{
"href": "http://localhost:3000/",
"text": "External URL"
}
]
}`,
},
"dingding_recv/dingding_test": {
`{
"link": {
"messageUrl": "dingtalk://dingtalkclient/page/link?pc_slide=false&url=http%3A%2F%2Flocalhost%3A3000%2Falerting%2Flist",
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = DingDingAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_DingDingAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DDingDingAlert&matcher=grafana_folder%3Ddefault\n",
"title": "[FIRING:1] DingDingAlert (default)"
},
"msgtype": "link"
}`,
},
"teams_recv/teams_test": {
`{
"attachments": [
{
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"body": [
{
"color": "attention",
"size": "large",
"text": "[FIRING:1] TeamsAlert (default)",
"type": "TextBlock",
"weight": "bolder",
"wrap": true
}, {
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = TeamsAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_TeamsAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=alertname%3DTeamsAlert\u0026matcher=grafana_folder%3Ddefault\n",
"type": "TextBlock",
"wrap": true
}, {
"actions": [
{
"title": "View URL",
"type": "Action.OpenUrl",
"url": "http://localhost:3000/alerting/list"
}
],
"type": "ActionSet"
}
],
"type": "AdaptiveCard",
"version": "1.4"
},
"contentType": "application/vnd.microsoft.card.adaptive"
}
],
"type": "message"
}`,
},
"webhook_recv/webhook_test": {
`{
"receiver": "webhook_recv",
"status": "firing",
"orgId": 1,
"alerts": [
{
"status": "firing",
"labels": {
"alertname": "WebhookAlert",
"grafana_folder": "default"
},
"annotations": {},
"startsAt": "%s",
"valueString": "[ var='A' labels={} value=1 ]",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://localhost:3000/alerting/grafana/UID_WebhookAlert/view",
"fingerprint": "15c59b0a380bd9f1",
"silenceURL": "http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DWebhookAlert&matcher=grafana_folder%%3Ddefault",
"dashboardURL": "",
"panelURL": ""
}
],
"groupLabels": {
"alertname": "WebhookAlert"
},
"commonLabels": {
"alertname": "WebhookAlert",
"grafana_folder": "default"
},
"commonAnnotations": {},
"externalURL": "http://localhost:3000/",
"version": "1",
"groupKey": "{}/{alertname=\"WebhookAlert\"}:{alertname=\"WebhookAlert\"}",
"truncatedAlerts": 0,
"title": "[FIRING:1] WebhookAlert (default)",
"state": "alerting",
"message": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = WebhookAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_WebhookAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DWebhookAlert&matcher=grafana_folder%%3Ddefault\n"
}`,
},
"discord_recv/discord_test": {
`{
"content": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = DiscordAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_DiscordAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DDiscordAlert&matcher=grafana_folder%3Ddefault\n",
"embeds": [
{
"color": 14037554,
"footer": {
"icon_url": "https://grafana.com/assets/img/fav32.png",
"text": "Grafana v"
},
"title": "[FIRING:1] DiscordAlert (default)",
"type": "rich",
"url": "http://localhost:3000/alerting/list"
}
],
"username": "Grafana"
}`,
},
"sensugo_recv/sensugo_test": {
`{
"check": {
"handlers": null,
"interval": 86400,
"issued": %s,
"metadata": {
"labels": {
"ruleURL": "http://localhost:3000/alerting/list"
},
"name": "default"
},
"output": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = SensuGoAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_SensuGoAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DSensuGoAlert&matcher=grafana_folder%%3Ddefault\n",
"status": 2
},
"entity": {
"metadata": {
"name": "default",
"namespace": "sensugo"
}
},
"ruleUrl": "http://localhost:3000/alerting/list"
}`,
},
"pushover_recv/pushover_test": {
"--abcd\r\nContent-Disposition: form-data; name=\"user\"\r\n\r\nmysecretkey\r\n--abcd\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nmysecrettoken\r\n--abcd\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n0\r\n--abcd\r\nContent-Disposition: form-data; name=\"sound\"\r\n\r\n\r\n--abcd\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n[FIRING:1] PushoverAlert (default)\r\n--abcd\r\nContent-Disposition: form-data; name=\"url\"\r\n\r\nhttp://localhost:3000/alerting/list\r\n--abcd\r\nContent-Disposition: form-data; name=\"url_title\"\r\n\r\nShow alert rule\r\n--abcd\r\nContent-Disposition: form-data; name=\"message\"\r\n\r\n**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = PushoverAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_PushoverAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DPushoverAlert&matcher=grafana_folder%3Ddefault\n\r\n--abcd\r\nContent-Disposition: form-data; name=\"html\"\r\n\r\n1\r\n--abcd--\r\n",
},
"telegram_recv/bot6sh027hs034h": {
"--abcd\r\nContent-Disposition: form-data; name=\"chat_id\"\r\n\r\ntelegram_chat_id\r\n--abcd\r\nContent-Disposition: form-data; name=\"parse_mode\"\r\n\r\nhtml\r\n--abcd\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\n**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = TelegramAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_TelegramAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DTelegramAlert&matcher=grafana_folder%3Ddefault\n\r\n--abcd--\r\n",
},
"googlechat_recv/googlechat_test": {
`{
"previewText": "[FIRING:1] GoogleChatAlert (default)",
"fallbackText": "[FIRING:1] GoogleChatAlert (default)",
"cards": [
{
"header": {
"title": "[FIRING:1] GoogleChatAlert (default)"
},
"sections": [
{
"widgets": [
{
"textParagraph": {
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = GoogleChatAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_GoogleChatAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DGoogleChatAlert&matcher=grafana_folder%%3Ddefault\n"
}
},
{
"buttons": [
{
"textButton": {
"text": "OPEN IN GRAFANA",
"onClick": {
"openLink": {
"url": "http://localhost:3000/alerting/list"
}
}
}
}
]
},
{
"textParagraph": {
"text": "%s"
}
}
]
}
]
}
]
}`,
},
"topics/my_kafka_topic": {
`{
"records": [
{
"value": {
"alert_state": "alerting",
"client": "Grafana",
"client_url": "http://localhost:3000/alerting/list",
"description": "[FIRING:1] KafkaAlert (default)",
"details": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = KafkaAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_KafkaAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DKafkaAlert&matcher=grafana_folder%3Ddefault\n",
"incident_key": "35c0bdb1715f9162a20d7b2a01cb2e3a4c5b1dc663571701e3f67212b696332f"
}
}
]
}`,
},
"line_recv/line_test": {
`message=%5BFIRING%3A1%5D+LineAlert+%28default%29%0Ahttp%3A%2Flocalhost%3A3000%2Falerting%2Flist%0A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+%5B+var%3D%27A%27+labels%3D%7B%7D+value%3D1+%5D%0ALabels%3A%0A+-+alertname+%3D+LineAlert%0A+-+grafana_folder+%3D+default%0AAnnotations%3A%0ASource%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fgrafana%2FUID_LineAlert%2Fview%0ASilence%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253DLineAlert%26matcher%3Dgrafana_folder%253Ddefault%0A`,
},
"threema_recv/threema_test": {
`from=%2A1234567&secret=myapisecret&text=%E2%9A%A0%EF%B8%8F+%5BFIRING%3A1%5D+ThreemaAlert+%28default%29%0A%0A%2AMessage%3A%2A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+%5B+var%3D%27A%27+labels%3D%7B%7D+value%3D1+%5D%0ALabels%3A%0A+-+alertname+%3D+ThreemaAlert%0A+-+grafana_folder+%3D+default%0AAnnotations%3A%0ASource%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fgrafana%2FUID_ThreemaAlert%2Fview%0ASilence%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253DThreemaAlert%26matcher%3Dgrafana_folder%253Ddefault%0A%0A%2AURL%3A%2A+http%3A%2Flocalhost%3A3000%2Falerting%2Flist%0A&to=abcdefgh`,
},
"victorops_recv/victorops_test": {
`{
"alert_url": "http://localhost:3000/alerting/list",
"entity_display_name": "[FIRING:1] VictorOpsAlert (default)",
"entity_id": "633ae988fa7074bcb51f3d1c5fef2ba1c5c4ccb45b3ecbf681f7d507b078b1ae",
"message_type": "CRITICAL",
"monitoring_tool": "Grafana v",
"state_message": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = VictorOpsAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_VictorOpsAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DVictorOpsAlert&matcher=grafana_folder%%3Ddefault\n",
"timestamp": %s
}`,
},
"opsgenie_recv/opsgenie_test": {
`{
"alias": "47e92f0f6ef9fe99f3954e0d6155f8d09c4b9a038d8c3105e82c0cee4c62956e",
"description": "[FIRING:1] OpsGenieAlert (default)\nhttp://localhost:3000/alerting/list\n\n**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = OpsGenieAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_OpsGenieAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DOpsGenieAlert&matcher=grafana_folder%3Ddefault\n",
"details": {
"url": "http://localhost:3000/alerting/list"
},
"message": "[FIRING:1] OpsGenieAlert (default)",
"source": "Grafana",
"tags": ["alertname:OpsGenieAlert","grafana_folder:default"]
}`,
},
// Prometheus Alertmanager.
"alertmanager_recv/alertmanager_test": {
`[
{
"labels": {
"__alert_rule_uid__": "UID_AlertmanagerAlert",
"alertname": "AlertmanagerAlert",
"grafana_folder": "default"
},
"annotations": {
"__value_string__": "[ var='A' labels={} value=1 ]"
},
"startsAt": "%s",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://localhost:3000/alerting/grafana/UID_AlertmanagerAlert/view",
"UpdatedAt": "%s",
"Timeout": false
}
]`,
},
}