Skip to content

Commit

Permalink
[Synthetics] Fixes project monitor decryption filters !! (elastic#199633
Browse files Browse the repository at this point in the history
)

## Summary

Fixes project monitor decryption filters , in case of edit, this ends of
decrypting all monitors, since search can be pretty wide !!

### Changes

- Only fetch relevant previous monitors
- Only decrypt relevant monitors
- Only fetch relevant previous fields as part of previous monitors

---------

Co-authored-by: Dominique Clarke <[email protected]>
  • Loading branch information
shahzad31 and dominiqueclarke authored Nov 15, 2024
1 parent 875313e commit 52ba132
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
export interface MonitorConfigUpdate {
normalizedMonitor: SyntheticsMonitor;
monitorWithRevision: SyntheticsMonitorWithSecretsAttributes;
previousMonitor: SavedObject<EncryptedSyntheticsMonitorAttributes>;
decryptedPreviousMonitor: SavedObject<SyntheticsMonitorWithSecretsAttributes>;
}

Expand All @@ -40,14 +39,14 @@ const updateConfigSavedObjects = async ({
monitorsToUpdate: MonitorConfigUpdate[];
}) => {
return await routeContext.savedObjectsClient.bulkUpdate<MonitorFields>(
monitorsToUpdate.map(({ previousMonitor, monitorWithRevision }) => ({
monitorsToUpdate.map(({ monitorWithRevision, decryptedPreviousMonitor }) => ({
type: syntheticsMonitorType,
id: previousMonitor.id,
id: decryptedPreviousMonitor.id,
attributes: {
...monitorWithRevision,
[ConfigKey.CONFIG_ID]: previousMonitor.id,
[ConfigKey.CONFIG_ID]: decryptedPreviousMonitor.id,
[ConfigKey.MONITOR_QUERY_ID]:
monitorWithRevision[ConfigKey.CUSTOM_HEARTBEAT_ID] || previousMonitor.id,
monitorWithRevision[ConfigKey.CUSTOM_HEARTBEAT_ID] || decryptedPreviousMonitor.id,
},
}))
);
Expand All @@ -67,15 +66,14 @@ async function syncUpdatedMonitors({
const { syntheticsMonitorClient } = routeContext;

return await syntheticsMonitorClient.editMonitors(
monitorsToUpdate.map(({ normalizedMonitor, previousMonitor, decryptedPreviousMonitor }) => ({
monitorsToUpdate.map(({ normalizedMonitor, decryptedPreviousMonitor }) => ({
monitor: {
...(normalizedMonitor as MonitorFields),
[ConfigKey.CONFIG_ID]: previousMonitor.id,
[ConfigKey.CONFIG_ID]: decryptedPreviousMonitor.id,
[ConfigKey.MONITOR_QUERY_ID]:
normalizedMonitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || previousMonitor.id,
normalizedMonitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || decryptedPreviousMonitor.id,
},
id: previousMonitor.id,
previousMonitor,
id: decryptedPreviousMonitor.id,
decryptedPreviousMonitor,
})),
privateLocations,
Expand Down Expand Up @@ -104,17 +102,17 @@ export const syncEditedMonitorBulk = async ({

const { failedPolicyUpdates, publicSyncErrors } = editSyncResponse;

monitorsToUpdate.forEach(({ normalizedMonitor, previousMonitor }) => {
monitorsToUpdate.forEach(({ normalizedMonitor, decryptedPreviousMonitor }) => {
const editedMonitorSavedObject = editedMonitorSavedObjects?.saved_objects.find(
(obj) => obj.id === previousMonitor.id
(obj) => obj.id === decryptedPreviousMonitor.id
);

sendTelemetryEvents(
server.logger,
server.telemetry,
formatTelemetryUpdateEvent(
editedMonitorSavedObject as SavedObjectsUpdateResponse<EncryptedSyntheticsMonitorAttributes>,
previousMonitor,
decryptedPreviousMonitor.updated_at,
server.stackVersion,
Boolean((normalizedMonitor as MonitorFields)[ConfigKey.SOURCE_INLINE]),
publicSyncErrors
Expand Down Expand Up @@ -150,9 +148,9 @@ export const rollbackCompletely = async ({
const { savedObjectsClient, server } = routeContext;
try {
await savedObjectsClient.bulkUpdate<MonitorFields>(
monitorsToUpdate.map(({ previousMonitor, decryptedPreviousMonitor }) => ({
monitorsToUpdate.map(({ decryptedPreviousMonitor }) => ({
type: syntheticsMonitorType,
id: previousMonitor.id,
id: decryptedPreviousMonitor.id,
attributes: decryptedPreviousMonitor.attributes,
}))
);
Expand All @@ -167,7 +165,6 @@ export const rollbackFailedUpdates = async ({
monitorsToUpdate,
}: {
monitorsToUpdate: Array<{
previousMonitor: SavedObject<EncryptedSyntheticsMonitorAttributes>;
decryptedPreviousMonitor: SavedObject<SyntheticsMonitorWithSecretsAttributes>;
}>;
routeContext: RouteContext;
Expand All @@ -194,12 +191,12 @@ export const rollbackFailedUpdates = async ({
});

const monitorsToRevert = monitorsToUpdate
.filter(({ previousMonitor }) => {
return failedConfigs[previousMonitor.id];
.filter(({ decryptedPreviousMonitor }) => {
return failedConfigs[decryptedPreviousMonitor.id];
})
.map(({ previousMonitor, decryptedPreviousMonitor }) => ({
.map(({ decryptedPreviousMonitor }) => ({
type: syntheticsMonitorType,
id: previousMonitor.id,
id: decryptedPreviousMonitor.id,
attributes: decryptedPreviousMonitor.attributes,
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export const syncEditedMonitor = async ({
server.telemetry,
formatTelemetryUpdateEvent(
editedMonitorSavedObject as SavedObjectsUpdateResponse<EncryptedSyntheticsMonitorAttributes>,
decryptedPreviousMonitor,
decryptedPreviousMonitor.updated_at,
server.stackVersion,
Boolean((normalizedMonitor as MonitorFields)[ConfigKey.SOURCE_INLINE]),
publicSyncErrors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const getSyntheticsProjectMonitorsRoute: SyntheticsRestApiRouteFactory =
} = routeContext;

const { projectName } = request.params;
const { per_page: perPage = 500, search_after: searchAfter } = request.query;
const { per_page: perPage = 1000, search_after: searchAfter } = request.query;
const decodedProjectName = decodeURI(projectName);
const decodedSearchAfter = searchAfter ? decodeURI(searchAfter) : undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ describe('monitor upgrade telemetry helpers', () => {
it('handles formatting update events', () => {
const actual = formatTelemetryUpdateEvent(
createTestConfig({}, '2011-10-05T16:48:00.000Z'),
testConfig,
testConfig.updated_at,
stackVersion,
false,
errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,22 @@ export function formatTelemetryEvent({

export function formatTelemetryUpdateEvent(
currentMonitor: SavedObjectsUpdateResponse<EncryptedSyntheticsMonitorAttributes>,
previousMonitor: SavedObject<EncryptedSyntheticsMonitorAttributes>,
previousMonitorUpdatedAt: string | undefined,
stackVersion: string,
isInlineScript: boolean,
errors?: ServiceLocationErrors | null
) {
let durationSinceLastUpdated: number = 0;
if (currentMonitor.updated_at && previousMonitor.updated_at) {
if (currentMonitor.updated_at && previousMonitorUpdatedAt) {
durationSinceLastUpdated =
new Date(currentMonitor.updated_at).getTime() -
new Date(previousMonitor.updated_at).getTime();
new Date(currentMonitor.updated_at).getTime() - new Date(previousMonitorUpdatedAt).getTime();
}

return formatTelemetryEvent({
stackVersion,
monitor: currentMonitor as SavedObject<EncryptedSyntheticsMonitorAttributes>,
durationSinceLastUpdated,
lastUpdatedAt: previousMonitor.updated_at,
lastUpdatedAt: previousMonitorUpdatedAt,
isInlineScript,
errors,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ import { normalizeProjectMonitor } from './normalizers';

type FailedError = Array<{ id?: string; reason: string; details: string; payload?: object }>;

export interface ExistingMonitor {
[ConfigKey.JOURNEY_ID]: string;
[ConfigKey.CONFIG_ID]: string;
[ConfigKey.REVISION]: number;
[ConfigKey.MONITOR_TYPE]: string;
}

export interface PreviousMonitorForUpdate extends ExistingMonitor {
updated_at?: string;
}

export const CANNOT_UPDATE_MONITOR_TO_DIFFERENT_TYPE = i18n.translate(
'xpack.synthetics.service.projectMonitors.cannotUpdateMonitorToDifferentType',
{
Expand Down Expand Up @@ -128,14 +139,13 @@ export class ProjectMonitorFormatter {

const normalizedNewMonitors: SyntheticsMonitor[] = [];
const normalizedUpdateMonitors: Array<{
previousMonitor: SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>;
previousMonitor: PreviousMonitorForUpdate;
monitor: SyntheticsMonitor;
}> = [];

for (const monitor of this.monitors) {
const previousMonitor = existingMonitors.find(
(monitorObj) =>
(monitorObj.attributes as SyntheticsMonitor)[ConfigKey.JOURNEY_ID] === monitor.id
(monitorObj) => monitorObj[ConfigKey.JOURNEY_ID] === monitor.id
);

const normM = await this.validateProjectMonitor({
Expand All @@ -146,7 +156,7 @@ export class ProjectMonitorFormatter {
if (normM) {
if (
previousMonitor &&
previousMonitor.attributes[ConfigKey.MONITOR_TYPE] !== normM[ConfigKey.MONITOR_TYPE]
previousMonitor[ConfigKey.MONITOR_TYPE] !== normM[ConfigKey.MONITOR_TYPE]
) {
this.failedMonitors.push({
reason: CANNOT_UPDATE_MONITOR_TO_DIFFERENT_TYPE,
Expand All @@ -157,7 +167,7 @@ export class ProjectMonitorFormatter {
'Monitor {monitorId} of type {previousType} cannot be updated to type {currentType}. Please delete the monitor first and try again.',
values: {
currentType: monitor.type,
previousType: previousMonitor.attributes[ConfigKey.MONITOR_TYPE],
previousType: previousMonitor[ConfigKey.MONITOR_TYPE],
monitorId: monitor.id,
},
}
Expand Down Expand Up @@ -246,19 +256,33 @@ export class ProjectMonitorFormatter {
}
};

public getProjectMonitorsForProject = async () => {
const finder = this.savedObjectsClient.createPointInTimeFinder({
public getProjectMonitorsForProject = async (): Promise<PreviousMonitorForUpdate[]> => {
const journeyIds = this.monitors.map((monitor) => monitor.id);
const journeyFilter = getSavedObjectKqlFilter({
field: ConfigKey.JOURNEY_ID,
values: journeyIds,
});
const finder = this.savedObjectsClient.createPointInTimeFinder<ExistingMonitor>({
type: syntheticsMonitorType,
perPage: 5000,
filter: this.projectFilter,
filter: `${this.projectFilter} AND ${journeyFilter}`,
fields: [
ConfigKey.JOURNEY_ID,
ConfigKey.CONFIG_ID,
ConfigKey.REVISION,
ConfigKey.MONITOR_TYPE,
],
});

const hits: Array<SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>> = [];
const hits: PreviousMonitorForUpdate[] = [];
for await (const result of finder.find()) {
hits.push(
...(result.saved_objects as Array<
SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>
>)
...result.saved_objects.map((monitor) => {
return {
...monitor.attributes,
updated_at: monitor.updated_at,
};
})
);
}

Expand Down Expand Up @@ -333,18 +357,16 @@ export class ProjectMonitorFormatter {
}
};

private getDecryptedMonitors = async (
monitors: Array<SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>>
) => {
const configIds = monitors.map((monitor) => monitor.attributes[ConfigKey.CONFIG_ID]);
private getDecryptedMonitors = async (monitors: PreviousMonitorForUpdate[]) => {
const configIds = monitors.map((monitor) => monitor[ConfigKey.CONFIG_ID]);
const monitorFilter = getSavedObjectKqlFilter({
field: ConfigKey.CONFIG_ID,
values: configIds,
});
const finder =
await this.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser<SyntheticsMonitorWithSecretsAttributes>(
{
search: monitorFilter,
filter: monitorFilter,
type: syntheticsMonitorType,
perPage: 500,
namespaces: [this.spaceId],
Expand All @@ -365,7 +387,7 @@ export class ProjectMonitorFormatter {
private updateMonitorsBulk = async (
monitors: Array<{
monitor: SyntheticsMonitor;
previousMonitor: SavedObjectsFindResult<EncryptedSyntheticsMonitorAttributes>;
previousMonitor: PreviousMonitorForUpdate;
}>
): Promise<
| {
Expand All @@ -390,22 +412,22 @@ export class ProjectMonitorFormatter {
const monitorsToUpdate: MonitorConfigUpdate[] = [];

decryptedPreviousMonitors.forEach((decryptedPreviousMonitor) => {
const monitor = monitors.find((m) => m.previousMonitor.id === decryptedPreviousMonitor.id);
const monitor = monitors.find(
(m) => m.previousMonitor[ConfigKey.CONFIG_ID] === decryptedPreviousMonitor.id
);
if (monitor) {
const normalizedMonitor = monitor?.monitor;
const previousMonitor = monitor?.previousMonitor;
const {
attributes: { [ConfigKey.REVISION]: _, ...normalizedPrevMonitorAttr },
} = normalizeSecrets(decryptedPreviousMonitor);

const monitorWithRevision = formatSecrets({
...normalizedPrevMonitorAttr,
...normalizedMonitor,
revision: (previousMonitor.attributes[ConfigKey.REVISION] || 0) + 1,
revision: (decryptedPreviousMonitor.attributes[ConfigKey.REVISION] || 0) + 1,
});
monitorsToUpdate.push({
normalizedMonitor,
previousMonitor,
monitorWithRevision,
decryptedPreviousMonitor,
});
Expand Down
15 changes: 15 additions & 0 deletions x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ export default function ({ getService }: FtrProviderContext) {

const firstPageResponse = await supertest
.get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
.query({
per_page: 500,
})
.set('kbn-xsrf', 'true')
.send()
.expect(200);
Expand Down Expand Up @@ -185,6 +188,9 @@ export default function ({ getService }: FtrProviderContext) {

const firstPageResponse = await supertest
.get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
.query({
per_page: 500,
})
.set('kbn-xsrf', 'true')
.send()
.expect(200);
Expand Down Expand Up @@ -279,6 +285,9 @@ export default function ({ getService }: FtrProviderContext) {

const firstPageResponse = await supertest
.get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
.query({
per_page: 500,
})
.set('kbn-xsrf', 'true')
.send()
.expect(200);
Expand Down Expand Up @@ -372,6 +381,9 @@ export default function ({ getService }: FtrProviderContext) {
.expect(200);
const firstPageResponse = await supertest
.get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project))
.query({
per_page: 500,
})
.set('kbn-xsrf', 'true')
.send()
.expect(200);
Expand Down Expand Up @@ -481,6 +493,9 @@ export default function ({ getService }: FtrProviderContext) {
encodeURI(projectName)
)
)
.query({
per_page: 500,
})
.set('kbn-xsrf', 'true')
.send()
.expect(200);
Expand Down

0 comments on commit 52ba132

Please sign in to comment.