mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Managed receiver resource permission in config api (#93632)
* Alerting: Managed receiver resource permission in config api
This commit is contained in:
parent
10582e48f7
commit
e86929eb0a
@ -609,7 +609,20 @@ func createMultiOrgAlertmanager(t *testing.T, configs map[int64]*ngmodels.AlertC
|
||||
}, // do not poll in tests.
|
||||
}
|
||||
|
||||
mam, err := notifier.NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, provStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"), secretsService, featuremgmt.WithManager(featuremgmt.FlagAlertingSimplifiedRouting))
|
||||
mam, err := notifier.NewMultiOrgAlertmanager(
|
||||
cfg,
|
||||
configStore,
|
||||
orgStore,
|
||||
kvStore,
|
||||
provStore,
|
||||
decryptFn,
|
||||
m.GetMultiOrgAlertmanagerMetrics(),
|
||||
nil,
|
||||
ngfakes.NewFakeReceiverPermissionsService(),
|
||||
log.New("testlogger"),
|
||||
secretsService,
|
||||
featuremgmt.WithManager(featuremgmt.FlagAlertingSimplifiedRouting),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
err = mam.LoadAndSyncAlertmanagersForOrgs(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
@ -311,7 +311,21 @@ func (ng *AlertNG) init() error {
|
||||
|
||||
decryptFn := ng.SecretsService.GetDecryptedValue
|
||||
multiOrgMetrics := ng.Metrics.GetMultiOrgAlertmanagerMetrics()
|
||||
moa, err := notifier.NewMultiOrgAlertmanager(ng.Cfg, ng.store, ng.store, ng.KVStore, ng.store, decryptFn, multiOrgMetrics, ng.NotificationService, moaLogger, ng.SecretsService, ng.FeatureToggles, overrides...)
|
||||
moa, err := notifier.NewMultiOrgAlertmanager(
|
||||
ng.Cfg,
|
||||
ng.store,
|
||||
ng.store,
|
||||
ng.KVStore,
|
||||
ng.store,
|
||||
decryptFn,
|
||||
multiOrgMetrics,
|
||||
ng.NotificationService,
|
||||
ng.ResourcePermissions,
|
||||
moaLogger,
|
||||
ng.SecretsService,
|
||||
ng.FeatureToggles,
|
||||
overrides...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2,16 +2,19 @@ package notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/errutil"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
@ -58,10 +61,32 @@ func (moa *MultiOrgAlertmanager) SaveAndApplyDefaultConfig(ctx context.Context,
|
||||
return err
|
||||
}
|
||||
|
||||
previousConfig, cleanPermissionsErr := moa.configStore.GetLatestAlertmanagerConfiguration(ctx, orgId)
|
||||
|
||||
err = orgAM.SaveAndApplyDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to cleanup permissions for receivers that are no longer defined and add defaults for new receivers.
|
||||
// Failure should not prevent the default config from being applied.
|
||||
if cleanPermissionsErr == nil {
|
||||
cleanPermissionsErr = func() error {
|
||||
defaultedConfig, err := moa.configStore.GetLatestAlertmanagerConfiguration(ctx, orgId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newReceiverNames, err := extractReceiverNames(defaultedConfig.AlertmanagerConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return moa.cleanPermissions(ctx, orgId, previousConfig, newReceiverNames)
|
||||
}()
|
||||
}
|
||||
if cleanPermissionsErr != nil {
|
||||
moa.logger.Error("Failed to clean permissions for receivers", "error", cleanPermissionsErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -130,12 +155,29 @@ func (moa *MultiOrgAlertmanager) ActivateHistoricalConfiguration(ctx context.Con
|
||||
}
|
||||
}
|
||||
|
||||
previousConfig, cleanPermissionsErr := moa.configStore.GetLatestAlertmanagerConfiguration(ctx, orgId)
|
||||
|
||||
if err := am.SaveAndApplyConfig(ctx, cfg); err != nil {
|
||||
moa.logger.Error("Unable to save and apply historical alertmanager configuration", "error", err, "org", orgId, "id", id)
|
||||
return AlertmanagerConfigRejectedError{err}
|
||||
}
|
||||
moa.logger.Info("Applied historical alertmanager configuration", "org", orgId, "id", id)
|
||||
|
||||
// Attempt to cleanup permissions for receivers that are no longer defined and add defaults for new receivers.
|
||||
// Failure should not prevent the default config from being applied.
|
||||
if cleanPermissionsErr == nil {
|
||||
cleanPermissionsErr = func() error {
|
||||
newReceiverNames, err := extractReceiverNames(config.AlertmanagerConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return moa.cleanPermissions(ctx, orgId, previousConfig, newReceiverNames)
|
||||
}()
|
||||
}
|
||||
if cleanPermissionsErr != nil {
|
||||
moa.logger.Error("Failed to clean permissions for receivers", "error", cleanPermissionsErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -231,13 +273,14 @@ func (moa *MultiOrgAlertmanager) SaveAndApplyAlertmanagerConfiguration(ctx conte
|
||||
}
|
||||
|
||||
// Get the last known working configuration
|
||||
_, err := moa.configStore.GetLatestAlertmanagerConfiguration(ctx, org)
|
||||
previousConfig, err := moa.configStore.GetLatestAlertmanagerConfiguration(ctx, org)
|
||||
if err != nil {
|
||||
// If we don't have a configuration there's nothing for us to know and we should just continue saving the new one
|
||||
if !errors.Is(err, store.ErrNoAlertmanagerConfiguration) {
|
||||
return fmt.Errorf("failed to get latest configuration %w", err)
|
||||
}
|
||||
}
|
||||
cleanPermissionsErr := err
|
||||
|
||||
if err := moa.Crypto.ProcessSecureSettings(ctx, org, config.AlertmanagerConfig.Receivers); err != nil {
|
||||
return fmt.Errorf("failed to post process Alertmanager configuration: %w", err)
|
||||
@ -268,6 +311,21 @@ func (moa *MultiOrgAlertmanager) SaveAndApplyAlertmanagerConfiguration(ctx conte
|
||||
return AlertmanagerConfigRejectedError{err}
|
||||
}
|
||||
|
||||
// Attempt to cleanup permissions for receivers that are no longer defined and add defaults for new receivers.
|
||||
// Failure should not prevent the default config from being applied.
|
||||
if cleanPermissionsErr == nil {
|
||||
cleanPermissionsErr = func() error {
|
||||
newReceiverNames := make(sets.Set[string], len(config.AlertmanagerConfig.Receivers))
|
||||
for _, r := range config.AlertmanagerConfig.Receivers {
|
||||
newReceiverNames.Insert(r.Name)
|
||||
}
|
||||
return moa.cleanPermissions(ctx, org, previousConfig, newReceiverNames)
|
||||
}()
|
||||
}
|
||||
if cleanPermissionsErr != nil {
|
||||
moa.logger.Error("Failed to clean permissions for receivers", "error", cleanPermissionsErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -352,3 +410,51 @@ func (moa *MultiOrgAlertmanager) mergeProvenance(ctx context.Context, config def
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// cleanPermissions will remove permissions for receivers that are no longer defined in the new configuration and
|
||||
// set default permissions for new receivers.
|
||||
func (moa *MultiOrgAlertmanager) cleanPermissions(ctx context.Context, orgID int64, previousConfig *models.AlertConfiguration, newReceiverNames sets.Set[string]) error {
|
||||
previousReceiverNames, err := extractReceiverNames(previousConfig.AlertmanagerConfiguration)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract receiver names from previous configuration: %w", err)
|
||||
}
|
||||
|
||||
var errs []error
|
||||
for receiverName := range previousReceiverNames.Difference(newReceiverNames) { // Deleted receivers.
|
||||
if err := moa.receiverResourcePermissions.DeleteResourcePermissions(ctx, orgID, legacy_storage.NameToUid(receiverName)); err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to delete permissions for receiver %s: %w", receiverName, err))
|
||||
}
|
||||
}
|
||||
|
||||
for receiverName := range newReceiverNames.Difference(previousReceiverNames) { // Added receivers.
|
||||
moa.receiverResourcePermissions.SetDefaultPermissions(ctx, orgID, nil, legacy_storage.NameToUid(receiverName))
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// extractReceiverNames extracts receiver names from the raw Alertmanager configuration. Unmarshalling ignores fields
|
||||
// unrelated to receiver names, making it more resilient to invalid configurations.
|
||||
func extractReceiverNames(rawConfig string) (sets.Set[string], error) {
|
||||
// Slimmed down version of the Alertmanager configuration to extract receiver names. This is more resilient to
|
||||
// invalid configurations when all we are interested in is the receiver names.
|
||||
type receiverUserConfig struct {
|
||||
AlertmanagerConfig struct {
|
||||
Receivers []struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
} `yaml:"receivers,omitempty" json:"receivers,omitempty"`
|
||||
} `yaml:"alertmanager_config" json:"alertmanager_config"`
|
||||
}
|
||||
|
||||
cfg := &receiverUserConfig{}
|
||||
if err := json.Unmarshal([]byte(rawConfig), cfg); err != nil {
|
||||
return nil, fmt.Errorf("unable to parse Alertmanager configuration: %w", err)
|
||||
}
|
||||
|
||||
receiverNames := make(sets.Set[string], len(cfg.AlertmanagerConfig.Receivers))
|
||||
for _, r := range cfg.AlertmanagerConfig.Receivers {
|
||||
receiverNames[r.Name] = struct{}{}
|
||||
}
|
||||
|
||||
return receiverNames, nil
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
alertingCluster "github.com/grafana/alerting/cluster"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/errutil"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
|
||||
alertingNotify "github.com/grafana/alerting/notify"
|
||||
|
||||
@ -100,6 +101,8 @@ type MultiOrgAlertmanager struct {
|
||||
|
||||
metrics *metrics.MultiOrgAlertmanager
|
||||
ns notifications.Service
|
||||
|
||||
receiverResourcePermissions ac.ReceiverPermissionsService
|
||||
}
|
||||
|
||||
type OrgAlertmanagerFactory func(ctx context.Context, orgID int64) (Alertmanager, error)
|
||||
@ -121,6 +124,7 @@ func NewMultiOrgAlertmanager(
|
||||
decryptFn alertingNotify.GetDecryptedValueFn,
|
||||
m *metrics.MultiOrgAlertmanager,
|
||||
ns notifications.Service,
|
||||
receiverResourcePermissions ac.ReceiverPermissionsService,
|
||||
l log.Logger,
|
||||
s secrets.Service,
|
||||
featureManager featuremgmt.FeatureToggles,
|
||||
@ -130,17 +134,18 @@ func NewMultiOrgAlertmanager(
|
||||
Crypto: NewCrypto(s, configStore, l),
|
||||
ProvStore: provStore,
|
||||
|
||||
logger: l,
|
||||
settings: cfg,
|
||||
featureManager: featureManager,
|
||||
alertmanagers: map[int64]Alertmanager{},
|
||||
configStore: configStore,
|
||||
orgStore: orgStore,
|
||||
kvStore: kvStore,
|
||||
decryptFn: decryptFn,
|
||||
metrics: m,
|
||||
ns: ns,
|
||||
peer: &NilPeer{},
|
||||
logger: l,
|
||||
settings: cfg,
|
||||
featureManager: featureManager,
|
||||
alertmanagers: map[int64]Alertmanager{},
|
||||
configStore: configStore,
|
||||
orgStore: orgStore,
|
||||
kvStore: kvStore,
|
||||
decryptFn: decryptFn,
|
||||
receiverResourcePermissions: receiverResourcePermissions,
|
||||
metrics: m,
|
||||
ns: ns,
|
||||
peer: &NilPeer{},
|
||||
}
|
||||
|
||||
if cfg.UnifiedAlerting.SkipClustering {
|
||||
|
@ -98,6 +98,7 @@ func TestMultiorgAlertmanager_RemoteSecondaryMode(t *testing.T) {
|
||||
secretsService.GetDecryptedValue,
|
||||
m.GetMultiOrgAlertmanagerMetrics(),
|
||||
nil,
|
||||
ngfakes.NewFakeReceiverPermissionsService(),
|
||||
nopLogger,
|
||||
secretsService,
|
||||
featuremgmt.WithFeatures(),
|
||||
|
@ -383,7 +383,20 @@ func setupMam(t *testing.T, cfg *setting.Cfg) *MultiOrgAlertmanager {
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
reg := prometheus.NewPedanticRegistry()
|
||||
m := metrics.NewNGAlert(reg)
|
||||
mam, err := NewMultiOrgAlertmanager(cfg, cs, orgStore, kvStore, provStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"), secretsService, featuremgmt.WithFeatures())
|
||||
mam, err := NewMultiOrgAlertmanager(
|
||||
cfg,
|
||||
cs,
|
||||
orgStore,
|
||||
kvStore,
|
||||
provStore,
|
||||
decryptFn,
|
||||
m.GetMultiOrgAlertmanagerMetrics(),
|
||||
nil,
|
||||
ngfakes.NewFakeReceiverPermissionsService(),
|
||||
log.New("testlogger"),
|
||||
secretsService,
|
||||
featuremgmt.WithFeatures(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return mam
|
||||
}
|
||||
|
@ -491,7 +491,20 @@ func createMultiOrgAlertmanager(t *testing.T, orgs []int64) *notifier.MultiOrgAl
|
||||
m := metrics.NewNGAlert(registry)
|
||||
secretsService := secretsManager.SetupTestService(t, fake_secrets.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
moa, err := notifier.NewMultiOrgAlertmanager(cfg, cfgStore, orgStore, kvStore, fakes.NewFakeProvisioningStore(), decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"), secretsService, featuremgmt.WithFeatures())
|
||||
moa, err := notifier.NewMultiOrgAlertmanager(
|
||||
cfg,
|
||||
cfgStore,
|
||||
orgStore,
|
||||
kvStore,
|
||||
fakes.NewFakeProvisioningStore(),
|
||||
decryptFn,
|
||||
m.GetMultiOrgAlertmanagerMetrics(),
|
||||
nil,
|
||||
fakes.NewFakeReceiverPermissionsService(),
|
||||
log.New("testlogger"),
|
||||
secretsService,
|
||||
featuremgmt.WithFeatures(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, moa.LoadAndSyncAlertmanagersForOrgs(context.Background()))
|
||||
require.Eventually(t, func() bool {
|
||||
|
Loading…
Reference in New Issue
Block a user