Skip to content

Commit

Permalink
[Monitoring] Migrate data source for legacy alerts to monitoring data…
Browse files Browse the repository at this point in the history
… directly (#87377)

* License expiration

* Fetch legacy alert data from the source

* Add back in the one test file

* Remove deprecated code

* Fix up tests

* Add test files

* Fix i18n

* Update tests

* PR feedback

* Fix types and tests

* Fix license headers

* Remove unused function

* Fix faulty license expiration logic
  • Loading branch information
chrisronline authored Feb 9, 2021
1 parent 87212e6 commit 231610c
Show file tree
Hide file tree
Showing 32 changed files with 1,802 additions and 792 deletions.
51 changes: 50 additions & 1 deletion x-pack/plugins/monitoring/common/types/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
*/

import { Alert, AlertTypeParams, SanitizedAlert } from '../../../alerts/common';
import { AlertParamType, AlertMessageTokenType, AlertSeverity } from '../enums';
import {
AlertParamType,
AlertMessageTokenType,
AlertSeverity,
AlertClusterHealthType,
} from '../enums';

export type CommonAlert = Alert<AlertTypeParams> | SanitizedAlert<AlertTypeParams>;

Expand Down Expand Up @@ -60,6 +65,8 @@ export interface AlertInstanceState {
| AlertDiskUsageState
| AlertThreadPoolRejectionsState
| AlertNodeState
| AlertLicenseState
| AlertNodesChangedState
>;
[x: string]: unknown;
}
Expand All @@ -74,6 +81,7 @@ export interface AlertState {
export interface AlertNodeState extends AlertState {
nodeId: string;
nodeName?: string;
meta: any;
[key: string]: unknown;
}

Expand All @@ -96,6 +104,14 @@ export interface AlertThreadPoolRejectionsState extends AlertState {
nodeName?: string;
}

export interface AlertLicenseState extends AlertState {
expiryDateMS: number;
}

export interface AlertNodesChangedState extends AlertState {
node: AlertClusterStatsNode;
}

export interface AlertUiState {
isFiring: boolean;
resolvedMS?: number;
Expand Down Expand Up @@ -228,3 +244,36 @@ export interface LegacyAlertNodesChangedList {
added: { [nodeName: string]: string };
restarted: { [nodeName: string]: string };
}

export interface AlertLicense {
status: string;
type: string;
expiryDateMS: number;
clusterUuid: string;
ccs?: string;
}

export interface AlertClusterStatsNodes {
clusterUuid: string;
recentNodes: AlertClusterStatsNode[];
priorNodes: AlertClusterStatsNode[];
ccs?: string;
}

export interface AlertClusterStatsNode {
nodeUuid: string;
nodeEphemeralId?: string;
nodeName?: string;
}

export interface AlertClusterHealth {
health: AlertClusterHealthType;
clusterUuid: string;
ccs?: string;
}

export interface AlertVersions {
clusterUuid: string;
ccs?: string;
versions: string[];
}
6 changes: 5 additions & 1 deletion x-pack/plugins/monitoring/common/types/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ export interface ElasticsearchLegacySource {
cluster_state?: {
status?: string;
nodes?: {
[nodeUuid: string]: {};
[nodeUuid: string]: {
ephemeral_id?: string;
name?: string;
};
};
master_node?: boolean;
};
Expand All @@ -170,6 +173,7 @@ export interface ElasticsearchLegacySource {
license?: {
status?: string;
type?: string;
expiry_date_in_millis?: number;
};
logstash_state?: {
pipeline?: {
Expand Down
88 changes: 1 addition & 87 deletions x-pack/plugins/monitoring/server/alerts/base_alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,16 @@ import {
AlertEnableAction,
CommonAlertFilter,
CommonAlertParams,
LegacyAlert,
} from '../../common/types/alerts';
import { fetchAvailableCcs } from '../lib/alerts/fetch_available_ccs';
import { fetchClusters } from '../lib/alerts/fetch_clusters';
import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern';
import { INDEX_PATTERN_ELASTICSEARCH, INDEX_ALERTS } from '../../common/constants';
import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants';
import { AlertSeverity } from '../../common/enums';
import { MonitoringLicenseService } from '../types';
import { mbSafeQuery } from '../lib/mb_safe_query';
import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index';
import { parseDuration } from '../../../alerts/common/parse_duration';
import { Globals } from '../static_globals';
import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts';
import { mapLegacySeverity } from '../lib/alerts/map_legacy_severity';

interface LegacyOptions {
watchName: string;
nodeNameLabel: string;
changeDataValues?: Partial<AlertData>;
}

type ExecutedState =
| {
Expand All @@ -60,7 +50,6 @@ interface AlertOptions {
name: string;
throttle?: string | null;
interval?: string;
legacy?: LegacyOptions;
defaultParams?: Partial<CommonAlertParams>;
actionVariables: Array<{ name: string; description: string }>;
fetchClustersRange?: number;
Expand Down Expand Up @@ -126,16 +115,6 @@ export class BaseAlert {
};
}

public isEnabled(licenseService: MonitoringLicenseService) {
if (this.alertOptions.legacy) {
const watcherFeature = licenseService.getWatcherFeature();
if (!watcherFeature.isAvailable || !watcherFeature.isEnabled) {
return false;
}
}
return true;
}

public getId() {
return this.rawAlert?.id;
}
Expand Down Expand Up @@ -271,10 +250,6 @@ export class BaseAlert {
params as CommonAlertParams,
availableCcs
);
if (this.alertOptions.legacy) {
const data = await this.fetchLegacyData(callCluster, clusters, availableCcs);
return await this.processLegacyData(data, clusters, services, state);
}
const data = await this.fetchData(params, callCluster, clusters, availableCcs);
return await this.processData(data, clusters, services, state);
}
Expand Down Expand Up @@ -312,35 +287,6 @@ export class BaseAlert {
throw new Error('Child classes must implement `fetchData`');
}

protected async fetchLegacyData(
callCluster: CallCluster,
clusters: AlertCluster[],
availableCcs: string[]
): Promise<AlertData[]> {
let alertIndexPattern = INDEX_ALERTS;
if (availableCcs) {
alertIndexPattern = getCcsIndexPattern(alertIndexPattern, availableCcs);
}
const legacyAlerts = await fetchLegacyAlerts(
callCluster,
clusters,
alertIndexPattern,
this.alertOptions.legacy!.watchName,
Globals.app.config.ui.max_bucket_size
);

return legacyAlerts.map((legacyAlert) => {
return {
clusterUuid: legacyAlert.metadata.cluster_uuid,
shouldFire: !legacyAlert.resolved_timestamp,
severity: mapLegacySeverity(legacyAlert.metadata.severity),
meta: legacyAlert,
nodeName: this.alertOptions.legacy!.nodeNameLabel,
...this.alertOptions.legacy!.changeDataValues,
};
});
}

protected async processData(
data: AlertData[],
clusters: AlertCluster[],
Expand Down Expand Up @@ -395,34 +341,6 @@ export class BaseAlert {
return state;
}

protected async processLegacyData(
data: AlertData[],
clusters: AlertCluster[],
services: AlertServices<AlertInstanceState, never, 'default'>,
state: ExecutedState
) {
const currentUTC = +new Date();
for (const item of data) {
const instanceId = `${this.alertOptions.id}:${item.clusterUuid}`;
const instance = services.alertInstanceFactory(instanceId);
if (!item.shouldFire) {
instance.replaceState({ alertStates: [] });
continue;
}
const cluster = clusters.find((c: AlertCluster) => c.clusterUuid === item.clusterUuid);
const alertState: AlertState = this.getDefaultAlertState(cluster!, item);
alertState.nodeName = item.nodeName;
alertState.ui.triggeredMS = currentUTC;
alertState.ui.isFiring = true;
alertState.ui.severity = item.severity;
alertState.ui.message = this.getUiMessage(alertState, item);
instance.replaceState({ alertStates: [alertState] });
this.executeActions(instance, alertState, item, cluster);
}
state.lastChecked = currentUTC;
return state;
}

protected getDefaultAlertState(cluster: AlertCluster, item: AlertData): AlertState {
return {
cluster,
Expand All @@ -437,10 +355,6 @@ export class BaseAlert {
};
}

protected getVersions(legacyAlert: LegacyAlert) {
return `[${legacyAlert.message.match(/(?<=Versions: \[).+?(?=\])/)}]`;
}

protected getUiMessage(
alertState: AlertState | unknown,
item: AlertData | unknown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import { ClusterHealthAlert } from './cluster_health_alert';
import { ALERT_CLUSTER_HEALTH } from '../../common/constants';
import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts';
import { AlertClusterHealthType, AlertSeverity } from '../../common/enums';
import { fetchClusterHealth } from '../lib/alerts/fetch_cluster_health';
import { fetchClusters } from '../lib/alerts/fetch_clusters';

const RealDate = Date;
Expand All @@ -26,8 +27,8 @@ jest.mock('../static_globals', () => ({
},
}));

jest.mock('../lib/alerts/fetch_legacy_alerts', () => ({
fetchLegacyAlerts: jest.fn(),
jest.mock('../lib/alerts/fetch_cluster_health', () => ({
fetchClusterHealth: jest.fn(),
}));
jest.mock('../lib/alerts/fetch_clusters', () => ({
fetchClusters: jest.fn(),
Expand Down Expand Up @@ -63,16 +64,16 @@ describe('ClusterHealthAlert', () => {
function FakeDate() {}
FakeDate.prototype.valueOf = () => 1;

const ccs = undefined;
const clusterUuid = 'abc123';
const clusterName = 'testCluster';
const legacyAlert = {
prefix: 'Elasticsearch cluster status is yellow.',
message: 'Allocate missing replica shards.',
metadata: {
severity: 2000,
cluster_uuid: clusterUuid,
const healths = [
{
health: AlertClusterHealthType.Yellow,
clusterUuid,
ccs,
},
};
];

const replaceState = jest.fn();
const scheduleActions = jest.fn();
Expand All @@ -94,8 +95,8 @@ describe('ClusterHealthAlert', () => {
beforeEach(() => {
// @ts-ignore
Date = FakeDate;
(fetchLegacyAlerts as jest.Mock).mockImplementation(() => {
return [legacyAlert];
(fetchClusterHealth as jest.Mock).mockImplementation(() => {
return healths;
});
(fetchClusters as jest.Mock).mockImplementation(() => {
return [{ clusterUuid, clusterName }];
Expand All @@ -120,8 +121,15 @@ describe('ClusterHealthAlert', () => {
alertStates: [
{
cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' },
ccs: undefined,
nodeName: 'Elasticsearch cluster alert',
ccs,
itemLabel: undefined,
nodeId: undefined,
nodeName: undefined,
meta: {
ccs,
clusterUuid,
health: AlertClusterHealthType.Yellow,
},
ui: {
isFiring: true,
message: {
Expand All @@ -140,7 +148,7 @@ describe('ClusterHealthAlert', () => {
},
],
},
severity: 'danger',
severity: AlertSeverity.Warning,
triggeredMS: 1,
lastCheckedMS: 0,
},
Expand All @@ -160,9 +168,15 @@ describe('ClusterHealthAlert', () => {
});
});

it('should not fire actions if there is no legacy alert', async () => {
(fetchLegacyAlerts as jest.Mock).mockImplementation(() => {
return [];
it('should not fire actions if the cluster health is green', async () => {
(fetchClusterHealth as jest.Mock).mockImplementation(() => {
return [
{
health: AlertClusterHealthType.Green,
clusterUuid,
ccs,
},
];
});
const alert = new ClusterHealthAlert();
const type = alert.getAlertType();
Expand Down
Loading

0 comments on commit 231610c

Please sign in to comment.