Alerting: Use interfaces for the Alertmanager (#73900)

This commit is contained in:
Santiago 2023-09-06 07:59:29 -03:00 committed by GitHub
parent f8026463a8
commit 93b9f9b537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 82 additions and 61 deletions

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/services/datasourceproxy"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/backtesting"
"github.com/grafana/grafana/pkg/services/ngalert/eval"
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
@ -34,28 +33,6 @@ type ExternalAlertmanagerProvider interface {
DroppedAlertmanagersFor(orgID int64) []*url.URL
}
type Alertmanager interface {
// Configuration
SaveAndApplyConfig(ctx context.Context, config *apimodels.PostableUserConfig) error
SaveAndApplyDefaultConfig(ctx context.Context) error
GetStatus() apimodels.GettableStatus
// Silences
CreateSilence(ps *apimodels.PostableSilence) (string, error)
DeleteSilence(silenceID string) error
GetSilence(silenceID string) (apimodels.GettableSilence, error)
ListSilences(filter []string) (apimodels.GettableSilences, error)
// Alerts
GetAlerts(active, silenced, inhibited bool, filter []string, receiver string) (apimodels.GettableAlerts, error)
GetAlertGroups(active, silenced, inhibited bool, filter []string, receiver string) (apimodels.AlertGroups, error)
// Receivers
GetReceivers(ctx context.Context) []apimodels.Receiver
TestReceivers(ctx context.Context, c apimodels.TestReceiversConfigBodyParams) (*notifier.TestReceiversResult, error)
TestTemplate(ctx context.Context, c apimodels.TestTemplatesConfigBodyParams) (*notifier.TestTemplatesResults, error)
}
type AlertingStore interface {
GetLatestAlertmanagerConfiguration(ctx context.Context, query *models.GetLatestAlertmanagerConfigurationQuery) (*models.AlertConfiguration, error)
}

View File

@ -458,7 +458,7 @@ func newTestTemplateResult(res *notifier.TestTemplatesResults) apimodels.TestTem
return apiRes
}
func (srv AlertmanagerSrv) AlertmanagerFor(orgID int64) (Alertmanager, *response.NormalResponse) {
func (srv AlertmanagerSrv) AlertmanagerFor(orgID int64) (notifier.Alertmanager, *response.NormalResponse) {
am, err := srv.mam.AlertmanagerFor(orgID)
if err == nil {
return am, nil

View File

@ -231,7 +231,7 @@ func TestAlertmanagerConfig(t *testing.T) {
am, err := sut.mam.AlertmanagerFor(1)
require.NoError(t, err)
hash := am.Base.ConfigHash()
hash := am.ConfigHash()
getResponse := sut.RouteGetAlertingConfig(&rc)
require.Equal(t, 200, getResponse.Status())
@ -243,7 +243,7 @@ func TestAlertmanagerConfig(t *testing.T) {
am, err = sut.mam.AlertmanagerFor(1)
require.NoError(t, err)
newHash := am.Base.ConfigHash()
newHash := am.ConfigHash()
require.Equal(t, hash, newHash)
})

View File

@ -44,7 +44,7 @@ type AlertingStore interface {
store.ImageStore
}
type Alertmanager struct {
type alertmanager struct {
Base *alertingNotify.GrafanaAlertmanager
logger log.Logger
@ -85,7 +85,7 @@ func (m maintenanceOptions) MaintenanceFunc(state alertingNotify.State) (int64,
func newAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store AlertingStore, kvStore kvstore.KVStore,
peer alertingNotify.ClusterPeer, decryptFn alertingNotify.GetDecryptedValueFn, ns notifications.Service,
m *metrics.Alertmanager) (*Alertmanager, error) {
m *metrics.Alertmanager) (*alertmanager, error) {
workingPath := filepath.Join(cfg.DataPath, workingDir, strconv.Itoa(int(orgID)))
fileStore := NewFileStore(orgID, kvStore, workingPath)
@ -133,7 +133,7 @@ func newAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
return nil, err
}
am := &Alertmanager{
am := &alertmanager{
Base: gam,
ConfigMetrics: m.AlertmanagerConfigMetrics,
Settings: cfg,
@ -148,20 +148,20 @@ func newAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
return am, nil
}
func (am *Alertmanager) Ready() bool {
func (am *alertmanager) Ready() bool {
// We consider AM as ready only when the config has been
// applied at least once successfully. Until then, some objects
// can still be nil.
return am.Base.Ready()
}
func (am *Alertmanager) StopAndWait() {
func (am *alertmanager) StopAndWait() {
am.Base.StopAndWait()
}
// SaveAndApplyDefaultConfig saves the default configuration to the database and applies it to the Alertmanager.
// It rolls back the save if we fail to apply the configuration.
func (am *Alertmanager) SaveAndApplyDefaultConfig(ctx context.Context) error {
func (am *alertmanager) SaveAndApplyDefaultConfig(ctx context.Context) error {
var outerErr error
am.Base.WithLock(func() {
cmd := &ngmodels.SaveAlertmanagerConfigurationCmd{
@ -193,7 +193,7 @@ func (am *Alertmanager) SaveAndApplyDefaultConfig(ctx context.Context) error {
// SaveAndApplyConfig saves the configuration the database and applies the configuration to the Alertmanager.
// It rollbacks the save if we fail to apply the configuration.
func (am *Alertmanager) SaveAndApplyConfig(ctx context.Context, cfg *apimodels.PostableUserConfig) error {
func (am *alertmanager) SaveAndApplyConfig(ctx context.Context, cfg *apimodels.PostableUserConfig) error {
rawConfig, err := json.Marshal(&cfg)
if err != nil {
return fmt.Errorf("failed to serialize to the Alertmanager configuration: %w", err)
@ -222,7 +222,7 @@ func (am *Alertmanager) SaveAndApplyConfig(ctx context.Context, cfg *apimodels.P
}
// ApplyConfig applies the configuration to the Alertmanager.
func (am *Alertmanager) ApplyConfig(ctx context.Context, dbCfg *ngmodels.AlertConfiguration) error {
func (am *alertmanager) ApplyConfig(ctx context.Context, dbCfg *ngmodels.AlertConfiguration) error {
var err error
cfg, err := Load([]byte(dbCfg.AlertmanagerConfiguration))
if err != nil {
@ -247,7 +247,7 @@ type AggregateMatchersUsage struct {
ObjectMatchers int
}
func (am *Alertmanager) updateConfigMetrics(cfg *apimodels.PostableUserConfig) {
func (am *alertmanager) updateConfigMetrics(cfg *apimodels.PostableUserConfig) {
var amu AggregateMatchersUsage
am.aggregateRouteMatchers(cfg.AlertmanagerConfig.Route, &amu)
am.aggregateInhibitMatchers(cfg.AlertmanagerConfig.InhibitRules, &amu)
@ -257,7 +257,7 @@ func (am *Alertmanager) updateConfigMetrics(cfg *apimodels.PostableUserConfig) {
am.ConfigMetrics.ObjectMatchers.Set(float64(amu.ObjectMatchers))
}
func (am *Alertmanager) aggregateRouteMatchers(r *apimodels.Route, amu *AggregateMatchersUsage) {
func (am *alertmanager) aggregateRouteMatchers(r *apimodels.Route, amu *AggregateMatchersUsage) {
amu.Matchers += len(r.Matchers)
amu.MatchRE += len(r.MatchRE)
amu.Match += len(r.Match)
@ -267,7 +267,7 @@ func (am *Alertmanager) aggregateRouteMatchers(r *apimodels.Route, amu *Aggregat
}
}
func (am *Alertmanager) aggregateInhibitMatchers(rules []config.InhibitRule, amu *AggregateMatchersUsage) {
func (am *alertmanager) aggregateInhibitMatchers(rules []config.InhibitRule, amu *AggregateMatchersUsage) {
for _, r := range rules {
amu.Matchers += len(r.SourceMatchers)
amu.Matchers += len(r.TargetMatchers)
@ -281,7 +281,7 @@ func (am *Alertmanager) aggregateInhibitMatchers(rules []config.InhibitRule, amu
// applyConfig applies a new configuration by re-initializing all components using the configuration provided.
// It returns a boolean indicating whether the user config was changed and an error.
// It is not safe to call concurrently.
func (am *Alertmanager) applyConfig(cfg *apimodels.PostableUserConfig, rawConfig []byte) (bool, error) {
func (am *alertmanager) applyConfig(cfg *apimodels.PostableUserConfig, rawConfig []byte) (bool, error) {
// First, let's make sure this config is not already loaded
var amConfigChanged bool
if rawConfig == nil {
@ -331,7 +331,7 @@ func (am *Alertmanager) applyConfig(cfg *apimodels.PostableUserConfig, rawConfig
}
// applyAndMarkConfig applies a configuration and marks it as applied if no errors occur.
func (am *Alertmanager) applyAndMarkConfig(ctx context.Context, hash string, cfg *apimodels.PostableUserConfig, rawConfig []byte) error {
func (am *alertmanager) applyAndMarkConfig(ctx context.Context, hash string, cfg *apimodels.PostableUserConfig, rawConfig []byte) error {
configChanged, err := am.applyConfig(cfg, rawConfig)
if err != nil {
return err
@ -348,12 +348,12 @@ func (am *Alertmanager) applyAndMarkConfig(ctx context.Context, hash string, cfg
return nil
}
func (am *Alertmanager) AppURL() string {
func (am *alertmanager) AppURL() string {
return am.Settings.AppURL
}
// buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
func (am *Alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
func (am *alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIReceiver, tmpl *alertingTemplates.Template) ([]*alertingNotify.Integration, error) {
receiverCfg, err := alertingNotify.BuildReceiverConfiguration(context.Background(), receiver, am.decryptFn)
if err != nil {
return nil, err
@ -381,7 +381,7 @@ func (am *Alertmanager) buildReceiverIntegrations(receiver *alertingNotify.APIRe
}
// PutAlerts receives the alerts and then sends them through the corresponding route based on whenever the alert has a receiver embedded or not
func (am *Alertmanager) PutAlerts(postableAlerts apimodels.PostableAlerts) error {
func (am *alertmanager) PutAlerts(postableAlerts apimodels.PostableAlerts) error {
alerts := make(alertingNotify.PostableAlerts, 0, len(postableAlerts.PostableAlerts))
for _, pa := range postableAlerts.PostableAlerts {
alerts = append(alerts, &alertingNotify.PostableAlert{
@ -395,6 +395,18 @@ func (am *Alertmanager) PutAlerts(postableAlerts apimodels.PostableAlerts) error
return am.Base.PutAlerts(alerts)
}
func (am *alertmanager) ConfigHash() [16]byte {
return am.Base.ConfigHash()
}
func (am *alertmanager) OrgID() int64 {
return am.orgID
}
func (am *alertmanager) FileStore() *FileStore {
return am.fileStore
}
// AlertValidationError is the error capturing the validation errors
// faced on the alerts.
type AlertValidationError struct {

View File

@ -18,7 +18,7 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
func setupAMTest(t *testing.T) *Alertmanager {
func setupAMTest(t *testing.T) *alertmanager {
dir := t.TempDir()
cfg := &setting.Cfg{
DataPath: dir,

View File

@ -4,10 +4,10 @@ import (
alertingNotify "github.com/grafana/alerting/notify"
)
func (am *Alertmanager) GetAlerts(active, silenced, inhibited bool, filter []string, receivers string) (alertingNotify.GettableAlerts, error) {
func (am *alertmanager) GetAlerts(active, silenced, inhibited bool, filter []string, receivers string) (alertingNotify.GettableAlerts, error) {
return am.Base.GetAlerts(active, silenced, inhibited, filter, receivers)
}
func (am *Alertmanager) GetAlertGroups(active, silenced, inhibited bool, filter []string, receivers string) (alertingNotify.AlertGroups, error) {
func (am *alertmanager) GetAlertGroups(active, silenced, inhibited bool, filter []string, receivers string) (alertingNotify.AlertGroups, error) {
return am.Base.GetAlertGroups(active, silenced, inhibited, filter, receivers)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/infra/log"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
@ -29,12 +30,43 @@ var (
ErrAlertmanagerNotReady = fmt.Errorf("Alertmanager is not ready yet")
)
type Alertmanager interface {
// Configuration
SaveAndApplyConfig(ctx context.Context, config *apimodels.PostableUserConfig) error
SaveAndApplyDefaultConfig(ctx context.Context) error
GetStatus() apimodels.GettableStatus
// Silences
CreateSilence(*apimodels.PostableSilence) (string, error)
DeleteSilence(string) error
GetSilence(string) (apimodels.GettableSilence, error)
ListSilences([]string) (apimodels.GettableSilences, error)
// Alerts
GetAlerts(active, silenced, inhibited bool, filter []string, receiver string) (apimodels.GettableAlerts, error)
GetAlertGroups(active, silenced, inhibited bool, filter []string, receiver string) (apimodels.AlertGroups, error)
PutAlerts(postableAlerts apimodels.PostableAlerts) error
// Receivers
GetReceivers(ctx context.Context) []apimodels.Receiver
TestReceivers(ctx context.Context, c apimodels.TestReceiversConfigBodyParams) (*TestReceiversResult, error)
TestTemplate(ctx context.Context, c apimodels.TestTemplatesConfigBodyParams) (*TestTemplatesResults, error)
ApplyConfig(context.Context, *models.AlertConfiguration) error
// State
StopAndWait()
Ready() bool
FileStore() *FileStore
OrgID() int64
ConfigHash() [16]byte
}
type MultiOrgAlertmanager struct {
Crypto Crypto
ProvStore provisioningStore
alertmanagersMtx sync.RWMutex
alertmanagers map[int64]*Alertmanager
alertmanagers map[int64]Alertmanager
settings *setting.Cfg
logger log.Logger
@ -63,7 +95,7 @@ func NewMultiOrgAlertmanager(cfg *setting.Cfg, configStore AlertingStore, orgSto
logger: l,
settings: cfg,
alertmanagers: map[int64]*Alertmanager{},
alertmanagers: map[int64]Alertmanager{},
configStore: configStore,
orgStore: orgStore,
kvStore: kvStore,
@ -244,7 +276,7 @@ func (moa *MultiOrgAlertmanager) SyncAlertmanagersForOrgs(ctx context.Context, o
moa.alertmanagers[orgID] = alertmanager
}
amsToStop := map[int64]*Alertmanager{}
amsToStop := map[int64]Alertmanager{}
for orgId, am := range moa.alertmanagers {
if _, exists := orgsFound[orgId]; !exists {
amsToStop[orgId] = am
@ -261,7 +293,7 @@ func (moa *MultiOrgAlertmanager) SyncAlertmanagersForOrgs(ctx context.Context, o
am.StopAndWait()
moa.logger.Info("Stopped Alertmanager", "org", orgID)
// Cleanup all the remaining resources from this alertmanager.
am.fileStore.CleanUp()
am.FileStore().CleanUp()
}
// We look for orphan directories and remove them. Orphan directories can
@ -348,7 +380,7 @@ func (moa *MultiOrgAlertmanager) StopAndWait() {
// AlertmanagerFor returns the Alertmanager instance for the organization provided.
// When the organization does not have an active Alertmanager, it returns a ErrNoAlertmanagerForOrg.
// When the Alertmanager of the organization is not ready, it returns a ErrAlertmanagerNotReady.
func (moa *MultiOrgAlertmanager) AlertmanagerFor(orgID int64) (*Alertmanager, error) {
func (moa *MultiOrgAlertmanager) AlertmanagerFor(orgID int64) (Alertmanager, error) {
moa.alertmanagersMtx.RLock()
defer moa.alertmanagersMtx.RUnlock()

View File

@ -285,9 +285,9 @@ func TestMultiOrgAlertmanager_AlertmanagerFor(t *testing.T) {
{
am, err := mam.AlertmanagerFor(2)
require.NoError(t, err)
require.Equal(t, *am.GetStatus().VersionInfo.Version, "N/A")
require.Equal(t, am.orgID, int64(2))
require.NotNil(t, am.Base.ConfigHash())
require.Equal(t, "N/A", *am.GetStatus().VersionInfo.Version)
require.Equal(t, int64(2), am.OrgID())
require.NotNil(t, am.ConfigHash())
}
// Let's now remove the previous queried organization.

View File

@ -32,7 +32,7 @@ type TestReceiverConfigResult struct {
Error error
}
func (am *Alertmanager) TestReceivers(ctx context.Context, c apimodels.TestReceiversConfigBodyParams) (*TestReceiversResult, error) {
func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestReceiversConfigBodyParams) (*TestReceiversResult, error) {
receivers := make([]*alertingNotify.APIReceiver, 0, len(c.Receivers))
for _, r := range c.Receivers {
integrations := make([]*alertingNotify.GrafanaIntegrationConfig, 0, len(r.GrafanaManagedReceivers))
@ -91,7 +91,7 @@ func (am *Alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei
}, err
}
func (am *Alertmanager) GetReceivers(_ context.Context) []apimodels.Receiver {
func (am *alertmanager) GetReceivers(_ context.Context) []apimodels.Receiver {
apiReceivers := make([]apimodels.Receiver, 0, len(am.Base.GetReceivers()))
for _, rcv := range am.Base.GetReceivers() {
// Build integrations slice for each receiver.

View File

@ -4,18 +4,18 @@ import (
alertingNotify "github.com/grafana/alerting/notify"
)
func (am *Alertmanager) ListSilences(filter []string) (alertingNotify.GettableSilences, error) {
func (am *alertmanager) ListSilences(filter []string) (alertingNotify.GettableSilences, error) {
return am.Base.ListSilences(filter)
}
func (am *Alertmanager) GetSilence(silenceID string) (alertingNotify.GettableSilence, error) {
func (am *alertmanager) GetSilence(silenceID string) (alertingNotify.GettableSilence, error) {
return am.Base.GetSilence(silenceID)
}
func (am *Alertmanager) CreateSilence(ps *alertingNotify.PostableSilence) (string, error) {
func (am *alertmanager) CreateSilence(ps *alertingNotify.PostableSilence) (string, error) {
return am.Base.CreateSilence(ps)
}
func (am *Alertmanager) DeleteSilence(silenceID string) error {
func (am *alertmanager) DeleteSilence(silenceID string) error {
return am.Base.DeleteSilence(silenceID)
}

View File

@ -7,7 +7,7 @@ import (
)
// TODO: We no longer do apimodels at this layer, move it to the API.
func (am *Alertmanager) GetStatus() apimodels.GettableStatus {
func (am *alertmanager) GetStatus() apimodels.GettableStatus {
config := &apimodels.PostableUserConfig{}
status := am.Base.GetStatus()
if status == nil {

View File

@ -29,7 +29,7 @@ var (
// TestTemplate tests the given template string against the given alerts. Existing templates are used to provide context for the test.
// If an existing template of the same filename as the one being tested is found, it will not be used as context.
func (am *Alertmanager) TestTemplate(ctx context.Context, c apimodels.TestTemplatesConfigBodyParams) (*TestTemplatesResults, error) {
func (am *alertmanager) TestTemplate(ctx context.Context, c apimodels.TestTemplatesConfigBodyParams) (*TestTemplatesResults, error) {
for _, alert := range c.Alerts {
addDefaultLabelsAndAnnotations(alert)
}