Skip to content

Commit

Permalink
[Uptime] Include synthetics-* for existing alerts (#160063)
Browse files Browse the repository at this point in the history
(cherry picked from commit a2cc9a6)
  • Loading branch information
shahzad31 committed Jul 6, 2023
1 parent 9ee8cd6 commit e711dcd
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory<ActionGroupIds>
}) {
const uptimeEsClient = new UptimeEsClient(
savedObjectsClient,
scopedClusterClient.asCurrentUser
scopedClusterClient.asCurrentUser,
{
isLegacyAlert: true,
}
);
const { share, basePath } = server;
const alertsLocator: LocatorPublic<AlertsLocatorParams> | undefined =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,10 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (
share.url.locators.get(alertsLocatorID);
const uptimeEsClient = new UptimeEsClient(
savedObjectsClient,
scopedClusterClient.asCurrentUser
scopedClusterClient.asCurrentUser,
{
isLegacyAlert: true,
}
);

const filterString = await formatFilterString(uptimeEsClient, filters, search, libs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export const tlsAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (
spaceId,
startedAt,
state,
rule,
}) {
const { share, basePath } = _server;
const alertsLocator: LocatorPublic<AlertsLocatorParams> | undefined =
Expand All @@ -163,7 +164,10 @@ export const tlsAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (

const uptimeEsClient = new UptimeEsClient(
savedObjectsClient,
scopedClusterClient.asCurrentUser
scopedClusterClient.asCurrentUser,
{
isLegacyAlert: true,
}
);

const certExpirationThreshold =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ export const tlsLegacyAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (_s

const uptimeEsClient = new UptimeEsClient(
savedObjectsClient,
scopedClusterClient.asCurrentUser
scopedClusterClient.asCurrentUser,
{
isLegacyAlert: true,
}
);
const { certs, total }: CertResult = await libs.requests.getCerts({
uptimeEsClient,
Expand Down
176 changes: 176 additions & 0 deletions x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* 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 { UptimeEsClient } from './lib';
import { savedObjectsClientMock, uiSettingsServiceMock } from '@kbn/core/server/mocks';
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
import { savedObjectsAdapter } from './saved_objects';

describe('UptimeEsClient', () => {
let uptimeEsClient: UptimeEsClient;
const savedObjectsClient = savedObjectsClientMock.create();
const esClient = elasticsearchClientMock.createClusterClient().asInternalUser;

beforeEach(() => {
uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient);
});

afterEach(() => {
jest.clearAllMocks();
});

describe('search', () => {
it('should call baseESClient.search with correct parameters', async () => {
const mockSearchParams = {
body: {
query: {
match_all: {},
},
},
};

const result = await uptimeEsClient.search({
body: {
query: {
match_all: {},
},
},
});

expect(esClient.search).toHaveBeenCalledWith(
{
index: 'heartbeat-8*,heartbeat-7*',
...mockSearchParams,
},
{ meta: true }
);
expect(result).toEqual({
body: {},
headers: {
'x-elastic-product': 'Elasticsearch',
},
meta: {},
statusCode: 200,
warnings: [],
});
});

it('should throw an error if baseESClient.search throws an error', async () => {
const mockSearchParams = {
body: {
query: {
match_all: {},
},
},
};
const mockError = new Error('Search error');
esClient.search.mockRejectedValueOnce(mockError);

await expect(uptimeEsClient.search(mockSearchParams)).rejects.toThrow(mockError);
expect(esClient.search).toHaveBeenCalledWith(
{
index: 'heartbeat-8*,heartbeat-7*',
...mockSearchParams,
},
{ meta: true }
);
});
});

describe('count', () => {
it('should call baseESClient.count with correct parameters', async () => {
const mockCountParams = {
index: 'example',
};

const result = await uptimeEsClient.count(mockCountParams);

expect(esClient.count).toHaveBeenCalledWith(mockCountParams, { meta: true });
expect(result).toEqual({
indices: 'heartbeat-8*,heartbeat-7*',
result: {
body: {},
headers: {
'x-elastic-product': 'Elasticsearch',
},
meta: {},
statusCode: 200,
warnings: [],
},
});
});

it('should throw an error if baseESClient.count throws an error', async () => {
const mockCountParams = {
index: 'example',
};
const mockError = new Error('Count error');
esClient.count.mockRejectedValueOnce(mockError);

await expect(uptimeEsClient.count(mockCountParams)).rejects.toThrow(mockError);
expect(esClient.count).toHaveBeenCalledWith(mockCountParams, { meta: true });
});
});

describe('getInspectEnabled', () => {
it('should return false if uiSettings is not available', async () => {
const result = await uptimeEsClient.getInspectEnabled();

expect(result).toBe(false);
});

it('should return the value from uiSettings if available', async () => {
const mockUiSettings = uiSettingsServiceMock.createClient();
uptimeEsClient.uiSettings = {
client: mockUiSettings,
} as any;

// @ts-expect-error
mockUiSettings.get.mockReturnValue(true);

await uptimeEsClient.getInspectEnabled();

expect(uptimeEsClient.isInspectorEnabled).toBe(true);
expect(mockUiSettings.get).toHaveBeenCalledWith('observability:enableInspectEsQueries');
});
});
describe('heartbeatIndices', () => {
it('appends synthetics-* in index for legacy alerts', async () => {
savedObjectsAdapter.getUptimeDynamicSettings = jest.fn().mockResolvedValue({
heartbeatIndices: 'heartbeat-8*,heartbeat-7*',
syntheticsIndexRemoved: true,
});
uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient, { isLegacyAlert: true });

const mockSearchParams = {
body: {
query: {
match_all: {},
},
},
};

await uptimeEsClient.search({
body: {
query: {
match_all: {},
},
},
});

expect(esClient.search).toHaveBeenCalledWith(
{
index: 'heartbeat-8*,heartbeat-7*,synthetics-*',
...mockSearchParams,
},
{ meta: true }
);
});
});

// Add more tests for other methods and edge cases
});
33 changes: 22 additions & 11 deletions x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,8 @@ import { enableInspectEsQueries } from '@kbn/observability-plugin/common';
import { getInspectResponse } from '@kbn/observability-shared-plugin/common';
import { API_URLS } from '../../../common/constants';
import { UptimeServerSetup } from './adapters';
import { UMLicenseCheck } from './domains';
import { UptimeRequests } from './requests';
import { savedObjectsAdapter } from './saved_objects/saved_objects';

export interface UMDomainLibs {
requests: UptimeRequests;
license: UMLicenseCheck;
}

export type { UMServerLibs } from '../uptime_server';

export interface CountResponse {
Expand All @@ -55,6 +48,7 @@ export class UptimeEsClient {
inspectableEsQueries: InspectResponse = [];
uiSettings?: CoreRequestHandlerContext['uiSettings'];
savedObjectsClient: SavedObjectsClientContract;
isLegacyAlert?: boolean;

constructor(
savedObjectsClient: SavedObjectsClientContract,
Expand All @@ -64,9 +58,17 @@ export class UptimeEsClient {
uiSettings?: CoreRequestHandlerContext['uiSettings'];
request?: KibanaRequest;
heartbeatIndices?: string;
isLegacyAlert?: boolean;
}
) {
const { isDev = false, uiSettings, request, heartbeatIndices = '' } = options ?? {};
const {
isLegacyAlert,
isDev = false,
uiSettings,
request,
heartbeatIndices = '',
} = options ?? {};
this.isLegacyAlert = isLegacyAlert;
this.uiSettings = uiSettings;
this.baseESClient = esClient;
this.savedObjectsClient = savedObjectsClient;
Expand Down Expand Up @@ -197,11 +199,20 @@ export class UptimeEsClient {
}

async getIndices() {
// if isLegacyAlert appends synthetics-* if it's not already there
let indices = '';
let syntheticsIndexRemoved = false;
if (this.heartbeatIndices) {
return this.heartbeatIndices;
indices = this.heartbeatIndices;
} else {
const settings = await savedObjectsAdapter.getUptimeDynamicSettings(this.savedObjectsClient);
indices = settings?.heartbeatIndices || '';
syntheticsIndexRemoved = settings.syntheticsIndexRemoved ?? false;
}
if (this.isLegacyAlert && !indices.includes('synthetics-') && syntheticsIndexRemoved) {
indices = indices + ',synthetics-*';
}
const settings = await savedObjectsAdapter.getUptimeDynamicSettings(this.savedObjectsClient);
return settings?.heartbeatIndices || '';
return indices;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ describe('remove890Indices migration', () => {
attributes: {
...doc.attributes,
heartbeatIndices: 'heartbeat-8*,something_else',
syntheticsIndexRemoved: true,
},
});
});
Expand All @@ -129,6 +130,7 @@ describe('remove890Indices migration', () => {
attributes: {
...doc.attributes,
heartbeatIndices: 'something_else,heartbeat-8*',
syntheticsIndexRemoved: true,
},
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ export const remove890Indices: SavedObjectMigrationFn<
indicesArr.push('heartbeat-8*');
}

const syntheticsIndexRemoved = indexToRemove > -1;

return {
...doc,
attributes: {
...doc.attributes,
heartbeatIndices: indicesArr.join(','),
...(syntheticsIndexRemoved && { syntheticsIndexRemoved }),
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,5 @@
* 2.0.
*/

import { SavedObjectsType } from '@kbn/core/server';
import { i18n } from '@kbn/i18n';
import { add820Indices, remove890Indices } from './migrations';

export const settingsObjectType = 'uptime-dynamic-settings';
export const settingsObjectId = 'uptime-dynamic-settings-singleton';

export const umDynamicSettings: SavedObjectsType = {
name: settingsObjectType,
hidden: false,
namespaceType: 'single',
mappings: {
dynamic: false,
properties: {
/* Leaving these commented to make it clear that these fields exist, even though we don't want them indexed.
When adding new fields please add them here. If they need to be searchable put them in the uncommented
part of properties.
heartbeatIndices: {
type: 'keyword',
},
certAgeThreshold: {
type: 'long',
},
certExpirationThreshold: {
type: 'long',
},
defaultConnectors: {
type: 'keyword',
},
*/
},
},
management: {
importableAndExportable: true,
icon: 'uptimeApp',
getTitle: () =>
i18n.translate('xpack.synthetics.uptimeSettings.index', {
defaultMessage: 'Uptime Settings - Index',
}),
},
migrations: {
// Takes a pre 8.2.0 doc, and converts it to 8.2.0
'8.2.0': add820Indices,
'8.9.0': remove890Indices,
},
};
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -36846,7 +36846,6 @@
"xpack.synthetics.totalDuration.metrics": "Durée de l’étape",
"xpack.synthetics.totalDuration.transferSize": "Taille du transfert",
"xpack.synthetics.uptimeFeatureCatalogueTitle": "Uptime",
"xpack.synthetics.uptimeSettings.index": "Paramètres Uptime - Index",
"xpack.synthetics.wait": "Attendre",
"xpack.synthetics.waterfall.applyFilters.label": "Sélectionner un élément auquel appliquer le filtre",
"xpack.synthetics.waterfall.applyFilters.message": "Cliquer pour ajouter ou retirer le filtre",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -36827,7 +36827,6 @@
"xpack.synthetics.totalDuration.metrics": "ステップ期間",
"xpack.synthetics.totalDuration.transferSize": "転送サイズ",
"xpack.synthetics.uptimeFeatureCatalogueTitle": "アップタイム",
"xpack.synthetics.uptimeSettings.index": "アップタイム設定 - インデックス",
"xpack.synthetics.wait": "待機",
"xpack.synthetics.waterfall.applyFilters.label": "フィルターを適用するアイテムを選択",
"xpack.synthetics.waterfall.applyFilters.message": "クリックして、フィルターを追加または削除します",
Expand Down
Loading

0 comments on commit e711dcd

Please sign in to comment.