Alertmanager: Add MergeState method (#108242)

* Alertmanager: Add MergeState method

* remove RemoteState in favor of ExternalState

* fix tests
pull/107710/head^2
Santiago 3 days ago committed by GitHub
parent bccc980b90
commit 8548530dc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      pkg/services/ngalert/notifier/alerts.go
  2. 9
      pkg/services/ngalert/notifier/multiorg_alertmanager.go
  3. 4
      pkg/services/ngalert/notifier/silences.go
  4. 9
      pkg/services/ngalert/notifier/state.go
  5. 10
      pkg/services/ngalert/remote/alertmanager.go
  6. 46
      pkg/services/ngalert/remote/forked_alertmanager_test.go

@ -13,7 +13,3 @@ func (am *alertmanager) GetAlerts(_ context.Context, active, silenced, inhibited
func (am *alertmanager) GetAlertGroups(_ context.Context, active, silenced, inhibited bool, filter []string, receivers string) (alertingNotify.AlertGroups, error) {
return am.Base.GetAlertGroups(active, silenced, inhibited, filter, receivers)
}
func (am *alertmanager) MergeNflog(b []byte) error {
return am.Base.MergeNflog(b)
}

@ -78,10 +78,15 @@ type Alertmanager interface {
Ready() bool
}
// ExternalState holds nflog entries and silences from an external Alertmanager.
type ExternalState struct {
Silences []byte
Nflog []byte
}
// StateMerger describes a type that is able to merge external state (nflog, silences) with its own.
type StateMerger interface {
MergeNflog([]byte) error
MergeSilences([]byte) error
MergeState(ExternalState) error
}
type MultiOrgAlertmanager struct {

@ -21,7 +21,3 @@ func (am *alertmanager) CreateSilence(_ context.Context, ps *alertingNotify.Post
func (am *alertmanager) DeleteSilence(_ context.Context, silenceID string) error {
return am.Base.DeleteSilence(silenceID)
}
func (am *alertmanager) MergeSilences(b []byte) error {
return am.Base.MergeSilences(b)
}

@ -0,0 +1,9 @@
package notifier
// MergeState incorporates external nflog entries and silences to the Alertmanager's state.
func (am *alertmanager) MergeState(state ExternalState) error {
if err := am.Base.MergeNflog(state.Nflog); err != nil {
return err
}
return am.Base.MergeSilences(state.Silences)
}

@ -389,15 +389,9 @@ func (am *Alertmanager) sendConfiguration(ctx context.Context, cfg *remoteClient
return nil
}
// RemoteState represents the state (silences, nflog) in use by a remote Alertmanager.
type RemoteState struct {
Silences []byte
Nflog []byte
}
// GetRemoteState gets the remote Alertmanager's internal state.
func (am *Alertmanager) GetRemoteState(ctx context.Context) (RemoteState, error) {
var rs RemoteState
func (am *Alertmanager) GetRemoteState(ctx context.Context) (notifier.ExternalState, error) {
var rs notifier.ExternalState
s, err := am.mimirClient.GetFullState(ctx)
if err != nil {

@ -29,7 +29,7 @@ func TestForkedAlertmanager_ModeRemoteSecondary(t *testing.T) {
t.Run("ApplyConfig", func(tt *testing.T) {
{
// If the remote Alertmanager is not ready, ApplyConfig should be called on both Alertmanagers.
internal, remote, forked := genTestAlertmanagersWithSyncInterval(tt, modeRemoteSecondary, 10*time.Minute)
internal, remote, forked := genTestAlertmanagers(tt, modeRemoteSecondary, withSyncInterval(10*time.Minute))
internal.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Once()
readyCall := remote.EXPECT().Ready().Return(false).Once()
remote.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Once().NotBefore(readyCall)
@ -46,7 +46,7 @@ func TestForkedAlertmanager_ModeRemoteSecondary(t *testing.T) {
// If the remote Alertmanager is ready and the sync interval has elapsed,
// the forked Alertmanager should sync the configuration on the remote Alertmanager
// and call ApplyConfig only on the internal Alertmanager.
internal, remote, forked := genTestAlertmanagersWithSyncInterval(tt, modeRemoteSecondary, 0)
internal, remote, forked := genTestAlertmanagers(tt, modeRemoteSecondary)
internal.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Twice()
remote.EXPECT().Ready().Return(true).Twice()
remote.EXPECT().CompareAndSendConfiguration(ctx, mock.Anything).Return(nil).Twice()
@ -58,7 +58,7 @@ func TestForkedAlertmanager_ModeRemoteSecondary(t *testing.T) {
// An error in the remote Alertmanager should not be returned,
// but it should result in the forked Alertmanager trying to sync
// the configuration in the next call to ApplyConfig, regardless of the sync interval.
internal, remote, forked := genTestAlertmanagersWithSyncInterval(tt, modeRemoteSecondary, 10*time.Minute)
internal, remote, forked := genTestAlertmanagers(tt, modeRemoteSecondary, withSyncInterval(10*time.Minute))
internal.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Twice()
remote.EXPECT().Ready().Return(false).Twice()
remote.EXPECT().ApplyConfig(ctx, mock.Anything).Return(expErr).Twice()
@ -66,7 +66,7 @@ func TestForkedAlertmanager_ModeRemoteSecondary(t *testing.T) {
require.NoError(tt, forked.ApplyConfig(ctx, &models.AlertConfiguration{}))
// Let's try the same thing but starting from a ready Alertmanager.
internal, remote, forked = genTestAlertmanagersWithSyncInterval(tt, modeRemoteSecondary, 10*time.Minute)
internal, remote, forked = genTestAlertmanagers(tt, modeRemoteSecondary, withSyncInterval(10*time.Minute))
internal.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Twice()
remote.EXPECT().Ready().Return(true).Twice()
remote.EXPECT().CompareAndSendConfiguration(ctx, mock.Anything).Return(expErr).Twice()
@ -696,14 +696,30 @@ func TestForkedAlertmanager_ModeRemotePrimary(t *testing.T) {
require.False(tt, forked.Ready())
})
}
func genTestAlertmanagers(t *testing.T, mode int) (*alertmanager_mock.AlertmanagerMock, *remote_alertmanager_mock.RemoteAlertmanagerMock, notifier.Alertmanager) {
t.Helper()
return genTestAlertmanagersWithSyncInterval(t, mode, 0)
type internalAlertmanagerMock struct {
*alertmanager_mock.AlertmanagerMock
mergeStateCalled bool
}
func (m *internalAlertmanagerMock) MergeState(notifier.ExternalState) error {
m.mergeStateCalled = true
return nil
}
func withSyncInterval(syncInterval time.Duration) func(RemoteSecondaryConfig) RemoteSecondaryConfig {
return func(rsc RemoteSecondaryConfig) RemoteSecondaryConfig {
rsc.SyncInterval = syncInterval
return rsc
}
}
func genTestAlertmanagersWithSyncInterval(t *testing.T, mode int, syncInterval time.Duration) (*alertmanager_mock.AlertmanagerMock, *remote_alertmanager_mock.RemoteAlertmanagerMock, notifier.Alertmanager) {
func genTestAlertmanagers(t *testing.T, mode int, options ...func(RemoteSecondaryConfig) RemoteSecondaryConfig) (*internalAlertmanagerMock, *remote_alertmanager_mock.RemoteAlertmanagerMock, notifier.Alertmanager) {
t.Helper()
internal := alertmanager_mock.NewAlertmanagerMock(t)
internal := &internalAlertmanagerMock{
alertmanager_mock.NewAlertmanagerMock(t),
false,
}
remote := remote_alertmanager_mock.NewRemoteAlertmanagerMock(t)
if mode == modeRemoteSecondary {
@ -711,11 +727,15 @@ func genTestAlertmanagersWithSyncInterval(t *testing.T, mode int, syncInterval t
1: {},
}
cfg := RemoteSecondaryConfig{
Logger: log.NewNopLogger(),
SyncInterval: syncInterval,
OrgID: 1,
Store: notifier.NewFakeConfigStore(t, configs),
Logger: log.NewNopLogger(),
OrgID: 1,
Store: notifier.NewFakeConfigStore(t, configs),
}
for _, opt := range options {
cfg = opt(cfg)
}
forked, err := NewRemoteSecondaryForkedAlertmanager(cfg, internal, remote)
require.NoError(t, err)
return internal, remote, forked

Loading…
Cancel
Save