Compare commits

...

1 Commits

Author SHA1 Message Date
Naman Verma
2068482f66 feat: add api to repair malformed channels created via v1 (#12910)
Some checks are pending
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
cacheci / tests (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
<!--A few plain bullets saying what changed and why, for a reviewer
skimming it - not a wall of text, not a restatement of the diff, not
generated boilerplate.-->
#### Description

No need to write a db migration, notification channels can be repaired
if user asks to repair.

<!--Reference issues using `Closes #issue-number` to enable automatic
closure on merge. -->
#### Issues closed by this PR

Part of https://github.com/SigNoz/pulse-pod/issues/342
2026-09-18 16:05:41 +00:00
11 changed files with 1114 additions and 90 deletions

View File

@@ -171,6 +171,14 @@ components:
- kind
- spec
type: object
AlertmanagertypesChannelDefect:
enum:
- none
- missing_type
- multiple_notifiers
- unsupported_notifier
- unrepresentable
type: string
AlertmanagertypesChannelEmailConfig:
properties:
headers:
@@ -384,6 +392,40 @@ components:
required:
- routingKey
type: object
AlertmanagertypesChannelRepair:
properties:
action:
$ref: '#/components/schemas/AlertmanagertypesChannelRepairAction'
applied:
type: boolean
blockers:
items:
type: string
type: array
channels:
items:
$ref: '#/components/schemas/AlertmanagertypesListedNotificationChannel'
nullable: true
type: array
defect:
$ref: '#/components/schemas/AlertmanagertypesChannelDefect'
detail:
type: string
id:
type: string
required:
- id
- defect
- action
- applied
type: object
AlertmanagertypesChannelRepairAction:
enum:
- none
- retype
- split
- delete
type: string
AlertmanagertypesChannelSlackAction:
properties:
confirm:
@@ -1033,6 +1075,11 @@ components:
- duration
- repeatType
type: object
AlertmanagertypesRepairChannelParams:
properties:
apply:
type: boolean
type: object
AlertmanagertypesRepeatOn:
enum:
- sunday
@@ -20306,6 +20353,85 @@ paths:
summary: Update notification channel
tags:
- channels
/api/v2/notification_channels/{id}/repair:
post:
deprecated: false
description: 'This endpoint diagnoses a stored channel that the v2 API cannot
read and applies the fitting action: a channel carrying several notifier configurations
is split into one channel per configuration, keeping this ID for the first;
a channel whose notifier kind v2 does not model is deleted; a channel with
an empty stored type has it rewritten from its data. A delete is refused while
a routing policy still names the channel. Nothing is written unless apply=true;
by default the response only shows what would happen.'
operationId: RepairNotificationChannel
parameters:
- in: query
name: apply
schema:
type: boolean
- in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/AlertmanagertypesRepairChannelParams'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/AlertmanagertypesChannelRepair'
status:
type: string
required:
- status
- data
type: object
description: OK
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- notification-channel:update
- tokenizer:
- notification-channel:update
summary: Repair notification channel
tags:
- channels
/api/v2/notification_channels/test:
post:
deprecated: false

View File

@@ -21,6 +21,7 @@ import type {
AlertmanagertypesPostableChannelDTO,
AlertmanagertypesPostableNotificationChannelDTO,
AlertmanagertypesReceiverDTO,
AlertmanagertypesRepairChannelParamsDTO,
AlertmanagertypesTestableNotificationChannelDTO,
AlertmanagertypesUpdatableNotificationChannelDTO,
CreateChannel201,
@@ -35,6 +36,9 @@ import type {
ListNotificationChannels200,
ListNotificationChannelsParams,
RenderErrorResponseDTO,
RepairNotificationChannel200,
RepairNotificationChannelParams,
RepairNotificationChannelPathParameters,
UpdateChannelByIDPathParameters,
UpdateNotificationChannel200,
UpdateNotificationChannelPathParameters,
@@ -1144,6 +1148,113 @@ export const useUpdateNotificationChannel = <
> => {
return useMutation(getUpdateNotificationChannelMutationOptions(options));
};
/**
* This endpoint diagnoses a stored channel that the v2 API cannot read and applies the fitting action: a channel carrying several notifier configurations is split into one channel per configuration, keeping this ID for the first; a channel whose notifier kind v2 does not model is deleted; a channel with an empty stored type has it rewritten from its data. A delete is refused while a routing policy still names the channel. Nothing is written unless apply=true; by default the response only shows what would happen.
* @summary Repair notification channel
*/
export const repairNotificationChannel = (
{ id }: RepairNotificationChannelPathParameters,
alertmanagertypesRepairChannelParamsDTO?: BodyType<AlertmanagertypesRepairChannelParamsDTO>,
params?: RepairNotificationChannelParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<RepairNotificationChannel200>({
url: `/api/v2/notification_channels/${id}/repair`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: alertmanagertypesRepairChannelParamsDTO,
params,
signal,
});
};
export const getRepairNotificationChannelMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof repairNotificationChannel>>,
TError,
{
pathParams: RepairNotificationChannelPathParameters;
data?: BodyType<AlertmanagertypesRepairChannelParamsDTO>;
params?: RepairNotificationChannelParams;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof repairNotificationChannel>>,
TError,
{
pathParams: RepairNotificationChannelPathParameters;
data?: BodyType<AlertmanagertypesRepairChannelParamsDTO>;
params?: RepairNotificationChannelParams;
},
TContext
> => {
const mutationKey = ['repairNotificationChannel'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof repairNotificationChannel>>,
{
pathParams: RepairNotificationChannelPathParameters;
data?: BodyType<AlertmanagertypesRepairChannelParamsDTO>;
params?: RepairNotificationChannelParams;
}
> = (props) => {
const { pathParams, data, params } = props ?? {};
return repairNotificationChannel(pathParams, data, params);
};
return { mutationFn, ...mutationOptions };
};
export type RepairNotificationChannelMutationResult = NonNullable<
Awaited<ReturnType<typeof repairNotificationChannel>>
>;
export type RepairNotificationChannelMutationBody =
| BodyType<AlertmanagertypesRepairChannelParamsDTO>
| undefined;
export type RepairNotificationChannelMutationError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Repair notification channel
*/
export const useRepairNotificationChannel = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof repairNotificationChannel>>,
TError,
{
pathParams: RepairNotificationChannelPathParameters;
data?: BodyType<AlertmanagertypesRepairChannelParamsDTO>;
params?: RepairNotificationChannelParams;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof repairNotificationChannel>>,
TError,
{
pathParams: RepairNotificationChannelPathParameters;
data?: BodyType<AlertmanagertypesRepairChannelParamsDTO>;
params?: RepairNotificationChannelParams;
},
TContext
> => {
return useMutation(getRepairNotificationChannelMutationOptions(options));
};
/**
* This endpoint sends a test notification for the configuration in the request body. The channel need not exist and nothing is persisted, so the body carries a configuration only.
* @summary Test notification channel

View File

@@ -596,6 +596,13 @@ export type AlertmanagertypesChannelConfigDTO =
| AlertmanagertypesChannelConfigVariantGithubComSigNozSignozPkgTypesAlertmanagertypesChannelJSMOpsConfigDTO
| AlertmanagertypesChannelConfigVariantGithubComSigNozSignozPkgTypesAlertmanagertypesChannelIncidentIOConfigDTO;
export enum AlertmanagertypesChannelDefectDTO {
none = 'none',
missing_type = 'missing_type',
multiple_notifiers = 'multiple_notifiers',
unsupported_notifier = 'unsupported_notifier',
unrepresentable = 'unrepresentable',
}
export enum AlertmanagertypesChannelKindDTO {
slack = 'slack',
email = 'email',
@@ -617,6 +624,63 @@ export enum AlertmanagertypesChannelListSortDTO {
created_at = 'created_at',
name = 'name',
}
export enum AlertmanagertypesChannelRepairActionDTO {
none = 'none',
retype = 'retype',
split = 'split',
delete = 'delete',
}
export interface AlertmanagertypesListedNotificationChannelDTO {
/**
* @type string
* @format date-time
*/
createdAt: string;
/**
* @type string
*/
displayName: string;
/**
* @type string
*/
id: string;
kind: AlertmanagertypesChannelKindDTO;
/**
* @type string
*/
name: string;
/**
* @type string
* @format date-time
*/
updatedAt: string;
}
export interface AlertmanagertypesChannelRepairDTO {
action: AlertmanagertypesChannelRepairActionDTO;
/**
* @type boolean
*/
applied: boolean;
/**
* @type array
*/
blockers?: string[];
/**
* @type array,null
*/
channels?: AlertmanagertypesListedNotificationChannelDTO[] | null;
defect: AlertmanagertypesChannelDefectDTO;
/**
* @type string
*/
detail?: string;
/**
* @type string
*/
id: string;
}
export interface ModelLabelSetDTO {
[key: string]: string;
}
@@ -1110,32 +1174,6 @@ export interface AlertmanagertypesJiraReceiverConfigDTO {
wont_fix_resolution?: string;
}
export interface AlertmanagertypesListedNotificationChannelDTO {
/**
* @type string
* @format date-time
*/
createdAt: string;
/**
* @type string
*/
displayName: string;
/**
* @type string
*/
id: string;
kind: AlertmanagertypesChannelKindDTO;
/**
* @type string
*/
name: string;
/**
* @type string
* @format date-time
*/
updatedAt: string;
}
export interface AlertmanagertypesListableNotificationChannelDTO {
/**
* @type array
@@ -2539,6 +2577,13 @@ export interface AlertmanagertypesReceiverDTO {
wechat_configs?: ConfigWechatConfigDTO[];
}
export interface AlertmanagertypesRepairChannelParamsDTO {
/**
* @type boolean
*/
apply?: boolean;
}
export interface AlertmanagertypesTestableNotificationChannelDTO {
config: AlertmanagertypesChannelConfigDTO;
}
@@ -13484,6 +13529,25 @@ export type UpdateNotificationChannel200 = {
status: string;
};
export type RepairNotificationChannelPathParameters = {
id: string;
};
export type RepairNotificationChannelParams = {
/**
* @type boolean
* @description undefined
*/
apply?: boolean;
};
export type RepairNotificationChannel200 = {
data: AlertmanagertypesChannelRepairDTO;
/**
* @type string
*/
status: string;
};
export type GetMyOrganization200 = {
data: TypesOrganizationDTO;
/**

View File

@@ -61,6 +61,10 @@ type Alertmanager interface {
// DeleteChannelByID deletes a channel for the organization.
DeleteChannelByID(context.Context, string, valuer.UUID) error
// RepairNotificationChannel diagnoses a stored channel v2 cannot read and
// reports the fitting action, applying it only when apply is set.
RepairNotificationChannel(context.Context, string, valuer.UUID, bool) (*alertmanagertypes.ChannelRepair, error)
// Config returns the alertmanagerserver configuration.
Config() alertmanagerserver.Config

View File

@@ -21,10 +21,19 @@ func NewMockAlertmanager(t interface {
mock.TestingT
Cleanup(func())
}) *MockAlertmanager {
if helper, ok := t.(interface{ Helper() }); ok {
helper.Helper()
}
mock := &MockAlertmanager{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
t.Cleanup(func() {
if helper, ok := t.(interface{ Helper() }); ok {
helper.Helper()
}
mock.AssertExpectations(t)
})
return mock
}
@@ -78,7 +87,7 @@ type MockAlertmanager_Collect_Call struct {
// Collect is a helper method to define mock.On call
// - context1 context.Context
// - uUID valuer.UUID
func (_e *MockAlertmanager_Expecter) Collect(context1 interface{}, uUID interface{}) *MockAlertmanager_Collect_Call {
func (_e *MockAlertmanager_Expecter) Collect(context1 any, uUID any) *MockAlertmanager_Collect_Call {
return &MockAlertmanager_Collect_Call{Call: _e.mock.On("Collect", context1, uUID)}
}
@@ -100,8 +109,8 @@ func (_c *MockAlertmanager_Collect_Call) Run(run func(context1 context.Context,
return _c
}
func (_c *MockAlertmanager_Collect_Call) Return(stringToV map[string]any, err error) *MockAlertmanager_Collect_Call {
_c.Call.Return(stringToV, err)
func (_c *MockAlertmanager_Collect_Call) Return(stringToAnyMoqParam map[string]any, err error) *MockAlertmanager_Collect_Call {
_c.Call.Return(stringToAnyMoqParam, err)
return _c
}
@@ -191,7 +200,7 @@ type MockAlertmanager_CreateChannel_Call struct {
// - context1 context.Context
// - s string
// - receiver *alertmanagertypes.Receiver
func (_e *MockAlertmanager_Expecter) CreateChannel(context1 interface{}, s interface{}, receiver interface{}) *MockAlertmanager_CreateChannel_Call {
func (_e *MockAlertmanager_Expecter) CreateChannel(context1 any, s any, receiver any) *MockAlertmanager_CreateChannel_Call {
return &MockAlertmanager_CreateChannel_Call{Call: _e.mock.On("CreateChannel", context1, s, receiver)}
}
@@ -254,7 +263,7 @@ type MockAlertmanager_CreateInhibitRules_Call struct {
// - ctx context.Context
// - orgID valuer.UUID
// - rules []config.InhibitRule
func (_e *MockAlertmanager_Expecter) CreateInhibitRules(ctx interface{}, orgID interface{}, rules interface{}) *MockAlertmanager_CreateInhibitRules_Call {
func (_e *MockAlertmanager_Expecter) CreateInhibitRules(ctx any, orgID any, rules any) *MockAlertmanager_CreateInhibitRules_Call {
return &MockAlertmanager_CreateInhibitRules_Call{Call: _e.mock.On("CreateInhibitRules", ctx, orgID, rules)}
}
@@ -328,7 +337,7 @@ type MockAlertmanager_CreateNotificationChannel_Call struct {
// - context1 context.Context
// - s string
// - postableNotificationChannel alertmanagertypes.PostableNotificationChannel
func (_e *MockAlertmanager_Expecter) CreateNotificationChannel(context1 interface{}, s interface{}, postableNotificationChannel interface{}) *MockAlertmanager_CreateNotificationChannel_Call {
func (_e *MockAlertmanager_Expecter) CreateNotificationChannel(context1 any, s any, postableNotificationChannel any) *MockAlertmanager_CreateNotificationChannel_Call {
return &MockAlertmanager_CreateNotificationChannel_Call{Call: _e.mock.On("CreateNotificationChannel", context1, s, postableNotificationChannel)}
}
@@ -401,7 +410,7 @@ type MockAlertmanager_CreateRoutePolicies_Call struct {
// CreateRoutePolicies is a helper method to define mock.On call
// - ctx context.Context
// - routeRequests []*alertmanagertypes.PostableRoutePolicy
func (_e *MockAlertmanager_Expecter) CreateRoutePolicies(ctx interface{}, routeRequests interface{}) *MockAlertmanager_CreateRoutePolicies_Call {
func (_e *MockAlertmanager_Expecter) CreateRoutePolicies(ctx any, routeRequests any) *MockAlertmanager_CreateRoutePolicies_Call {
return &MockAlertmanager_CreateRoutePolicies_Call{Call: _e.mock.On("CreateRoutePolicies", ctx, routeRequests)}
}
@@ -469,7 +478,7 @@ type MockAlertmanager_CreateRoutePolicy_Call struct {
// CreateRoutePolicy is a helper method to define mock.On call
// - ctx context.Context
// - route *alertmanagertypes.PostableRoutePolicy
func (_e *MockAlertmanager_Expecter) CreateRoutePolicy(ctx interface{}, route interface{}) *MockAlertmanager_CreateRoutePolicy_Call {
func (_e *MockAlertmanager_Expecter) CreateRoutePolicy(ctx any, route any) *MockAlertmanager_CreateRoutePolicy_Call {
return &MockAlertmanager_CreateRoutePolicy_Call{Call: _e.mock.On("CreateRoutePolicy", ctx, route)}
}
@@ -527,7 +536,7 @@ type MockAlertmanager_DeleteAllInhibitRulesByRuleId_Call struct {
// - ctx context.Context
// - orgID valuer.UUID
// - ruleId string
func (_e *MockAlertmanager_Expecter) DeleteAllInhibitRulesByRuleId(ctx interface{}, orgID interface{}, ruleId interface{}) *MockAlertmanager_DeleteAllInhibitRulesByRuleId_Call {
func (_e *MockAlertmanager_Expecter) DeleteAllInhibitRulesByRuleId(ctx any, orgID any, ruleId any) *MockAlertmanager_DeleteAllInhibitRulesByRuleId_Call {
return &MockAlertmanager_DeleteAllInhibitRulesByRuleId_Call{Call: _e.mock.On("DeleteAllInhibitRulesByRuleId", ctx, orgID, ruleId)}
}
@@ -589,7 +598,7 @@ type MockAlertmanager_DeleteAllRoutePoliciesByRuleId_Call struct {
// DeleteAllRoutePoliciesByRuleId is a helper method to define mock.On call
// - ctx context.Context
// - ruleId string
func (_e *MockAlertmanager_Expecter) DeleteAllRoutePoliciesByRuleId(ctx interface{}, ruleId interface{}) *MockAlertmanager_DeleteAllRoutePoliciesByRuleId_Call {
func (_e *MockAlertmanager_Expecter) DeleteAllRoutePoliciesByRuleId(ctx any, ruleId any) *MockAlertmanager_DeleteAllRoutePoliciesByRuleId_Call {
return &MockAlertmanager_DeleteAllRoutePoliciesByRuleId_Call{Call: _e.mock.On("DeleteAllRoutePoliciesByRuleId", ctx, ruleId)}
}
@@ -647,7 +656,7 @@ type MockAlertmanager_DeleteChannelByID_Call struct {
// - context1 context.Context
// - s string
// - uUID valuer.UUID
func (_e *MockAlertmanager_Expecter) DeleteChannelByID(context1 interface{}, s interface{}, uUID interface{}) *MockAlertmanager_DeleteChannelByID_Call {
func (_e *MockAlertmanager_Expecter) DeleteChannelByID(context1 any, s any, uUID any) *MockAlertmanager_DeleteChannelByID_Call {
return &MockAlertmanager_DeleteChannelByID_Call{Call: _e.mock.On("DeleteChannelByID", context1, s, uUID)}
}
@@ -710,7 +719,7 @@ type MockAlertmanager_DeleteNotificationConfig_Call struct {
// - ctx context.Context
// - orgID valuer.UUID
// - ruleId string
func (_e *MockAlertmanager_Expecter) DeleteNotificationConfig(ctx interface{}, orgID interface{}, ruleId interface{}) *MockAlertmanager_DeleteNotificationConfig_Call {
func (_e *MockAlertmanager_Expecter) DeleteNotificationConfig(ctx any, orgID any, ruleId any) *MockAlertmanager_DeleteNotificationConfig_Call {
return &MockAlertmanager_DeleteNotificationConfig_Call{Call: _e.mock.On("DeleteNotificationConfig", ctx, orgID, ruleId)}
}
@@ -772,7 +781,7 @@ type MockAlertmanager_DeleteRoutePolicyByID_Call struct {
// DeleteRoutePolicyByID is a helper method to define mock.On call
// - ctx context.Context
// - routeID string
func (_e *MockAlertmanager_Expecter) DeleteRoutePolicyByID(ctx interface{}, routeID interface{}) *MockAlertmanager_DeleteRoutePolicyByID_Call {
func (_e *MockAlertmanager_Expecter) DeleteRoutePolicyByID(ctx any, routeID any) *MockAlertmanager_DeleteRoutePolicyByID_Call {
return &MockAlertmanager_DeleteRoutePolicyByID_Call{Call: _e.mock.On("DeleteRoutePolicyByID", ctx, routeID)}
}
@@ -841,7 +850,7 @@ type MockAlertmanager_GetAlerts_Call struct {
// - context1 context.Context
// - s string
// - gettableAlertsParams alertmanagertypes.GettableAlertsParams
func (_e *MockAlertmanager_Expecter) GetAlerts(context1 interface{}, s interface{}, gettableAlertsParams interface{}) *MockAlertmanager_GetAlerts_Call {
func (_e *MockAlertmanager_Expecter) GetAlerts(context1 any, s any, gettableAlertsParams any) *MockAlertmanager_GetAlerts_Call {
return &MockAlertmanager_GetAlerts_Call{Call: _e.mock.On("GetAlerts", context1, s, gettableAlertsParams)}
}
@@ -868,8 +877,8 @@ func (_c *MockAlertmanager_GetAlerts_Call) Run(run func(context1 context.Context
return _c
}
func (_c *MockAlertmanager_GetAlerts_Call) Return(v alertmanagertypes.DeprecatedGettableAlerts, err error) *MockAlertmanager_GetAlerts_Call {
_c.Call.Return(v, err)
func (_c *MockAlertmanager_GetAlerts_Call) Return(deprecatedGettableAlerts alertmanagertypes.DeprecatedGettableAlerts, err error) *MockAlertmanager_GetAlerts_Call {
_c.Call.Return(deprecatedGettableAlerts, err)
return _c
}
@@ -913,7 +922,7 @@ type MockAlertmanager_GetAllRoutePolicies_Call struct {
// GetAllRoutePolicies is a helper method to define mock.On call
// - ctx context.Context
func (_e *MockAlertmanager_Expecter) GetAllRoutePolicies(ctx interface{}) *MockAlertmanager_GetAllRoutePolicies_Call {
func (_e *MockAlertmanager_Expecter) GetAllRoutePolicies(ctx any) *MockAlertmanager_GetAllRoutePolicies_Call {
return &MockAlertmanager_GetAllRoutePolicies_Call{Call: _e.mock.On("GetAllRoutePolicies", ctx)}
}
@@ -977,7 +986,7 @@ type MockAlertmanager_GetChannelByID_Call struct {
// - context1 context.Context
// - s string
// - uUID valuer.UUID
func (_e *MockAlertmanager_Expecter) GetChannelByID(context1 interface{}, s interface{}, uUID interface{}) *MockAlertmanager_GetChannelByID_Call {
func (_e *MockAlertmanager_Expecter) GetChannelByID(context1 any, s any, uUID any) *MockAlertmanager_GetChannelByID_Call {
return &MockAlertmanager_GetChannelByID_Call{Call: _e.mock.On("GetChannelByID", context1, s, uUID)}
}
@@ -1050,7 +1059,7 @@ type MockAlertmanager_GetConfig_Call struct {
// GetConfig is a helper method to define mock.On call
// - context1 context.Context
// - s string
func (_e *MockAlertmanager_Expecter) GetConfig(context1 interface{}, s interface{}) *MockAlertmanager_GetConfig_Call {
func (_e *MockAlertmanager_Expecter) GetConfig(context1 any, s any) *MockAlertmanager_GetConfig_Call {
return &MockAlertmanager_GetConfig_Call{Call: _e.mock.On("GetConfig", context1, s)}
}
@@ -1118,7 +1127,7 @@ type MockAlertmanager_GetRoutePolicyByID_Call struct {
// GetRoutePolicyByID is a helper method to define mock.On call
// - ctx context.Context
// - routeID string
func (_e *MockAlertmanager_Expecter) GetRoutePolicyByID(ctx interface{}, routeID interface{}) *MockAlertmanager_GetRoutePolicyByID_Call {
func (_e *MockAlertmanager_Expecter) GetRoutePolicyByID(ctx any, routeID any) *MockAlertmanager_GetRoutePolicyByID_Call {
return &MockAlertmanager_GetRoutePolicyByID_Call{Call: _e.mock.On("GetRoutePolicyByID", ctx, routeID)}
}
@@ -1185,7 +1194,7 @@ type MockAlertmanager_ListAllChannels_Call struct {
// ListAllChannels is a helper method to define mock.On call
// - context1 context.Context
func (_e *MockAlertmanager_Expecter) ListAllChannels(context1 interface{}) *MockAlertmanager_ListAllChannels_Call {
func (_e *MockAlertmanager_Expecter) ListAllChannels(context1 any) *MockAlertmanager_ListAllChannels_Call {
return &MockAlertmanager_ListAllChannels_Call{Call: _e.mock.On("ListAllChannels", context1)}
}
@@ -1248,7 +1257,7 @@ type MockAlertmanager_ListChannels_Call struct {
// ListChannels is a helper method to define mock.On call
// - context1 context.Context
// - s string
func (_e *MockAlertmanager_Expecter) ListChannels(context1 interface{}, s interface{}) *MockAlertmanager_ListChannels_Call {
func (_e *MockAlertmanager_Expecter) ListChannels(context1 any, s any) *MockAlertmanager_ListChannels_Call {
return &MockAlertmanager_ListChannels_Call{Call: _e.mock.On("ListChannels", context1, s)}
}
@@ -1317,7 +1326,7 @@ type MockAlertmanager_ListNotificationChannels_Call struct {
// - context1 context.Context
// - s string
// - listChannelsParams *alertmanagertypes.ListChannelsParams
func (_e *MockAlertmanager_Expecter) ListNotificationChannels(context1 interface{}, s interface{}, listChannelsParams interface{}) *MockAlertmanager_ListNotificationChannels_Call {
func (_e *MockAlertmanager_Expecter) ListNotificationChannels(context1 any, s any, listChannelsParams any) *MockAlertmanager_ListNotificationChannels_Call {
return &MockAlertmanager_ListNotificationChannels_Call{Call: _e.mock.On("ListNotificationChannels", context1, s, listChannelsParams)}
}
@@ -1355,8 +1364,8 @@ func (_c *MockAlertmanager_ListNotificationChannels_Call) RunAndReturn(run func(
}
// PutAlerts provides a mock function for the type MockAlertmanager
func (_mock *MockAlertmanager) PutAlerts(context1 context.Context, s string, v alertmanagertypes.PostableAlerts) error {
ret := _mock.Called(context1, s, v)
func (_mock *MockAlertmanager) PutAlerts(context1 context.Context, s string, postableAlerts alertmanagertypes.PostableAlerts) error {
ret := _mock.Called(context1, s, postableAlerts)
if len(ret) == 0 {
panic("no return value specified for PutAlerts")
@@ -1364,7 +1373,7 @@ func (_mock *MockAlertmanager) PutAlerts(context1 context.Context, s string, v a
var r0 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string, alertmanagertypes.PostableAlerts) error); ok {
r0 = returnFunc(context1, s, v)
r0 = returnFunc(context1, s, postableAlerts)
} else {
r0 = ret.Error(0)
}
@@ -1379,12 +1388,12 @@ type MockAlertmanager_PutAlerts_Call struct {
// PutAlerts is a helper method to define mock.On call
// - context1 context.Context
// - s string
// - v alertmanagertypes.PostableAlerts
func (_e *MockAlertmanager_Expecter) PutAlerts(context1 interface{}, s interface{}, v interface{}) *MockAlertmanager_PutAlerts_Call {
return &MockAlertmanager_PutAlerts_Call{Call: _e.mock.On("PutAlerts", context1, s, v)}
// - postableAlerts alertmanagertypes.PostableAlerts
func (_e *MockAlertmanager_Expecter) PutAlerts(context1 any, s any, postableAlerts any) *MockAlertmanager_PutAlerts_Call {
return &MockAlertmanager_PutAlerts_Call{Call: _e.mock.On("PutAlerts", context1, s, postableAlerts)}
}
func (_c *MockAlertmanager_PutAlerts_Call) Run(run func(context1 context.Context, s string, v alertmanagertypes.PostableAlerts)) *MockAlertmanager_PutAlerts_Call {
func (_c *MockAlertmanager_PutAlerts_Call) Run(run func(context1 context.Context, s string, postableAlerts alertmanagertypes.PostableAlerts)) *MockAlertmanager_PutAlerts_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
@@ -1412,7 +1421,87 @@ func (_c *MockAlertmanager_PutAlerts_Call) Return(err error) *MockAlertmanager_P
return _c
}
func (_c *MockAlertmanager_PutAlerts_Call) RunAndReturn(run func(context1 context.Context, s string, v alertmanagertypes.PostableAlerts) error) *MockAlertmanager_PutAlerts_Call {
func (_c *MockAlertmanager_PutAlerts_Call) RunAndReturn(run func(context1 context.Context, s string, postableAlerts alertmanagertypes.PostableAlerts) error) *MockAlertmanager_PutAlerts_Call {
_c.Call.Return(run)
return _c
}
// RepairNotificationChannel provides a mock function for the type MockAlertmanager
func (_mock *MockAlertmanager) RepairNotificationChannel(context1 context.Context, s string, uUID valuer.UUID, b bool) (*alertmanagertypes.ChannelRepair, error) {
ret := _mock.Called(context1, s, uUID, b)
if len(ret) == 0 {
panic("no return value specified for RepairNotificationChannel")
}
var r0 *alertmanagertypes.ChannelRepair
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string, valuer.UUID, bool) (*alertmanagertypes.ChannelRepair, error)); ok {
return returnFunc(context1, s, uUID, b)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, string, valuer.UUID, bool) *alertmanagertypes.ChannelRepair); ok {
r0 = returnFunc(context1, s, uUID, b)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*alertmanagertypes.ChannelRepair)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, string, valuer.UUID, bool) error); ok {
r1 = returnFunc(context1, s, uUID, b)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockAlertmanager_RepairNotificationChannel_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RepairNotificationChannel'
type MockAlertmanager_RepairNotificationChannel_Call struct {
*mock.Call
}
// RepairNotificationChannel is a helper method to define mock.On call
// - context1 context.Context
// - s string
// - uUID valuer.UUID
// - b bool
func (_e *MockAlertmanager_Expecter) RepairNotificationChannel(context1 any, s any, uUID any, b any) *MockAlertmanager_RepairNotificationChannel_Call {
return &MockAlertmanager_RepairNotificationChannel_Call{Call: _e.mock.On("RepairNotificationChannel", context1, s, uUID, b)}
}
func (_c *MockAlertmanager_RepairNotificationChannel_Call) Run(run func(context1 context.Context, s string, uUID valuer.UUID, b bool)) *MockAlertmanager_RepairNotificationChannel_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
var arg2 valuer.UUID
if args[2] != nil {
arg2 = args[2].(valuer.UUID)
}
var arg3 bool
if args[3] != nil {
arg3 = args[3].(bool)
}
run(
arg0,
arg1,
arg2,
arg3,
)
})
return _c
}
func (_c *MockAlertmanager_RepairNotificationChannel_Call) Return(channelRepair *alertmanagertypes.ChannelRepair, err error) *MockAlertmanager_RepairNotificationChannel_Call {
_c.Call.Return(channelRepair, err)
return _c
}
func (_c *MockAlertmanager_RepairNotificationChannel_Call) RunAndReturn(run func(context1 context.Context, s string, uUID valuer.UUID, b bool) (*alertmanagertypes.ChannelRepair, error)) *MockAlertmanager_RepairNotificationChannel_Call {
_c.Call.Return(run)
return _c
}
@@ -1442,7 +1531,7 @@ type MockAlertmanager_SetConfig_Call struct {
// SetConfig is a helper method to define mock.On call
// - context1 context.Context
// - config1 *alertmanagertypes.Config
func (_e *MockAlertmanager_Expecter) SetConfig(context1 interface{}, config1 interface{}) *MockAlertmanager_SetConfig_Call {
func (_e *MockAlertmanager_Expecter) SetConfig(context1 any, config1 any) *MockAlertmanager_SetConfig_Call {
return &MockAlertmanager_SetConfig_Call{Call: _e.mock.On("SetConfig", context1, config1)}
}
@@ -1499,7 +1588,7 @@ type MockAlertmanager_SetDefaultConfig_Call struct {
// SetDefaultConfig is a helper method to define mock.On call
// - context1 context.Context
// - s string
func (_e *MockAlertmanager_Expecter) SetDefaultConfig(context1 interface{}, s interface{}) *MockAlertmanager_SetDefaultConfig_Call {
func (_e *MockAlertmanager_Expecter) SetDefaultConfig(context1 any, s any) *MockAlertmanager_SetDefaultConfig_Call {
return &MockAlertmanager_SetDefaultConfig_Call{Call: _e.mock.On("SetDefaultConfig", context1, s)}
}
@@ -1558,7 +1647,7 @@ type MockAlertmanager_SetNotificationConfig_Call struct {
// - orgID valuer.UUID
// - ruleId string
// - config1 *alertmanagertypes.NotificationConfig
func (_e *MockAlertmanager_Expecter) SetNotificationConfig(ctx interface{}, orgID interface{}, ruleId interface{}, config1 interface{}) *MockAlertmanager_SetNotificationConfig_Call {
func (_e *MockAlertmanager_Expecter) SetNotificationConfig(ctx any, orgID any, ruleId any, config1 any) *MockAlertmanager_SetNotificationConfig_Call {
return &MockAlertmanager_SetNotificationConfig_Call{Call: _e.mock.On("SetNotificationConfig", ctx, orgID, ruleId, config1)}
}
@@ -1624,7 +1713,7 @@ type MockAlertmanager_Start_Call struct {
// Start is a helper method to define mock.On call
// - context1 context.Context
func (_e *MockAlertmanager_Expecter) Start(context1 interface{}) *MockAlertmanager_Start_Call {
func (_e *MockAlertmanager_Expecter) Start(context1 any) *MockAlertmanager_Start_Call {
return &MockAlertmanager_Start_Call{Call: _e.mock.On("Start", context1)}
}
@@ -1675,7 +1764,7 @@ type MockAlertmanager_Stop_Call struct {
// Stop is a helper method to define mock.On call
// - context1 context.Context
func (_e *MockAlertmanager_Expecter) Stop(context1 interface{}) *MockAlertmanager_Stop_Call {
func (_e *MockAlertmanager_Expecter) Stop(context1 any) *MockAlertmanager_Stop_Call {
return &MockAlertmanager_Stop_Call{Call: _e.mock.On("Stop", context1)}
}
@@ -1729,7 +1818,7 @@ type MockAlertmanager_TestAlert_Call struct {
// - orgID string
// - ruleID string
// - receiversMap map[*alertmanagertypes.PostableAlert][]string
func (_e *MockAlertmanager_Expecter) TestAlert(ctx interface{}, orgID interface{}, ruleID interface{}, receiversMap interface{}) *MockAlertmanager_TestAlert_Call {
func (_e *MockAlertmanager_Expecter) TestAlert(ctx any, orgID any, ruleID any, receiversMap any) *MockAlertmanager_TestAlert_Call {
return &MockAlertmanager_TestAlert_Call{Call: _e.mock.On("TestAlert", ctx, orgID, ruleID, receiversMap)}
}
@@ -1797,7 +1886,7 @@ type MockAlertmanager_TestNotificationChannel_Call struct {
// - context1 context.Context
// - s string
// - testableNotificationChannel alertmanagertypes.TestableNotificationChannel
func (_e *MockAlertmanager_Expecter) TestNotificationChannel(context1 interface{}, s interface{}, testableNotificationChannel interface{}) *MockAlertmanager_TestNotificationChannel_Call {
func (_e *MockAlertmanager_Expecter) TestNotificationChannel(context1 any, s any, testableNotificationChannel any) *MockAlertmanager_TestNotificationChannel_Call {
return &MockAlertmanager_TestNotificationChannel_Call{Call: _e.mock.On("TestNotificationChannel", context1, s, testableNotificationChannel)}
}
@@ -1860,7 +1949,7 @@ type MockAlertmanager_TestReceiver_Call struct {
// - context1 context.Context
// - s string
// - receiver *alertmanagertypes.Receiver
func (_e *MockAlertmanager_Expecter) TestReceiver(context1 interface{}, s interface{}, receiver interface{}) *MockAlertmanager_TestReceiver_Call {
func (_e *MockAlertmanager_Expecter) TestReceiver(context1 any, s any, receiver any) *MockAlertmanager_TestReceiver_Call {
return &MockAlertmanager_TestReceiver_Call{Call: _e.mock.On("TestReceiver", context1, s, receiver)}
}
@@ -1923,7 +2012,7 @@ type MockAlertmanager_UpdateAllRoutePoliciesByRuleId_Call struct {
// - ctx context.Context
// - ruleId string
// - routes []*alertmanagertypes.PostableRoutePolicy
func (_e *MockAlertmanager_Expecter) UpdateAllRoutePoliciesByRuleId(ctx interface{}, ruleId interface{}, routes interface{}) *MockAlertmanager_UpdateAllRoutePoliciesByRuleId_Call {
func (_e *MockAlertmanager_Expecter) UpdateAllRoutePoliciesByRuleId(ctx any, ruleId any, routes any) *MockAlertmanager_UpdateAllRoutePoliciesByRuleId_Call {
return &MockAlertmanager_UpdateAllRoutePoliciesByRuleId_Call{Call: _e.mock.On("UpdateAllRoutePoliciesByRuleId", ctx, ruleId, routes)}
}
@@ -1987,7 +2076,7 @@ type MockAlertmanager_UpdateChannelByReceiverAndID_Call struct {
// - s string
// - receiver *alertmanagertypes.Receiver
// - uUID valuer.UUID
func (_e *MockAlertmanager_Expecter) UpdateChannelByReceiverAndID(context1 interface{}, s interface{}, receiver interface{}, uUID interface{}) *MockAlertmanager_UpdateChannelByReceiverAndID_Call {
func (_e *MockAlertmanager_Expecter) UpdateChannelByReceiverAndID(context1 any, s any, receiver any, uUID any) *MockAlertmanager_UpdateChannelByReceiverAndID_Call {
return &MockAlertmanager_UpdateChannelByReceiverAndID_Call{Call: _e.mock.On("UpdateChannelByReceiverAndID", context1, s, receiver, uUID)}
}
@@ -2067,7 +2156,7 @@ type MockAlertmanager_UpdateNotificationChannel_Call struct {
// - s string
// - uUID valuer.UUID
// - updatableNotificationChannel alertmanagertypes.UpdatableNotificationChannel
func (_e *MockAlertmanager_Expecter) UpdateNotificationChannel(context1 interface{}, s interface{}, uUID interface{}, updatableNotificationChannel interface{}) *MockAlertmanager_UpdateNotificationChannel_Call {
func (_e *MockAlertmanager_Expecter) UpdateNotificationChannel(context1 any, s any, uUID any, updatableNotificationChannel any) *MockAlertmanager_UpdateNotificationChannel_Call {
return &MockAlertmanager_UpdateNotificationChannel_Call{Call: _e.mock.On("UpdateNotificationChannel", context1, s, uUID, updatableNotificationChannel)}
}
@@ -2146,7 +2235,7 @@ type MockAlertmanager_UpdateRoutePolicyByID_Call struct {
// - ctx context.Context
// - routeID string
// - route *alertmanagertypes.PostableRoutePolicy
func (_e *MockAlertmanager_Expecter) UpdateRoutePolicyByID(ctx interface{}, routeID interface{}, route interface{}) *MockAlertmanager_UpdateRoutePolicyByID_Call {
func (_e *MockAlertmanager_Expecter) UpdateRoutePolicyByID(ctx any, routeID any, route any) *MockAlertmanager_UpdateRoutePolicyByID_Call {
return &MockAlertmanager_UpdateRoutePolicyByID_Call{Call: _e.mock.On("UpdateRoutePolicyByID", ctx, routeID, route)}
}
@@ -2189,10 +2278,19 @@ func NewMockHandler(t interface {
mock.TestingT
Cleanup(func())
}) *MockHandler {
if helper, ok := t.(interface{ Helper() }); ok {
helper.Helper()
}
mock := &MockHandler{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
t.Cleanup(func() {
if helper, ok := t.(interface{ Helper() }); ok {
helper.Helper()
}
mock.AssertExpectations(t)
})
return mock
}
@@ -2224,7 +2322,7 @@ type MockHandler_CreateChannel_Call struct {
// CreateChannel is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) CreateChannel(responseWriter interface{}, request interface{}) *MockHandler_CreateChannel_Call {
func (_e *MockHandler_Expecter) CreateChannel(responseWriter any, request any) *MockHandler_CreateChannel_Call {
return &MockHandler_CreateChannel_Call{Call: _e.mock.On("CreateChannel", responseWriter, request)}
}
@@ -2270,7 +2368,7 @@ type MockHandler_CreateNotificationChannel_Call struct {
// CreateNotificationChannel is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) CreateNotificationChannel(responseWriter interface{}, request interface{}) *MockHandler_CreateNotificationChannel_Call {
func (_e *MockHandler_Expecter) CreateNotificationChannel(responseWriter any, request any) *MockHandler_CreateNotificationChannel_Call {
return &MockHandler_CreateNotificationChannel_Call{Call: _e.mock.On("CreateNotificationChannel", responseWriter, request)}
}
@@ -2316,7 +2414,7 @@ type MockHandler_CreateRoutePolicy_Call struct {
// CreateRoutePolicy is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) CreateRoutePolicy(responseWriter interface{}, request interface{}) *MockHandler_CreateRoutePolicy_Call {
func (_e *MockHandler_Expecter) CreateRoutePolicy(responseWriter any, request any) *MockHandler_CreateRoutePolicy_Call {
return &MockHandler_CreateRoutePolicy_Call{Call: _e.mock.On("CreateRoutePolicy", responseWriter, request)}
}
@@ -2362,7 +2460,7 @@ type MockHandler_DeleteChannelByID_Call struct {
// DeleteChannelByID is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) DeleteChannelByID(responseWriter interface{}, request interface{}) *MockHandler_DeleteChannelByID_Call {
func (_e *MockHandler_Expecter) DeleteChannelByID(responseWriter any, request any) *MockHandler_DeleteChannelByID_Call {
return &MockHandler_DeleteChannelByID_Call{Call: _e.mock.On("DeleteChannelByID", responseWriter, request)}
}
@@ -2408,7 +2506,7 @@ type MockHandler_DeleteNotificationChannel_Call struct {
// DeleteNotificationChannel is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) DeleteNotificationChannel(responseWriter interface{}, request interface{}) *MockHandler_DeleteNotificationChannel_Call {
func (_e *MockHandler_Expecter) DeleteNotificationChannel(responseWriter any, request any) *MockHandler_DeleteNotificationChannel_Call {
return &MockHandler_DeleteNotificationChannel_Call{Call: _e.mock.On("DeleteNotificationChannel", responseWriter, request)}
}
@@ -2454,7 +2552,7 @@ type MockHandler_DeleteRoutePolicyByID_Call struct {
// DeleteRoutePolicyByID is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) DeleteRoutePolicyByID(responseWriter interface{}, request interface{}) *MockHandler_DeleteRoutePolicyByID_Call {
func (_e *MockHandler_Expecter) DeleteRoutePolicyByID(responseWriter any, request any) *MockHandler_DeleteRoutePolicyByID_Call {
return &MockHandler_DeleteRoutePolicyByID_Call{Call: _e.mock.On("DeleteRoutePolicyByID", responseWriter, request)}
}
@@ -2500,7 +2598,7 @@ type MockHandler_GetAlerts_Call struct {
// GetAlerts is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) GetAlerts(responseWriter interface{}, request interface{}) *MockHandler_GetAlerts_Call {
func (_e *MockHandler_Expecter) GetAlerts(responseWriter any, request any) *MockHandler_GetAlerts_Call {
return &MockHandler_GetAlerts_Call{Call: _e.mock.On("GetAlerts", responseWriter, request)}
}
@@ -2546,7 +2644,7 @@ type MockHandler_GetAllRoutePolicies_Call struct {
// GetAllRoutePolicies is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) GetAllRoutePolicies(responseWriter interface{}, request interface{}) *MockHandler_GetAllRoutePolicies_Call {
func (_e *MockHandler_Expecter) GetAllRoutePolicies(responseWriter any, request any) *MockHandler_GetAllRoutePolicies_Call {
return &MockHandler_GetAllRoutePolicies_Call{Call: _e.mock.On("GetAllRoutePolicies", responseWriter, request)}
}
@@ -2592,7 +2690,7 @@ type MockHandler_GetChannelByID_Call struct {
// GetChannelByID is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) GetChannelByID(responseWriter interface{}, request interface{}) *MockHandler_GetChannelByID_Call {
func (_e *MockHandler_Expecter) GetChannelByID(responseWriter any, request any) *MockHandler_GetChannelByID_Call {
return &MockHandler_GetChannelByID_Call{Call: _e.mock.On("GetChannelByID", responseWriter, request)}
}
@@ -2638,7 +2736,7 @@ type MockHandler_GetNotificationChannel_Call struct {
// GetNotificationChannel is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) GetNotificationChannel(responseWriter interface{}, request interface{}) *MockHandler_GetNotificationChannel_Call {
func (_e *MockHandler_Expecter) GetNotificationChannel(responseWriter any, request any) *MockHandler_GetNotificationChannel_Call {
return &MockHandler_GetNotificationChannel_Call{Call: _e.mock.On("GetNotificationChannel", responseWriter, request)}
}
@@ -2684,7 +2782,7 @@ type MockHandler_GetRoutePolicyByID_Call struct {
// GetRoutePolicyByID is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) GetRoutePolicyByID(responseWriter interface{}, request interface{}) *MockHandler_GetRoutePolicyByID_Call {
func (_e *MockHandler_Expecter) GetRoutePolicyByID(responseWriter any, request any) *MockHandler_GetRoutePolicyByID_Call {
return &MockHandler_GetRoutePolicyByID_Call{Call: _e.mock.On("GetRoutePolicyByID", responseWriter, request)}
}
@@ -2730,7 +2828,7 @@ type MockHandler_ListAllChannels_Call struct {
// ListAllChannels is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) ListAllChannels(responseWriter interface{}, request interface{}) *MockHandler_ListAllChannels_Call {
func (_e *MockHandler_Expecter) ListAllChannels(responseWriter any, request any) *MockHandler_ListAllChannels_Call {
return &MockHandler_ListAllChannels_Call{Call: _e.mock.On("ListAllChannels", responseWriter, request)}
}
@@ -2776,7 +2874,7 @@ type MockHandler_ListChannels_Call struct {
// ListChannels is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) ListChannels(responseWriter interface{}, request interface{}) *MockHandler_ListChannels_Call {
func (_e *MockHandler_Expecter) ListChannels(responseWriter any, request any) *MockHandler_ListChannels_Call {
return &MockHandler_ListChannels_Call{Call: _e.mock.On("ListChannels", responseWriter, request)}
}
@@ -2822,7 +2920,7 @@ type MockHandler_ListNotificationChannels_Call struct {
// ListNotificationChannels is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) ListNotificationChannels(responseWriter interface{}, request interface{}) *MockHandler_ListNotificationChannels_Call {
func (_e *MockHandler_Expecter) ListNotificationChannels(responseWriter any, request any) *MockHandler_ListNotificationChannels_Call {
return &MockHandler_ListNotificationChannels_Call{Call: _e.mock.On("ListNotificationChannels", responseWriter, request)}
}
@@ -2854,6 +2952,52 @@ func (_c *MockHandler_ListNotificationChannels_Call) RunAndReturn(run func(respo
return _c
}
// RepairNotificationChannel provides a mock function for the type MockHandler
func (_mock *MockHandler) RepairNotificationChannel(responseWriter http.ResponseWriter, request *http.Request) {
_mock.Called(responseWriter, request)
return
}
// MockHandler_RepairNotificationChannel_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RepairNotificationChannel'
type MockHandler_RepairNotificationChannel_Call struct {
*mock.Call
}
// RepairNotificationChannel is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) RepairNotificationChannel(responseWriter any, request any) *MockHandler_RepairNotificationChannel_Call {
return &MockHandler_RepairNotificationChannel_Call{Call: _e.mock.On("RepairNotificationChannel", responseWriter, request)}
}
func (_c *MockHandler_RepairNotificationChannel_Call) Run(run func(responseWriter http.ResponseWriter, request *http.Request)) *MockHandler_RepairNotificationChannel_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 http.ResponseWriter
if args[0] != nil {
arg0 = args[0].(http.ResponseWriter)
}
var arg1 *http.Request
if args[1] != nil {
arg1 = args[1].(*http.Request)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockHandler_RepairNotificationChannel_Call) Return() *MockHandler_RepairNotificationChannel_Call {
_c.Call.Return()
return _c
}
func (_c *MockHandler_RepairNotificationChannel_Call) RunAndReturn(run func(responseWriter http.ResponseWriter, request *http.Request)) *MockHandler_RepairNotificationChannel_Call {
_c.Run(run)
return _c
}
// TestNotificationChannel provides a mock function for the type MockHandler
func (_mock *MockHandler) TestNotificationChannel(responseWriter http.ResponseWriter, request *http.Request) {
_mock.Called(responseWriter, request)
@@ -2868,7 +3012,7 @@ type MockHandler_TestNotificationChannel_Call struct {
// TestNotificationChannel is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) TestNotificationChannel(responseWriter interface{}, request interface{}) *MockHandler_TestNotificationChannel_Call {
func (_e *MockHandler_Expecter) TestNotificationChannel(responseWriter any, request any) *MockHandler_TestNotificationChannel_Call {
return &MockHandler_TestNotificationChannel_Call{Call: _e.mock.On("TestNotificationChannel", responseWriter, request)}
}
@@ -2914,7 +3058,7 @@ type MockHandler_TestReceiver_Call struct {
// TestReceiver is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) TestReceiver(responseWriter interface{}, request interface{}) *MockHandler_TestReceiver_Call {
func (_e *MockHandler_Expecter) TestReceiver(responseWriter any, request any) *MockHandler_TestReceiver_Call {
return &MockHandler_TestReceiver_Call{Call: _e.mock.On("TestReceiver", responseWriter, request)}
}
@@ -2960,7 +3104,7 @@ type MockHandler_UpdateChannelByID_Call struct {
// UpdateChannelByID is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) UpdateChannelByID(responseWriter interface{}, request interface{}) *MockHandler_UpdateChannelByID_Call {
func (_e *MockHandler_Expecter) UpdateChannelByID(responseWriter any, request any) *MockHandler_UpdateChannelByID_Call {
return &MockHandler_UpdateChannelByID_Call{Call: _e.mock.On("UpdateChannelByID", responseWriter, request)}
}
@@ -3006,7 +3150,7 @@ type MockHandler_UpdateNotificationChannel_Call struct {
// UpdateNotificationChannel is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) UpdateNotificationChannel(responseWriter interface{}, request interface{}) *MockHandler_UpdateNotificationChannel_Call {
func (_e *MockHandler_Expecter) UpdateNotificationChannel(responseWriter any, request any) *MockHandler_UpdateNotificationChannel_Call {
return &MockHandler_UpdateNotificationChannel_Call{Call: _e.mock.On("UpdateNotificationChannel", responseWriter, request)}
}
@@ -3052,7 +3196,7 @@ type MockHandler_UpdateRoutePolicy_Call struct {
// UpdateRoutePolicy is a helper method to define mock.On call
// - responseWriter http.ResponseWriter
// - request *http.Request
func (_e *MockHandler_Expecter) UpdateRoutePolicy(responseWriter interface{}, request interface{}) *MockHandler_UpdateRoutePolicy_Call {
func (_e *MockHandler_Expecter) UpdateRoutePolicy(responseWriter any, request any) *MockHandler_UpdateRoutePolicy_Call {
return &MockHandler_UpdateRoutePolicy_Call{Call: _e.mock.On("UpdateRoutePolicy", responseWriter, request)}
}

View File

@@ -29,6 +29,8 @@ type Handler interface {
DeleteNotificationChannel(http.ResponseWriter, *http.Request)
RepairNotificationChannel(http.ResponseWriter, *http.Request)
TestNotificationChannel(http.ResponseWriter, *http.Request)
GetAllRoutePolicies(http.ResponseWriter, *http.Request)

View File

@@ -168,6 +168,37 @@ func (handler *handler) DeleteNotificationChannel(rw http.ResponseWriter, req *h
render.Success(rw, http.StatusNoContent, nil)
}
func (handler *handler) RepairNotificationChannel(rw http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
id, err := valuer.NewUUID(mux.Vars(req)["id"])
if err != nil {
render.Error(rw, errors.NewInvalidInputf(errors.CodeInvalidInput, "id is not a valid uuid-v7"))
return
}
params := new(alertmanagertypes.RepairChannelParams)
if err := binding.Query.BindQuery(req.URL.Query(), params); err != nil {
render.Error(rw, err)
return
}
repair, err := handler.alertmanager.RepairNotificationChannel(ctx, claims.OrgID, id, params.Apply)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, repair)
}
func (handler *handler) TestNotificationChannel(rw http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second)
defer cancel()

View File

@@ -2,6 +2,7 @@ package signozalertmanager
import (
"context"
"fmt"
"time"
amConfig "github.com/prometheus/alertmanager/config"
@@ -343,6 +344,139 @@ func (provider *provider) TestNotificationChannel(ctx context.Context, orgID str
return provider.service.TestReceiver(ctx, orgID, receiver)
}
// RepairNotificationChannel refuses a delete while a route policy still names
// the channel, as DeleteChannelByID does. A split adds the new channels to
// every route policy naming the original, so what fanned out before still does.
func (provider *provider) RepairNotificationChannel(ctx context.Context, orgID string, id valuer.UUID, apply bool) (*alertmanagertypes.ChannelRepair, error) {
channel, err := provider.configStore.GetChannelByID(ctx, orgID, id)
if err != nil {
return nil, err
}
repair := channel.Diagnose()
switch repair.Action {
case alertmanagertypes.ChannelRepairActionRetype:
return provider.retypeChannel(ctx, orgID, channel, repair, apply)
case alertmanagertypes.ChannelRepairActionSplit:
return provider.splitChannel(ctx, orgID, channel, repair, apply)
case alertmanagertypes.ChannelRepairActionDelete:
return provider.deleteDefectiveChannel(ctx, orgID, channel, repair, apply)
}
repair.Channels = []*alertmanagertypes.ListedNotificationChannel{channel.ToListedNotificationChannel()}
return repair, nil
}
func (provider *provider) retypeChannel(ctx context.Context, orgID string, channel *alertmanagertypes.Channel, repair *alertmanagertypes.ChannelRepair, apply bool) (*alertmanagertypes.ChannelRepair, error) {
if err := channel.Retype(); err != nil {
return nil, err
}
repair.Channels = []*alertmanagertypes.ListedNotificationChannel{channel.ToListedNotificationChannel()}
if !apply {
return repair, nil
}
if err := provider.configStore.UpdateChannel(ctx, orgID, channel); err != nil {
return nil, err
}
repair.Applied = true
return repair, nil
}
func (provider *provider) splitChannel(ctx context.Context, orgID string, channel *alertmanagertypes.Channel, repair *alertmanagertypes.ChannelRepair, apply bool) (*alertmanagertypes.ChannelRepair, error) {
channels, err := channel.SplitByNotifier()
if err != nil {
return nil, err
}
for _, split := range channels {
repair.Channels = append(repair.Channels, split.ToListedNotificationChannel())
}
policies, err := provider.notificationManager.GetRoutePoliciesByChannel(ctx, orgID, channel.DisplayName)
if err != nil {
return nil, err
}
if !apply {
return repair, nil
}
config, err := provider.configStore.Get(ctx, orgID)
if err != nil {
return nil, err
}
if err := config.SetGlobalConfig(provider.config.Signoz.Global); err != nil {
return nil, err
}
receivers := make([]*alertmanagertypes.Receiver, 0, len(channels))
for _, split := range channels {
receiver, err := alertmanagertypes.NewReceiver(split.Data)
if err != nil {
return nil, err
}
receivers = append(receivers, receiver)
}
if err := config.UpdateReceiver(receivers[0]); err != nil {
return nil, err
}
for _, receiver := range receivers[1:] {
if err := config.CreateReceiverV2(receiver); err != nil {
return nil, err
}
}
for _, split := range channels[1:] {
if err := provider.configStore.CreateChannel(ctx, split); err != nil {
return nil, err
}
}
if err := provider.configStore.UpdateChannel(ctx, orgID, channel, alertmanagertypes.WithCb(func(ctx context.Context) error {
return provider.configStore.Set(ctx, config)
})); err != nil {
return nil, err
}
added := make([]string, 0, len(channels)-1)
for _, split := range channels[1:] {
added = append(added, split.DisplayName)
}
for _, policy := range policies {
postable := &alertmanagertypes.PostableRoutePolicy{
Expression: policy.Expression,
ExpressionKind: policy.ExpressionKind,
Channels: append(policy.Channels, added...),
Name: policy.Name,
Description: policy.Description,
Tags: policy.Tags,
}
if _, err := provider.UpdateRoutePolicyByID(ctx, policy.ID.String(), postable); err != nil {
return nil, err
}
}
repair.Applied = true
return repair, nil
}
func (provider *provider) deleteDefectiveChannel(ctx context.Context, orgID string, channel *alertmanagertypes.Channel, repair *alertmanagertypes.ChannelRepair, apply bool) (*alertmanagertypes.ChannelRepair, error) {
policies, err := provider.notificationManager.GetRoutePoliciesByChannel(ctx, orgID, channel.DisplayName)
if err != nil {
return nil, err
}
for _, policy := range policies {
repair.Blockers = append(repair.Blockers, fmt.Sprintf("used by routing policy %q", policy.Name))
}
if !apply {
return repair, nil
}
if err := provider.DeleteChannelByID(ctx, orgID, channel.ID); err != nil {
return nil, err
}
repair.Applied = true
return repair, nil
}
func (provider *provider) Config() alertmanagerserver.Config {
return provider.config.Signoz.Config
}

View File

@@ -266,6 +266,34 @@ func (provider *provider) addAlertmanagerRoutes(router *mux.Router) error {
return err
}
if err := router.Handle("/api/v2/notification_channels/{id}/repair", handler.New(
provider.authzMiddleware.CheckResources(provider.alertmanagerHandler.RepairNotificationChannel, authtypes.SigNozAdminRoleName),
handler.OpenAPIDef{
ID: "RepairNotificationChannel",
Tags: []string{"channels"},
Summary: "Repair notification channel",
Description: "This endpoint diagnoses a stored channel that the v2 API cannot read and applies the fitting action: a channel carrying several notifier configurations is split into one channel per configuration, keeping this ID for the first; a channel whose notifier kind v2 does not model is deleted; a channel with an empty stored type has it rewritten from its data. A delete is refused while a routing policy still names the channel. Nothing is written unless apply=true; by default the response only shows what would happen.",
Request: nil,
RequestQuery: new(alertmanagertypes.RepairChannelParams),
RequestContentType: "",
Response: new(alertmanagertypes.ChannelRepair),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
Deprecated: false,
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceNotificationChannel.Scope(coretypes.VerbUpdate)}),
},
handler.WithResourceDefs(handler.BasicResourceDef{
Resource: coretypes.ResourceMetaResourceNotificationChannel,
Verb: coretypes.VerbUpdate,
Category: coretypes.ActionCategoryConfigurationChange,
ID: coretypes.PathParam("id"),
Selector: coretypes.IDSelector,
}),
)).Methods(http.MethodPost).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v2/notification_channels/test", handler.New(
provider.authzMiddleware.CheckResources(provider.alertmanagerHandler.TestNotificationChannel, authtypes.SigNozAdminRoleName, authtypes.SigNozEditorRoleName),
handler.OpenAPIDef{

View File

@@ -0,0 +1,189 @@
package alertmanagertypes
import (
"fmt"
"reflect"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/prometheus/alertmanager/config"
)
// ChannelDefect names why v2 cannot read a stored channel. Unrepresentable
// covers everything the read path rejects that no repair action addresses.
type ChannelDefect struct {
valuer.String
}
var (
ChannelDefectNone = ChannelDefect{valuer.NewString("none")}
ChannelDefectMissingType = ChannelDefect{valuer.NewString("missing_type")}
ChannelDefectMultipleNotifiers = ChannelDefect{valuer.NewString("multiple_notifiers")}
ChannelDefectUnsupportedNotifier = ChannelDefect{valuer.NewString("unsupported_notifier")}
ChannelDefectUnrepresentable = ChannelDefect{valuer.NewString("unrepresentable")}
)
func (ChannelDefect) Enum() []any {
return []any{ChannelDefectNone, ChannelDefectMissingType, ChannelDefectMultipleNotifiers, ChannelDefectUnsupportedNotifier, ChannelDefectUnrepresentable}
}
type ChannelRepairAction struct {
valuer.String
}
var (
ChannelRepairActionNone = ChannelRepairAction{valuer.NewString("none")}
ChannelRepairActionRetype = ChannelRepairAction{valuer.NewString("retype")}
ChannelRepairActionSplit = ChannelRepairAction{valuer.NewString("split")}
ChannelRepairActionDelete = ChannelRepairAction{valuer.NewString("delete")}
)
func (ChannelRepairAction) Enum() []any {
return []any{ChannelRepairActionNone, ChannelRepairActionRetype, ChannelRepairActionSplit, ChannelRepairActionDelete}
}
// RepairChannelParams defaults to a dry run; only apply=true writes anything.
type RepairChannelParams struct {
Apply bool `query:"apply" json:"apply"`
}
// ChannelRepair is one channel's diagnosis and the action that makes it
// readable by v2. Channels lists what exists once the action is applied: the
// channel itself for none and retype, every part of a split, nothing for a
// delete. Blockers explain why an action was not, or would not be, applied.
type ChannelRepair struct {
ID valuer.UUID `json:"id" required:"true"`
Defect ChannelDefect `json:"defect" required:"true"`
Detail string `json:"detail"`
Action ChannelRepairAction `json:"action" required:"true"`
Blockers []string `json:"blockers,omitempty"`
Channels []*ListedNotificationChannel `json:"channels"`
Applied bool `json:"applied" required:"true"`
}
// Diagnose reads the channel the way v2 does and reports the first defect in
// the order a repair has to address them: data that does not decode, several
// notifiers, a notifier v2 does not model, a missing stored type, and last
// anything else the read path rejects.
func (c *Channel) Diagnose() *ChannelRepair {
repair := &ChannelRepair{ID: c.ID, Defect: ChannelDefectNone, Action: ChannelRepairActionNone}
receiver, err := NewReceiver(c.Data)
if err != nil {
repair.Defect, repair.Detail = ChannelDefectUnrepresentable, err.Error()
return repair
}
if total := countNotifierConfigs(receiver); total > 1 {
repair.Defect, repair.Action = ChannelDefectMultipleNotifiers, ChannelRepairActionSplit
repair.Detail = fmt.Sprintf("carries %d notifier configurations", total)
return repair
}
if !hasModelledNotifier(receiver) {
repair.Defect, repair.Action = ChannelDefectUnsupportedNotifier, ChannelRepairActionDelete
repair.Detail = fmt.Sprintf("notifier %q is not modelled by v2", receiverChannelType(receiver))
return repair
}
if c.Type == "" {
repair.Defect, repair.Action = ChannelDefectMissingType, ChannelRepairActionRetype
repair.Detail = fmt.Sprintf("stored type is empty, receiver carries %q", receiverChannelType(receiver))
return repair
}
if _, err := c.toPostableNotificationChannel(); err != nil {
repair.Defect, repair.Detail = ChannelDefectUnrepresentable, err.Error()
return repair
}
return repair
}
// Retype derives the stored type from the notifier the data carries, leaving
// the data itself untouched.
func (c *Channel) Retype() error {
receiver, err := NewReceiver(c.Data)
if err != nil {
return err
}
c.Type = receiverChannelType(receiver)
c.UpdatedAt = time.Now()
return nil
}
// SplitByNotifier turns a receiver carrying several notifier configurations into
// one channel per configuration. The first keeps this channel's identity so
// references to it stay valid; the rest are new channels numbered after it.
func (c *Channel) SplitByNotifier() ([]*Channel, error) {
receiver, err := NewReceiver(c.Data)
if err != nil {
return nil, err
}
singles := splitReceiverByNotifier(receiver)
if len(singles) < 2 {
return nil, errors.NewInvalidInputf(ErrCodeAlertmanagerChannelInvalid, "channel %q carries %d notifier configuration; nothing to split", c.DisplayName, len(singles))
}
if err := c.Update(singles[0]); err != nil {
return nil, err
}
channels := []*Channel{c}
for i, single := range singles[1:] {
single.Name = fmt.Sprintf("%s (%d)", c.DisplayName, i+2)
channel, err := NewChannelFromReceiver(single, c.OrgID)
if err != nil {
return nil, err
}
channels = append(channels, channel)
}
return channels, nil
}
func hasModelledNotifier(receiver *Receiver) bool {
for _, channelKind := range channelKinds {
if channelKind.countConfigs(receiver) > 0 {
return true
}
}
return false
}
// splitReceiverByNotifier yields one receiver per *_configs entry, walking
// SigNoz's own notifier lists first and upstream's second, each in declaration
// order.
func splitReceiverByNotifier(receiver *Receiver) []*Receiver {
var singles []*Receiver
for _, upstream := range []bool{false, true} {
holder := reflect.ValueOf(receiver).Elem()
if upstream {
holder = reflect.ValueOf(receiver.Receiver).Elem()
}
for i := 0; i < holder.NumField(); i++ {
list := holder.Field(i)
if list.Kind() != reflect.Slice || !receiverTypeRegex.MatchString(holder.Type().Field(i).Tag.Get("yaml")) {
continue
}
for j := 0; j < list.Len(); j++ {
single := &Receiver{Receiver: &config.Receiver{Name: receiver.Name}}
target := reflect.ValueOf(single).Elem()
if upstream {
target = reflect.ValueOf(single.Receiver).Elem()
}
target.Field(i).Set(reflect.Append(reflect.MakeSlice(list.Type(), 0, 1), list.Index(j)))
singles = append(singles, single)
}
}
}
return singles
}

View File

@@ -0,0 +1,191 @@
import uuid
from collections.abc import Callable
from http import HTTPStatus
import requests
from fixtures import types
from fixtures.auth import (
USER_ADMIN_EMAIL,
USER_ADMIN_PASSWORD,
)
TIMEOUT = 10
V2_BASE_URL = "/api/v2/notification_channels"
def test_repair_reports_nothing_for_a_readable_channel(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
cleanup_notification_channels: list[str],
) -> None:
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
name = f"v2-healthy-{uuid.uuid4().hex[:8]}"
response = requests.post(
signoz.self.host_configs["8080"].get(V2_BASE_URL),
json={"name": name, "config": {"kind": "slack", "spec": {"apiUrl": "https://hooks.slack.test/services/T/B/X", "channel": "#alerts"}}},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.CREATED, response.text
channel_id = response.json()["data"]["id"]
cleanup_notification_channels.append(channel_id)
response = requests.post(
signoz.self.host_configs["8080"].get(f"{V2_BASE_URL}/{channel_id}/repair"),
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
repair = response.json()["data"]
assert repair["id"] == channel_id
assert repair["defect"] == "none"
assert repair["action"] == "none"
assert repair["applied"] is False
assert [channel["id"] for channel in repair["channels"]] == [channel_id]
def test_repair_deletes_a_v1_channel_of_an_unmodelled_kind(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
cleanup_notification_channels: list[str],
) -> None:
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
name = f"v1-telegram-{uuid.uuid4().hex[:8]}"
response = requests.post(
signoz.self.host_configs["8080"].get("/api/v1/channels"),
json={"name": name, "telegram_configs": [{"chat": 12345, "token": "telegram-bot-token"}]},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.CREATED, response.text
channel_id = response.json()["data"]["id"]
cleanup_notification_channels.append(channel_id)
response = requests.post(
signoz.self.host_configs["8080"].get(f"{V2_BASE_URL}/{channel_id}/repair"),
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
repair = response.json()["data"]
assert repair["defect"] == "unsupported_notifier"
assert repair["action"] == "delete"
assert repair["applied"] is False
assert repair["channels"] is None or repair["channels"] == []
# A dry run leaves the channel in place.
response = requests.get(
signoz.self.host_configs["8080"].get(V2_BASE_URL),
params={"query": name},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
assert response.json()["data"]["total"] == 1
response = requests.post(
signoz.self.host_configs["8080"].get(f"{V2_BASE_URL}/{channel_id}/repair"),
params={"apply": "true"},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
assert response.json()["data"]["applied"] is True
cleanup_notification_channels.remove(channel_id)
response = requests.get(
signoz.self.host_configs["8080"].get(V2_BASE_URL),
params={"query": name},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
assert response.json()["data"]["total"] == 0
def test_repair_splits_a_v1_channel_carrying_several_notifiers(
signoz: types.SigNoz,
create_user_admin: None, # pylint: disable=unused-argument
get_token: Callable[[str, str], str],
cleanup_notification_channels: list[str],
) -> None:
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
name = f"v1-fanout-{uuid.uuid4().hex[:8]}"
# Only v1 accepts a receiver with more than one notifier configuration.
response = requests.post(
signoz.self.host_configs["8080"].get("/api/v1/channels"),
json={
"name": name,
"slack_configs": [{"api_url": "https://hooks.slack.test/services/T/B/X", "channel": "#alerts"}],
"webhook_configs": [{"url": "https://webhook.test/hook"}],
},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.CREATED, response.text
channel_id = response.json()["data"]["id"]
cleanup_notification_channels.append(channel_id)
response = requests.post(
signoz.self.host_configs["8080"].get(f"{V2_BASE_URL}/{channel_id}/repair"),
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
repair = response.json()["data"]
assert repair["defect"] == "multiple_notifiers"
assert repair["action"] == "split"
assert repair["applied"] is False
assert [channel["displayName"] for channel in repair["channels"]] == [name, f"{name} (2)"]
assert [channel["kind"] for channel in repair["channels"]] == ["slack", "webhook"]
assert repair["channels"][0]["id"] == channel_id
response = requests.get(
signoz.self.host_configs["8080"].get(V2_BASE_URL),
params={"query": name},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
assert response.json()["data"]["total"] == 1
response = requests.post(
signoz.self.host_configs["8080"].get(f"{V2_BASE_URL}/{channel_id}/repair"),
params={"apply": "true"},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
repair = response.json()["data"]
assert repair["applied"] is True
cleanup_notification_channels.append(repair["channels"][1]["id"])
response = requests.get(
signoz.self.host_configs["8080"].get(V2_BASE_URL),
params={"query": name},
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
listed = response.json()["data"]
assert listed["total"] == 2
assert sorted(channel["kind"] for channel in listed["channels"]) == ["slack", "webhook"]
# The original now reads through v2 as the first notifier alone.
response = requests.get(
signoz.self.host_configs["8080"].get(f"{V2_BASE_URL}/{channel_id}"),
headers={"Authorization": f"Bearer {token}"},
timeout=TIMEOUT,
)
assert response.status_code == HTTPStatus.OK, response.text
assert response.json()["data"]["config"]["kind"] == "slack"