mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: validate that the receiver exist when updating routing tree (#51561)
* Alerting: validate that the receiver exist when updating routing tree * rename interface * add missing file * change constructor * fix e2e tests * only import package once * add unit test for bug * wording * close response body * Update pkg/services/ngalert/api/tooling/definitions/alertmanager_validation.go * refactor to remove database roundtrip
This commit is contained in:
parent
578ab71ba9
commit
e64cde8727
@ -101,6 +101,19 @@ func (r *Route) Validate() error {
|
||||
return r.validateChild()
|
||||
}
|
||||
|
||||
func (r *Route) ValidateReceivers(receivers map[string]struct{}) error {
|
||||
if _, exists := receivers[r.Receiver]; !exists {
|
||||
return fmt.Errorf("receiver '%s' does not exist", r.Receiver)
|
||||
}
|
||||
for _, children := range r.Routes {
|
||||
err := children.ValidateReceivers(receivers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mt *MuteTimeInterval) Validate() error {
|
||||
s, err := yaml.Marshal(mt.MuteTimeInterval)
|
||||
if err != nil {
|
||||
|
@ -16,7 +16,8 @@ type NotificationPolicyService struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewNotificationPolicyService(am AMConfigStore, prov ProvisioningStore, xact TransactionManager, log log.Logger) *NotificationPolicyService {
|
||||
func NewNotificationPolicyService(am AMConfigStore, prov ProvisioningStore,
|
||||
xact TransactionManager, log log.Logger) *NotificationPolicyService {
|
||||
return &NotificationPolicyService{
|
||||
amStore: am,
|
||||
provenanceStore: prov,
|
||||
@ -63,11 +64,18 @@ func (nps *NotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgI
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
revision, err := getLastConfiguration(ctx, orgID, nps.amStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
receivers, err := nps.receiversToMap(revision.cfg.AlertmanagerConfig.Receivers)
|
||||
err = tree.ValidateReceivers(receivers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
revision.cfg.AlertmanagerConfig.Config.Route = &tree
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
@ -98,3 +106,11 @@ func (nps *NotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgI
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nps *NotificationPolicyService) receiversToMap(records []*definitions.PostableApiReceiver) (map[string]struct{}, error) {
|
||||
receivers := map[string]struct{}{}
|
||||
for _, receiver := range records {
|
||||
receivers[receiver.Name] = struct{}{}
|
||||
}
|
||||
return receivers, nil
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/common/model"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -23,6 +25,7 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
|
||||
t.Run("service stitches policy tree into org's AM config", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
|
||||
newRoute := createTestRoutingTree()
|
||||
|
||||
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
||||
@ -33,6 +36,56 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
require.Equal(t, "a new receiver", updated.Receiver)
|
||||
})
|
||||
|
||||
t.Run("not existing receiver reference will error", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
|
||||
newRoute := createTestRoutingTree()
|
||||
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
||||
Receiver: "not-existing",
|
||||
})
|
||||
|
||||
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("existing receiver reference will pass", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
sut.amStore = &MockAMConfigStore{}
|
||||
sut.amStore.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
Return(
|
||||
func(ctx context.Context, query *models.GetLatestAlertmanagerConfigurationQuery) error {
|
||||
cfg, _ := deserializeAlertmanagerConfig([]byte(defaultConfig))
|
||||
cfg.AlertmanagerConfig.Receivers = append(cfg.AlertmanagerConfig.Receivers,
|
||||
&definitions.PostableApiReceiver{
|
||||
Receiver: config.Receiver{
|
||||
// default one from createTestRoutingTree()
|
||||
Name: "a new receiver",
|
||||
},
|
||||
})
|
||||
cfg.AlertmanagerConfig.Receivers = append(cfg.AlertmanagerConfig.Receivers,
|
||||
&definitions.PostableApiReceiver{
|
||||
Receiver: config.Receiver{
|
||||
Name: "existing",
|
||||
},
|
||||
})
|
||||
data, _ := serializeAlertmanagerConfig(*cfg)
|
||||
query.Result = &models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: string(data),
|
||||
}
|
||||
return nil
|
||||
})
|
||||
sut.amStore.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil)
|
||||
newRoute := createTestRoutingTree()
|
||||
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
||||
Receiver: "existing",
|
||||
})
|
||||
|
||||
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("default provenance of records is none", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
|
||||
|
@ -44,13 +44,32 @@ func TestProvisioning(t *testing.T) {
|
||||
url := fmt.Sprintf("http://%s/api/v1/provisioning/policies", grafanaListedAddr)
|
||||
body := `
|
||||
{
|
||||
"receiver": "grafana-default-email",
|
||||
"receiver": "test-receiver",
|
||||
"group_by": [
|
||||
"..."
|
||||
],
|
||||
"routes": []
|
||||
}`
|
||||
|
||||
// As we check if the receiver exists that is referenced in the policy,
|
||||
// we first need to create it, so the tests passes correctly.
|
||||
urlReceiver := fmt.Sprintf("http://%s/api/v1/provisioning/contact-points", grafanaListedAddr)
|
||||
bodyReceiver := `
|
||||
{
|
||||
"name": "test-receiver",
|
||||
"type": "slack",
|
||||
"settings": {
|
||||
"recipient": "value_recipient",
|
||||
"token": "value_token"
|
||||
}
|
||||
}`
|
||||
|
||||
req := createTestRequest("POST", urlReceiver, "admin", bodyReceiver)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, 202, resp.StatusCode)
|
||||
|
||||
t.Run("un-authenticated GET should 401", func(t *testing.T) {
|
||||
req := createTestRequest("GET", url, "", "")
|
||||
|
||||
@ -127,7 +146,6 @@ func TestProvisioning(t *testing.T) {
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
require.Equal(t, 202, resp.StatusCode)
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user