Alerting: API to delete extra Alertmanager configurations (#106892)

This commit is contained in:
Alexander Akhmetov
2025-06-18 12:37:56 +02:00
committed by GitHub
parent 02bdac447d
commit def5d889d0
4 changed files with 92 additions and 8 deletions

View File

@ -127,6 +127,7 @@ type ConvertPrometheusSrv struct {
}
type Alertmanager interface {
DeleteExtraConfiguration(ctx context.Context, org int64, identifier string) error
GetAlertmanagerConfiguration(ctx context.Context, org int64, withAutogen bool) (apimodels.GettableUserConfig, error)
}
@ -579,7 +580,26 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetAlertmanagerConfig(c *
}
func (srv *ConvertPrometheusSrv) RouteConvertPrometheusDeleteAlertmanagerConfig(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusNotImplemented, "Not Implemented", nil)
if !srv.featureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingImportAlertmanagerAPI) {
return response.Error(http.StatusNotImplemented, "Not Implemented", nil)
}
logger := srv.logger.FromContext(c.Req.Context())
identifier, err := parseConfigIdentifierHeader(c)
if err != nil {
logger.Error("Failed to parse config identifier header", "error", err)
return errorToResponse(err)
}
err = srv.am.DeleteExtraConfiguration(c.Req.Context(), c.GetOrgID(), identifier)
if err != nil {
logger.Error("Failed to delete alertmanager configuration", "error", err, "identifier", identifier)
return errorToResponse(fmt.Errorf("failed to delete alertmanager configuration: %w", err))
}
logger.Info("Successfully deleted extra alertmanager configuration", "identifier", identifier)
return successfulResponse()
}
// parseBooleanHeader parses a boolean header value, returning an error if the header

View File

@ -1510,7 +1510,7 @@ func (m *mockAlertmanager) GetAlertmanagerConfiguration(ctx context.Context, org
return args.Get(0).(apimodels.GettableUserConfig), args.Error(1)
}
func (m *mockAlertmanager) DeleteAndApplyExtraConfiguration(ctx context.Context, org int64, identifier string) error {
func (m *mockAlertmanager) DeleteExtraConfiguration(ctx context.Context, org int64, identifier string) error {
args := m.Called(ctx, org, identifier)
return args.Error(0)
}
@ -1799,3 +1799,67 @@ func TestFormatMergeMatchers(t *testing.T) {
require.Equal(t, "env=prod,team=backend", result)
})
}
func TestRouteConvertPrometheusDeleteAlertmanagerConfig(t *testing.T) {
const identifier = "test-config"
const orgID = int64(1)
mockAM := &mockAlertmanager{}
ft := featuremgmt.WithFeatures(featuremgmt.FlagAlertingImportAlertmanagerAPI)
srv, _, _ := createConvertPrometheusSrv(t, withAlertmanager(mockAM), withFeatureToggles(ft))
t.Run("should parse identifier header and call DeleteExtraConfiguration", func(t *testing.T) {
mockAM.On("DeleteExtraConfiguration", mock.Anything, orgID, identifier).Return(nil).Once()
rc := createRequestCtx()
rc.Req.Header.Set(configIdentifierHeader, identifier)
response := srv.RouteConvertPrometheusDeleteAlertmanagerConfig(rc)
require.Equal(t, http.StatusAccepted, response.Status())
mockAM.AssertExpectations(t)
})
t.Run("should return error when identifier header is missing", func(t *testing.T) {
rc := createRequestCtx()
response := srv.RouteConvertPrometheusDeleteAlertmanagerConfig(rc)
require.Equal(t, http.StatusBadRequest, response.Status())
require.Contains(t, string(response.Body()), "identifier cannot be empty")
})
t.Run("should return error when DeleteExtraConfiguration fails", func(t *testing.T) {
mockAM.On("DeleteExtraConfiguration", mock.Anything, orgID, identifier).Return(errors.New("delete error")).Once()
rc := createRequestCtx()
rc.Req.Header.Set(configIdentifierHeader, identifier)
response := srv.RouteConvertPrometheusDeleteAlertmanagerConfig(rc)
require.Equal(t, http.StatusInternalServerError, response.Status())
mockAM.AssertExpectations(t)
})
t.Run("should return not implemented when feature toggle is disabled", func(t *testing.T) {
ft := featuremgmt.WithFeatures()
srv, _, _ := createConvertPrometheusSrv(t, withAlertmanager(mockAM), withFeatureToggles(ft))
rc := createRequestCtx()
rc.Req.Header.Set(configIdentifierHeader, identifier)
response := srv.RouteConvertPrometheusDeleteAlertmanagerConfig(rc)
require.Equal(t, http.StatusNotImplemented, response.Status())
})
t.Run("should return error for empty identifier header", func(t *testing.T) {
rc := createRequestCtx()
rc.Req.Header.Set(configIdentifierHeader, "")
response := srv.RouteConvertPrometheusDeleteAlertmanagerConfig(rc)
require.Equal(t, http.StatusBadRequest, response.Status())
require.Contains(t, string(response.Body()), "identifier cannot be empty")
})
}

View File

@ -412,8 +412,8 @@ func (moa *MultiOrgAlertmanager) SaveAndApplyExtraConfiguration(ctx context.Cont
return nil
}
// DeleteAndApplyExtraConfiguration deletes an ExtraConfiguration by its identifier while preserving the main AlertmanagerConfig.
func (moa *MultiOrgAlertmanager) DeleteAndApplyExtraConfiguration(ctx context.Context, org int64, identifier string) error {
// DeleteExtraConfiguration deletes an ExtraConfiguration by its identifier while preserving the main AlertmanagerConfig.
func (moa *MultiOrgAlertmanager) DeleteExtraConfiguration(ctx context.Context, org int64, identifier string) error {
modifyFunc := func(configs []definitions.ExtraConfiguration) ([]definitions.ExtraConfiguration, error) {
filtered := make([]definitions.ExtraConfiguration, 0, len(configs))
for _, ec := range configs {

View File

@ -150,7 +150,7 @@ receivers:
})
}
func TestMultiOrgAlertmanager_DeleteAndApplyExtraConfiguration(t *testing.T) {
func TestMultiOrgAlertmanager_DeleteExtraConfiguration(t *testing.T) {
orgID := int64(1)
t.Run("successfully delete existing extra configuration", func(t *testing.T) {
@ -176,7 +176,7 @@ receivers:
require.NoError(t, err)
require.Len(t, gettableConfig.ExtraConfigs, 1)
err = mam.DeleteAndApplyExtraConfiguration(ctx, orgID, identifier)
err = mam.DeleteExtraConfiguration(ctx, orgID, identifier)
require.NoError(t, err)
gettableConfig, err = mam.GetAlertmanagerConfiguration(ctx, orgID, false)
@ -189,7 +189,7 @@ receivers:
ctx := context.Background()
require.NoError(t, mam.LoadAndSyncAlertmanagersForOrgs(ctx))
err := mam.DeleteAndApplyExtraConfiguration(ctx, orgID, "non-existent")
err := mam.DeleteExtraConfiguration(ctx, orgID, "non-existent")
require.NoError(t, err)
})
@ -197,7 +197,7 @@ receivers:
mam := setupMam(t, nil)
ctx := context.Background()
err := mam.DeleteAndApplyExtraConfiguration(ctx, 999, "test-config")
err := mam.DeleteExtraConfiguration(ctx, 999, "test-config")
require.Error(t, err)
require.ErrorContains(t, err, "failed to get current configuration")
})