* Alerting: add provenance guard to config api
* add tests
* only guard if config valid
* adapt error message
* simplify logic
* rename arguments
* make logic more straight forward
* rename opt to options
* remove useless maps
(cherry picked from commit 4cc8c6f745)
Co-authored-by: Jean-Philippe Quéméner <JohnnyQQQQ@users.noreply.github.com>
This commit is contained in:
co-authored by
Jean-Philippe Quéméner
parent
67cbd5015d
commit
92d995d658
@@ -0,0 +1,146 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
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/util/cmputil"
|
||||
amConfig "github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
)
|
||||
|
||||
func (srv AlertmanagerSrv) provenanceGuard(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
if err := checkRoutes(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkTemplates(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkContactPoints(currentConfig.AlertmanagerConfig.Receivers, newConfig.AlertmanagerConfig.Receivers); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkMuteTimes(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRoutes(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
reporter := cmputil.DiffReporter{}
|
||||
options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(labels.Matcher{})}
|
||||
routesEqual := cmp.Equal(currentConfig.AlertmanagerConfig.Route, newConfig.AlertmanagerConfig.Route, options...)
|
||||
if !routesEqual && currentConfig.AlertmanagerConfig.Route.Provenance != ngmodels.ProvenanceNone {
|
||||
return fmt.Errorf("policies were provisioned and cannot be changed through the UI")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTemplates(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
for name, template := range currentConfig.TemplateFiles {
|
||||
provenance := ngmodels.ProvenanceNone
|
||||
if prov, present := currentConfig.TemplateFileProvenances[name]; present {
|
||||
provenance = prov
|
||||
}
|
||||
if provenance == ngmodels.ProvenanceNone {
|
||||
continue // we are only interested in non none
|
||||
}
|
||||
found := false
|
||||
for newName, newTemplate := range newConfig.TemplateFiles {
|
||||
if name != newName {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
if template != newTemplate {
|
||||
return fmt.Errorf("cannot save provisioned template '%s'", name)
|
||||
}
|
||||
break // we found the template and we can proceed
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("cannot delete provisioned template '%s'", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkContactPoints(currReceivers []*apimodels.GettableApiReceiver, newReceivers []*apimodels.PostableApiReceiver) error {
|
||||
newCPs := make(map[string]*apimodels.PostableGrafanaReceiver)
|
||||
for _, postedReceiver := range newReceivers {
|
||||
for _, postedContactPoint := range postedReceiver.GrafanaManagedReceivers {
|
||||
newCPs[postedContactPoint.UID] = postedContactPoint
|
||||
}
|
||||
}
|
||||
for _, existingReceiver := range currReceivers {
|
||||
for _, contactPoint := range existingReceiver.GrafanaManagedReceivers {
|
||||
if contactPoint.Provenance == ngmodels.ProvenanceNone {
|
||||
continue // we are only interested in non none
|
||||
}
|
||||
postedContactPoint, present := newCPs[contactPoint.UID]
|
||||
if !present {
|
||||
return fmt.Errorf("cannot delete provisioned contact point '%s'", contactPoint.Name)
|
||||
}
|
||||
editErr := fmt.Errorf("cannot save provisioned contact point '%s'", contactPoint.Name)
|
||||
if contactPoint.DisableResolveMessage != postedContactPoint.DisableResolveMessage {
|
||||
return editErr
|
||||
}
|
||||
if contactPoint.Name != postedContactPoint.Name {
|
||||
return editErr
|
||||
}
|
||||
if contactPoint.Type != postedContactPoint.Type {
|
||||
return editErr
|
||||
}
|
||||
for key := range contactPoint.SecureFields {
|
||||
if value, present := postedContactPoint.SecureSettings[key]; present && value != "" {
|
||||
return editErr
|
||||
}
|
||||
}
|
||||
existingSettings, err := contactPoint.Settings.Map()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newSettings, err := postedContactPoint.Settings.Map()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, val := range existingSettings {
|
||||
if newVal, present := newSettings[key]; present {
|
||||
if val != newVal {
|
||||
return editErr
|
||||
}
|
||||
} else {
|
||||
return editErr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMuteTimes(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
newMTs := make(map[string]amConfig.MuteTimeInterval)
|
||||
for _, newMuteTime := range newConfig.AlertmanagerConfig.MuteTimeIntervals {
|
||||
newMTs[newMuteTime.Name] = newMuteTime
|
||||
}
|
||||
for _, muteTime := range currentConfig.AlertmanagerConfig.MuteTimeIntervals {
|
||||
provenance := ngmodels.ProvenanceNone
|
||||
if prov, present := currentConfig.AlertmanagerConfig.MuteTimeProvenances[muteTime.Name]; present {
|
||||
provenance = prov
|
||||
}
|
||||
if provenance == ngmodels.ProvenanceNone {
|
||||
continue // we are only interested in non none
|
||||
}
|
||||
postedMT, present := newMTs[muteTime.Name]
|
||||
if !present {
|
||||
return fmt.Errorf("cannot delete provisioned mute time '%s'", muteTime.Name)
|
||||
}
|
||||
reporter := cmputil.DiffReporter{}
|
||||
options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty()}
|
||||
timesEqual := cmp.Equal(muteTime.TimeIntervals, postedMT.TimeIntervals, options...)
|
||||
if !timesEqual {
|
||||
return fmt.Errorf("cannot save provisioned mute time '%s'", muteTime.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user