mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Synthetics] Add error handling for private locations (#137115)
* add error handling for private locations * add jest tests * add tests * adjust types * adjust tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
cc31f24af5
commit
0fbfa50075
13 changed files with 673 additions and 145 deletions
|
@ -5,7 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SavedObject, SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import {
|
||||
SavedObject,
|
||||
SavedObjectsErrorHelpers,
|
||||
SavedObjectsClientContract,
|
||||
KibanaRequest,
|
||||
} from '@kbn/core/server';
|
||||
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
import {
|
||||
ConfigKey,
|
||||
|
@ -21,6 +26,7 @@ import { validateMonitor } from './monitor_validation';
|
|||
import { sendTelemetryEvents, formatTelemetryEvent } from '../telemetry/monitor_upgrade_sender';
|
||||
import { formatSecrets } from '../../synthetics_service/utils/secrets';
|
||||
import type { UptimeServerSetup } from '../../legacy_uptime/lib/adapters/framework';
|
||||
import { deleteMonitor } from './delete_monitor';
|
||||
|
||||
export const addSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({
|
||||
method: 'POST',
|
||||
|
@ -89,6 +95,8 @@ export const addSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => ({
|
|||
monitorSavedObject: newMonitor,
|
||||
server,
|
||||
syntheticsMonitorClient,
|
||||
savedObjectsClient,
|
||||
request,
|
||||
});
|
||||
|
||||
if (errors && errors.length > 0) {
|
||||
|
@ -110,27 +118,43 @@ export const syncNewMonitor = async ({
|
|||
monitorSavedObject,
|
||||
server,
|
||||
syntheticsMonitorClient,
|
||||
savedObjectsClient,
|
||||
request,
|
||||
}: {
|
||||
monitor: SyntheticsMonitor;
|
||||
monitorSavedObject: SavedObject<EncryptedSyntheticsMonitor>;
|
||||
server: UptimeServerSetup;
|
||||
syntheticsMonitorClient: SyntheticsMonitorClient;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
request: KibanaRequest;
|
||||
}) => {
|
||||
const errors = await syntheticsMonitorClient.addMonitor(
|
||||
monitor as MonitorFields,
|
||||
monitorSavedObject.id
|
||||
);
|
||||
try {
|
||||
const errors = await syntheticsMonitorClient.addMonitor(
|
||||
monitor as MonitorFields,
|
||||
monitorSavedObject.id,
|
||||
request
|
||||
);
|
||||
|
||||
sendTelemetryEvents(
|
||||
server.logger,
|
||||
server.telemetry,
|
||||
formatTelemetryEvent({
|
||||
monitor: monitorSavedObject,
|
||||
errors,
|
||||
isInlineScript: Boolean((monitor as MonitorFields)[ConfigKey.SOURCE_INLINE]),
|
||||
kibanaVersion: server.kibanaVersion,
|
||||
})
|
||||
);
|
||||
sendTelemetryEvents(
|
||||
server.logger,
|
||||
server.telemetry,
|
||||
formatTelemetryEvent({
|
||||
monitor: monitorSavedObject,
|
||||
errors,
|
||||
isInlineScript: Boolean((monitor as MonitorFields)[ConfigKey.SOURCE_INLINE]),
|
||||
kibanaVersion: server.kibanaVersion,
|
||||
})
|
||||
);
|
||||
|
||||
return errors;
|
||||
return errors;
|
||||
} catch (e) {
|
||||
await deleteMonitor({
|
||||
savedObjectsClient,
|
||||
server,
|
||||
monitorId: monitorSavedObject.id,
|
||||
syntheticsMonitorClient,
|
||||
request,
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SavedObjectsClientContract, SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import {
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsErrorHelpers,
|
||||
KibanaRequest,
|
||||
} from '@kbn/core/server';
|
||||
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
import {
|
||||
ConfigKey,
|
||||
|
@ -50,6 +54,7 @@ export const deleteSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () =>
|
|||
server,
|
||||
monitorId,
|
||||
syntheticsMonitorClient,
|
||||
request,
|
||||
});
|
||||
|
||||
if (errors && errors.length > 0) {
|
||||
|
@ -74,11 +79,13 @@ export const deleteMonitor = async ({
|
|||
server,
|
||||
monitorId,
|
||||
syntheticsMonitorClient,
|
||||
request,
|
||||
}: {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
server: UptimeServerSetup;
|
||||
monitorId: string;
|
||||
syntheticsMonitorClient: SyntheticsMonitorClient;
|
||||
request: KibanaRequest;
|
||||
}) => {
|
||||
const { logger, telemetry, kibanaVersion, encryptedSavedObjects } = server;
|
||||
const encryptedSavedObjectsClient = encryptedSavedObjects.getClient();
|
||||
|
@ -99,12 +106,16 @@ export const deleteMonitor = async ({
|
|||
|
||||
const normalizedMonitor = normalizeSecrets(monitor);
|
||||
|
||||
const errors = await syntheticsMonitorClient.deleteMonitor(
|
||||
{
|
||||
...normalizedMonitor.attributes,
|
||||
id:
|
||||
(normalizedMonitor.attributes as MonitorFields)[ConfigKey.CUSTOM_HEARTBEAT_ID] ||
|
||||
monitorId,
|
||||
},
|
||||
request
|
||||
);
|
||||
await savedObjectsClient.delete(syntheticsMonitorType, monitorId);
|
||||
const errors = await syntheticsMonitorClient.deleteMonitor({
|
||||
...normalizedMonitor.attributes,
|
||||
id:
|
||||
(normalizedMonitor.attributes as MonitorFields)[ConfigKey.CUSTOM_HEARTBEAT_ID] || monitorId,
|
||||
});
|
||||
|
||||
sendTelemetryEvents(
|
||||
logger,
|
||||
|
|
|
@ -7,8 +7,17 @@
|
|||
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { syncEditedMonitor } from './edit_monitor';
|
||||
import { SavedObjectsUpdateResponse, SavedObject } from '@kbn/core/server';
|
||||
import { EncryptedSyntheticsMonitor, SyntheticsMonitor } from '../../../common/runtime_types';
|
||||
import {
|
||||
SavedObjectsUpdateResponse,
|
||||
SavedObject,
|
||||
SavedObjectsClientContract,
|
||||
KibanaRequest,
|
||||
} from '@kbn/core/server';
|
||||
import {
|
||||
EncryptedSyntheticsMonitor,
|
||||
SyntheticsMonitor,
|
||||
SyntheticsMonitorWithSecrets,
|
||||
} from '../../../common/runtime_types';
|
||||
import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters';
|
||||
import { SyntheticsService } from '../../synthetics_service/synthetics_service';
|
||||
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
|
@ -24,7 +33,7 @@ describe('syncEditedMonitor', () => {
|
|||
const serverMock: UptimeServerSetup = {
|
||||
uptimeEsClient: { search: jest.fn() },
|
||||
kibanaVersion: null,
|
||||
authSavedObjectsClient: { bulkUpdate: jest.fn(), get: jest.fn() },
|
||||
authSavedObjectsClient: { bulkUpdate: jest.fn(), get: jest.fn(), update: jest.fn() },
|
||||
logger,
|
||||
config: {
|
||||
service: {
|
||||
|
@ -32,6 +41,13 @@ describe('syncEditedMonitor', () => {
|
|||
password: '12345',
|
||||
},
|
||||
},
|
||||
fleet: {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockReturnValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
},
|
||||
} as unknown as UptimeServerSetup;
|
||||
|
||||
const editedMonitor = {
|
||||
|
@ -57,7 +73,10 @@ describe('syncEditedMonitor', () => {
|
|||
fields_under_root: true,
|
||||
} as unknown as SyntheticsMonitor;
|
||||
|
||||
const previousMonitor = { id: 'saved-obj-id' } as SavedObject<EncryptedSyntheticsMonitor>;
|
||||
const previousMonitor = {
|
||||
id: 'saved-obj-id',
|
||||
attributes: { name: editedMonitor.name },
|
||||
} as SavedObject<EncryptedSyntheticsMonitor>;
|
||||
const editedMonitorSavedObject = {
|
||||
id: 'saved-obj-id',
|
||||
} as SavedObjectsUpdateResponse<EncryptedSyntheticsMonitor>;
|
||||
|
@ -73,8 +92,13 @@ describe('syncEditedMonitor', () => {
|
|||
editedMonitor,
|
||||
editedMonitorSavedObject,
|
||||
previousMonitor,
|
||||
decryptedPreviousMonitor:
|
||||
previousMonitor as unknown as SavedObject<SyntheticsMonitorWithSecrets>,
|
||||
syntheticsMonitorClient,
|
||||
server: serverMock,
|
||||
request: {} as unknown as KibanaRequest,
|
||||
savedObjectsClient:
|
||||
serverMock.authSavedObjectsClient as unknown as SavedObjectsClientContract,
|
||||
});
|
||||
|
||||
expect(syntheticsService.editConfig).toHaveBeenCalledWith(
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SavedObjectsUpdateResponse, SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
SavedObjectsUpdateResponse,
|
||||
SavedObject,
|
||||
SavedObjectsClientContract,
|
||||
KibanaRequest,
|
||||
} from '@kbn/core/server';
|
||||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
|
||||
import {
|
||||
|
@ -70,9 +75,10 @@ export const editSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => (
|
|||
namespace: previousMonitor.namespaces?.[0],
|
||||
}
|
||||
);
|
||||
const normalizedPreviousMonitor = normalizeSecrets(decryptedPreviousMonitor).attributes;
|
||||
|
||||
const editedMonitor = {
|
||||
...normalizeSecrets(decryptedPreviousMonitor).attributes,
|
||||
...normalizedPreviousMonitor,
|
||||
...monitor,
|
||||
};
|
||||
|
||||
|
@ -101,7 +107,10 @@ export const editSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory = () => (
|
|||
editedMonitor,
|
||||
editedMonitorSavedObject,
|
||||
previousMonitor,
|
||||
decryptedPreviousMonitor,
|
||||
syntheticsMonitorClient,
|
||||
savedObjectsClient,
|
||||
request,
|
||||
});
|
||||
|
||||
// Return service sync errors in OK response
|
||||
|
@ -127,31 +136,51 @@ export const syncEditedMonitor = async ({
|
|||
editedMonitor,
|
||||
editedMonitorSavedObject,
|
||||
previousMonitor,
|
||||
decryptedPreviousMonitor,
|
||||
server,
|
||||
syntheticsMonitorClient,
|
||||
savedObjectsClient,
|
||||
request,
|
||||
}: {
|
||||
editedMonitor: SyntheticsMonitor;
|
||||
editedMonitorSavedObject: SavedObjectsUpdateResponse<EncryptedSyntheticsMonitor>;
|
||||
previousMonitor: SavedObject<EncryptedSyntheticsMonitor>;
|
||||
decryptedPreviousMonitor: SavedObject<SyntheticsMonitorWithSecrets>;
|
||||
server: UptimeServerSetup;
|
||||
syntheticsMonitorClient: SyntheticsMonitorClient;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
request: KibanaRequest;
|
||||
}) => {
|
||||
const errors = await syntheticsMonitorClient.editMonitor(
|
||||
editedMonitor as MonitorFields,
|
||||
editedMonitorSavedObject.id
|
||||
);
|
||||
try {
|
||||
const errors = await syntheticsMonitorClient.editMonitor(
|
||||
editedMonitor as MonitorFields,
|
||||
editedMonitorSavedObject.id,
|
||||
request
|
||||
);
|
||||
|
||||
sendTelemetryEvents(
|
||||
server.logger,
|
||||
server.telemetry,
|
||||
formatTelemetryUpdateEvent(
|
||||
editedMonitorSavedObject,
|
||||
previousMonitor,
|
||||
server.kibanaVersion,
|
||||
Boolean((editedMonitor as MonitorFields)[ConfigKey.SOURCE_INLINE]),
|
||||
errors
|
||||
)
|
||||
);
|
||||
sendTelemetryEvents(
|
||||
server.logger,
|
||||
server.telemetry,
|
||||
formatTelemetryUpdateEvent(
|
||||
editedMonitorSavedObject,
|
||||
previousMonitor,
|
||||
server.kibanaVersion,
|
||||
Boolean((editedMonitor as MonitorFields)[ConfigKey.SOURCE_INLINE]),
|
||||
errors
|
||||
)
|
||||
);
|
||||
|
||||
return errors;
|
||||
return errors;
|
||||
} catch (e) {
|
||||
server.logger.error(
|
||||
`Unable to update Synthetics monitor ${decryptedPreviousMonitor.attributes[ConfigKey.NAME]}`
|
||||
);
|
||||
await savedObjectsClient.update<MonitorFields>(
|
||||
syntheticsMonitorType,
|
||||
editedMonitorSavedObject.id,
|
||||
decryptedPreviousMonitor.attributes
|
||||
);
|
||||
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,12 +4,162 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { testMonitorPolicy } from './test_policy';
|
||||
import { KibanaRequest } from '@kbn/core/server';
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters';
|
||||
import { formatSyntheticsPolicy } from '../../../common/formatters/format_synthetics_policy';
|
||||
import { DataStream, MonitorFields, ScheduleUnit, SourceType } from '../../../common/runtime_types';
|
||||
import {
|
||||
DataStream,
|
||||
MonitorFields,
|
||||
ScheduleUnit,
|
||||
SourceType,
|
||||
HeartbeatConfig,
|
||||
} from '../../../common/runtime_types';
|
||||
import { SyntheticsPrivateLocation } from './synthetics_private_location';
|
||||
import { testMonitorPolicy } from './test_policy';
|
||||
|
||||
describe('SyntheticsPrivateLocation', () => {
|
||||
const mockPrivateLocation = {
|
||||
id: 'policyId',
|
||||
label: 'Test Location',
|
||||
isServiceManaged: false,
|
||||
};
|
||||
const testConfig = {
|
||||
id: 'testId',
|
||||
type: 'http',
|
||||
enabled: true,
|
||||
schedule: '@every 3m',
|
||||
'service.name': '',
|
||||
locations: [mockPrivateLocation],
|
||||
tags: [],
|
||||
timeout: '16',
|
||||
name: 'Test Monitor',
|
||||
urls: 'https://www.google.com',
|
||||
max_redirects: '0',
|
||||
password: '12345678',
|
||||
proxy_url: '',
|
||||
'check.response.body.negative': [],
|
||||
'check.response.body.positive': [],
|
||||
'response.include_body': 'on_error',
|
||||
'check.response.headers': {},
|
||||
'response.include_headers': true,
|
||||
'check.response.status': [],
|
||||
'check.request.body': { type: 'text', value: '' },
|
||||
'check.request.headers': {},
|
||||
'check.request.method': 'GET',
|
||||
username: '',
|
||||
} as unknown as HeartbeatConfig;
|
||||
|
||||
const serverMock: UptimeServerSetup = {
|
||||
uptimeEsClient: { search: jest.fn() },
|
||||
logger: loggerMock.create(),
|
||||
authSavedObjectsClient: {
|
||||
bulkUpdate: jest.fn(),
|
||||
get: jest.fn().mockReturnValue({
|
||||
attributes: {
|
||||
locations: [mockPrivateLocation],
|
||||
},
|
||||
}),
|
||||
},
|
||||
config: {
|
||||
service: {
|
||||
username: 'dev',
|
||||
password: '12345',
|
||||
manifestUrl: 'http://localhost:8080/api/manifest',
|
||||
},
|
||||
},
|
||||
fleet: {
|
||||
authz: {
|
||||
fromRequest: jest
|
||||
.fn()
|
||||
.mockReturnValue({ integrations: { writeIntegrationPolicies: true } }),
|
||||
},
|
||||
packagePolicyService: {
|
||||
get: jest.fn().mockReturnValue({}),
|
||||
},
|
||||
},
|
||||
} as unknown as UptimeServerSetup;
|
||||
|
||||
it.each([
|
||||
[
|
||||
true,
|
||||
'Unable to create Synthetics package policy for monitor Test Monitor with private location Test Location',
|
||||
],
|
||||
[
|
||||
false,
|
||||
'Unable to create Synthetics package policy for monitor Test Monitor. Fleet write permissions are needed to use Synthetics private locations.',
|
||||
],
|
||||
])('throws errors for create monitor', async (writeIntegrationPolicies, error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
authz: {
|
||||
fromRequest: jest.fn().mockReturnValue({ integrations: { writeIntegrationPolicies } }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await syntheticsPrivateLocation.createMonitor(testConfig, {} as unknown as KibanaRequest);
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error(error));
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
[
|
||||
true,
|
||||
'Unable to update Synthetics package policy for monitor Test Monitor with private location Test Location',
|
||||
],
|
||||
[
|
||||
false,
|
||||
'Unable to update Synthetics package policy for monitor Test Monitor. Fleet write permissions are needed to use Synthetics private locations.',
|
||||
],
|
||||
])('throws errors for edit monitor', async (writeIntegrationPolicies, error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
authz: {
|
||||
fromRequest: jest.fn().mockReturnValue({ integrations: { writeIntegrationPolicies } }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await syntheticsPrivateLocation.editMonitor(testConfig, {} as unknown as KibanaRequest);
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error(error));
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
[
|
||||
true,
|
||||
'Unable to delete Synthetics package policy for monitor Test Monitor with private location Test Location',
|
||||
],
|
||||
[
|
||||
false,
|
||||
'Unable to delete Synthetics package policy for monitor Test Monitor. Fleet write permissions are needed to use Synthetics private locations.',
|
||||
],
|
||||
])('throws errors for delete monitor', async (writeIntegrationPolicies, error) => {
|
||||
const syntheticsPrivateLocation = new SyntheticsPrivateLocation({
|
||||
...serverMock,
|
||||
fleet: {
|
||||
...serverMock.fleet,
|
||||
authz: {
|
||||
fromRequest: jest.fn().mockReturnValue({ integrations: { writeIntegrationPolicies } }),
|
||||
},
|
||||
},
|
||||
});
|
||||
try {
|
||||
await syntheticsPrivateLocation.deleteMonitor(testConfig, {} as unknown as KibanaRequest);
|
||||
} catch (e) {
|
||||
expect(e).toEqual(new Error(e));
|
||||
}
|
||||
});
|
||||
|
||||
it('formats monitors stream properly', () => {
|
||||
const test = formatSyntheticsPolicy(testMonitorPolicy, DataStream.BROWSER, dummyBrowserConfig);
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { NewPackagePolicy, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common';
|
||||
import { KibanaRequest } from '@kbn/core/server';
|
||||
import { NewPackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import { formatSyntheticsPolicy } from '../../../common/formatters/format_synthetics_policy';
|
||||
import { getSyntheticsPrivateLocations } from '../../legacy_uptime/lib/saved_objects/private_locations';
|
||||
import {
|
||||
|
@ -58,7 +58,9 @@ export class SyntheticsPrivateLocation {
|
|||
);
|
||||
|
||||
if (!newPolicy) {
|
||||
throw new Error('Could not create new synthetics policy');
|
||||
throw new Error(
|
||||
`Unable to create Synthetics package policy for private location ${privateLocation.label}`
|
||||
);
|
||||
}
|
||||
|
||||
newPolicy.is_managed = true;
|
||||
|
@ -87,33 +89,72 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
|
||||
async createMonitor(config: HeartbeatConfig) {
|
||||
try {
|
||||
const { locations } = config;
|
||||
async checkPermissions(request: KibanaRequest, error: string) {
|
||||
const {
|
||||
integrations: { writeIntegrationPolicies },
|
||||
} = await this.server.fleet.authz.fromRequest(request);
|
||||
|
||||
const privateLocations: PrivateLocation[] = await getSyntheticsPrivateLocations(
|
||||
this.server.authSavedObjectsClient!
|
||||
);
|
||||
|
||||
const fleetManagedLocations = locations.filter((loc) => !loc.isServiceManaged);
|
||||
|
||||
for (const privateLocation of fleetManagedLocations) {
|
||||
const location = privateLocations?.find((loc) => loc.id === privateLocation.id)!;
|
||||
const newPolicy = await this.generateNewPolicy(config, location);
|
||||
|
||||
if (!newPolicy) {
|
||||
throw new Error('Unable to create Synthetics package policy for private location');
|
||||
}
|
||||
|
||||
await this.createPolicy(newPolicy, this.getPolicyId(config, location));
|
||||
}
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
return null;
|
||||
if (!writeIntegrationPolicies) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async editMonitor(config: HeartbeatConfig) {
|
||||
async createMonitor(config: HeartbeatConfig, request: KibanaRequest) {
|
||||
const { locations } = config;
|
||||
|
||||
await this.checkPermissions(
|
||||
request,
|
||||
`Unable to create Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
}. Fleet write permissions are needed to use Synthetics private locations.`
|
||||
);
|
||||
|
||||
const privateLocations: PrivateLocation[] = await getSyntheticsPrivateLocations(
|
||||
this.server.authSavedObjectsClient!
|
||||
);
|
||||
|
||||
const fleetManagedLocations = locations.filter((loc) => !loc.isServiceManaged);
|
||||
|
||||
for (const privateLocation of fleetManagedLocations) {
|
||||
const location = privateLocations?.find((loc) => loc.id === privateLocation.id);
|
||||
|
||||
if (!location) {
|
||||
throw new Error(
|
||||
`Unable to find Synthetics private location for agentId ${privateLocation.id}`
|
||||
);
|
||||
}
|
||||
|
||||
const newPolicy = await this.generateNewPolicy(config, location);
|
||||
|
||||
if (!newPolicy) {
|
||||
throw new Error(
|
||||
`Unable to create Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
} with private location ${location.label}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.createPolicy(newPolicy, this.getPolicyId(config, location));
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
throw new Error(
|
||||
`Unable to create Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
} with private location ${location.label}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async editMonitor(config: HeartbeatConfig, request: KibanaRequest) {
|
||||
await this.checkPermissions(
|
||||
request,
|
||||
`Unable to update Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
}. Fleet write permissions are needed to use Synthetics private locations.`
|
||||
);
|
||||
|
||||
const { locations } = config;
|
||||
|
||||
const allPrivateLocations = await getSyntheticsPrivateLocations(
|
||||
|
@ -131,7 +172,11 @@ export class SyntheticsPrivateLocation {
|
|||
const newPolicy = await this.generateNewPolicy(config, privateLocation);
|
||||
|
||||
if (!newPolicy) {
|
||||
throw new Error('Unable to create Synthetics package policy for private location');
|
||||
throw new Error(
|
||||
`Unable to ${
|
||||
hasPolicy ? 'update' : 'create'
|
||||
} Synthetics package policy for private location ${privateLocation.label}`
|
||||
);
|
||||
}
|
||||
|
||||
if (hasPolicy) {
|
||||
|
@ -142,13 +187,26 @@ export class SyntheticsPrivateLocation {
|
|||
} else if (hasPolicy) {
|
||||
const soClient = this.server.authSavedObjectsClient!;
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
await this.server.fleet.packagePolicyService.delete(soClient, esClient, [currId], {
|
||||
force: true,
|
||||
});
|
||||
try {
|
||||
await this.server.fleet.packagePolicyService.delete(soClient, esClient, [currId], {
|
||||
force: true,
|
||||
});
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
throw new Error(
|
||||
`Unable to delete Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
} with private location ${privateLocation.label}`
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
return null;
|
||||
throw new Error(
|
||||
`Unable to ${hasPolicy ? 'update' : 'create'} Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
} with private location ${privateLocation.label}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,15 +215,10 @@ export class SyntheticsPrivateLocation {
|
|||
const soClient = this.server.authSavedObjectsClient;
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
if (soClient && esClient) {
|
||||
try {
|
||||
return await this.server.fleet.packagePolicyService.create(soClient, esClient, newPolicy, {
|
||||
id,
|
||||
overwrite: true,
|
||||
});
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
return null;
|
||||
}
|
||||
return await this.server.fleet.packagePolicyService.create(soClient, esClient, newPolicy, {
|
||||
id,
|
||||
overwrite: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,20 +226,15 @@ export class SyntheticsPrivateLocation {
|
|||
const soClient = this.server.authSavedObjectsClient;
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
if (soClient && esClient) {
|
||||
try {
|
||||
return await this.server.fleet.packagePolicyService.update(
|
||||
soClient,
|
||||
esClient,
|
||||
id,
|
||||
updatedPolicy,
|
||||
{
|
||||
force: true,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
return null;
|
||||
}
|
||||
return await this.server.fleet.packagePolicyService.update(
|
||||
soClient,
|
||||
esClient,
|
||||
id,
|
||||
updatedPolicy,
|
||||
{
|
||||
force: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,33 +248,10 @@ export class SyntheticsPrivateLocation {
|
|||
}
|
||||
}
|
||||
|
||||
async findMonitor(config: HeartbeatConfig) {
|
||||
const soClient = this.server.authSavedObjectsClient;
|
||||
try {
|
||||
const list = await this.server.fleet.packagePolicyService.list(soClient!, {
|
||||
page: 1,
|
||||
perPage: 10000,
|
||||
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:synthetics`,
|
||||
});
|
||||
|
||||
const { locations } = config;
|
||||
|
||||
const fleetManagedLocationIds = locations
|
||||
.filter((loc) => !loc.isServiceManaged)
|
||||
.map((loc) => config.id + '-' + loc.id);
|
||||
|
||||
return list.items.filter((policy) => {
|
||||
return fleetManagedLocationIds.includes(policy.name);
|
||||
});
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteMonitor(config: HeartbeatConfig) {
|
||||
async deleteMonitor(config: HeartbeatConfig, request: KibanaRequest) {
|
||||
const soClient = this.server.authSavedObjectsClient;
|
||||
const esClient = this.server.uptimeEsClient.baseESClient;
|
||||
|
||||
if (soClient && esClient) {
|
||||
const { locations } = config;
|
||||
|
||||
|
@ -237,6 +262,13 @@ export class SyntheticsPrivateLocation {
|
|||
for (const privateLocation of monitorPrivateLocations) {
|
||||
const location = allPrivateLocations?.find((loc) => loc.id === privateLocation.id);
|
||||
if (location) {
|
||||
await this.checkPermissions(
|
||||
request,
|
||||
`Unable to delete Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
}. Fleet write permissions are needed to use Synthetics private locations.`
|
||||
);
|
||||
|
||||
try {
|
||||
await this.server.fleet.packagePolicyService.delete(
|
||||
soClient,
|
||||
|
@ -248,7 +280,11 @@ export class SyntheticsPrivateLocation {
|
|||
);
|
||||
} catch (e) {
|
||||
this.server.logger.error(e);
|
||||
return null;
|
||||
throw new Error(
|
||||
`Unable to delete Synthetics package policy for monitor ${
|
||||
config[ConfigKey.NAME]
|
||||
} with private location ${location.label}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,6 +171,8 @@ export class ProjectMonitorFormatter {
|
|||
monitor: normalizedMonitor,
|
||||
monitorSavedObject: newMonitor,
|
||||
syntheticsMonitorClient: this.syntheticsMonitorClient,
|
||||
savedObjectsClient: this.savedObjectsClient,
|
||||
request: this.request,
|
||||
});
|
||||
this.createdMonitors.push(monitor.id);
|
||||
}
|
||||
|
@ -182,6 +184,9 @@ export class ProjectMonitorFormatter {
|
|||
details: e.message,
|
||||
payload: monitor,
|
||||
});
|
||||
if (this.staleMonitorsMap[monitor.id]) {
|
||||
this.staleMonitorsMap[monitor.id].stale = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -271,8 +276,11 @@ export class ProjectMonitorFormatter {
|
|||
editedMonitor: normalizedMonitor,
|
||||
editedMonitorSavedObject: editedMonitor,
|
||||
previousMonitor,
|
||||
decryptedPreviousMonitor,
|
||||
server: this.server,
|
||||
syntheticsMonitorClient: this.syntheticsMonitorClient,
|
||||
savedObjectsClient: this.savedObjectsClient,
|
||||
request: this.request,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -314,6 +322,7 @@ export class ProjectMonitorFormatter {
|
|||
server: this.server,
|
||||
monitorId,
|
||||
syntheticsMonitorClient: this.syntheticsMonitorClient,
|
||||
request: this.request,
|
||||
});
|
||||
this.deletedMonitors.push(journeyId);
|
||||
} catch (e) {
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { KibanaRequest } from '@kbn/core/server';
|
||||
import { SyntheticsMonitorClient } from './synthetics_monitor_client';
|
||||
import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters';
|
||||
import { SyntheticsService } from '../synthetics_service';
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import times from 'lodash/times';
|
||||
import {
|
||||
LocationStatus,
|
||||
|
@ -20,6 +20,7 @@ describe('SyntheticsMonitorClient', () => {
|
|||
const mockEsClient = {
|
||||
search: jest.fn(),
|
||||
};
|
||||
const mockRequest = {} as unknown as KibanaRequest;
|
||||
|
||||
const logger = loggerMock.create();
|
||||
|
||||
|
@ -84,7 +85,7 @@ describe('SyntheticsMonitorClient', () => {
|
|||
const client = new SyntheticsMonitorClient(syntheticsService, serverMock);
|
||||
client.privateLocationAPI.createMonitor = jest.fn();
|
||||
|
||||
await client.addMonitor(monitor, id);
|
||||
await client.addMonitor(monitor, id, mockRequest);
|
||||
|
||||
expect(syntheticsService.addConfig).toHaveBeenCalledTimes(1);
|
||||
expect(client.privateLocationAPI.createMonitor).toHaveBeenCalledTimes(1);
|
||||
|
@ -97,7 +98,7 @@ describe('SyntheticsMonitorClient', () => {
|
|||
const client = new SyntheticsMonitorClient(syntheticsService, serverMock);
|
||||
client.privateLocationAPI.editMonitor = jest.fn();
|
||||
|
||||
await client.editMonitor(monitor, id);
|
||||
await client.editMonitor(monitor, id, mockRequest);
|
||||
|
||||
expect(syntheticsService.editConfig).toHaveBeenCalledTimes(1);
|
||||
expect(client.privateLocationAPI.editMonitor).toHaveBeenCalledTimes(1);
|
||||
|
@ -109,7 +110,7 @@ describe('SyntheticsMonitorClient', () => {
|
|||
const client = new SyntheticsMonitorClient(syntheticsService, serverMock);
|
||||
client.privateLocationAPI.deleteMonitor = jest.fn();
|
||||
|
||||
await client.deleteMonitor(monitor as unknown as SyntheticsMonitorWithId);
|
||||
await client.deleteMonitor(monitor as unknown as SyntheticsMonitorWithId, mockRequest);
|
||||
|
||||
expect(syntheticsService.deleteConfigs).toHaveBeenCalledTimes(1);
|
||||
expect(client.privateLocationAPI.deleteMonitor).toHaveBeenCalledTimes(1);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { KibanaRequest } from '@kbn/core/server';
|
||||
import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters';
|
||||
import { SyntheticsPrivateLocation } from '../private_location/synthetics_private_location';
|
||||
import { SyntheticsService } from '../synthetics_service';
|
||||
|
@ -26,7 +26,7 @@ export class SyntheticsMonitorClient {
|
|||
this.privateLocationAPI = new SyntheticsPrivateLocation(server);
|
||||
}
|
||||
|
||||
async addMonitor(monitor: MonitorFields, id: string) {
|
||||
async addMonitor(monitor: MonitorFields, id: string, request: KibanaRequest) {
|
||||
await this.syntheticsService.setupIndexTemplates();
|
||||
|
||||
const config = formatHeartbeatRequest({
|
||||
|
@ -38,14 +38,15 @@ export class SyntheticsMonitorClient {
|
|||
const { privateLocations, publicLocations } = this.parseLocations(config);
|
||||
|
||||
if (privateLocations.length > 0) {
|
||||
await this.privateLocationAPI.createMonitor(config);
|
||||
await this.privateLocationAPI.createMonitor(config, request);
|
||||
}
|
||||
|
||||
if (publicLocations.length > 0) {
|
||||
return await this.syntheticsService.addConfig(config);
|
||||
}
|
||||
}
|
||||
async editMonitor(editedMonitor: MonitorFields, id: string) {
|
||||
|
||||
async editMonitor(editedMonitor: MonitorFields, id: string, request: KibanaRequest) {
|
||||
const editedConfig = formatHeartbeatRequest({
|
||||
monitor: editedMonitor,
|
||||
monitorId: id,
|
||||
|
@ -54,7 +55,7 @@ export class SyntheticsMonitorClient {
|
|||
|
||||
const { publicLocations } = this.parseLocations(editedConfig);
|
||||
|
||||
await this.privateLocationAPI.editMonitor(editedConfig);
|
||||
await this.privateLocationAPI.editMonitor(editedConfig, request);
|
||||
|
||||
if (publicLocations.length > 0) {
|
||||
return await this.syntheticsService.editConfig(editedConfig);
|
||||
|
@ -62,8 +63,9 @@ export class SyntheticsMonitorClient {
|
|||
|
||||
await this.syntheticsService.editConfig(editedConfig);
|
||||
}
|
||||
async deleteMonitor(monitor: SyntheticsMonitorWithId) {
|
||||
await this.privateLocationAPI.deleteMonitor(monitor);
|
||||
|
||||
async deleteMonitor(monitor: SyntheticsMonitorWithId, request: KibanaRequest) {
|
||||
await this.privateLocationAPI.deleteMonitor(monitor, request);
|
||||
return await this.syntheticsService.deleteConfigs([monitor]);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import uuid from 'uuid';
|
||||
import { omit } from 'lodash';
|
||||
import expect from '@kbn/expect';
|
||||
import { secretKeys } from '@kbn/synthetics-plugin/common/constants/monitor_management';
|
||||
|
@ -11,6 +12,7 @@ import { DataStream, HTTPFields } from '@kbn/synthetics-plugin/common/runtime_ty
|
|||
import { API_URLS } from '@kbn/synthetics-plugin/common/constants';
|
||||
import { DEFAULT_FIELDS } from '@kbn/synthetics-plugin/common/constants/monitor_defaults';
|
||||
import { ALL_SPACES_ID } from '@kbn/security-plugin/common/constants';
|
||||
import { syntheticsMonitorType } from '@kbn/synthetics-plugin/server/legacy_uptime/lib/saved_objects/synthetics_monitor';
|
||||
import { format as formatUrl } from 'url';
|
||||
import supertest from 'supertest';
|
||||
import { serviceApiKeyPrivileges } from '@kbn/synthetics-plugin/server/synthetics_service/get_api_key';
|
||||
|
@ -22,6 +24,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
this.tags('skipCloud');
|
||||
|
||||
const supertestAPI = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const security = getService('security');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
let _httpMonitorJson: HTTPFields;
|
||||
let httpMonitorJson: HTTPFields;
|
||||
|
@ -107,7 +112,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
});
|
||||
|
||||
it('cannot create a valid monitor without a monitor type', async () => {
|
||||
it('cannot create a invalid monitor without a monitor type', async () => {
|
||||
// Delete a required property to make payload invalid
|
||||
const newMonitor = {
|
||||
name: 'Sample name',
|
||||
|
@ -222,5 +227,67 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(apiResponse.body.message).eql('Unable to create synthetics-monitor');
|
||||
});
|
||||
});
|
||||
|
||||
it('handles private location errors and immediately deletes monitor if integration policy is unable to be saved', async () => {
|
||||
const name = `Monitor with private location ${uuid.v4()}`;
|
||||
const newMonitor = {
|
||||
name,
|
||||
type: 'http',
|
||||
urls: 'https://elastic.co',
|
||||
locations: [
|
||||
{
|
||||
id: 'policy-id',
|
||||
label: 'Private Europe West',
|
||||
isServiceManaged: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const username = 'admin';
|
||||
const roleName = `synthetics_admin`;
|
||||
const password = `${username}-password`;
|
||||
const SPACE_ID = `test-space-${uuid.v4()}`;
|
||||
const SPACE_NAME = `test-space-name ${uuid.v4()}`;
|
||||
try {
|
||||
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
|
||||
|
||||
// use a user without fleet permissions to cause an error
|
||||
await security.role.create(roleName, {
|
||||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
uptime: ['all'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
},
|
||||
],
|
||||
});
|
||||
await security.user.create(username, {
|
||||
password,
|
||||
roles: [roleName],
|
||||
full_name: 'a kibana user',
|
||||
});
|
||||
await supertestWithoutAuth
|
||||
.post(API_URLS.SYNTHETICS_MONITORS)
|
||||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(newMonitor)
|
||||
.expect(500);
|
||||
|
||||
const response = await supertestAPI
|
||||
.get(API_URLS.SYNTHETICS_MONITORS)
|
||||
.auth(username, password)
|
||||
.query({
|
||||
filter: `${syntheticsMonitorType}.attributes.name: "${name}"`,
|
||||
})
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.total).eql(0);
|
||||
} finally {
|
||||
await security.user.delete(username);
|
||||
await security.role.delete(roleName);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { PackagePolicy } from '@kbn/fleet-plugin/common';
|
|||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { getFixtureJson } from './helper/get_fixture_json';
|
||||
import { PrivateLocationTestService } from './services/private_location_test_service';
|
||||
|
||||
import { comparePolicies, getTestProjectSyntheticsPolicy } from './sample_data/test_policy';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
|
|
|
@ -4,33 +4,52 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import uuid from 'uuid';
|
||||
import expect from '@kbn/expect';
|
||||
import { HTTPFields, MonitorFields } from '@kbn/synthetics-plugin/common/runtime_types';
|
||||
import { API_URLS } from '@kbn/synthetics-plugin/common/constants';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { getFixtureJson } from './helper/get_fixture_json';
|
||||
import { PrivateLocationTestService } from './services/private_location_test_service';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
describe('[DELETE] /internal/uptime/service/monitors', function () {
|
||||
this.tags('skipCloud');
|
||||
|
||||
const supertest = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const security = getService('security');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
const testPrivateLocations = new PrivateLocationTestService(getService);
|
||||
|
||||
let _httpMonitorJson: HTTPFields;
|
||||
let httpMonitorJson: HTTPFields;
|
||||
let testPolicyId = '';
|
||||
|
||||
const saveMonitor = async (monitor: MonitorFields) => {
|
||||
const res = await supertest
|
||||
.post(API_URLS.SYNTHETICS_MONITORS)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(monitor);
|
||||
.send(monitor)
|
||||
.expect(200);
|
||||
|
||||
return res.body;
|
||||
};
|
||||
|
||||
before(() => {
|
||||
before(async () => {
|
||||
_httpMonitorJson = getFixtureJson('http_monitor');
|
||||
await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200);
|
||||
await supertest
|
||||
.post('/api/fleet/epm/packages/synthetics/0.9.5')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({ force: true })
|
||||
.expect(200);
|
||||
|
||||
const testPolicyName = 'Fleet test server policy' + Date.now();
|
||||
const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName);
|
||||
testPolicyId = apiResponse.body.item.id;
|
||||
await testPrivateLocations.setTestLocations([testPolicyId]);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -80,5 +99,71 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.set('kbn-xsrf', 'true')
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('handles private location errors and does not delete the monitor if integration policy is unable to be deleted', async () => {
|
||||
const name = `Monitor with a private location ${uuid.v4()}`;
|
||||
const newMonitor = {
|
||||
name,
|
||||
type: 'http',
|
||||
urls: 'https://elastic.co',
|
||||
locations: [
|
||||
{
|
||||
id: testPolicyId,
|
||||
label: 'Private Europe West',
|
||||
isServiceManaged: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const username = 'admin';
|
||||
const roleName = `synthetics_admin`;
|
||||
const password = `${username}-password`;
|
||||
const SPACE_ID = `test-space-${uuid.v4()}`;
|
||||
const SPACE_NAME = `test-space-name ${uuid.v4()}`;
|
||||
let monitorId = '';
|
||||
|
||||
try {
|
||||
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
|
||||
|
||||
// use a user without fleet permissions to cause an error
|
||||
await security.role.create(roleName, {
|
||||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
uptime: ['all'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
},
|
||||
],
|
||||
});
|
||||
await security.user.create(username, {
|
||||
password,
|
||||
roles: [roleName],
|
||||
full_name: 'a kibana user',
|
||||
});
|
||||
const { id } = await saveMonitor(newMonitor as MonitorFields);
|
||||
monitorId = id;
|
||||
await supertestWithoutAuth
|
||||
.delete(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
|
||||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(500);
|
||||
|
||||
const response = await supertest
|
||||
.get(`${API_URLS.SYNTHETICS_MONITORS}/${monitorId}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
// ensure monitor was not deleted
|
||||
expect(response.body.attributes.urls).eql(newMonitor.urls);
|
||||
} finally {
|
||||
await security.user.delete(username);
|
||||
await security.role.delete(roleName);
|
||||
await supertest
|
||||
.delete(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import uuid from 'uuid';
|
||||
import expect from '@kbn/expect';
|
||||
import { omit } from 'lodash';
|
||||
import { SimpleSavedObject } from '@kbn/core/public';
|
||||
|
@ -12,14 +13,22 @@ import { ConfigKey, HTTPFields, MonitorFields } from '@kbn/synthetics-plugin/com
|
|||
import { API_URLS } from '@kbn/synthetics-plugin/common/constants';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { getFixtureJson } from './helper/get_fixture_json';
|
||||
import { PrivateLocationTestService } from './services/private_location_test_service';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
describe('[PUT] /internal/uptime/service/monitors', function () {
|
||||
this.tags('skipCloud');
|
||||
|
||||
const supertest = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const security = getService('security');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
const testPrivateLocations = new PrivateLocationTestService(getService);
|
||||
|
||||
let _httpMonitorJson: HTTPFields;
|
||||
let httpMonitorJson: HTTPFields;
|
||||
let testPolicyId = '';
|
||||
|
||||
const saveMonitor = async (monitor: MonitorFields) => {
|
||||
const res = await supertest
|
||||
|
@ -31,8 +40,14 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
return res.body as SimpleSavedObject<MonitorFields>;
|
||||
};
|
||||
|
||||
before(() => {
|
||||
before(async () => {
|
||||
_httpMonitorJson = getFixtureJson('http_monitor');
|
||||
await supertest.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200);
|
||||
|
||||
const testPolicyName = 'Fleet test server policy' + Date.now();
|
||||
const apiResponse = await testPrivateLocations.addFleetPolicy(testPolicyName);
|
||||
testPolicyId = apiResponse.body.item.id;
|
||||
await testPrivateLocations.setTestLocations([testPolicyId]);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -110,5 +125,81 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(apiResponse.status).eql(400);
|
||||
expect(apiResponse.body.message).eql('Monitor type is invalid');
|
||||
});
|
||||
|
||||
it('handles private location errors and does not update the monitor if integration policy is unable to be updated', async () => {
|
||||
const name = 'Monitor with private location';
|
||||
const newMonitor = {
|
||||
name,
|
||||
type: 'http',
|
||||
urls: 'https://elastic.co',
|
||||
locations: [
|
||||
{
|
||||
id: 'us_central_west',
|
||||
label: 'Europe West',
|
||||
isServiceManaged: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const username = 'admin';
|
||||
const roleName = `synthetics_admin`;
|
||||
const password = `${username}-password`;
|
||||
const SPACE_ID = `test-space-${uuid.v4()}`;
|
||||
const SPACE_NAME = `test-space-name ${uuid.v4()}`;
|
||||
let monitorId = '';
|
||||
|
||||
try {
|
||||
await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
|
||||
|
||||
// use a user without fleet permissions to cause an error
|
||||
await security.role.create(roleName, {
|
||||
kibana: [
|
||||
{
|
||||
feature: {
|
||||
uptime: ['all'],
|
||||
},
|
||||
spaces: ['*'],
|
||||
},
|
||||
],
|
||||
});
|
||||
await security.user.create(username, {
|
||||
password,
|
||||
roles: [roleName],
|
||||
full_name: 'a kibana user',
|
||||
});
|
||||
const { id, attributes: savedMonitor } = await saveMonitor(newMonitor as MonitorFields);
|
||||
monitorId = id;
|
||||
const toUpdate = {
|
||||
...savedMonitor,
|
||||
locations: [
|
||||
...savedMonitor.locations,
|
||||
{ id: testPolicyId, label: 'Private location', isServiceManaged: false },
|
||||
],
|
||||
urls: 'https://google.com',
|
||||
};
|
||||
await supertestWithoutAuth
|
||||
.put(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
|
||||
.auth(username, password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(toUpdate)
|
||||
.expect(500);
|
||||
|
||||
const response = await supertest
|
||||
.get(`${API_URLS.SYNTHETICS_MONITORS}/${monitorId}`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
|
||||
// ensure monitor was not updated
|
||||
expect(response.body.attributes.urls).eql(newMonitor.urls);
|
||||
expect(response.body.attributes.locations).eql(newMonitor.locations);
|
||||
} finally {
|
||||
await security.user.delete(username);
|
||||
await security.role.delete(roleName);
|
||||
await supertest
|
||||
.delete(API_URLS.SYNTHETICS_MONITORS + '/' + monitorId)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue