diff --git a/pkg/services/ngalert/remote/forked_alertmanager_test.go b/pkg/services/ngalert/remote/forked_alertmanager_test.go index 18389205692..b9ae3d28b66 100644 --- a/pkg/services/ngalert/remote/forked_alertmanager_test.go +++ b/pkg/services/ngalert/remote/forked_alertmanager_test.go @@ -89,7 +89,7 @@ func TestForkedAlertmanager_ModeRemoteSecondary(t *testing.T) { internal.EXPECT().ApplyConfig(ctx, mock.Anything).Return(expErr).Once() readyCall := remote.EXPECT().Ready().Return(false).Once() remote.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Once().NotBefore(readyCall) - require.Error(tt, forked.ApplyConfig(ctx, &models.AlertConfiguration{}), expErr) + require.ErrorIs(tt, forked.ApplyConfig(ctx, &models.AlertConfiguration{}), expErr) } }) @@ -375,6 +375,28 @@ func TestForkedAlertmanager_ModeRemotePrimary(t *testing.T) { ctx := context.Background() expErr := errors.New("test error") + t.Run("ApplyConfig", func(tt *testing.T) { + { + // If the remote Alertmanager is not ready, ApplyConfig should be called on both Alertmanagers, + // first on the remote, then on the internal. + internal, remote, forked := genTestAlertmanagers(tt, modeRemotePrimary) + remoteCall := remote.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Once() + internal.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Once().NotBefore(remoteCall) + require.NoError(tt, forked.ApplyConfig(ctx, &models.AlertConfiguration{})) + + // An error in the remote Alertmanager should be returned. + _, remote, forked = genTestAlertmanagers(tt, modeRemotePrimary) + remote.EXPECT().ApplyConfig(ctx, mock.Anything).Return(expErr).Once() + require.ErrorIs(tt, forked.ApplyConfig(ctx, &models.AlertConfiguration{}), expErr) + + // An error in the internal Alertmanager should not be returned. + internal, remote, forked = genTestAlertmanagers(tt, modeRemotePrimary) + remote.EXPECT().ApplyConfig(ctx, mock.Anything).Return(nil).Once() + internal.EXPECT().ApplyConfig(ctx, mock.Anything).Return(expErr).Once() + require.NoError(tt, forked.ApplyConfig(ctx, &models.AlertConfiguration{})) + } + }) + t.Run("GetStatus", func(tt *testing.T) { // We care about the status of the remote Alertmanager. _, remote, forked := genTestAlertmanagers(tt, modeRemotePrimary) @@ -626,7 +648,7 @@ func genTestAlertmanagersWithSyncInterval(t *testing.T, mode int, syncInterval t require.NoError(t, err) return internal, remote, forked } - return internal, remote, NewRemotePrimaryForkedAlertmanager(internal, remote) + return internal, remote, NewRemotePrimaryForkedAlertmanager(log.NewNopLogger(), internal, remote) } // errConfigStore returns an error when a method is called. diff --git a/pkg/services/ngalert/remote/remote_primary_forked_alertmanager.go b/pkg/services/ngalert/remote/remote_primary_forked_alertmanager.go index 0f010c459a0..a10a2c41397 100644 --- a/pkg/services/ngalert/remote/remote_primary_forked_alertmanager.go +++ b/pkg/services/ngalert/remote/remote_primary_forked_alertmanager.go @@ -2,32 +2,47 @@ package remote import ( "context" + "fmt" + "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/models" "github.com/grafana/grafana/pkg/services/ngalert/notifier" ) type RemotePrimaryForkedAlertmanager struct { + log log.Logger + internal notifier.Alertmanager - remote notifier.Alertmanager + remote remoteAlertmanager } -func NewRemotePrimaryForkedAlertmanager(internal, remote notifier.Alertmanager) *RemotePrimaryForkedAlertmanager { +func NewRemotePrimaryForkedAlertmanager(log log.Logger, internal notifier.Alertmanager, remote remoteAlertmanager) *RemotePrimaryForkedAlertmanager { return &RemotePrimaryForkedAlertmanager{ + log: log, internal: internal, remote: remote, } } +// ApplyConfig will send the configuration to the remote Alertmanager on startup. func (fam *RemotePrimaryForkedAlertmanager) ApplyConfig(ctx context.Context, config *models.AlertConfiguration) error { + if err := fam.remote.ApplyConfig(ctx, config); err != nil { + return fmt.Errorf("failed to call ApplyConfig on the remote Alertmanager: %w", err) + } + + if err := fam.internal.ApplyConfig(ctx, config); err != nil { + fam.log.Error("Error applying config to the internal Alertmanager", "err", err) + } return nil } +// TODO: save the new configuration hash in memory. func (fam *RemotePrimaryForkedAlertmanager) SaveAndApplyConfig(ctx context.Context, config *apimodels.PostableUserConfig) error { return nil } +// TODO: save the new configuration hash in memory. func (fam *RemotePrimaryForkedAlertmanager) SaveAndApplyDefaultConfig(ctx context.Context) error { return nil }