Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] [Synthetics] Handle private locations simultaneous edits !! (#195874) #199387

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,7 @@
"urls"
],
"synthetics-param": [],
"synthetics-private-location": [],
"synthetics-privates-locations": [],
"tag": [
"color",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3549,6 +3549,10 @@
"dynamic": false,
"properties": {}
},
"synthetics-private-location": {
"dynamic": false,
"properties": {}
},
"synthetics-privates-locations": {
"dynamic": false,
"properties": {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const STANDARD_LIST_TYPES = [
'synthetics-monitor',
'uptime-dynamic-settings',
'synthetics-privates-locations',
'synthetics-private-location',

'osquery-saved-query',
'osquery-pack',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
"synthetics-dynamic-settings": "4b40a93eb3e222619bf4e7fe34a9b9e7ab91a0a7",
"synthetics-monitor": "5ceb25b6249bd26902c9b34273c71c3dce06dbea",
"synthetics-param": "3ebb744e5571de678b1312d5c418c8188002cf5e",
"synthetics-private-location": "8cecc9e4f39637d2f8244eb7985c0690ceab24be",
"synthetics-privates-locations": "f53d799d5c9bc8454aaa32c6abc99a899b025d5c",
"tag": "e2544392fe6563e215bb677abc8b01c2601ef2dc",
"task": "3c89a7c918d5b896a5f8800f06e9114ad7e7aea3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const previouslyRegisteredTypes = [
'synthetics-monitor',
'synthetics-param',
'synthetics-privates-locations',
'synthetics-private-location',
'tag',
'task',
'telemetry',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
* 2.0.
*/

export const privateLocationsSavedObjectId = 'synthetics-privates-locations-singleton';
export const privateLocationsSavedObjectName = 'synthetics-privates-locations';
export const legacyPrivateLocationsSavedObjectId = 'synthetics-privates-locations-singleton';
export const legacyPrivateLocationsSavedObjectName = 'synthetics-privates-locations';

export const privateLocationSavedObjectName = 'synthetics-private-location';
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@

import { journey, step, before, after, expect } from '@elastic/synthetics';
import { waitForLoadingToFinish } from '@kbn/ux-plugin/e2e/journeys/utils';
import { SyntheticsServices } from './services/synthetics_services';
import { byTestId } from '../../helpers/utils';
import {
addTestMonitor,
cleanPrivateLocations,
cleanTestMonitors,
getPrivateLocations,
} from './services/add_monitor';
import { addTestMonitor, cleanPrivateLocations, cleanTestMonitors } from './services/add_monitor';
import { syntheticsAppPageProvider } from '../page_objects/synthetics_app';

journey(`PrivateLocationsSettings`, async ({ page, params }) => {
const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params });
const services = new SyntheticsServices(params);

page.setDefaultTimeout(2 * 30000);

Expand Down Expand Up @@ -78,16 +75,14 @@ journey(`PrivateLocationsSettings`, async ({ page, params }) => {
await page.click('text=Private Locations');
await page.click('h1:has-text("Settings")');

const privateLocations = await getPrivateLocations(params);
const privateLocations = await services.getPrivateLocations();

const locations = privateLocations.attributes.locations;
expect(privateLocations.length).toBe(1);

expect(locations.length).toBe(1);

locationId = locations[0].id;
locationId = privateLocations[0].id;

await addTestMonitor(params.kibanaUrl, 'test-monitor', {
locations: [locations[0]],
locations: [privateLocations[0]],
type: 'browser',
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

import axios from 'axios';
import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
import {
privateLocationsSavedObjectId,
privateLocationsSavedObjectName,
} from '@kbn/synthetics-plugin/common/saved_objects/private_locations';
import { legacyPrivateLocationsSavedObjectName } from '@kbn/synthetics-plugin/common/saved_objects/private_locations';

export const enableMonitorManagedViaApi = async (kibanaUrl: string) => {
try {
Expand Down Expand Up @@ -46,21 +43,6 @@ export const addTestMonitor = async (
}
};

export const getPrivateLocations = async (params: Record<string, any>) => {
const getService = params.getService;
const server = getService('kibanaServer');

try {
return await server.savedObjects.get({
id: privateLocationsSavedObjectId,
type: privateLocationsSavedObjectName,
});
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
};

export const cleanTestMonitors = async (params: Record<string, any>) => {
const getService = params.getService;
const server = getService('kibanaServer');
Expand All @@ -79,7 +61,11 @@ export const cleanPrivateLocations = async (params: Record<string, any>) => {

try {
await server.savedObjects.clean({
types: [privateLocationsSavedObjectName, 'ingest-agent-policies', 'ingest-package-policies'],
types: [
legacyPrivateLocationsSavedObjectName,
'ingest-agent-policies',
'ingest-package-policies',
],
});
} catch (e) {
// eslint-disable-next-line no-console
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import type { Client } from '@elastic/elasticsearch';
import { KbnClient } from '@kbn/test';
import pMap from 'p-map';
import { makeDownSummary, makeUpSummary } from '@kbn/observability-synthetics-test-data';
import { SyntheticsMonitor } from '@kbn/synthetics-plugin/common/runtime_types';
import {
SyntheticsMonitor,
SyntheticsPrivateLocations,
} from '@kbn/synthetics-plugin/common/runtime_types';
import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
import { journeyStart, journeySummary, step1, step2 } from './data/browser_docs';

Expand Down Expand Up @@ -251,4 +254,12 @@ export class SyntheticsServices {
});
return connector.data;
}

async getPrivateLocations(): Promise<SyntheticsPrivateLocations> {
const response = await this.requester.request({
path: SYNTHETICS_API_URLS.PRIVATE_LOCATIONS,
method: 'GET',
});
return response.data as SyntheticsPrivateLocations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
import { KibanaFeatureScope } from '@kbn/features-plugin/common';
import { syntheticsMonitorType, syntheticsParamType } from '../common/types/saved_objects';
import { SYNTHETICS_RULE_TYPES } from '../common/constants/synthetics_alerts';
import { privateLocationsSavedObjectName } from '../common/saved_objects/private_locations';
import {
legacyPrivateLocationsSavedObjectName,
privateLocationSavedObjectName,
} from '../common/saved_objects/private_locations';
import { PLUGIN } from '../common/constants/plugin';
import {
syntheticsSettingsObjectType,
Expand Down Expand Up @@ -71,7 +74,8 @@ export const syntheticsFeature = {
syntheticsSettingsObjectType,
syntheticsMonitorType,
syntheticsApiKeyObjectType,
privateLocationsSavedObjectName,
privateLocationSavedObjectName,
legacyPrivateLocationsSavedObjectName,
syntheticsParamType,
// uptime settings object is also registered here since feature is shared between synthetics and uptime
uptimeSettingsObjectType,
Expand Down Expand Up @@ -102,7 +106,7 @@ export const syntheticsFeature = {
syntheticsSettingsObjectType,
syntheticsMonitorType,
syntheticsApiKeyObjectType,
privateLocationsSavedObjectName,
legacyPrivateLocationsSavedObjectName,
// uptime settings object is also registered here since feature is shared between synthetics and uptime
uptimeSettingsObjectType,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ describe('syncEditedMonitor', () => {
bulkUpdate: jest.fn(),
get: jest.fn(),
update: jest.fn(),
createPointInTimeFinder: jest.fn().mockImplementation(({ perPage, type: soType }) => ({
close: jest.fn(async () => {}),
find: jest.fn().mockReturnValue({
async *[Symbol.asyncIterator]() {
yield {
saved_objects: [],
};
},
}),
})),
},
logger,
config: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
*/

import { schema, TypeOf } from '@kbn/config-schema';
import { migrateLegacyPrivateLocations } from './migrate_legacy_private_locations';
import { SyntheticsRestApiRouteFactory } from '../../types';
import { getPrivateLocationsAndAgentPolicies } from './get_private_locations';
import {
privateLocationsSavedObjectId,
privateLocationsSavedObjectName,
} from '../../../../common/saved_objects/private_locations';
import { privateLocationSavedObjectName } from '../../../../common/saved_objects/private_locations';
import { SYNTHETICS_API_URLS } from '../../../../common/constants';
import type { SyntheticsPrivateLocationsAttributes } from '../../../runtime_types/private_locations';
import { PrivateLocationAttributes } from '../../../runtime_types/private_locations';
import { toClientContract, toSavedObjectContract } from './helpers';
import { PrivateLocation } from '../../../../common/runtime_types';

Expand All @@ -40,7 +38,11 @@ export const addPrivateLocationRoute: SyntheticsRestApiRouteFactory<PrivateLocat
body: PrivateLocationSchema,
},
},
handler: async ({ response, request, savedObjectsClient, syntheticsMonitorClient }) => {
handler: async (routeContext) => {
await migrateLegacyPrivateLocations(routeContext);

const { response, request, savedObjectsClient, syntheticsMonitorClient } = routeContext;

const location = request.body as PrivateLocationObject;

const { locations, agentPolicies } = await getPrivateLocationsAndAgentPolicies(
Expand All @@ -65,7 +67,6 @@ export const addPrivateLocationRoute: SyntheticsRestApiRouteFactory<PrivateLocat
});
}

const existingLocations = locations.filter((loc) => loc.id !== location.agentPolicyId);
const formattedLocation = toSavedObjectContract({
...location,
id: location.agentPolicyId,
Expand All @@ -80,17 +81,17 @@ export const addPrivateLocationRoute: SyntheticsRestApiRouteFactory<PrivateLocat
});
}

const result = await savedObjectsClient.create<SyntheticsPrivateLocationsAttributes>(
privateLocationsSavedObjectName,
{ locations: [...existingLocations, formattedLocation] },
const soClient = routeContext.server.coreStart.savedObjects.createInternalRepository();

const result = await soClient.create<PrivateLocationAttributes>(
privateLocationSavedObjectName,
formattedLocation,
{
id: privateLocationsSavedObjectId,
overwrite: true,
id: location.agentPolicyId,
initialNamespaces: ['*'],
}
);

const allLocations = toClientContract(result.attributes, agentPolicies);

return allLocations.find((loc) => loc.id === location.agentPolicyId)!;
return toClientContract(result.attributes, agentPolicies);
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@

import { schema } from '@kbn/config-schema';
import { isEmpty } from 'lodash';
import { migrateLegacyPrivateLocations } from './migrate_legacy_private_locations';
import { getMonitorsByLocation } from './get_location_monitors';
import { getPrivateLocationsAndAgentPolicies } from './get_private_locations';
import { SyntheticsRestApiRouteFactory } from '../../types';
import { SYNTHETICS_API_URLS } from '../../../../common/constants';
import {
privateLocationsSavedObjectId,
privateLocationsSavedObjectName,
} from '../../../../common/saved_objects/private_locations';
import type { SyntheticsPrivateLocationsAttributes } from '../../../runtime_types/private_locations';
import { privateLocationSavedObjectName } from '../../../../common/saved_objects/private_locations';

export const deletePrivateLocationRoute: SyntheticsRestApiRouteFactory<undefined> = () => ({
method: 'DELETE',
Expand All @@ -28,12 +25,16 @@ export const deletePrivateLocationRoute: SyntheticsRestApiRouteFactory<undefined
}),
},
},
handler: async ({ response, savedObjectsClient, syntheticsMonitorClient, request, server }) => {
handler: async (routeContext) => {
await migrateLegacyPrivateLocations(routeContext);

const { savedObjectsClient, syntheticsMonitorClient, request, response, server } = routeContext;
const { locationId } = request.params as { locationId: string };

const { locations } = await getPrivateLocationsAndAgentPolicies(
savedObjectsClient,
syntheticsMonitorClient
syntheticsMonitorClient,
true
);

if (!locations.find((loc) => loc.id === locationId)) {
Expand All @@ -55,17 +56,8 @@ export const deletePrivateLocationRoute: SyntheticsRestApiRouteFactory<undefined
});
}

const remainingLocations = locations.filter((loc) => loc.id !== locationId);

await savedObjectsClient.create<SyntheticsPrivateLocationsAttributes>(
privateLocationsSavedObjectName,
{ locations: remainingLocations },
{
id: privateLocationsSavedObjectId,
overwrite: true,
}
);

return;
await savedObjectsClient.delete(privateLocationSavedObjectName, locationId, {
force: true,
});
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import { schema } from '@kbn/config-schema';
import { migrateLegacyPrivateLocations } from './migrate_legacy_private_locations';
import { AgentPolicyInfo } from '../../../../common/types';
import { SyntheticsRestApiRouteFactory } from '../../types';
import { PrivateLocation, SyntheticsPrivateLocations } from '../../../../common/runtime_types';
import { SYNTHETICS_API_URLS } from '../../../../common/constants';
import { getPrivateLocations } from '../../../synthetics_service/get_private_locations';
import type { SyntheticsPrivateLocationsAttributes } from '../../../runtime_types/private_locations';
import { SyntheticsMonitorClient } from '../../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
import { toClientContract } from './helpers';
import { allLocationsToClientContract } from './helpers';

export const getPrivateLocationsRoute: SyntheticsRestApiRouteFactory<
SyntheticsPrivateLocations | PrivateLocation
Expand All @@ -29,14 +30,17 @@ export const getPrivateLocationsRoute: SyntheticsRestApiRouteFactory<
}),
},
},
handler: async ({ savedObjectsClient, syntheticsMonitorClient, request, response }) => {
handler: async (routeContext) => {
await migrateLegacyPrivateLocations(routeContext);

const { savedObjectsClient, syntheticsMonitorClient, request, response } = routeContext;
const { id } = request.params as { id?: string };

const { locations, agentPolicies } = await getPrivateLocationsAndAgentPolicies(
savedObjectsClient,
syntheticsMonitorClient
);
const list = toClientContract({ locations }, agentPolicies);
const list = allLocationsToClientContract({ locations }, agentPolicies);
if (!id) return list;
const location = list.find((loc) => loc.id === id || loc.label === id);
if (!location) {
Expand All @@ -53,7 +57,7 @@ export const getPrivateLocationsRoute: SyntheticsRestApiRouteFactory<
export const getPrivateLocationsAndAgentPolicies = async (
savedObjectsClient: SavedObjectsClientContract,
syntheticsMonitorClient: SyntheticsMonitorClient,
excludeAgentPolicies: boolean = false
excludeAgentPolicies = false
): Promise<SyntheticsPrivateLocationsAttributes & { agentPolicies: AgentPolicyInfo[] }> => {
try {
const [privateLocations, agentPolicies] = await Promise.all([
Expand Down
Loading