mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[RAM] add observability feature for server less (#168636)
## Summary FIX => https://github.com/elastic/kibana/issues/168034 ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: mgiota <panagiota.mitsopoulou@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
fea32812c8
commit
a35f91e3a5
64 changed files with 787 additions and 85 deletions
|
@ -25,6 +25,11 @@ xpack.serverless.plugin.developer.projectSwitcher.currentType: 'observability'
|
|||
## Disable adding the component template `.fleet_agent_id_verification-1` to every index template for each datastream for each integration
|
||||
xpack.fleet.agentIdVerificationEnabled: false
|
||||
|
||||
## Enable the capability for the observability feature ID in the serverless environment to take ownership of the rules.
|
||||
## The value need to be a featureId observability Or stackAlerts Or siem
|
||||
xpack.alerting.rules.overwriteProducer: 'observability'
|
||||
xpack.observability.createO11yGenericFeatureId: true
|
||||
|
||||
## APM Serverless Onboarding flow
|
||||
xpack.apm.serverlessOnboarding: true
|
||||
|
||||
|
|
|
@ -7,3 +7,10 @@
|
|||
*/
|
||||
|
||||
export const OBSERVABILITY_THRESHOLD_RULE_TYPE_ID = 'observability.rules.custom_threshold';
|
||||
|
||||
export enum ApmRuleType {
|
||||
ErrorCount = 'apm.error_rate', // ErrorRate was renamed to ErrorCount but the key is kept as `error_rate` for backwards-compat.
|
||||
TransactionErrorRate = 'apm.transaction_error_rate',
|
||||
TransactionDuration = 'apm.transaction_duration',
|
||||
Anomaly = 'apm.anomaly',
|
||||
}
|
||||
|
|
|
@ -39,6 +39,13 @@ const rulesSchema = schema.object({
|
|||
enforce: schema.boolean({ defaultValue: false }), // if enforce is false, only warnings will be shown
|
||||
}),
|
||||
maxScheduledPerMinute: schema.number({ defaultValue: 10000, max: 10000, min: 0 }),
|
||||
overwriteProducer: schema.maybe(
|
||||
schema.oneOf([
|
||||
schema.literal('observability'),
|
||||
schema.literal('siem'),
|
||||
schema.literal('stackAlerts'),
|
||||
])
|
||||
),
|
||||
run: schema.object({
|
||||
timeout: schema.maybe(schema.string({ validate: validateDurationSchema })),
|
||||
actions: schema.object({
|
||||
|
|
|
@ -15,6 +15,7 @@ import { ILicenseState } from './license_state';
|
|||
import { licenseStateMock } from './license_state.mock';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { createGetAlertIndicesAliasFn } from './create_get_alert_indices_alias';
|
||||
import { AlertingConfig } from '../config';
|
||||
|
||||
describe('createGetAlertIndicesAliasFn', () => {
|
||||
const logger = loggingSystemMock.create().get();
|
||||
|
@ -23,6 +24,7 @@ describe('createGetAlertIndicesAliasFn', () => {
|
|||
const inMemoryMetrics = inMemoryMetricsMock.create();
|
||||
|
||||
const ruleTypeRegistryParams: ConstructorOptions = {
|
||||
config: {} as AlertingConfig,
|
||||
logger,
|
||||
taskManager,
|
||||
taskRunnerFactory: new TaskRunnerFactory(),
|
||||
|
|
|
@ -296,6 +296,7 @@ export class AlertingPlugin {
|
|||
}
|
||||
|
||||
const ruleTypeRegistry = new RuleTypeRegistry({
|
||||
config: this.config,
|
||||
logger: this.logger,
|
||||
taskManager: plugins.taskManager,
|
||||
taskRunnerFactory: this.taskRunnerFactory,
|
||||
|
|
|
@ -17,6 +17,7 @@ import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock';
|
|||
import { alertsServiceMock } from './alerts_service/alerts_service.mock';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { RecoveredActionGroupId } from '../common';
|
||||
import { AlertingConfig } from './config';
|
||||
|
||||
const logger = loggingSystemMock.create().get();
|
||||
let mockedLicenseState: jest.Mocked<ILicenseState>;
|
||||
|
@ -30,6 +31,7 @@ beforeEach(() => {
|
|||
jest.clearAllMocks();
|
||||
mockedLicenseState = licenseStateMock.create();
|
||||
ruleTypeRegistryParams = {
|
||||
config: {} as AlertingConfig,
|
||||
logger,
|
||||
taskManager,
|
||||
taskRunnerFactory: new TaskRunnerFactory(),
|
||||
|
@ -582,6 +584,63 @@ describe('Create Lifecycle', () => {
|
|||
|
||||
expect(alertsService.register).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('registers rule with no overwrite on producer', () => {
|
||||
const ruleType: RuleType<never, never, never, never, never, 'default', 'recovered', {}> = {
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
minimumLicenseRequired: 'basic',
|
||||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
category: 'test',
|
||||
producer: 'alerts',
|
||||
ruleTaskTimeout: '20m',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
registry.register(ruleType);
|
||||
expect(registry.get('test').producer).toEqual('alerts');
|
||||
});
|
||||
});
|
||||
|
||||
describe('register() with overwriteProducer', () => {
|
||||
test('registers rule and overwrite producer', () => {
|
||||
const ruleType: RuleType<never, never, never, never, never, 'default', 'recovered', {}> = {
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
actionGroups: [
|
||||
{
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
},
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
minimumLicenseRequired: 'basic',
|
||||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
category: 'test',
|
||||
producer: 'alerts',
|
||||
ruleTaskTimeout: '20m',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry({
|
||||
...ruleTypeRegistryParams,
|
||||
config: { rules: { overwriteProducer: 'observability' } } as unknown as AlertingConfig,
|
||||
});
|
||||
registry.register(ruleType);
|
||||
expect(registry.get('test').producer).toEqual('observability');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get()', () => {
|
||||
|
|
|
@ -39,8 +39,10 @@ import { InMemoryMetrics } from './monitoring';
|
|||
import { AlertingRulesConfig } from '.';
|
||||
import { AlertsService } from './alerts_service/alerts_service';
|
||||
import { getRuleTypeIdValidLegacyConsumers } from './rule_type_registry_deprecated_consumers';
|
||||
import { AlertingConfig } from './config';
|
||||
|
||||
export interface ConstructorOptions {
|
||||
config: AlertingConfig;
|
||||
logger: Logger;
|
||||
taskManager: TaskManagerSetupContract;
|
||||
taskRunnerFactory: TaskRunnerFactory;
|
||||
|
@ -148,6 +150,7 @@ export type UntypedNormalizedRuleType = NormalizedRuleType<
|
|||
>;
|
||||
|
||||
export class RuleTypeRegistry {
|
||||
private readonly config: AlertingConfig;
|
||||
private readonly logger: Logger;
|
||||
private readonly taskManager: TaskManagerSetupContract;
|
||||
private readonly ruleTypes: Map<string, UntypedNormalizedRuleType> = new Map();
|
||||
|
@ -159,6 +162,7 @@ export class RuleTypeRegistry {
|
|||
private readonly alertsService: AlertsService | null;
|
||||
|
||||
constructor({
|
||||
config,
|
||||
logger,
|
||||
taskManager,
|
||||
taskRunnerFactory,
|
||||
|
@ -168,6 +172,7 @@ export class RuleTypeRegistry {
|
|||
inMemoryMetrics,
|
||||
alertsService,
|
||||
}: ConstructorOptions) {
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
this.taskManager = taskManager;
|
||||
this.taskRunnerFactory = taskRunnerFactory;
|
||||
|
@ -277,7 +282,7 @@ export class RuleTypeRegistry {
|
|||
ActionGroupIds,
|
||||
RecoveryActionGroupId,
|
||||
AlertData
|
||||
>(ruleType);
|
||||
>(ruleType, this.config);
|
||||
|
||||
this.ruleTypes.set(
|
||||
ruleTypeIdSchema.validate(ruleType.id),
|
||||
|
@ -457,7 +462,8 @@ function augmentActionGroupsWithReserved<
|
|||
ActionGroupIds,
|
||||
RecoveryActionGroupId,
|
||||
AlertData
|
||||
>
|
||||
>,
|
||||
config: AlertingConfig
|
||||
): NormalizedRuleType<
|
||||
Params,
|
||||
ExtractedParams,
|
||||
|
@ -505,6 +511,7 @@ function augmentActionGroupsWithReserved<
|
|||
|
||||
return {
|
||||
...ruleType,
|
||||
...(config?.rules?.overwriteProducer ? { producer: config.rules.overwriteProducer } : {}),
|
||||
actionGroups: [...actionGroups, ...reservedActionGroups],
|
||||
recoveryActionGroup: recoveryActionGroup ?? RecoveredActionGroup,
|
||||
validLegacyConsumers: getRuleTypeIdValidLegacyConsumers(id),
|
||||
|
|
|
@ -15,6 +15,7 @@ import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
|
|||
import { isRuleExportable } from './is_rule_exportable';
|
||||
import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock';
|
||||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { AlertingConfig } from '../config';
|
||||
|
||||
let ruleTypeRegistryParams: ConstructorOptions;
|
||||
let logger: MockedLogger;
|
||||
|
@ -27,6 +28,7 @@ beforeEach(() => {
|
|||
mockedLicenseState = licenseStateMock.create();
|
||||
logger = loggerMock.create();
|
||||
ruleTypeRegistryParams = {
|
||||
config: {} as AlertingConfig,
|
||||
logger: loggingSystemMock.create().get(),
|
||||
taskManager,
|
||||
alertsService: null,
|
||||
|
|
|
@ -16,6 +16,7 @@ import type { ActionGroup } from '@kbn/alerting-plugin/common';
|
|||
import { formatDurationFromTimeUnitChar } from '@kbn/observability-plugin/common';
|
||||
import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity';
|
||||
import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
ERROR_GROUP_ID,
|
||||
ERROR_GROUP_NAME,
|
||||
|
@ -28,13 +29,6 @@ import { getEnvironmentLabel } from '../environment_filter_values';
|
|||
|
||||
export const APM_SERVER_FEATURE_ID = 'apm';
|
||||
|
||||
export enum ApmRuleType {
|
||||
ErrorCount = 'apm.error_rate', // ErrorRate was renamed to ErrorCount but the key is kept as `error_rate` for backwards-compat.
|
||||
TransactionErrorRate = 'apm.transaction_error_rate',
|
||||
TransactionDuration = 'apm.transaction_duration',
|
||||
Anomaly = 'apm.anomaly',
|
||||
}
|
||||
|
||||
export enum AggregationType {
|
||||
Avg = 'avg',
|
||||
P95 = '95th',
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { union } from 'lodash';
|
||||
import { ApmRuleType } from './apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
SERVICE_ENVIRONMENT,
|
||||
SERVICE_NAME,
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity';
|
||||
import { AggregationType, ApmRuleType } from './apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { AggregationType } from './apm_rule_types';
|
||||
|
||||
export const searchConfigurationSchema = schema.object({
|
||||
query: schema.object({
|
||||
|
|
|
@ -7,13 +7,12 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { lazy } from 'react';
|
||||
import { ALERT_REASON } from '@kbn/rule-data-utils';
|
||||
import { ALERT_REASON, ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import type { ObservabilityRuleTypeRegistry } from '@kbn/observability-plugin/public';
|
||||
import {
|
||||
getAlertUrlErrorCount,
|
||||
getAlertUrlTransaction,
|
||||
} from '../../../../common/utils/formatters';
|
||||
import { ApmRuleType } from '../../../../common/rules/apm_rule_types';
|
||||
import {
|
||||
anomalyMessage,
|
||||
errorCountMessage,
|
||||
|
|
|
@ -7,10 +7,8 @@
|
|||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import {
|
||||
ApmRuleType,
|
||||
APM_SERVER_FEATURE_ID,
|
||||
} from '../../../../../common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { APM_SERVER_FEATURE_ID } from '../../../../../common/rules/apm_rule_types';
|
||||
import { getInitialAlertValues } from '../../utils/get_initial_alert_values';
|
||||
import { ApmPluginStartDeps } from '../../../../plugin';
|
||||
import { useServiceName } from '../../../../hooks/use_service_name';
|
||||
|
@ -70,6 +68,7 @@ export function AlertingFlyout(props: Props) {
|
|||
start,
|
||||
end,
|
||||
} as AlertMetadata,
|
||||
useRuleProducer: true,
|
||||
}),
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
[
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
*/
|
||||
|
||||
import { getInitialAlertValues } from './get_initial_alert_values';
|
||||
import {
|
||||
ApmRuleType,
|
||||
RULE_TYPES_CONFIG,
|
||||
} from '../../../../common/rules/apm_rule_types';
|
||||
import { RULE_TYPES_CONFIG } from '../../../../common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
|
||||
test('handles null rule type and undefined service name', () => {
|
||||
expect(getInitialAlertValues(null, undefined)).toEqual({ tags: ['apm'] });
|
||||
|
|
|
@ -5,10 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
ApmRuleType,
|
||||
RULE_TYPES_CONFIG,
|
||||
} from '../../../../common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { RULE_TYPES_CONFIG } from '../../../../common/rules/apm_rule_types';
|
||||
|
||||
export function getInitialAlertValues(
|
||||
ruleType: ApmRuleType | null,
|
||||
|
|
|
@ -110,7 +110,7 @@ export function AlertsOverview() {
|
|||
}
|
||||
id={'service-overview-alerts'}
|
||||
configurationId={AlertConsumers.OBSERVABILITY}
|
||||
featureIds={[AlertConsumers.APM]}
|
||||
featureIds={[AlertConsumers.APM, AlertConsumers.OBSERVABILITY]}
|
||||
query={esQuery}
|
||||
showAlertStatusWithFlapping
|
||||
/>
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState } from 'react';
|
||||
import { ApmRuleType } from '../../../../../common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { AlertingFlyout } from '../../../alerting/ui_components/alerting_flyout';
|
||||
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
|
||||
|
||||
|
|
|
@ -14,10 +14,8 @@ import {
|
|||
} from '@kbn/licensing-plugin/server';
|
||||
|
||||
import { APM_INDEX_SETTINGS_SAVED_OBJECT_TYPE } from '@kbn/apm-data-access-plugin/server/saved_objects/apm_indices';
|
||||
import {
|
||||
ApmRuleType,
|
||||
APM_SERVER_FEATURE_ID,
|
||||
} from '../common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { APM_SERVER_FEATURE_ID } from '../common/rules/apm_rule_types';
|
||||
|
||||
const ruleTypes = Object.values(ApmRuleType);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_REASON,
|
||||
ALERT_SEVERITY,
|
||||
ApmRuleType,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { createLifecycleRuleTypeFactory } from '@kbn/rule-registry-plugin/server';
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/common';
|
||||
|
@ -43,7 +44,7 @@ import {
|
|||
} from '../../../../../common/environment_filter_values';
|
||||
import {
|
||||
ANOMALY_ALERT_SEVERITY_TYPES,
|
||||
ApmRuleType,
|
||||
APM_SERVER_FEATURE_ID,
|
||||
formatAnomalyReason,
|
||||
RULE_TYPES_CONFIG,
|
||||
} from '../../../../../common/rules/apm_rule_types';
|
||||
|
@ -94,7 +95,7 @@ export function registerAnomalyRuleType({
|
|||
],
|
||||
},
|
||||
category: DEFAULT_APP_CATEGORIES.observability.id,
|
||||
producer: 'apm',
|
||||
producer: APM_SERVER_FEATURE_ID,
|
||||
minimumLicenseRequired: 'basic',
|
||||
isExportable: true,
|
||||
executor: async ({
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
termQuery,
|
||||
} from '@kbn/observability-plugin/server';
|
||||
import { ProcessorEvent } from '@kbn/observability-plugin/common';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
ERROR_GROUP_ID,
|
||||
PROCESSOR_EVENT,
|
||||
|
@ -21,7 +22,6 @@ import { environmentQuery } from '../../../../../common/utils/environment_query'
|
|||
import { APMEventClient } from '../../../../lib/helpers/create_es_client/create_apm_event_client';
|
||||
import { getGroupByTerms } from '../utils/get_groupby_terms';
|
||||
import { getAllGroupByFields } from '../../../../../common/rules/get_all_groupby_fields';
|
||||
import { ApmRuleType } from '../../../../../common/rules/apm_rule_types';
|
||||
import {
|
||||
BarSeriesDataMap,
|
||||
getFilteredBarSeries,
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_REASON,
|
||||
ApmRuleType,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { createLifecycleRuleTypeFactory } from '@kbn/rule-registry-plugin/server';
|
||||
import {
|
||||
|
@ -34,7 +35,6 @@ import {
|
|||
SERVICE_NAME,
|
||||
} from '../../../../../common/es_fields/apm';
|
||||
import {
|
||||
ApmRuleType,
|
||||
APM_SERVER_FEATURE_ID,
|
||||
formatErrorCountReason,
|
||||
RULE_TYPES_CONFIG,
|
||||
|
|
|
@ -11,10 +11,8 @@ import {
|
|||
rangeQuery,
|
||||
termQuery,
|
||||
} from '@kbn/observability-plugin/server';
|
||||
import {
|
||||
AggregationType,
|
||||
ApmRuleType,
|
||||
} from '../../../../../common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { AggregationType } from '../../../../../common/rules/apm_rule_types';
|
||||
import {
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_TYPE,
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_REASON,
|
||||
ApmRuleType,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { createLifecycleRuleTypeFactory } from '@kbn/rule-registry-plugin/server';
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/common';
|
||||
|
@ -38,7 +39,6 @@ import {
|
|||
TRANSACTION_TYPE,
|
||||
} from '../../../../../common/es_fields/apm';
|
||||
import {
|
||||
ApmRuleType,
|
||||
APM_SERVER_FEATURE_ID,
|
||||
formatTransactionDurationReason,
|
||||
RULE_TYPES_CONFIG,
|
||||
|
@ -87,9 +87,9 @@ export const transactionDurationActionVariables = [
|
|||
|
||||
export function registerTransactionDurationRuleType({
|
||||
alerting,
|
||||
apmConfig,
|
||||
ruleDataClient,
|
||||
getApmIndices,
|
||||
apmConfig,
|
||||
logger,
|
||||
basePath,
|
||||
}: RegisterRuleDependencies) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
rangeQuery,
|
||||
termQuery,
|
||||
} from '@kbn/observability-plugin/server';
|
||||
import { ApmRuleType } from '../../../../../common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_TYPE,
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_REASON,
|
||||
ApmRuleType,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { createLifecycleRuleTypeFactory } from '@kbn/rule-registry-plugin/server';
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/common';
|
||||
|
@ -39,7 +40,6 @@ import {
|
|||
} from '../../../../../common/es_fields/apm';
|
||||
import { EventOutcome } from '../../../../../common/event_outcome';
|
||||
import {
|
||||
ApmRuleType,
|
||||
APM_SERVER_FEATURE_ID,
|
||||
formatTransactionErrorRateReason,
|
||||
RULE_TYPES_CONFIG,
|
||||
|
@ -83,9 +83,9 @@ export const transactionErrorRateActionVariables = [
|
|||
export function registerTransactionErrorRateRuleType({
|
||||
alerting,
|
||||
alertsLocator,
|
||||
apmConfig,
|
||||
basePath,
|
||||
getApmIndices,
|
||||
apmConfig,
|
||||
logger,
|
||||
ruleDataClient,
|
||||
}: RegisterRuleDependencies) {
|
||||
|
|
|
@ -56,7 +56,9 @@ export const createRuleTypeMocks = () => {
|
|||
publicBaseUrl: 'http://localhost:5601/eyr',
|
||||
serverBasePath: '/eyr',
|
||||
} as IBasePath,
|
||||
apmConfig: { searchAggregatedTransactions: true } as any as APMConfig,
|
||||
apmConfig: {
|
||||
searchAggregatedTransactions: true,
|
||||
} as any as APMConfig,
|
||||
getApmIndices: async () => ({
|
||||
error: 'apm-*',
|
||||
transaction: 'apm-*',
|
||||
|
|
|
@ -60,4 +60,5 @@ export const observabilityAlertFeatureIds: ValidFeatureId[] = [
|
|||
export const observabilityRuleCreationValidConsumers: RuleCreationValidConsumer[] = [
|
||||
AlertConsumers.INFRASTRUCTURE,
|
||||
AlertConsumers.LOGS,
|
||||
AlertConsumers.OBSERVABILITY,
|
||||
];
|
||||
|
|
|
@ -39,6 +39,7 @@ export function AlertFlyout(props: Props) {
|
|||
series: props.series,
|
||||
},
|
||||
validConsumers: observabilityRuleCreationValidConsumers,
|
||||
useRuleProducer: true,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[triggersActionsUI, onCloseFlyout]
|
||||
|
|
|
@ -9,6 +9,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||
import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common';
|
||||
|
||||
import { ALL_VALUE, SLOResponse } from '@kbn/slo-schema';
|
||||
import { AlertConsumers } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names';
|
||||
import { useKibana } from '../../utils/kibana_react';
|
||||
import { sloKeys } from './query_key_factory';
|
||||
|
||||
|
@ -80,7 +81,7 @@ export function useFetchActiveAlerts({ sloIdsAndInstanceIds = [] }: Params): Use
|
|||
try {
|
||||
const response = await http.post<FindApiResponse>(`${BASE_RAC_ALERTS_API_PATH}/find`, {
|
||||
body: JSON.stringify({
|
||||
feature_ids: ['slo'],
|
||||
feature_ids: [AlertConsumers.SLO, AlertConsumers.OBSERVABILITY],
|
||||
size: 0,
|
||||
query: {
|
||||
bool: {
|
||||
|
|
|
@ -250,6 +250,7 @@ export function HeaderControl({ isLoading, slo }: Props) {
|
|||
canChangeTrigger={false}
|
||||
onClose={onCloseRuleFlyout}
|
||||
initialValues={{ name: `${slo.name} burn rate`, params: { sloId: slo.id } }}
|
||||
useRuleProducer
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ export function SloDetailsAlerts({ slo }: Props) {
|
|||
configurationId={AlertConsumers.OBSERVABILITY}
|
||||
id={ALERTS_TABLE_ID}
|
||||
data-test-subj="alertTable"
|
||||
featureIds={[AlertConsumers.SLO]}
|
||||
featureIds={[AlertConsumers.SLO, AlertConsumers.OBSERVABILITY]}
|
||||
query={{
|
||||
bool: {
|
||||
filter: [
|
||||
|
|
|
@ -264,6 +264,7 @@ export function SloEditForm({ slo }: Props) {
|
|||
ruleTypeId={SLO_BURN_RATE_RULE_TYPE_ID}
|
||||
onClose={handleCloseRuleFlyout}
|
||||
onSave={handleCloseRuleFlyout}
|
||||
useRuleProducer
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
|
|
|
@ -276,6 +276,7 @@ export function SloListItem({
|
|||
onClose={() => {
|
||||
setIsAddRuleFlyoutOpen(false);
|
||||
}}
|
||||
useRuleProducer
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ const configSchema = schema.object({
|
|||
groupByPageSize: schema.number({ defaultValue: 10_000 }),
|
||||
}),
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
createO11yGenericFeatureId: schema.boolean({ defaultValue: false }),
|
||||
});
|
||||
|
||||
export const config: PluginConfigDescriptor = {
|
||||
|
|
|
@ -26,6 +26,11 @@ import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/serve
|
|||
import { SharePluginSetup } from '@kbn/share-plugin/server';
|
||||
import { SpacesPluginSetup } from '@kbn/spaces-plugin/server';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import {
|
||||
ApmRuleType,
|
||||
ES_QUERY_ID,
|
||||
OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { ObservabilityConfig } from '.';
|
||||
import { casesFeatureId, observabilityFeatureId, sloFeatureId } from '../common';
|
||||
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../common/constants';
|
||||
|
@ -72,6 +77,13 @@ interface PluginStart {
|
|||
|
||||
const sloRuleTypes = [SLO_BURN_RATE_RULE_TYPE_ID];
|
||||
|
||||
const o11yRuleTypes = [
|
||||
SLO_BURN_RATE_RULE_TYPE_ID,
|
||||
OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
ES_QUERY_ID,
|
||||
...Object.values(ApmRuleType),
|
||||
];
|
||||
|
||||
export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
||||
private logger: Logger;
|
||||
|
||||
|
@ -180,6 +192,58 @@ export class ObservabilityPlugin implements Plugin<ObservabilityPluginSetup> {
|
|||
});
|
||||
}
|
||||
|
||||
if (config.createO11yGenericFeatureId) {
|
||||
plugins.features.registerKibanaFeature({
|
||||
id: observabilityFeatureId,
|
||||
name: i18n.translate('xpack.observability.nameFeatureTitle', {
|
||||
defaultMessage: 'Observability',
|
||||
}),
|
||||
order: 1000,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
app: [observabilityFeatureId],
|
||||
catalogue: [observabilityFeatureId],
|
||||
alerting: o11yRuleTypes,
|
||||
privileges: {
|
||||
all: {
|
||||
app: [observabilityFeatureId],
|
||||
catalogue: [observabilityFeatureId],
|
||||
api: ['rac'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
alerting: {
|
||||
rule: {
|
||||
all: o11yRuleTypes,
|
||||
},
|
||||
alert: {
|
||||
all: o11yRuleTypes,
|
||||
},
|
||||
},
|
||||
ui: ['read', 'write'],
|
||||
},
|
||||
read: {
|
||||
app: [observabilityFeatureId],
|
||||
catalogue: [observabilityFeatureId],
|
||||
api: ['rac'],
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
alerting: {
|
||||
rule: {
|
||||
read: o11yRuleTypes,
|
||||
},
|
||||
alert: {
|
||||
read: o11yRuleTypes,
|
||||
},
|
||||
},
|
||||
ui: ['read'],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const { ruleDataService } = plugins.ruleRegistry;
|
||||
|
||||
const savedObjectTypes = [SO_SLO_TYPE];
|
||||
|
|
|
@ -22,7 +22,6 @@ export class AlertingBuiltinsPlugin
|
|||
|
||||
public setup(core: CoreSetup<StackAlertsStartDeps>, { alerting, features }: StackAlertsDeps) {
|
||||
features.registerKibanaFeature(BUILT_IN_ALERTS_FEATURE);
|
||||
|
||||
registerBuiltInRuleTypes({
|
||||
logger: this.logger,
|
||||
data: core
|
||||
|
|
|
@ -59,6 +59,7 @@ import {
|
|||
RuleActionAlertsFilterProperty,
|
||||
} from '@kbn/alerting-plugin/common';
|
||||
import { AlertingConnectorFeatureId } from '@kbn/actions-plugin/common';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { RuleReducerAction, InitialRule } from './rule_reducer';
|
||||
import {
|
||||
RuleTypeModel,
|
||||
|
@ -250,11 +251,11 @@ export const RuleForm = ({
|
|||
validConsumers,
|
||||
})
|
||||
)
|
||||
.filter((item) =>
|
||||
rule.consumer === ALERTS_FEATURE_ID
|
||||
.filter((item) => {
|
||||
return rule.consumer === ALERTS_FEATURE_ID
|
||||
? !item.ruleTypeModel.requiresAppContext
|
||||
: item.ruleType!.producer === rule.consumer
|
||||
);
|
||||
: item.ruleType!.producer === rule.consumer;
|
||||
});
|
||||
|
||||
const availableRuleTypesResult = getAvailableRuleTypes(ruleTypes);
|
||||
setAvailableRuleTypes(availableRuleTypesResult);
|
||||
|
@ -274,9 +275,13 @@ export const RuleForm = ({
|
|||
},
|
||||
new Map()
|
||||
);
|
||||
setSolutions(
|
||||
new Map([...solutionsResult.entries()].sort(([, a], [, b]) => a.localeCompare(b)))
|
||||
);
|
||||
const solutionsEntries = [...solutionsResult.entries()];
|
||||
const isOnlyO11y =
|
||||
availableRuleTypesResult.length === 1 &&
|
||||
availableRuleTypesResult.every((rt) => rt.ruleType.producer === AlertConsumers.OBSERVABILITY);
|
||||
if (!isOnlyO11y) {
|
||||
setSolutions(new Map(solutionsEntries.sort(([, a], [, b]) => a.localeCompare(b))));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
ruleTypes,
|
||||
|
@ -400,6 +405,16 @@ export const RuleForm = ({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ruleTypeRegistry, availableRuleTypes, searchText, JSON.stringify(solutionsFilter)]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ruleTypeModel) {
|
||||
const ruleType = ruleTypes.find((rt) => rt.id === ruleTypeModel.id);
|
||||
if (ruleType && useRuleProducer && !MULTI_CONSUMER_RULE_TYPE_IDS.includes(ruleType.id)) {
|
||||
setConsumer(ruleType.producer as RuleCreationValidConsumer);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ruleTypeModel, ruleTypes]);
|
||||
|
||||
const authorizedConsumers = useMemo(() => {
|
||||
// If the app context provides a consumer, we assume that consumer is
|
||||
// is what we set for all rules that is created in that context
|
||||
|
|
|
@ -80,4 +80,17 @@ describe('RuleFormConsumerSelectionModal', () => {
|
|||
expect(mockOnChange).toHaveBeenLastCalledWith('stackAlerts');
|
||||
expect(screen.queryByTestId('ruleFormConsumerSelect')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display nothing if observability is one of the consumer', () => {
|
||||
render(
|
||||
<RuleFormConsumerSelection
|
||||
consumers={['logs', 'observability']}
|
||||
onChange={mockOnChange}
|
||||
errors={{}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(mockOnChange).toHaveBeenLastCalledWith('observability');
|
||||
expect(screen.queryByTestId('ruleFormConsumerSelect')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -121,13 +121,15 @@ export const RuleFormConsumerSelection = (props: RuleFormConsumerSelectionProps)
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (formattedSelectOptions.length === 1) {
|
||||
onChange(formattedSelectOptions[0].value as RuleCreationValidConsumer);
|
||||
if (consumers.length === 1) {
|
||||
onChange(consumers[0] as RuleCreationValidConsumer);
|
||||
} else if (consumers.includes(AlertConsumers.OBSERVABILITY)) {
|
||||
onChange(AlertConsumers.OBSERVABILITY as RuleCreationValidConsumer);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [formattedSelectOptions]);
|
||||
}, [consumers]);
|
||||
|
||||
if (formattedSelectOptions.length <= 1) {
|
||||
if (consumers.length <= 1 || consumers.includes(AlertConsumers.OBSERVABILITY)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
|
|
@ -838,4 +838,5 @@ export interface NotifyWhenSelectOptions {
|
|||
export type RuleCreationValidConsumer =
|
||||
| typeof AlertConsumers.LOGS
|
||||
| typeof AlertConsumers.INFRASTRUCTURE
|
||||
| typeof AlertConsumers.OBSERVABILITY
|
||||
| typeof STACK_ALERTS_FEATURE_ID;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import expect from '@kbn/expect';
|
||||
import { range } from 'lodash';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { errorCountActionVariables } from '@kbn/apm-plugin/server/routes/alerts/rule_types/error_count/register_error_count_rule_type';
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance';
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Client, errors } from '@elastic/elasticsearch';
|
|||
import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common';
|
||||
import pRetry from 'p-retry';
|
||||
import type { SuperTest, Test } from 'supertest';
|
||||
import { ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { ApmRuleParamsType } from '@kbn/apm-plugin/common/rules/schema';
|
||||
import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type';
|
||||
import { RollupInterval } from '@kbn/apm-plugin/common/rollup';
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AggregationType, ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { AggregationType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { transactionDurationActionVariables } from '@kbn/apm-plugin/server/routes/alerts/rule_types/transaction_duration/register_transaction_duration_rule_type';
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import expect from '@kbn/expect';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { transactionErrorRateActionVariables } from '@kbn/apm-plugin/server/routes/alerts/rule_types/transaction_error_rate/register_transaction_error_rate_rule_type';
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import expect from '@kbn/expect';
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { AggregationType, ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { AggregationType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import {
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import expect from '@kbn/expect';
|
||||
import { AggregationType, ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { AggregationType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import {
|
||||
|
|
|
@ -11,7 +11,8 @@ import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregati
|
|||
import { ApmDocumentType, ApmTransactionDocumentType } from '@kbn/apm-plugin/common/document_type';
|
||||
import { RollupInterval } from '@kbn/apm-plugin/common/rollup';
|
||||
import { apm, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { AggregationType, ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { AggregationType } from '@kbn/apm-plugin/common/rules/apm_rule_types';
|
||||
import { ApmRuleType } from '@kbn/rule-data-utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import {
|
||||
createApmRule,
|
||||
|
|
|
@ -12,7 +12,7 @@ import type {
|
|||
|
||||
import { MetricThresholdParams } from '@kbn/infra-plugin/common/alerting/metrics';
|
||||
import { ThresholdParams } from '@kbn/observability-plugin/common/custom_threshold_rule/types';
|
||||
|
||||
import { SloBurnRateRuleParams } from './slo_api';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export function AlertingApiProvider({ getService }: FtrProviderContext) {
|
||||
|
@ -117,7 +117,7 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) {
|
|||
}: {
|
||||
ruleTypeId: string;
|
||||
name: string;
|
||||
params: MetricThresholdParams | ThresholdParams;
|
||||
params: MetricThresholdParams | ThresholdParams | SloBurnRateRuleParams;
|
||||
actions?: any[];
|
||||
tags?: any[];
|
||||
schedule?: { interval: string };
|
||||
|
@ -140,5 +140,16 @@ export function AlertingApiProvider({ getService }: FtrProviderContext) {
|
|||
});
|
||||
return body;
|
||||
},
|
||||
|
||||
async findRule(ruleId: string) {
|
||||
if (!ruleId) {
|
||||
throw new Error(`'ruleId' is undefined`);
|
||||
}
|
||||
const response = await supertest
|
||||
.get('/api/alerting/rules/_find')
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo');
|
||||
return response.body.data.find((obj: any) => obj.id === ruleId);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { AlertingApiProvider } from './alerting_api';
|
|||
import { SamlToolsProvider } from './saml_tools';
|
||||
import { DataViewApiProvider } from './data_view_api';
|
||||
import { SvlCasesServiceProvider } from './svl_cases';
|
||||
import { SloApiProvider } from './slo_api';
|
||||
|
||||
export const services = {
|
||||
// deployment agnostic FTR services
|
||||
|
@ -24,6 +25,7 @@ export const services = {
|
|||
samlTools: SamlToolsProvider,
|
||||
dataViewApi: DataViewApiProvider,
|
||||
svlCases: SvlCasesServiceProvider,
|
||||
sloApi: SloApiProvider,
|
||||
};
|
||||
|
||||
export type InheritedFtrProviderContext = GenericFtrProviderContext<typeof services, {}>;
|
||||
|
|
89
x-pack/test_serverless/api_integration/services/slo_api.ts
Normal file
89
x-pack/test_serverless/api_integration/services/slo_api.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
type DurationUnit = 'm' | 'h' | 'd' | 'w' | 'M';
|
||||
|
||||
interface Duration {
|
||||
value: number;
|
||||
unit: DurationUnit;
|
||||
}
|
||||
|
||||
interface WindowSchema {
|
||||
id: string;
|
||||
burnRateThreshold: number;
|
||||
maxBurnRateThreshold: number;
|
||||
longWindow: Duration;
|
||||
shortWindow: Duration;
|
||||
actionGroup: string;
|
||||
}
|
||||
|
||||
export interface SloBurnRateRuleParams {
|
||||
sloId: string;
|
||||
windows: WindowSchema[];
|
||||
}
|
||||
|
||||
interface SloParams {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
indicator: {
|
||||
type: 'sli.kql.custom';
|
||||
params: {
|
||||
index: string;
|
||||
good: string;
|
||||
total: string;
|
||||
timestampField: string;
|
||||
};
|
||||
};
|
||||
timeWindow: {
|
||||
duration: string;
|
||||
type: string;
|
||||
};
|
||||
budgetingMethod: string;
|
||||
objective: {
|
||||
target: number;
|
||||
};
|
||||
groupBy: string;
|
||||
}
|
||||
|
||||
export function SloApiProvider({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const retry = getService('retry');
|
||||
const requestTimeout = 30 * 1000;
|
||||
const retryTimeout = 120 * 1000;
|
||||
|
||||
return {
|
||||
async create(slo: SloParams) {
|
||||
const { body } = await supertest
|
||||
.post(`/api/observability/slos`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo')
|
||||
.send(slo);
|
||||
|
||||
return body;
|
||||
},
|
||||
|
||||
async delete() {},
|
||||
|
||||
async waitForSloCreated({ sloId }: { sloId: string }) {
|
||||
if (!sloId) {
|
||||
throw new Error(`'sloId is undefined`);
|
||||
}
|
||||
return await retry.tryForTime(retryTimeout, async () => {
|
||||
const response = await supertest
|
||||
.get(`/api/observability/slos/${sloId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo')
|
||||
.timeout(requestTimeout);
|
||||
|
||||
return response.body;
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
|
@ -164,7 +164,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
groupBy: 'all',
|
||||
searchType: 'esQuery',
|
||||
},
|
||||
[ALERT_RULE_PRODUCER]: 'stackAlerts',
|
||||
[ALERT_RULE_PRODUCER]: hits1[ALERT_RULE_PRODUCER],
|
||||
[ALERT_RULE_REVISION]: 0,
|
||||
[ALERT_RULE_TYPE_ID]: '.es-query',
|
||||
[ALERT_RULE_TAGS]: [],
|
||||
|
|
|
@ -437,3 +437,20 @@ export async function snoozeRule({
|
|||
.expect(204);
|
||||
return body;
|
||||
}
|
||||
|
||||
export async function findRule({
|
||||
supertest,
|
||||
ruleId,
|
||||
}: {
|
||||
supertest: SuperTest<Test>;
|
||||
ruleId: string;
|
||||
}) {
|
||||
if (!ruleId) {
|
||||
throw new Error(`'ruleId' is undefined`);
|
||||
}
|
||||
const response = await supertest
|
||||
.get(`/api/alerting/rule/${ruleId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo');
|
||||
return response.body || {};
|
||||
}
|
||||
|
|
|
@ -962,7 +962,7 @@ function validateEventLog(event: any, params: ValidateEventLogParams) {
|
|||
id: params.ruleId,
|
||||
license: 'basic',
|
||||
category: params.ruleTypeId,
|
||||
ruleset: 'stackAlerts',
|
||||
ruleset: event?.rule.ruleset,
|
||||
name: params.name,
|
||||
});
|
||||
|
||||
|
|
|
@ -190,7 +190,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
groupBy: 'all',
|
||||
searchType: 'esQuery',
|
||||
},
|
||||
[ALERT_RULE_PRODUCER]: 'stackAlerts',
|
||||
[ALERT_RULE_PRODUCER]: alertDocument[ALERT_RULE_PRODUCER],
|
||||
[ALERT_RULE_REVISION]: 0,
|
||||
[ALERT_RULE_TYPE_ID]: '.es-query',
|
||||
[ALERT_RULE_TAGS]: [],
|
||||
|
@ -311,7 +311,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
groupBy: 'all',
|
||||
searchType: 'esQuery',
|
||||
},
|
||||
[ALERT_RULE_PRODUCER]: 'stackAlerts',
|
||||
[ALERT_RULE_PRODUCER]: alertDocument[ALERT_RULE_PRODUCER],
|
||||
[ALERT_RULE_REVISION]: 0,
|
||||
[ALERT_RULE_TYPE_ID]: '.es-query',
|
||||
[ALERT_RULE_TAGS]: [],
|
||||
|
@ -520,7 +520,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
groupBy: 'all',
|
||||
searchType: 'esQuery',
|
||||
},
|
||||
[ALERT_RULE_PRODUCER]: 'stackAlerts',
|
||||
[ALERT_RULE_PRODUCER]: alertDocument[ALERT_RULE_PRODUCER],
|
||||
[ALERT_RULE_REVISION]: 0,
|
||||
[ALERT_RULE_TYPE_ID]: '.es-query',
|
||||
[ALERT_RULE_TAGS]: [],
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { cleanup, generate } from '@kbn/infra-forge';
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const esClient = getService('es');
|
||||
const supertest = getService('supertest');
|
||||
const esDeleteAllIndices = getService('esDeleteAllIndices');
|
||||
const logger = getService('log');
|
||||
const alertingApi = getService('alertingApi');
|
||||
const dataViewApi = getService('dataViewApi');
|
||||
const sloApi = getService('sloApi');
|
||||
|
||||
describe('Burn rate rule', () => {
|
||||
const RULE_TYPE_ID = 'slo.rules.burnRate';
|
||||
// DATE_VIEW should match the index template:
|
||||
// x-pack/packages/kbn-infra-forge/src/data_sources/composable/template.json
|
||||
const DATE_VIEW = 'kbn-data-forge-fake_hosts';
|
||||
const ALERT_ACTION_INDEX = 'alert-action-slo';
|
||||
const DATA_VIEW_ID = 'data-view-id';
|
||||
let infraDataIndex: string;
|
||||
let actionId: string;
|
||||
let ruleId: string;
|
||||
|
||||
before(async () => {
|
||||
infraDataIndex = await generate({ esClient, lookback: 'now-15m', logger });
|
||||
await dataViewApi.create({
|
||||
name: DATE_VIEW,
|
||||
id: DATA_VIEW_ID,
|
||||
title: DATE_VIEW,
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await supertest
|
||||
.delete(`/api/alerting/rule/${ruleId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo');
|
||||
await supertest
|
||||
.delete(`/api/actions/connector/${actionId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo');
|
||||
await esClient.deleteByQuery({
|
||||
index: '.kibana-event-log-*',
|
||||
query: { term: { 'rule.id': ruleId } },
|
||||
conflicts: 'proceed',
|
||||
});
|
||||
await dataViewApi.delete({
|
||||
id: DATA_VIEW_ID,
|
||||
});
|
||||
await supertest
|
||||
.delete('/api/observability/slos/my-custom-id')
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo');
|
||||
|
||||
await esDeleteAllIndices([ALERT_ACTION_INDEX, infraDataIndex]);
|
||||
await cleanup({ esClient, logger });
|
||||
});
|
||||
|
||||
describe('Rule creation', () => {
|
||||
it('creates rule successfully', async () => {
|
||||
actionId = await alertingApi.createIndexConnector({
|
||||
name: 'Index Connector: Slo Burn rate API test',
|
||||
indexName: ALERT_ACTION_INDEX,
|
||||
});
|
||||
|
||||
await sloApi.create({
|
||||
id: 'my-custom-id',
|
||||
name: 'my custom name',
|
||||
description: 'my custom description',
|
||||
indicator: {
|
||||
type: 'sli.kql.custom',
|
||||
params: {
|
||||
index: infraDataIndex,
|
||||
good: 'system.cpu.total.norm.pct > 1',
|
||||
total: 'system.cpu.total.norm.pct: *',
|
||||
timestampField: '@timestamp',
|
||||
},
|
||||
},
|
||||
timeWindow: {
|
||||
duration: '7d',
|
||||
type: 'rolling',
|
||||
},
|
||||
budgetingMethod: 'occurrences',
|
||||
objective: {
|
||||
target: 0.999,
|
||||
},
|
||||
groupBy: '*',
|
||||
});
|
||||
|
||||
const createdRule = await alertingApi.createRule({
|
||||
tags: ['observability'],
|
||||
consumer: 'observability',
|
||||
name: 'SLO Burn Rate rule',
|
||||
ruleTypeId: RULE_TYPE_ID,
|
||||
schedule: {
|
||||
interval: '1m',
|
||||
},
|
||||
params: {
|
||||
sloId: 'my-custom-id',
|
||||
windows: [
|
||||
{
|
||||
id: '1',
|
||||
actionGroup: 'slo.burnRate.alert',
|
||||
burnRateThreshold: 14.4,
|
||||
maxBurnRateThreshold: 720,
|
||||
longWindow: {
|
||||
value: 1,
|
||||
unit: 'h',
|
||||
},
|
||||
shortWindow: {
|
||||
value: 5,
|
||||
unit: 'm',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
actionGroup: 'slo.burnRate.high',
|
||||
burnRateThreshold: 6,
|
||||
maxBurnRateThreshold: 120,
|
||||
longWindow: {
|
||||
value: 6,
|
||||
unit: 'h',
|
||||
},
|
||||
shortWindow: {
|
||||
value: 30,
|
||||
unit: 'm',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
actionGroup: 'slo.burnRate.medium',
|
||||
burnRateThreshold: 3,
|
||||
maxBurnRateThreshold: 30,
|
||||
longWindow: {
|
||||
value: 24,
|
||||
unit: 'h',
|
||||
},
|
||||
shortWindow: {
|
||||
value: 120,
|
||||
unit: 'm',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
actionGroup: 'slo.burnRate.low',
|
||||
burnRateThreshold: 1,
|
||||
maxBurnRateThreshold: 10,
|
||||
longWindow: {
|
||||
value: 72,
|
||||
unit: 'h',
|
||||
},
|
||||
shortWindow: {
|
||||
value: 360,
|
||||
unit: 'm',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actions: [],
|
||||
});
|
||||
ruleId = createdRule.id;
|
||||
expect(ruleId).not.to.be(undefined);
|
||||
});
|
||||
|
||||
it('should be active', async () => {
|
||||
const executionStatus = await alertingApi.waitForRuleStatus({
|
||||
ruleId,
|
||||
expectedStatus: 'active',
|
||||
});
|
||||
expect(executionStatus).to.be('active');
|
||||
});
|
||||
|
||||
it('should find the created rule with correct information about the consumer', async () => {
|
||||
const match = await alertingApi.findRule(ruleId);
|
||||
expect(match).not.to.be(undefined);
|
||||
expect(match.consumer).to.be('observability');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -82,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const createdRule = await alertingApi.createRule({
|
||||
tags: ['observability'],
|
||||
consumer: 'apm',
|
||||
consumer: 'observability',
|
||||
name: 'Threshold rule',
|
||||
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
params: {
|
||||
|
@ -139,6 +139,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(executionStatus).to.be('active');
|
||||
});
|
||||
|
||||
it('should find the created rule with correct information about the consumer', async () => {
|
||||
const match = await alertingApi.findRule(ruleId);
|
||||
expect(match).not.to.be(undefined);
|
||||
expect(match.consumer).to.be('observability');
|
||||
});
|
||||
|
||||
it('should set correct information in the alert document', async () => {
|
||||
const resp = await alertingApi.waitForAlertInIndex({
|
||||
indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX,
|
||||
|
@ -149,7 +155,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'kibana.alert.rule.category',
|
||||
'Custom threshold (Technical Preview)'
|
||||
);
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'apm');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0);
|
||||
|
|
|
@ -68,7 +68,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const createdRule = await alertingApi.createRule({
|
||||
tags: ['observability'],
|
||||
consumer: 'apm',
|
||||
consumer: 'observability',
|
||||
name: 'Threshold rule',
|
||||
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
params: {
|
||||
|
@ -125,6 +125,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(executionStatus).to.be('active');
|
||||
});
|
||||
|
||||
it('should find the created rule with correct information about the consumer', async () => {
|
||||
const match = await alertingApi.findRule(ruleId);
|
||||
expect(match).not.to.be(undefined);
|
||||
expect(match.consumer).to.be('observability');
|
||||
});
|
||||
|
||||
it('should set correct information in the alert document', async () => {
|
||||
const resp = await alertingApi.waitForAlertInIndex({
|
||||
indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX,
|
||||
|
@ -135,7 +141,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'kibana.alert.rule.category',
|
||||
'Custom threshold (Technical Preview)'
|
||||
);
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'apm');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0);
|
||||
|
|
|
@ -84,7 +84,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const createdRule = await alertingApi.createRule({
|
||||
tags: ['observability'],
|
||||
consumer: 'apm',
|
||||
consumer: 'observability',
|
||||
name: 'Threshold rule',
|
||||
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
params: {
|
||||
|
@ -143,6 +143,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(executionStatus).to.be('active');
|
||||
});
|
||||
|
||||
it('should find the created rule with correct information about the consumer', async () => {
|
||||
const match = await alertingApi.findRule(ruleId);
|
||||
expect(match).not.to.be(undefined);
|
||||
expect(match.consumer).to.be('observability');
|
||||
});
|
||||
|
||||
it('should set correct information in the alert document', async () => {
|
||||
const resp = await alertingApi.waitForAlertInIndex({
|
||||
indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX,
|
||||
|
@ -153,7 +159,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'kibana.alert.rule.category',
|
||||
'Custom threshold (Technical Preview)'
|
||||
);
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'apm');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0);
|
||||
|
|
|
@ -78,7 +78,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const createdRule = await alertingApi.createRule({
|
||||
tags: ['observability'],
|
||||
consumer: 'apm',
|
||||
consumer: 'observability',
|
||||
name: 'Threshold rule',
|
||||
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
params: {
|
||||
|
@ -133,6 +133,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(executionStatus).to.be('active');
|
||||
});
|
||||
|
||||
it('should find the created rule with correct information about the consumer', async () => {
|
||||
const match = await alertingApi.findRule(ruleId);
|
||||
expect(match).not.to.be(undefined);
|
||||
expect(match.consumer).to.be('observability');
|
||||
});
|
||||
|
||||
it('should set correct information in the alert document', async () => {
|
||||
const resp = await alertingApi.waitForAlertInIndex({
|
||||
indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX,
|
||||
|
@ -143,7 +149,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'kibana.alert.rule.category',
|
||||
'Custom threshold (Technical Preview)'
|
||||
);
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'apm');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0);
|
||||
|
|
|
@ -88,7 +88,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const createdRule = await alertingApi.createRule({
|
||||
tags: ['observability'],
|
||||
consumer: 'apm',
|
||||
consumer: 'observability',
|
||||
name: 'Threshold rule',
|
||||
ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
params: {
|
||||
|
@ -150,6 +150,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(executionStatus).to.be('active');
|
||||
});
|
||||
|
||||
it('should find the created rule with correct information about the consumer', async () => {
|
||||
const match = await alertingApi.findRule(ruleId);
|
||||
expect(match).not.to.be(undefined);
|
||||
expect(match.consumer).to.be('observability');
|
||||
});
|
||||
|
||||
it('should set correct information in the alert document', async () => {
|
||||
const resp = await alertingApi.waitForAlertInIndex({
|
||||
indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX,
|
||||
|
@ -162,7 +168,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'kibana.alert.rule.category',
|
||||
'Custom threshold (Technical Preview)'
|
||||
);
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'apm');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability');
|
||||
expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0);
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { createEsQueryRule } from '../../common/alerting/helpers/alerting_api_helper';
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const esClient = getService('es');
|
||||
const supertest = getService('supertest');
|
||||
const esDeleteAllIndices = getService('esDeleteAllIndices');
|
||||
const alertingApi = getService('alertingApi');
|
||||
|
||||
describe('ElasticSearch query rule', () => {
|
||||
const RULE_TYPE_ID = '.es-query';
|
||||
const ALERT_ACTION_INDEX = 'alert-action-es-query';
|
||||
let actionId: string;
|
||||
let ruleId: string;
|
||||
|
||||
after(async () => {
|
||||
await supertest
|
||||
.delete(`/api/alerting/rule/${ruleId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo');
|
||||
await supertest
|
||||
.delete(`/api/actions/connector/${actionId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo');
|
||||
await esClient.deleteByQuery({
|
||||
index: '.kibana-event-log-*',
|
||||
query: { term: { 'rule.id': ruleId } },
|
||||
conflicts: 'proceed',
|
||||
});
|
||||
await esDeleteAllIndices([ALERT_ACTION_INDEX]);
|
||||
});
|
||||
|
||||
describe('Rule creation', () => {
|
||||
it('creates rule successfully', async () => {
|
||||
actionId = await alertingApi.createIndexConnector({
|
||||
name: 'Index Connector: Alerting API test',
|
||||
indexName: ALERT_ACTION_INDEX,
|
||||
});
|
||||
|
||||
const createdRule = await createEsQueryRule({
|
||||
supertest,
|
||||
consumer: 'observability',
|
||||
name: 'always fire',
|
||||
ruleTypeId: RULE_TYPE_ID,
|
||||
params: {
|
||||
size: 100,
|
||||
thresholdComparator: '>',
|
||||
threshold: [-1],
|
||||
index: ['alert-test-data'],
|
||||
timeField: 'date',
|
||||
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
|
||||
timeWindowSize: 20,
|
||||
timeWindowUnit: 's',
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
group: 'query matched',
|
||||
id: actionId,
|
||||
params: {
|
||||
documents: [
|
||||
{
|
||||
ruleId: '{{rule.id}}',
|
||||
ruleName: '{{rule.name}}',
|
||||
ruleParams: '{{rule.params}}',
|
||||
spaceId: '{{rule.spaceId}}',
|
||||
tags: '{{rule.tags}}',
|
||||
alertId: '{{alert.id}}',
|
||||
alertActionGroup: '{{alert.actionGroup}}',
|
||||
instanceContextValue: '{{context.instanceContextValue}}',
|
||||
instanceStateValue: '{{state.instanceStateValue}}',
|
||||
},
|
||||
],
|
||||
},
|
||||
frequency: {
|
||||
notify_when: 'onActiveAlert',
|
||||
throttle: null,
|
||||
summary: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
ruleId = createdRule.id;
|
||||
expect(ruleId).not.to.be(undefined);
|
||||
});
|
||||
|
||||
it('should be active', async () => {
|
||||
const executionStatus = await alertingApi.waitForRuleStatus({
|
||||
ruleId,
|
||||
expectedStatus: 'active',
|
||||
});
|
||||
expect(executionStatus).to.be('active');
|
||||
});
|
||||
|
||||
it('should find the created rule with correct information about the consumer', async () => {
|
||||
const match = await alertingApi.findRule(ruleId);
|
||||
expect(match).not.to.be(undefined);
|
||||
expect(match.consumer).to.be('observability');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -16,5 +16,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./telemetry/telemetry_config'));
|
||||
loadTestFile(require.resolve('./apm_api_integration/feature_flags.ts'));
|
||||
loadTestFile(require.resolve('./cases'));
|
||||
loadTestFile(require.resolve('./burn_rate_rule/burn_rate_rule'));
|
||||
loadTestFile(require.resolve('./es_query_rule/es_query_rule'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
createIndexConnector,
|
||||
snoozeRule,
|
||||
createLatencyThresholdRule,
|
||||
createEsQueryRule,
|
||||
} from '../../../../api_integration/test_suites/common/alerting/helpers/alerting_api_helper';
|
||||
|
||||
export default ({ getPageObject, getService }: FtrProviderContext) => {
|
||||
|
@ -90,6 +91,64 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
|
|||
await svlCommonPage.forceLogout();
|
||||
});
|
||||
|
||||
it('should create an ES Query Rule and display it when consumer is observability', async () => {
|
||||
const esQuery = await createEsQueryRule({
|
||||
supertest,
|
||||
name: 'ES Query',
|
||||
consumer: 'observability',
|
||||
ruleTypeId: '.es-query',
|
||||
params: {
|
||||
size: 100,
|
||||
thresholdComparator: '>',
|
||||
threshold: [-1],
|
||||
index: ['alert-test-data'],
|
||||
timeField: 'date',
|
||||
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
|
||||
timeWindowSize: 20,
|
||||
timeWindowUnit: 's',
|
||||
},
|
||||
});
|
||||
ruleIdList = [esQuery.id];
|
||||
|
||||
await refreshRulesList();
|
||||
const searchResults = await svlTriggersActionsUI.getRulesList();
|
||||
expect(searchResults.length).toEqual(1);
|
||||
expect(searchResults[0].name).toEqual('ES QueryElasticsearch query');
|
||||
});
|
||||
|
||||
it('should create an ES Query rule but not display it when consumer is stackAlerts', async () => {
|
||||
const esQuery = await createEsQueryRule({
|
||||
supertest,
|
||||
name: 'ES Query',
|
||||
consumer: 'stackAlerts',
|
||||
ruleTypeId: '.es-query',
|
||||
params: {
|
||||
size: 100,
|
||||
thresholdComparator: '>',
|
||||
threshold: [-1],
|
||||
index: ['alert-test-data'],
|
||||
timeField: 'date',
|
||||
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
|
||||
timeWindowSize: 20,
|
||||
timeWindowUnit: 's',
|
||||
},
|
||||
});
|
||||
ruleIdList = [esQuery.id];
|
||||
|
||||
await refreshRulesList();
|
||||
await testSubjects.missingOrFail('rule-row');
|
||||
});
|
||||
|
||||
it('should create and display an APM latency rule', async () => {
|
||||
const apmLatency = await createLatencyThresholdRule({ supertest, name: 'Apm latency' });
|
||||
ruleIdList = [apmLatency.id];
|
||||
|
||||
await refreshRulesList();
|
||||
const searchResults = await svlTriggersActionsUI.getRulesList();
|
||||
expect(searchResults.length).toEqual(1);
|
||||
expect(searchResults[0].name).toEqual('Apm latencyLatency threshold');
|
||||
});
|
||||
|
||||
it('should display rules in alphabetical order', async () => {
|
||||
const rule1 = await createRule({
|
||||
supertest,
|
||||
|
@ -600,7 +659,9 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
|
|||
await testSubjects.click('ruleTypeFilterButton');
|
||||
}
|
||||
|
||||
expect(await (await testSubjects.find('ruleType0Group')).getVisibleText()).toEqual('Apm');
|
||||
expect(await (await testSubjects.find('ruleType0Group')).getVisibleText()).toEqual(
|
||||
'Observability'
|
||||
);
|
||||
});
|
||||
|
||||
await testSubjects.click('ruleTypeapm.anomalyFilterOption');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue