Contact point testing (#37308)
This commit adds contact point testing to ngalerts via a new API endpoint. This endpoint accepts JSON containing a list of receiver configurations which are validated and then tested with a notification for a test alert. The endpoint returns JSON for each receiver with a status and error message. It accepts a configurable timeout via the Request-Timeout header (in seconds) up to a maximum of 30 seconds.
This commit is contained in:
@@ -85,7 +85,7 @@ func TestAlertmanagerConfigurationIsTransactional(t *testing.T) {
|
||||
}
|
||||
`
|
||||
resp := postRequest(t, alertConfigURL, payload, http.StatusBadRequest) // nolint
|
||||
require.JSONEq(t, `{"message":"failed to save and apply Alertmanager configuration: failed to validate receiver \"slack.receiver\" of type \"slack\": token must be specified when using the Slack chat API"}`, getBody(t, resp.Body))
|
||||
require.JSONEq(t, `{"message":"failed to save and apply Alertmanager configuration: the receiver is invalid: failed to validate receiver \"slack.receiver\" of type \"slack\": token must be specified when using the Slack chat API"}`, getBody(t, resp.Body))
|
||||
|
||||
resp = getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
require.JSONEq(t, defaultAlertmanagerConfigJSON, getBody(t, resp.Body))
|
||||
|
||||
@@ -30,6 +30,328 @@ import (
|
||||
"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{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
})
|
||||
store := testinfra.SetUpDatabase(t, dir)
|
||||
store.Bus = bus.GetBus()
|
||||
grafanaListedAddr := testinfra.StartGrafana(t, dir, path, store)
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
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 := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"error":"no receivers"}`, string(b))
|
||||
})
|
||||
|
||||
t.Run("assert working receiver returns OK", func(t *testing.T) {
|
||||
// Setup Grafana and its Database
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
})
|
||||
store := testinfra.SetUpDatabase(t, dir)
|
||||
store.Bus = bus.GetBus()
|
||||
grafanaListedAddr := testinfra.StartGrafana(t, dir, path, store)
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Login: "grafana",
|
||||
Password: "password",
|
||||
})
|
||||
|
||||
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
|
||||
mockEmails := &mockEmailHandler{}
|
||||
bus.AddHandlerCtx("", mockEmails.sendEmailCommandHandlerSync)
|
||||
t.Cleanup(func() {
|
||||
bus.AddHandlerCtx("", oldEmailBus)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
var result apimodels.TestReceiversResult
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
|
||||
|
||||
require.Len(t, result.Receivers, 1)
|
||||
require.Len(t, result.Receivers[0].Configs, 1)
|
||||
require.Equal(t, apimodels.TestReceiversResult{
|
||||
Receivers: []apimodels.TestReceiverResult{{
|
||||
Name: "receiver-1",
|
||||
Configs: []apimodels.TestReceiverConfigResult{{
|
||||
Name: "receiver-1",
|
||||
UID: result.Receivers[0].Configs[0].UID,
|
||||
Status: "ok",
|
||||
}},
|
||||
}},
|
||||
NotifedAt: result.NotifedAt,
|
||||
}, result)
|
||||
|
||||
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{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
})
|
||||
store := testinfra.SetUpDatabase(t, dir)
|
||||
store.Bus = bus.GetBus()
|
||||
grafanaListedAddr := testinfra.StartGrafana(t, dir, path, store)
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Login: "grafana",
|
||||
Password: "password",
|
||||
})
|
||||
|
||||
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
|
||||
mockEmails := &mockEmailHandler{}
|
||||
bus.AddHandlerCtx("", mockEmails.sendEmailCommandHandlerSync)
|
||||
t.Cleanup(func() {
|
||||
bus.AddHandlerCtx("", oldEmailBus)
|
||||
})
|
||||
|
||||
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)
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, resp.Body.Close())
|
||||
})
|
||||
|
||||
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)
|
||||
require.Equal(t, apimodels.TestReceiversResult{
|
||||
Receivers: []apimodels.TestReceiverResult{{
|
||||
Name: "receiver-1",
|
||||
Configs: []apimodels.TestReceiverConfigResult{{
|
||||
Name: "receiver-1",
|
||||
UID: result.Receivers[0].Configs[0].UID,
|
||||
Status: "failed",
|
||||
Error: "the receiver is invalid: failed to validate receiver \"receiver-1\" of type \"email\": could not find addresses in settings",
|
||||
}},
|
||||
}},
|
||||
NotifedAt: result.NotifedAt,
|
||||
}, result)
|
||||
})
|
||||
|
||||
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{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
})
|
||||
store := testinfra.SetUpDatabase(t, dir)
|
||||
store.Bus = bus.GetBus()
|
||||
grafanaListedAddr := testinfra.StartGrafana(t, dir, path, store)
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Login: "grafana",
|
||||
Password: "password",
|
||||
})
|
||||
|
||||
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
|
||||
mockEmails := &mockEmailHandlerWithTimeout{
|
||||
timeout: 5 * time.Second,
|
||||
}
|
||||
bus.AddHandlerCtx("", mockEmails.sendEmailCommandHandlerSync)
|
||||
t.Cleanup(func() {
|
||||
bus.AddHandlerCtx("", oldEmailBus)
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
var result apimodels.TestReceiversResult
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
|
||||
|
||||
require.Len(t, result.Receivers, 1)
|
||||
require.Len(t, result.Receivers[0].Configs, 1)
|
||||
require.Equal(t, apimodels.TestReceiversResult{
|
||||
Receivers: []apimodels.TestReceiverResult{{
|
||||
Name: "receiver-1",
|
||||
Configs: []apimodels.TestReceiverConfigResult{{
|
||||
Name: "receiver-1",
|
||||
UID: result.Receivers[0].Configs[0].UID,
|
||||
Status: "failed",
|
||||
Error: "the receiver timed out: context deadline exceeded",
|
||||
}},
|
||||
}},
|
||||
NotifedAt: result.NotifedAt,
|
||||
}, result)
|
||||
})
|
||||
|
||||
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{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
})
|
||||
store := testinfra.SetUpDatabase(t, dir)
|
||||
store.Bus = bus.GetBus()
|
||||
grafanaListedAddr := testinfra.StartGrafana(t, dir, path, store)
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Login: "grafana",
|
||||
Password: "password",
|
||||
})
|
||||
|
||||
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
|
||||
mockEmails := &mockEmailHandlerWithTimeout{
|
||||
timeout: 5 * time.Second,
|
||||
}
|
||||
bus.AddHandlerCtx("", mockEmails.sendEmailCommandHandlerSync)
|
||||
t.Cleanup(func() {
|
||||
bus.AddHandlerCtx("", oldEmailBus)
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
var result apimodels.TestReceiversResult
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
|
||||
|
||||
require.Len(t, result.Receivers, 2)
|
||||
require.Len(t, result.Receivers[0].Configs, 1)
|
||||
require.Len(t, result.Receivers[1].Configs, 1)
|
||||
require.Equal(t, apimodels.TestReceiversResult{
|
||||
Receivers: []apimodels.TestReceiverResult{{
|
||||
Name: "receiver-1",
|
||||
Configs: []apimodels.TestReceiverConfigResult{{
|
||||
Name: "receiver-1",
|
||||
UID: result.Receivers[0].Configs[0].UID,
|
||||
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",
|
||||
Configs: []apimodels.TestReceiverConfigResult{{
|
||||
Name: "receiver-2",
|
||||
UID: result.Receivers[1].Configs[0].UID,
|
||||
Status: "failed",
|
||||
Error: "the receiver timed out: context deadline exceeded",
|
||||
}},
|
||||
}},
|
||||
NotifedAt: result.NotifedAt,
|
||||
}, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotificationChannels(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
EnableFeatureToggles: []string{"ngalert"},
|
||||
@@ -391,6 +713,21 @@ func (e *mockEmailHandler) sendEmailCommandHandlerSync(_ context.Context, cmd *m
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user