diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index 2a59a387a71..a64825f2704 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -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) } diff --git a/pkg/services/ngalert/api/api_alertmanager.go b/pkg/services/ngalert/api/api_alertmanager.go index 95c98ff10ad..9b680b6ba21 100644 --- a/pkg/services/ngalert/api/api_alertmanager.go +++ b/pkg/services/ngalert/api/api_alertmanager.go @@ -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 diff --git a/pkg/services/ngalert/api/api_alertmanager_test.go b/pkg/services/ngalert/api/api_alertmanager_test.go index 9536065db46..e27caf0e5fe 100644 --- a/pkg/services/ngalert/api/api_alertmanager_test.go +++ b/pkg/services/ngalert/api/api_alertmanager_test.go @@ -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) }) diff --git a/pkg/services/ngalert/notifier/alertmanager.go b/pkg/services/ngalert/notifier/alertmanager.go index 5eb4bcfcaa8..53d86909ca8 100644 --- a/pkg/services/ngalert/notifier/alertmanager.go +++ b/pkg/services/ngalert/notifier/alertmanager.go @@ -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 { diff --git a/pkg/services/ngalert/notifier/alertmanager_test.go b/pkg/services/ngalert/notifier/alertmanager_test.go index d22f51069e4..6b9569c99c5 100644 --- a/pkg/services/ngalert/notifier/alertmanager_test.go +++ b/pkg/services/ngalert/notifier/alertmanager_test.go @@ -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, diff --git a/pkg/services/ngalert/notifier/alerts.go b/pkg/services/ngalert/notifier/alerts.go index c74eb8d31c7..77200dd797a 100644 --- a/pkg/services/ngalert/notifier/alerts.go +++ b/pkg/services/ngalert/notifier/alerts.go @@ -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) } diff --git a/pkg/services/ngalert/notifier/multiorg_alertmanager.go b/pkg/services/ngalert/notifier/multiorg_alertmanager.go index 1b7a5440fdb..d4a052dc35e 100644 --- a/pkg/services/ngalert/notifier/multiorg_alertmanager.go +++ b/pkg/services/ngalert/notifier/multiorg_alertmanager.go @@ -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() diff --git a/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go b/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go index 7710a0783d1..93b6ac1a715 100644 --- a/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go +++ b/pkg/services/ngalert/notifier/multiorg_alertmanager_test.go @@ -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. diff --git a/pkg/services/ngalert/notifier/receivers.go b/pkg/services/ngalert/notifier/receivers.go index 5cc12f15f86..ed10dcf4e3d 100644 --- a/pkg/services/ngalert/notifier/receivers.go +++ b/pkg/services/ngalert/notifier/receivers.go @@ -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. diff --git a/pkg/services/ngalert/notifier/silences.go b/pkg/services/ngalert/notifier/silences.go index bbc57bba855..de1acb4a533 100644 --- a/pkg/services/ngalert/notifier/silences.go +++ b/pkg/services/ngalert/notifier/silences.go @@ -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) } diff --git a/pkg/services/ngalert/notifier/status.go b/pkg/services/ngalert/notifier/status.go index 05678c279a1..bae821f6d40 100644 --- a/pkg/services/ngalert/notifier/status.go +++ b/pkg/services/ngalert/notifier/status.go @@ -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 { diff --git a/pkg/services/ngalert/notifier/templates.go b/pkg/services/ngalert/notifier/templates.go index ccd8ecdb7df..e17374f9007 100644 --- a/pkg/services/ngalert/notifier/templates.go +++ b/pkg/services/ngalert/notifier/templates.go @@ -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) }