grafana/pkg/services/ngalert/provisioning/notification_policies.go
Alexander Weaver 60ec10566f
Extract Route validation from serialization methods so it can be re-used (#47649)
* Extract validation and reject invalid policies

* Validation in dedicated file

* Tests for validation

* Extract root route validation

* Update call and drop TODO

* empty commit to kick actions

* Normalization should be idempotent

* Cleaner representation of validation errors, chain errors properly

* Make internal validate unexported

* Fix missed rename

* Genericize error message

* Improve method names

* Rebase, fix

* Update asserts
2022-04-27 15:15:41 -05:00

111 lines
2.9 KiB
Go

package provisioning
import (
"context"
"fmt"
"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"
)
type NotificationPolicyService struct {
amStore AMConfigStore
provenanceStore ProvisioningStore
xact TransactionManager
log log.Logger
}
func NewNotificationPolicyService(am AMConfigStore, prov ProvisioningStore, xact TransactionManager, log log.Logger) *NotificationPolicyService {
return &NotificationPolicyService{
amStore: am,
provenanceStore: prov,
xact: xact,
log: log,
}
}
func (nps *NotificationPolicyService) GetAMConfigStore() AMConfigStore {
return nps.amStore
}
func (nps *NotificationPolicyService) GetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
q := models.GetLatestAlertmanagerConfigurationQuery{
OrgID: orgID,
}
err := nps.amStore.GetLatestAlertmanagerConfiguration(ctx, &q)
if err != nil {
return definitions.Route{}, err
}
cfg, err := DeserializeAlertmanagerConfig([]byte(q.Result.AlertmanagerConfiguration))
if err != nil {
return definitions.Route{}, err
}
if cfg.AlertmanagerConfig.Config.Route == nil {
return definitions.Route{}, fmt.Errorf("no route present in current alertmanager config")
}
provenance, err := nps.provenanceStore.GetProvenance(ctx, cfg.AlertmanagerConfig.Route, orgID)
if err != nil {
return definitions.Route{}, err
}
result := *cfg.AlertmanagerConfig.Route
result.Provenance = provenance
return result, nil
}
func (nps *NotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgID int64, tree definitions.Route, p models.Provenance) error {
err := tree.Validate()
if err != nil {
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
}
q := models.GetLatestAlertmanagerConfigurationQuery{
OrgID: orgID,
}
err = nps.amStore.GetLatestAlertmanagerConfiguration(ctx, &q)
if err != nil {
return err
}
concurrencyToken := q.Result.ConfigurationHash
cfg, err := DeserializeAlertmanagerConfig([]byte(q.Result.AlertmanagerConfiguration))
if err != nil {
return err
}
cfg.AlertmanagerConfig.Config.Route = &tree
serialized, err := SerializeAlertmanagerConfig(*cfg)
if err != nil {
return err
}
cmd := models.SaveAlertmanagerConfigurationCmd{
AlertmanagerConfiguration: string(serialized),
ConfigurationVersion: q.Result.ConfigurationVersion,
FetchedConfigurationHash: concurrencyToken,
Default: false,
OrgID: orgID,
}
err = nps.xact.InTransaction(ctx, func(ctx context.Context) error {
err = nps.amStore.UpdateAlertmanagerConfiguration(ctx, &cmd)
if err != nil {
return err
}
err = nps.provenanceStore.SetProvenance(ctx, &tree, orgID, p)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
}