mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Update alerting module to 20230418161049-5f374e58cb32 + refactoring (#66622)
* update to alerting 20230418161049-5f374e58cb32 * rename renamed structs in https://github.com/grafana/alerting/pull/73 * update ValidateContactPoint to use BuildReceiverConfiguration * update logger factory according to changes * rewrite integration builder Co-authored-by: Santiago <santiagohernandez.1997@gmail.com>
This commit is contained in:
parent
12e5101b91
commit
a8b4a4bb45
2
go.mod
2
go.mod
@ -63,7 +63,7 @@ require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/wire v0.5.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/grafana/alerting v0.0.0-20230315185333-d1e3c68ac064
|
||||
github.com/grafana/alerting v0.0.0-20230418161049-5f374e58cb32
|
||||
github.com/grafana/grafana-aws-sdk v0.12.0
|
||||
github.com/grafana/grafana-azure-sdk-go v1.6.0
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.159.0
|
||||
|
4
go.sum
4
go.sum
@ -1272,6 +1272,10 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20230315185333-d1e3c68ac064 h1:MtsWzSTav7NGKolO+TaJQUcyR7VY0YpUROVsJX8ktIU=
|
||||
github.com/grafana/alerting v0.0.0-20230315185333-d1e3c68ac064/go.mod h1:nHfrSTdV7/l74N5/ezqlQ+JwSvIChhN3G5+PjCfwG/E=
|
||||
github.com/grafana/alerting v0.0.0-20230410151633-4a7ecc241d72 h1:WuQGIUeDIyPviylMaMD1d2nEYIiD/icHYO0rc/AH8kQ=
|
||||
github.com/grafana/alerting v0.0.0-20230410151633-4a7ecc241d72/go.mod h1:nHfrSTdV7/l74N5/ezqlQ+JwSvIChhN3G5+PjCfwG/E=
|
||||
github.com/grafana/alerting v0.0.0-20230418161049-5f374e58cb32 h1:LdPoVBj+CA5oHLeUejDzqy8/c4Fa0UfTtCcOHka0Jws=
|
||||
github.com/grafana/alerting v0.0.0-20230418161049-5f374e58cb32/go.mod h1:nHfrSTdV7/l74N5/ezqlQ+JwSvIChhN3G5+PjCfwG/E=
|
||||
github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw=
|
||||
github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
|
||||
github.com/grafana/cuetsy v0.1.8 h1:l0AKXfHr0clu6qPirirDzNC/W5mqq5gG7iruOVolG34=
|
||||
|
@ -406,8 +406,8 @@ func statusForTestReceivers(v []notifier.TestReceiverResult) int {
|
||||
for _, next := range receiver.Configs {
|
||||
if next.Error != nil {
|
||||
var (
|
||||
invalidReceiverErr alertingNotify.InvalidReceiverError
|
||||
receiverTimeoutErr alertingNotify.ReceiverTimeoutError
|
||||
invalidReceiverErr alertingNotify.IntegrationValidationError
|
||||
receiverTimeoutErr alertingNotify.IntegrationTimeoutError
|
||||
)
|
||||
if errors.As(next.Error, &invalidReceiverErr) {
|
||||
numBadRequests += 1
|
||||
|
@ -108,7 +108,7 @@ func TestStatusForTestReceivers(t *testing.T) {
|
||||
Name: "test1",
|
||||
UID: "uid1",
|
||||
Status: "failed",
|
||||
Error: alertingNotify.InvalidReceiverError{},
|
||||
Error: alertingNotify.IntegrationValidationError{},
|
||||
}},
|
||||
}, {
|
||||
Name: "test2",
|
||||
@ -116,7 +116,7 @@ func TestStatusForTestReceivers(t *testing.T) {
|
||||
Name: "test2",
|
||||
UID: "uid2",
|
||||
Status: "failed",
|
||||
Error: alertingNotify.InvalidReceiverError{},
|
||||
Error: alertingNotify.IntegrationValidationError{},
|
||||
}},
|
||||
}}))
|
||||
})
|
||||
@ -128,7 +128,7 @@ func TestStatusForTestReceivers(t *testing.T) {
|
||||
Name: "test1",
|
||||
UID: "uid1",
|
||||
Status: "failed",
|
||||
Error: alertingNotify.ReceiverTimeoutError{},
|
||||
Error: alertingNotify.IntegrationTimeoutError{},
|
||||
}},
|
||||
}, {
|
||||
Name: "test2",
|
||||
@ -136,7 +136,7 @@ func TestStatusForTestReceivers(t *testing.T) {
|
||||
Name: "test2",
|
||||
UID: "uid2",
|
||||
Status: "failed",
|
||||
Error: alertingNotify.ReceiverTimeoutError{},
|
||||
Error: alertingNotify.IntegrationTimeoutError{},
|
||||
}},
|
||||
}}))
|
||||
})
|
||||
@ -148,7 +148,7 @@ func TestStatusForTestReceivers(t *testing.T) {
|
||||
Name: "test1",
|
||||
UID: "uid1",
|
||||
Status: "failed",
|
||||
Error: alertingNotify.InvalidReceiverError{},
|
||||
Error: alertingNotify.IntegrationValidationError{},
|
||||
}},
|
||||
}, {
|
||||
Name: "test2",
|
||||
@ -156,7 +156,7 @@ func TestStatusForTestReceivers(t *testing.T) {
|
||||
Name: "test2",
|
||||
UID: "uid2",
|
||||
Status: "failed",
|
||||
Error: alertingNotify.ReceiverTimeoutError{},
|
||||
Error: alertingNotify.IntegrationTimeoutError{},
|
||||
}},
|
||||
}}))
|
||||
})
|
||||
|
@ -3,9 +3,7 @@ package notifier
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -54,7 +52,7 @@ type Alertmanager struct {
|
||||
fileStore *FileStore
|
||||
NotificationService notifications.Service
|
||||
|
||||
decryptFn receivers.GetDecryptedValueFn
|
||||
decryptFn alertingNotify.GetDecryptedValueFn
|
||||
orgID int64
|
||||
}
|
||||
|
||||
@ -84,7 +82,7 @@ func (m maintenanceOptions) MaintenanceFunc(state alertingNotify.State) (int64,
|
||||
}
|
||||
|
||||
func newAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store AlertingStore, kvStore kvstore.KVStore,
|
||||
peer alertingNotify.ClusterPeer, decryptFn receivers.GetDecryptedValueFn, ns notifications.Service,
|
||||
peer alertingNotify.ClusterPeer, decryptFn alertingNotify.GetDecryptedValueFn, ns notifications.Service,
|
||||
m *metrics.Alertmanager) (*Alertmanager, error) {
|
||||
workingPath := filepath.Join(cfg.DataPath, workingDir, strconv.Itoa(int(orgID)))
|
||||
fileStore := NewFileStore(orgID, kvStore, workingPath)
|
||||
@ -317,7 +315,7 @@ func (am *Alertmanager) WorkingDirPath() string {
|
||||
}
|
||||
|
||||
// buildIntegrationsMap builds a map of name to the list of Grafana integration notifiers off of a list of receiver config.
|
||||
func (am *Alertmanager) buildIntegrationsMap(receivers []*apimodels.PostableApiReceiver, templates *alertingNotify.Template) (map[string][]*alertingNotify.Integration, error) {
|
||||
func (am *Alertmanager) buildIntegrationsMap(receivers []*alertingNotify.APIReceiver, templates *alertingTemplates.Template) (map[string][]*alertingNotify.Integration, error) {
|
||||
integrationsMap := make(map[string][]*alertingNotify.Integration, len(receivers))
|
||||
for _, receiver := range receivers {
|
||||
integrations, err := am.buildReceiverIntegrations(receiver, templates)
|
||||
@ -331,66 +329,48 @@ func (am *Alertmanager) buildIntegrationsMap(receivers []*apimodels.PostableApiR
|
||||
}
|
||||
|
||||
// buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
|
||||
func (am *Alertmanager) buildReceiverIntegrations(receiver *apimodels.PostableApiReceiver, tmpl *alertingNotify.Template) ([]*alertingNotify.Integration, error) {
|
||||
integrations := make([]*alertingNotify.Integration, 0, len(receiver.GrafanaManagedReceivers))
|
||||
for i, r := range receiver.GrafanaManagedReceivers {
|
||||
n, err := am.buildReceiverIntegration(PostableGrafanaReceiverToGrafanaReceiver(r), tmpl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
integrations = append(integrations, alertingNotify.NewIntegration(n, n, r.Type, i))
|
||||
func (am *Alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
|
||||
receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, am.decryptFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &sender{am.NotificationService}
|
||||
img := newImageStore(am.Store)
|
||||
integrations, err := alertingNotify.BuildReceiverIntegrations(
|
||||
receiverCfg,
|
||||
tmpl,
|
||||
img,
|
||||
LoggerFactory,
|
||||
func(n receivers.Metadata) (receivers.WebhookSender, error) {
|
||||
return s, nil
|
||||
},
|
||||
func(n receivers.Metadata) (receivers.EmailSender, error) {
|
||||
return s, nil
|
||||
},
|
||||
am.orgID,
|
||||
setting.BuildVersion,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return integrations, nil
|
||||
}
|
||||
|
||||
func (am *Alertmanager) buildReceiverIntegration(r *alertingNotify.GrafanaReceiver, tmpl *alertingNotify.Template) (alertingNotify.NotificationChannel, error) {
|
||||
// secure settings are already encrypted at this point
|
||||
secureSettings := make(map[string][]byte, len(r.SecureSettings))
|
||||
|
||||
for k, v := range r.SecureSettings {
|
||||
d, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return nil, alertingNotify.InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: errors.New("failed to decode secure setting"),
|
||||
}
|
||||
}
|
||||
secureSettings[k] = d
|
||||
func (am *Alertmanager) buildReceiverIntegration(r *alertingNotify.GrafanaIntegrationConfig, tmpl *alertingTemplates.Template) (*alertingNotify.Integration, error) {
|
||||
apiReceiver := &alertingNotify.APIReceiver{
|
||||
GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
|
||||
Integrations: []*alertingNotify.GrafanaIntegrationConfig{r},
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
cfg = &receivers.NotificationChannelConfig{
|
||||
UID: r.UID,
|
||||
OrgID: am.orgID,
|
||||
Name: r.Name,
|
||||
Type: r.Type,
|
||||
DisableResolveMessage: r.DisableResolveMessage,
|
||||
Settings: r.Settings,
|
||||
SecureSettings: secureSettings,
|
||||
}
|
||||
)
|
||||
factoryConfig, err := receivers.NewFactoryConfig(cfg, NewNotificationSender(am.NotificationService), am.decryptFn, tmpl, newImageStore(am.Store), LoggerFactory, setting.BuildVersion)
|
||||
integrations, err := am.buildReceiverIntegrations(apiReceiver, tmpl)
|
||||
if err != nil {
|
||||
return nil, alertingNotify.InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: err,
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
receiverFactory, exists := alertingNotify.Factory(r.Type)
|
||||
if !exists {
|
||||
return nil, alertingNotify.InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: fmt.Errorf("notifier %s is not supported", r.Type),
|
||||
}
|
||||
if len(integrations) == 0 {
|
||||
// This should not happen, but it is better to return some error rather than having a panic.
|
||||
return nil, fmt.Errorf("failed to build integration")
|
||||
}
|
||||
n, err := receiverFactory(factoryConfig)
|
||||
if err != nil {
|
||||
return nil, alertingNotify.InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
return integrations[0], nil
|
||||
}
|
||||
|
||||
// PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
|
||||
|
@ -8,8 +8,8 @@ import (
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
)
|
||||
|
||||
func PostableGrafanaReceiverToGrafanaReceiver(p *apimodels.PostableGrafanaReceiver) *alertingNotify.GrafanaReceiver {
|
||||
return &alertingNotify.GrafanaReceiver{
|
||||
func PostableGrafanaReceiverToGrafanaIntegrationConfig(p *apimodels.PostableGrafanaReceiver) *alertingNotify.GrafanaIntegrationConfig {
|
||||
return &alertingNotify.GrafanaIntegrationConfig{
|
||||
UID: p.UID,
|
||||
Name: p.Name,
|
||||
Type: p.Type,
|
||||
@ -20,16 +20,16 @@ func PostableGrafanaReceiverToGrafanaReceiver(p *apimodels.PostableGrafanaReceiv
|
||||
}
|
||||
|
||||
func PostableApiReceiverToApiReceiver(r *apimodels.PostableApiReceiver) *alertingNotify.APIReceiver {
|
||||
receivers := alertingNotify.GrafanaReceivers{
|
||||
Receivers: make([]*alertingNotify.GrafanaReceiver, 0, len(r.GrafanaManagedReceivers)),
|
||||
integrations := alertingNotify.GrafanaIntegrations{
|
||||
Integrations: make([]*alertingNotify.GrafanaIntegrationConfig, 0, len(r.GrafanaManagedReceivers)),
|
||||
}
|
||||
for _, receiver := range r.GrafanaManagedReceivers {
|
||||
receivers.Receivers = append(receivers.Receivers, PostableGrafanaReceiverToGrafanaReceiver(receiver))
|
||||
for _, cfg := range r.GrafanaManagedReceivers {
|
||||
integrations.Integrations = append(integrations.Integrations, PostableGrafanaReceiverToGrafanaIntegrationConfig(cfg))
|
||||
}
|
||||
|
||||
return &alertingNotify.APIReceiver{
|
||||
ConfigReceiver: r.Receiver,
|
||||
GrafanaReceivers: receivers,
|
||||
ConfigReceiver: r.Receiver,
|
||||
GrafanaIntegrations: integrations,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
)
|
||||
|
||||
func TestPostableGrafanaReceiverToGrafanaReceiver(t *testing.T) {
|
||||
func TestPostableGrafanaReceiverToGrafanaIntegrationConfig(t *testing.T) {
|
||||
r := &apimodels.PostableGrafanaReceiver{
|
||||
UID: "test-uid",
|
||||
Name: "test-name",
|
||||
@ -22,8 +22,8 @@ func TestPostableGrafanaReceiverToGrafanaReceiver(t *testing.T) {
|
||||
"test": "data",
|
||||
},
|
||||
}
|
||||
actual := PostableGrafanaReceiverToGrafanaReceiver(r)
|
||||
require.Equal(t, alertingNotify.GrafanaReceiver{
|
||||
actual := PostableGrafanaReceiverToGrafanaIntegrationConfig(r)
|
||||
require.Equal(t, alertingNotify.GrafanaIntegrationConfig{
|
||||
UID: "test-uid",
|
||||
Name: "test-name",
|
||||
Type: "slack",
|
||||
@ -43,7 +43,7 @@ func TestPostableApiReceiverToApiReceiver(t *testing.T) {
|
||||
},
|
||||
}
|
||||
actual := PostableApiReceiverToApiReceiver(r)
|
||||
require.Empty(t, actual.Receivers)
|
||||
require.Empty(t, actual.Integrations)
|
||||
require.Equal(t, r.Receiver, actual.ConfigReceiver)
|
||||
})
|
||||
t.Run("converts receivers", func(t *testing.T) {
|
||||
@ -77,10 +77,10 @@ func TestPostableApiReceiverToApiReceiver(t *testing.T) {
|
||||
},
|
||||
}
|
||||
actual := PostableApiReceiverToApiReceiver(r)
|
||||
require.Len(t, actual.Receivers, 2)
|
||||
require.Len(t, actual.Integrations, 2)
|
||||
require.Equal(t, r.Receiver, actual.ConfigReceiver)
|
||||
require.Equal(t, *PostableGrafanaReceiverToGrafanaReceiver(r.GrafanaManagedReceivers[0]), *actual.Receivers[0])
|
||||
require.Equal(t, *PostableGrafanaReceiverToGrafanaReceiver(r.GrafanaManagedReceivers[1]), *actual.Receivers[1])
|
||||
require.Equal(t, *PostableGrafanaReceiverToGrafanaIntegrationConfig(r.GrafanaManagedReceivers[0]), *actual.Integrations[0])
|
||||
require.Equal(t, *PostableGrafanaReceiverToGrafanaIntegrationConfig(r.GrafanaManagedReceivers[1]), *actual.Integrations[1])
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
alertingTemplates "github.com/grafana/alerting/templates"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
api "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
@ -94,14 +95,14 @@ type AlertingConfiguration struct {
|
||||
AlertmanagerConfig api.PostableApiAlertingConfig
|
||||
RawAlertmanagerConfig []byte
|
||||
|
||||
AlertmanagerTemplates *alertingNotify.Template
|
||||
AlertmanagerTemplates *alertingTemplates.Template
|
||||
|
||||
IntegrationsFunc func(receivers []*api.PostableApiReceiver, templates *alertingNotify.Template) (map[string][]*alertingNotify.Integration, error)
|
||||
ReceiverIntegrationsFunc func(r *alertingNotify.GrafanaReceiver, tmpl *alertingNotify.Template) (alertingNotify.NotificationChannel, error)
|
||||
IntegrationsFunc func(receivers []*alertingNotify.APIReceiver, templates *alertingTemplates.Template) (map[string][]*alertingNotify.Integration, error)
|
||||
ReceiverIntegrationsFunc func(r *alertingNotify.GrafanaIntegrationConfig, tmpl *alertingTemplates.Template) (*alertingNotify.Integration, error)
|
||||
}
|
||||
|
||||
func (a AlertingConfiguration) BuildReceiverIntegrationsFunc() func(next *alertingNotify.GrafanaReceiver, tmpl *alertingNotify.Template) (alertingNotify.Notifier, error) {
|
||||
return func(next *alertingNotify.GrafanaReceiver, tmpl *alertingNotify.Template) (alertingNotify.Notifier, error) {
|
||||
func (a AlertingConfiguration) BuildReceiverIntegrationsFunc() func(next *alertingNotify.GrafanaIntegrationConfig, tmpl *alertingTemplates.Template) (alertingNotify.Notifier, error) {
|
||||
return func(next *alertingNotify.GrafanaIntegrationConfig, tmpl *alertingTemplates.Template) (alertingNotify.Notifier, error) {
|
||||
return a.ReceiverIntegrationsFunc(next, tmpl)
|
||||
}
|
||||
}
|
||||
@ -119,14 +120,14 @@ func (a AlertingConfiguration) MuteTimeIntervals() []alertingNotify.MuteTimeInte
|
||||
}
|
||||
|
||||
func (a AlertingConfiguration) ReceiverIntegrations() (map[string][]*alertingNotify.Integration, error) {
|
||||
return a.IntegrationsFunc(a.AlertmanagerConfig.Receivers, a.AlertmanagerTemplates)
|
||||
return a.IntegrationsFunc(PostableApiAlertingConfigToApiReceivers(a.AlertmanagerConfig), a.AlertmanagerTemplates)
|
||||
}
|
||||
|
||||
func (a AlertingConfiguration) RoutingTree() *alertingNotify.Route {
|
||||
return a.AlertmanagerConfig.Route.AsAMRoute()
|
||||
}
|
||||
|
||||
func (a AlertingConfiguration) Templates() *alertingNotify.Template {
|
||||
func (a AlertingConfiguration) Templates() *alertingTemplates.Template {
|
||||
return a.AlertmanagerTemplates
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
@ -188,40 +187,20 @@ func TestEmailNotifierIntegration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func createSut(t *testing.T, messageTmpl string, subjectTmpl string, emailTmpl *template.Template, ns receivers.NotificationSender) *alertingEmail.Notifier {
|
||||
func createSut(t *testing.T, messageTmpl string, subjectTmpl string, emailTmpl *template.Template, ns receivers.EmailSender) *alertingEmail.Notifier {
|
||||
t.Helper()
|
||||
|
||||
jsonData := map[string]interface{}{
|
||||
"addresses": "someops@example.com;somedev@example.com",
|
||||
"singleEmail": true,
|
||||
if subjectTmpl == "" {
|
||||
subjectTmpl = alertingTemplates.DefaultMessageTitleEmbed
|
||||
}
|
||||
if messageTmpl != "" {
|
||||
jsonData["message"] = messageTmpl
|
||||
}
|
||||
|
||||
if subjectTmpl != "" {
|
||||
jsonData["subject"] = subjectTmpl
|
||||
}
|
||||
bytes, err := json.Marshal(jsonData)
|
||||
require.NoError(t, err)
|
||||
|
||||
fc := receivers.FactoryConfig{
|
||||
Config: &receivers.NotificationChannelConfig{
|
||||
Name: "ops",
|
||||
Type: "alertingEmail",
|
||||
Settings: json.RawMessage(bytes),
|
||||
return alertingEmail.New(alertingEmail.Config{
|
||||
SingleEmail: true,
|
||||
Addresses: []string{
|
||||
"someops@example.com",
|
||||
"somedev@example.com",
|
||||
},
|
||||
NotificationService: ns,
|
||||
DecryptFunc: func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string {
|
||||
return fallback
|
||||
},
|
||||
ImageStore: &images.UnavailableImageStore{},
|
||||
Template: emailTmpl,
|
||||
Logger: &alertingLogging.FakeLogger{},
|
||||
}
|
||||
emailNotifier, err := alertingEmail.New(fc)
|
||||
require.NoError(t, err)
|
||||
return emailNotifier
|
||||
Message: messageTmpl,
|
||||
Subject: subjectTmpl,
|
||||
}, receivers.Metadata{}, emailTmpl, ns, &images.UnavailableImageStore{}, &alertingLogging.FakeLogger{})
|
||||
}
|
||||
|
||||
func getSingleSentMessage(t *testing.T, ns *emailSender) *notifications.Message {
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
var LoggerFactory alertingLogging.LoggerFactory = func(ctx ...interface{}) alertingLogging.Logger {
|
||||
return &logWrapper{log.New(ctx...)}
|
||||
var LoggerFactory alertingLogging.LoggerFactory = func(logger string, ctx ...interface{}) alertingLogging.Logger {
|
||||
return &logWrapper{log.New(append([]interface{}{logger}, ctx...)...)}
|
||||
}
|
||||
|
||||
type logWrapper struct {
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/receivers"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@ -49,14 +48,14 @@ type MultiOrgAlertmanager struct {
|
||||
orgStore store.OrgStore
|
||||
kvStore kvstore.KVStore
|
||||
|
||||
decryptFn receivers.GetDecryptedValueFn
|
||||
decryptFn alertingNotify.GetDecryptedValueFn
|
||||
|
||||
metrics *metrics.MultiOrgAlertmanager
|
||||
ns notifications.Service
|
||||
}
|
||||
|
||||
func NewMultiOrgAlertmanager(cfg *setting.Cfg, configStore AlertingStore, orgStore store.OrgStore,
|
||||
kvStore kvstore.KVStore, provStore provisioning.ProvisioningStore, decryptFn receivers.GetDecryptedValueFn,
|
||||
kvStore kvstore.KVStore, provStore provisioning.ProvisioningStore, decryptFn alertingNotify.GetDecryptedValueFn,
|
||||
m *metrics.MultiOrgAlertmanager, ns notifications.Service, l log.Logger, s secrets.Service,
|
||||
) (*MultiOrgAlertmanager, error) {
|
||||
moa := &MultiOrgAlertmanager{
|
||||
|
@ -35,9 +35,9 @@ type TestReceiverConfigResult struct {
|
||||
func (am *Alertmanager) TestReceivers(ctx context.Context, c apimodels.TestReceiversConfigBodyParams) (*TestReceiversResult, error) {
|
||||
receivers := make([]*alertingNotify.APIReceiver, 0, len(c.Receivers))
|
||||
for _, r := range c.Receivers {
|
||||
greceivers := make([]*alertingNotify.GrafanaReceiver, 0, len(r.GrafanaManagedReceivers))
|
||||
integrations := make([]*alertingNotify.GrafanaIntegrationConfig, 0, len(r.GrafanaManagedReceivers))
|
||||
for _, gr := range r.PostableGrafanaReceivers.GrafanaManagedReceivers {
|
||||
greceivers = append(greceivers, &alertingNotify.GrafanaReceiver{
|
||||
integrations = append(integrations, &alertingNotify.GrafanaIntegrationConfig{
|
||||
UID: gr.UID,
|
||||
Name: gr.Name,
|
||||
Type: gr.Type,
|
||||
@ -48,8 +48,8 @@ func (am *Alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
|
||||
}
|
||||
receivers = append(receivers, &alertingNotify.APIReceiver{
|
||||
ConfigReceiver: r.Receiver,
|
||||
GrafanaReceivers: alertingNotify.GrafanaReceivers{
|
||||
Receivers: greceivers,
|
||||
GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
|
||||
Integrations: integrations,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -11,19 +11,20 @@ import (
|
||||
)
|
||||
|
||||
func TestInvalidReceiverError_Error(t *testing.T) {
|
||||
e := alertingNotify.InvalidReceiverError{
|
||||
Receiver: &alertingNotify.GrafanaReceiver{
|
||||
e := alertingNotify.IntegrationValidationError{
|
||||
Integration: &alertingNotify.GrafanaIntegrationConfig{
|
||||
Name: "test",
|
||||
Type: "test-type",
|
||||
UID: "uid",
|
||||
},
|
||||
Err: errors.New("this is an error"),
|
||||
}
|
||||
require.Equal(t, "the receiver is invalid: this is an error", e.Error())
|
||||
require.Equal(t, `failed to validate integration "test" (UID uid) of type "test-type": this is an error`, e.Error())
|
||||
}
|
||||
|
||||
func TestReceiverTimeoutError_Error(t *testing.T) {
|
||||
e := alertingNotify.ReceiverTimeoutError{
|
||||
Receiver: &alertingNotify.GrafanaReceiver{
|
||||
e := alertingNotify.IntegrationTimeoutError{
|
||||
Integration: &alertingNotify.GrafanaIntegrationConfig{
|
||||
Name: "test",
|
||||
UID: "uid",
|
||||
},
|
||||
@ -44,18 +45,18 @@ func (e timeoutError) Timeout() bool {
|
||||
|
||||
func TestProcessNotifierError(t *testing.T) {
|
||||
t.Run("assert ReceiverTimeoutError is returned for context deadline exceeded", func(t *testing.T) {
|
||||
r := &alertingNotify.GrafanaReceiver{
|
||||
r := &alertingNotify.GrafanaIntegrationConfig{
|
||||
Name: "test",
|
||||
UID: "uid",
|
||||
}
|
||||
require.Equal(t, alertingNotify.ReceiverTimeoutError{
|
||||
Receiver: r,
|
||||
Err: context.DeadlineExceeded,
|
||||
}, alertingNotify.ProcessNotifierError(r, context.DeadlineExceeded))
|
||||
require.Equal(t, alertingNotify.IntegrationTimeoutError{
|
||||
Integration: r,
|
||||
Err: context.DeadlineExceeded,
|
||||
}, alertingNotify.ProcessIntegrationError(r, context.DeadlineExceeded))
|
||||
})
|
||||
|
||||
t.Run("assert ReceiverTimeoutError is returned for *url.Error timeout", func(t *testing.T) {
|
||||
r := &alertingNotify.GrafanaReceiver{
|
||||
r := &alertingNotify.GrafanaIntegrationConfig{
|
||||
Name: "test",
|
||||
UID: "uid",
|
||||
}
|
||||
@ -64,18 +65,18 @@ func TestProcessNotifierError(t *testing.T) {
|
||||
URL: "https://grafana.net",
|
||||
Err: timeoutError{},
|
||||
}
|
||||
require.Equal(t, alertingNotify.ReceiverTimeoutError{
|
||||
Receiver: r,
|
||||
Err: urlError,
|
||||
}, alertingNotify.ProcessNotifierError(r, urlError))
|
||||
require.Equal(t, alertingNotify.IntegrationTimeoutError{
|
||||
Integration: r,
|
||||
Err: urlError,
|
||||
}, alertingNotify.ProcessIntegrationError(r, urlError))
|
||||
})
|
||||
|
||||
t.Run("assert unknown error is returned unmodified", func(t *testing.T) {
|
||||
r := &alertingNotify.GrafanaReceiver{
|
||||
r := &alertingNotify.GrafanaIntegrationConfig{
|
||||
Name: "test",
|
||||
UID: "uid",
|
||||
}
|
||||
err := errors.New("this is an error")
|
||||
require.Equal(t, err, alertingNotify.ProcessNotifierError(r, err))
|
||||
require.Equal(t, err, alertingNotify.ProcessIntegrationError(r, err))
|
||||
})
|
||||
}
|
||||
|
@ -50,7 +50,3 @@ func (s sender) SendEmail(ctx context.Context, cmd *receivers.SendEmailSettings)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func NewNotificationSender(ns notifications.Service) receivers.NotificationSender {
|
||||
return &sender{ns: ns}
|
||||
}
|
||||
|
22
pkg/services/ngalert/provisioning/compat.go
Normal file
22
pkg/services/ngalert/provisioning/compat.go
Normal file
@ -0,0 +1,22 @@
|
||||
package provisioning
|
||||
|
||||
import (
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
)
|
||||
|
||||
func EmbeddedContactPointToGrafanaIntegrationConfig(e definitions.EmbeddedContactPoint) (alertingNotify.GrafanaIntegrationConfig, error) {
|
||||
data, err := e.Settings.MarshalJSON()
|
||||
if err != nil {
|
||||
return alertingNotify.GrafanaIntegrationConfig{}, err
|
||||
}
|
||||
return alertingNotify.GrafanaIntegrationConfig{
|
||||
UID: e.UID,
|
||||
Name: e.Name,
|
||||
Type: e.Type,
|
||||
DisableResolveMessage: e.DisableResolveMessage,
|
||||
Settings: data,
|
||||
SecureSettings: nil,
|
||||
}, nil
|
||||
}
|
@ -7,9 +7,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/grafana/alerting/logging"
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/receivers"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
@ -18,7 +16,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@ -136,7 +133,7 @@ func (ecp *ContactPointService) getContactPointDecrypted(ctx context.Context, or
|
||||
|
||||
func (ecp *ContactPointService) CreateContactPoint(ctx context.Context, orgID int64,
|
||||
contactPoint apimodels.EmbeddedContactPoint, provenance models.Provenance) (apimodels.EmbeddedContactPoint, error) {
|
||||
if err := ValidateContactPoint(contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
if err := ValidateContactPoint(ctx, contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
return apimodels.EmbeddedContactPoint{}, fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
@ -257,7 +254,7 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
|
||||
}
|
||||
|
||||
// validate merged values
|
||||
if err := ValidateContactPoint(contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
if err := ValidateContactPoint(ctx, contactPoint, ecp.encryptionService.GetDecryptedValue); err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
@ -500,28 +497,23 @@ func replaceReferences(oldName, newName string, routes ...*apimodels.Route) {
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateContactPoint(e apimodels.EmbeddedContactPoint, decryptFunc receivers.GetDecryptedValueFn) error {
|
||||
func ValidateContactPoint(ctx context.Context, e apimodels.EmbeddedContactPoint, decryptFunc alertingNotify.GetDecryptedValueFn) error {
|
||||
if e.Type == "" {
|
||||
return fmt.Errorf("type should not be an empty string")
|
||||
}
|
||||
if e.Settings == nil {
|
||||
return fmt.Errorf("settings should not be empty")
|
||||
}
|
||||
factory, exists := alertingNotify.Factory(e.Type)
|
||||
if !exists {
|
||||
return fmt.Errorf("unknown type '%s'", e.Type)
|
||||
}
|
||||
jsonBytes, err := e.Settings.MarshalJSON()
|
||||
integration, err := EmbeddedContactPointToGrafanaIntegrationConfig(e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, _ := receivers.NewFactoryConfig(&receivers.NotificationChannelConfig{
|
||||
Settings: jsonBytes,
|
||||
Type: e.Type,
|
||||
}, nil, decryptFunc, nil, nil, func(ctx ...interface{}) logging.Logger {
|
||||
return &logging.FakeLogger{}
|
||||
}, setting.BuildVersion)
|
||||
if _, err := factory(cfg); err != nil {
|
||||
_, err = alertingNotify.BuildReceiverConfiguration(ctx, &alertingNotify.APIReceiver{
|
||||
GrafanaIntegrations: alertingNotify.GrafanaIntegrations{
|
||||
Integrations: []*alertingNotify.GrafanaIntegrationConfig{&integration},
|
||||
},
|
||||
}, decryptFunc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -95,7 +95,7 @@ func (config *ReceiverV1) mapToModel(name string) (definitions.EmbeddedContactPo
|
||||
}
|
||||
// As the values are not encrypted when coming from disk files,
|
||||
// we can simply return the fallback for validation.
|
||||
err := provisioning.ValidateContactPoint(cp, func(_ context.Context, _ map[string][]byte, _, fallback string) string {
|
||||
err := provisioning.ValidateContactPoint(context.Background(), cp, func(_ context.Context, _ map[string][]byte, _, fallback string) string {
|
||||
return fallback
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -480,7 +480,7 @@ func (m *migration) validateAlertmanagerConfig(config *PostableUserConfig) error
|
||||
return err
|
||||
}
|
||||
var (
|
||||
cfg = &alertingNotify.GrafanaReceiver{
|
||||
cfg = &alertingNotify.GrafanaIntegrationConfig{
|
||||
UID: gr.UID,
|
||||
Name: gr.Name,
|
||||
Type: gr.Type,
|
||||
@ -504,7 +504,7 @@ func (m *migration) validateAlertmanagerConfig(config *PostableUserConfig) error
|
||||
return fallback
|
||||
}
|
||||
_, err = alertingNotify.BuildReceiverConfiguration(context.Background(), &alertingNotify.APIReceiver{
|
||||
GrafanaReceivers: alertingNotify.GrafanaReceivers{Receivers: []*alertingNotify.GrafanaReceiver{cfg}},
|
||||
GrafanaIntegrations: alertingNotify.GrafanaIntegrations{Integrations: []*alertingNotify.GrafanaIntegrationConfig{cfg}},
|
||||
}, decryptFunc)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -72,7 +72,7 @@ func Test_validateAlertmanagerConfig(t *testing.T) {
|
||||
SecureSettings: map[string]string{"url": invalidUri},
|
||||
},
|
||||
},
|
||||
err: fmt.Errorf("failed to validate receiver \"SlackWithBadURL\" of type \"slack\": failed to parse notifier SlackWithBadURL (UID: test-uid): invalid URL %q", invalidUri),
|
||||
err: fmt.Errorf("failed to validate integration \"SlackWithBadURL\" (UID test-uid) of type \"slack\": invalid URL %q", invalidUri),
|
||||
},
|
||||
{
|
||||
name: "when a slack receiver has an invalid recipient - it should not error",
|
||||
|
@ -102,7 +102,7 @@ func TestIntegrationAlertmanagerConfigurationIsTransactional(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
var res map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(b, &res))
|
||||
require.Equal(t, `failed to save and apply Alertmanager configuration: failed to build integration map: the receiver is invalid: failed to validate receiver "slack.receiver" of type "slack": token must be specified when using the Slack chat API`, res["message"])
|
||||
require.Regexp(t, `^failed to save and apply Alertmanager configuration: failed to build integration map: failed to validate integration "slack.receiver" \(UID [^\)]+\) of type "slack": token must be specified when using the Slack chat API`, res["message"])
|
||||
resp = getRequest(t, alertConfigURL, http.StatusOK) // nolint
|
||||
|
||||
require.JSONEq(t, defaultAlertmanagerConfigJSON, getBody(t, resp.Body))
|
||||
|
@ -205,32 +205,34 @@ func TestIntegrationTestReceivers(t *testing.T) {
|
||||
require.NoError(t, json.Unmarshal(b, &result))
|
||||
require.Len(t, result.Receivers, 1)
|
||||
require.Len(t, result.Receivers[0].Configs, 1)
|
||||
require.Regexp(t, `failed to validate integration "receiver-1" \(UID[^\)]+\) of type "email": could not find addresses in settings`, result.Receivers[0].Configs[0].Error)
|
||||
|
||||
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"
|
||||
"alert": {
|
||||
"annotations": {
|
||||
"summary": "Notification test",
|
||||
"__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]"
|
||||
},
|
||||
"labels": {
|
||||
"alertname": "TestAlert",
|
||||
"instance": "Grafana"
|
||||
}
|
||||
]
|
||||
}],
|
||||
"notified_at": "%s"
|
||||
}`,
|
||||
},
|
||||
"receivers": [{
|
||||
"name":"receiver-1",
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"name": "receiver-1",
|
||||
"uid": "%s",
|
||||
"status": "failed",
|
||||
"error": %q
|
||||
}
|
||||
]
|
||||
}],
|
||||
"notified_at": "%s"
|
||||
}`,
|
||||
result.Receivers[0].Configs[0].UID,
|
||||
result.Receivers[0].Configs[0].Error,
|
||||
result.NotifiedAt.Format(time.RFC3339Nano))
|
||||
require.JSONEq(t, expectedJSON, string(b))
|
||||
})
|
||||
@ -392,6 +394,7 @@ func TestIntegrationTestReceivers(t *testing.T) {
|
||||
require.Len(t, result.Receivers, 2)
|
||||
require.Len(t, result.Receivers[0].Configs, 1)
|
||||
require.Len(t, result.Receivers[1].Configs, 1)
|
||||
require.Regexp(t, `failed to validate integration "receiver-1" \(UID[^\)]+\) of type "email": could not find addresses in settings`, result.Receivers[0].Configs[0].Error)
|
||||
|
||||
expectedJSON := fmt.Sprintf(`{
|
||||
"alert": {
|
||||
@ -411,7 +414,7 @@ func TestIntegrationTestReceivers(t *testing.T) {
|
||||
"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"
|
||||
"error": %q
|
||||
}
|
||||
]
|
||||
}, {
|
||||
@ -428,6 +431,7 @@ func TestIntegrationTestReceivers(t *testing.T) {
|
||||
"notified_at": "%s"
|
||||
}`,
|
||||
result.Receivers[0].Configs[0].UID,
|
||||
result.Receivers[0].Configs[0].Error,
|
||||
result.Receivers[1].Configs[0].UID,
|
||||
result.NotifiedAt.Format(time.RFC3339Nano))
|
||||
require.JSONEq(t, expectedJSON, string(b))
|
||||
@ -1056,6 +1060,7 @@ func (nc *mockNotificationChannel) ServeHTTP(res http.ResponseWriter, req *http.
|
||||
body := getBody(nc.t, req.Body)
|
||||
|
||||
nc.receivedNotifications[key] = append(nc.receivedNotifications[key], body)
|
||||
res.Header().Set("Content-Type", "application/json")
|
||||
res.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(res, nc.responses[paths[0]])
|
||||
}
|
||||
|
@ -10,11 +10,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
|
||||
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/quota"
|
||||
@ -75,7 +76,8 @@ func postRequest(t *testing.T, url string, body string, expStatusCode int) *http
|
||||
if expStatusCode != resp.StatusCode {
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
t.Fatal(string(b))
|
||||
t.Log(string(b))
|
||||
require.Equal(t, expStatusCode, resp.StatusCode)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user