grafana/pkg/services/ngalert/provisioning/notification_policies.go
Alexander Weaver dde0b93cf1
Alerting: Provisioning API - Notification Policies (#46755)
* Base-line API for provisioning notification policies

* Wire API up, some simple tests

* Return provenance status through API

* Fix missing call

* Transactions

* Clarity in package dependencies

* Unify receivers in definitions

* Fix issue introduced by receiver change

* Drop unused internal test implementation

* FGAC hooks for provisioning routes

* Polish, swap names

* Asserting on number of exposed routes

* Don't bubble up updated object

* Integrate with new concurrency token feature in store

* Back out duplicated changes

* Remove redundant tests

* Regenerate and create unit tests for API layer

* Integration tests for auth

* Address linter errors

* Put route behind toggle

* Use alternative store API and fix feature toggle in tests

* Fixes, polish

* Fix whitespace

* Re-kick drone

* Rename services to provisioning
2022-04-05 16:48:51 -05:00

139 lines
3.4 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,
}
}
// TODO: move to Swagger codegen
type EmbeddedRoutingTree struct {
definitions.Route
Provenance models.Provenance
}
func (nps *NotificationPolicyService) GetAMConfigStore() AMConfigStore {
return nps.amStore
}
func (nps *NotificationPolicyService) GetPolicyTree(ctx context.Context, orgID int64) (EmbeddedRoutingTree, error) {
q := models.GetLatestAlertmanagerConfigurationQuery{
OrgID: orgID,
}
err := nps.amStore.GetLatestAlertmanagerConfiguration(ctx, &q)
if err != nil {
return EmbeddedRoutingTree{}, err
}
cfg, err := DeserializeAlertmanagerConfig([]byte(q.Result.AlertmanagerConfiguration))
if err != nil {
return EmbeddedRoutingTree{}, err
}
if cfg.AlertmanagerConfig.Config.Route == nil {
return EmbeddedRoutingTree{}, fmt.Errorf("no route present in current alertmanager config")
}
adapter := provenanceOrgAdapter{
inner: cfg.AlertmanagerConfig.Route,
orgID: orgID,
}
provenance, err := nps.provenanceStore.GetProvenance(ctx, adapter)
if err != nil {
return EmbeddedRoutingTree{}, err
}
result := EmbeddedRoutingTree{
Route: *cfg.AlertmanagerConfig.Route,
Provenance: provenance,
}
return result, nil
}
func (nps *NotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgID int64, tree definitions.Route, p models.Provenance) 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
}
adapter := provenanceOrgAdapter{
inner: &tree,
orgID: orgID,
}
err = nps.provenanceStore.SetProvenance(ctx, adapter, p)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
}
type provenanceOrgAdapter struct {
inner models.ProvisionableInOrg
orgID int64
}
func (a provenanceOrgAdapter) ResourceType() string {
return a.inner.ResourceType()
}
func (a provenanceOrgAdapter) ResourceID() string {
return a.inner.ResourceID()
}
func (a provenanceOrgAdapter) ResourceOrgID() int64 {
return a.orgID
}