Remote Alertmanager: Optionally merge remote state before starting the internal Alertmanager (#107710)

* Remote Alertmanager: Use the same struct for Grafana stat and Mimir full state

* Alertmanager: Add methods to merge nflog and silences

* update grafana/alerting version

* make update-workspace

* update mocks

* remove unnecesary methods from the remote Alertmanager implementation, create separate StateMerger interface

* (WIP) Remote Alertmanager: Optionally merge remote state before starting the internal Alertmanager

* cleanup ngalert.go

* restore defaults.ini

* move state parsing logic to 'remote' package, clean up ngalert.go

* remove GetBase, implement MegeNflog and MergeSilences

* delete fmt.Println

* FetchRemoteState -> GetRemoteState

* UserGrafanaState -> UserState

* remove duplicate clusterpb import

* reorder MimirClient interface

* use general getState() method for Grafana state and Mimir full state

* remove unnecessary state merging methods from the Alertmanager interface

* remove pullState field

* reduce diff

* add info log after merging

* merge silences and nflog entries in the same method

* merge the remote state in the forked AM

* reduce diff

* update remote AM mock

* tests

* make error more specific

* typo
This commit is contained in:
Santiago
2025-07-25 15:06:59 +00:00
committed by GitHub
parent 5f6fc38430
commit dcb965b7dc
9 changed files with 467 additions and 282 deletions
@@ -13,6 +13,8 @@ import (
models "github.com/grafana/grafana/pkg/services/ngalert/models"
notifier "github.com/grafana/grafana/pkg/services/ngalert/notifier"
notify "github.com/grafana/alerting/notify"
v2models "github.com/prometheus/alertmanager/api/v2/models"
@@ -413,6 +415,62 @@ func (_c *RemoteAlertmanagerMock_GetReceivers_Call) RunAndReturn(run func(contex
return _c
}
// GetRemoteState provides a mock function with given fields: _a0
func (_m *RemoteAlertmanagerMock) GetRemoteState(_a0 context.Context) (notifier.ExternalState, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for GetRemoteState")
}
var r0 notifier.ExternalState
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) (notifier.ExternalState, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(context.Context) notifier.ExternalState); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(notifier.ExternalState)
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RemoteAlertmanagerMock_GetRemoteState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRemoteState'
type RemoteAlertmanagerMock_GetRemoteState_Call struct {
*mock.Call
}
// GetRemoteState is a helper method to define mock.On call
// - _a0 context.Context
func (_e *RemoteAlertmanagerMock_Expecter) GetRemoteState(_a0 interface{}) *RemoteAlertmanagerMock_GetRemoteState_Call {
return &RemoteAlertmanagerMock_GetRemoteState_Call{Call: _e.mock.On("GetRemoteState", _a0)}
}
func (_c *RemoteAlertmanagerMock_GetRemoteState_Call) Run(run func(_a0 context.Context)) *RemoteAlertmanagerMock_GetRemoteState_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *RemoteAlertmanagerMock_GetRemoteState_Call) Return(_a0 notifier.ExternalState, _a1 error) *RemoteAlertmanagerMock_GetRemoteState_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *RemoteAlertmanagerMock_GetRemoteState_Call) RunAndReturn(run func(context.Context) (notifier.ExternalState, error)) *RemoteAlertmanagerMock_GetRemoteState_Call {
_c.Call.Return(run)
return _c
}
// GetSilence provides a mock function with given fields: _a0, _a1
func (_m *RemoteAlertmanagerMock) GetSilence(_a0 context.Context, _a1 string) (v2models.GettableSilence, error) {
ret := _m.Called(_a0, _a1)