;
diff --git a/x-pack/plugins/uptime/kibana.json b/x-pack/plugins/uptime/kibana.json
index 409436d734011..803d1d82d933a 100644
--- a/x-pack/plugins/uptime/kibana.json
+++ b/x-pack/plugins/uptime/kibana.json
@@ -6,21 +6,24 @@
"id": "uptime",
"kibanaVersion": "kibana",
"optionalPlugins": [
+ "cloud",
"data",
+ "fleet",
"home",
- "ml",
- "fleet"
+ "ml"
],
"requiredPlugins": [
"alerting",
"embeddable",
+ "encryptedSavedObjects",
"inspector",
"features",
"licensing",
- "triggersActionsUi",
- "usageCollection",
+ "observability",
"ruleRegistry",
- "observability"
+ "security",
+ "triggersActionsUi",
+ "usageCollection"
],
"server": true,
"ui": true,
diff --git a/x-pack/plugins/uptime/server/kibana.index.ts b/x-pack/plugins/uptime/server/kibana.index.ts
index 131510e62c5d9..945a4295148a2 100644
--- a/x-pack/plugins/uptime/server/kibana.index.ts
+++ b/x-pack/plugins/uptime/server/kibana.index.ts
@@ -11,7 +11,7 @@ import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
import { PLUGIN } from '../common/constants/plugin';
import { compose } from './lib/compose/kibana';
import { initUptimeServer } from './uptime_server';
-import { UptimeCorePlugins, UptimeCoreSetup } from './lib/adapters/framework';
+import { UptimeCorePluginsSetup, UptimeCoreSetup } from './lib/adapters/framework';
import { umDynamicSettings } from './lib/saved_objects/uptime_settings';
import { UptimeRuleRegistry } from './plugin';
@@ -29,7 +29,7 @@ export interface KibanaServer extends Server {
export const initServerWithKibana = (
server: UptimeCoreSetup,
- plugins: UptimeCorePlugins,
+ plugins: UptimeCorePluginsSetup,
ruleRegistry: UptimeRuleRegistry,
logger: Logger
) => {
diff --git a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts
index d9648a8aae575..029c6164c0481 100644
--- a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts
+++ b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts
@@ -12,12 +12,19 @@ import type {
IScopedClusterClient,
} from 'src/core/server';
import { ObservabilityPluginSetup } from '../../../../../observability/server';
+import {
+ EncryptedSavedObjectsPluginSetup,
+ EncryptedSavedObjectsPluginStart,
+} from '../../../../../encrypted_saved_objects/server';
import { UMKibanaRoute } from '../../../rest_api';
import { PluginSetupContract } from '../../../../../features/server';
import { MlPluginSetup as MlSetup } from '../../../../../ml/server';
import { RuleRegistryPluginSetupContract } from '../../../../../rule_registry/server';
import { UptimeESClient } from '../../lib';
import type { UptimeRouter } from '../../../types';
+import { SecurityPluginStart } from '../../../../../security/server';
+import { CloudSetup } from '../../../../../cloud/server';
+import { FleetStartContract } from '../../../../../fleet/server';
import { UptimeConfig } from '../../../../common/config';
export type UMElasticsearchQueryFn = (
@@ -35,16 +42,27 @@ export type UMSavedObjectsQueryFn = (
export interface UptimeCoreSetup {
router: UptimeRouter;
config: UptimeConfig;
+ cloud?: CloudSetup;
+ fleet: FleetStartContract;
+ security: SecurityPluginStart;
+ encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
}
-export interface UptimeCorePlugins {
+export interface UptimeCorePluginsSetup {
features: PluginSetupContract;
alerting: any;
- elasticsearch: any;
observability: ObservabilityPluginSetup;
usageCollection: UsageCollectionSetup;
ml: MlSetup;
+ cloud?: CloudSetup;
ruleRegistry: RuleRegistryPluginSetupContract;
+ encryptedSavedObjects: EncryptedSavedObjectsPluginSetup;
+}
+
+export interface UptimeCorePluginsStart {
+ security: SecurityPluginStart;
+ fleet: FleetStartContract;
+ encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
}
export interface UMBackendFrameworkAdapter {
diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts
index cf241386ec277..7dc962c38fec7 100644
--- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts
+++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts
@@ -19,7 +19,7 @@ import { DURATION_ANOMALY } from '../../../common/constants/alerts';
import { commonStateTranslations, durationAnomalyTranslations } from './translations';
import { AnomaliesTableRecord } from '../../../../ml/common/types/anomalies';
import { getSeverityType } from '../../../../ml/common/util/anomaly_utils';
-import { UptimeCorePlugins } from '../adapters/framework';
+import { UptimeCorePluginsSetup } from '../adapters/framework';
import { UptimeAlertTypeFactory } from './types';
import { Ping } from '../../../common/runtime_types/ping';
import { getMLJobId } from '../../../common/lib';
@@ -45,7 +45,7 @@ export const getAnomalySummary = (anomaly: AnomaliesTableRecord, monitorInfo: Pi
};
const getAnomalies = async (
- plugins: UptimeCorePlugins,
+ plugins: UptimeCorePluginsSetup,
savedObjectsClient: SavedObjectsClientContract,
params: Record,
lastCheckedAt: string
diff --git a/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts
index bc9aa76cb4a5b..6481a1e2ebdcf 100644
--- a/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts
+++ b/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts
@@ -7,7 +7,7 @@
import { Logger } from 'kibana/server';
import { UMServerLibs } from '../../lib';
-import { UptimeCorePlugins, UptimeCoreSetup } from '../../adapters';
+import { UptimeCorePluginsSetup, UptimeCoreSetup } from '../../adapters';
import type { UptimeRouter } from '../../../types';
import type { IRuleDataClient } from '../../../../../rule_registry/server';
import { ruleRegistryMocks } from '../../../../../rule_registry/server/mocks';
@@ -27,8 +27,8 @@ export const bootstrapDependencies = (customRequests?: any, customPlugins: any =
const router = {} as UptimeRouter;
// these server/libs parameters don't have any functionality, which is fine
// because we aren't testing them here
- const server: UptimeCoreSetup = { router, config: {} };
- const plugins: UptimeCorePlugins = customPlugins as any;
+ const server = { router, config: {} } as UptimeCoreSetup;
+ const plugins: UptimeCorePluginsSetup = customPlugins as any;
const libs: UMServerLibs = { requests: {} } as UMServerLibs;
libs.requests = { ...libs.requests, ...customRequests };
return { server, libs, plugins };
diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts
index f4ac2f354d814..f734628e61b95 100644
--- a/x-pack/plugins/uptime/server/lib/alerts/types.ts
+++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts
@@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters';
+import { UptimeCorePluginsSetup, UptimeCoreSetup } from '../adapters';
import { UMServerLibs } from '../lib';
import { AlertTypeWithExecutor } from '../../../../rule_registry/server';
import { AlertInstanceContext, AlertTypeState } from '../../../../alerting/common';
@@ -32,5 +32,5 @@ export type DefaultUptimeAlertInstance = AlertTy
export type UptimeAlertTypeFactory = (
server: UptimeCoreSetup,
libs: UMServerLibs,
- plugins: UptimeCorePlugins
+ plugins: UptimeCorePluginsSetup
) => DefaultUptimeAlertInstance;
diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/index.ts b/x-pack/plugins/uptime/server/lib/saved_objects/index.ts
new file mode 100644
index 0000000000000..ee1cfbbc55acd
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/saved_objects/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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.
+ */
+
+export { savedObjectsAdapter } from './saved_objects';
diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts b/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts
index 7a53a37b804e9..5aa6b7ea7c5a9 100644
--- a/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts
+++ b/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts
@@ -9,33 +9,43 @@ import {
SavedObjectsErrorHelpers,
SavedObjectsServiceSetup,
} from '../../../../../../src/core/server';
+import { EncryptedSavedObjectsPluginSetup } from '../../../../encrypted_saved_objects/server';
+
import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants';
import { DynamicSettings } from '../../../common/runtime_types';
import { UMSavedObjectsQueryFn } from '../adapters';
import { UptimeConfig } from '../../../common/config';
import { settingsObjectId, umDynamicSettings } from './uptime_settings';
import { syntheticsMonitor } from './synthetics_monitor';
-
-export interface UMSavedObjectsAdapter {
- config: UptimeConfig;
- getUptimeDynamicSettings: UMSavedObjectsQueryFn;
- setUptimeDynamicSettings: UMSavedObjectsQueryFn;
-}
+import { syntheticsServiceApiKey } from './service_api_key';
export const registerUptimeSavedObjects = (
savedObjectsService: SavedObjectsServiceSetup,
+ encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
config: UptimeConfig
) => {
savedObjectsService.registerType(umDynamicSettings);
if (config?.unsafe?.service.enabled) {
savedObjectsService.registerType(syntheticsMonitor);
+ savedObjectsService.registerType(syntheticsServiceApiKey);
+
+ encryptedSavedObjects.registerType({
+ type: syntheticsServiceApiKey.name,
+ attributesToEncrypt: new Set(['apiKey']),
+ });
}
};
+export interface UMSavedObjectsAdapter {
+ config: UptimeConfig;
+ getUptimeDynamicSettings: UMSavedObjectsQueryFn;
+ setUptimeDynamicSettings: UMSavedObjectsQueryFn;
+}
+
export const savedObjectsAdapter: UMSavedObjectsAdapter = {
config: null,
- getUptimeDynamicSettings: async (client): Promise => {
+ getUptimeDynamicSettings: async (client) => {
try {
const obj = await client.get(umDynamicSettings.name, settingsObjectId);
return obj?.attributes ?? DYNAMIC_SETTINGS_DEFAULTS;
@@ -50,7 +60,7 @@ export const savedObjectsAdapter: UMSavedObjectsAdapter = {
throw getErr;
}
},
- setUptimeDynamicSettings: async (client, settings): Promise => {
+ setUptimeDynamicSettings: async (client, settings) => {
await client.create(umDynamicSettings.name, settings, {
id: settingsObjectId,
overwrite: true,
diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/service_api_key.ts b/x-pack/plugins/uptime/server/lib/saved_objects/service_api_key.ts
new file mode 100644
index 0000000000000..9a85b71356461
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/saved_objects/service_api_key.ts
@@ -0,0 +1,74 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import {
+ SavedObjectsClientContract,
+ SavedObjectsErrorHelpers,
+ SavedObjectsType,
+} from '../../../../../../src/core/server';
+import { SyntheticsServiceApiKey } from '../../../common/runtime_types/synthetics_service_api_key';
+import { EncryptedSavedObjectsClient } from '../../../../encrypted_saved_objects/server';
+
+export const syntheticsApiKeyID = 'ba997842-b0cf-4429-aa9d-578d9bf0d391';
+const syntheticsApiKeyObjectType = 'uptime-synthetics-api-key';
+
+export const syntheticsServiceApiKey: SavedObjectsType = {
+ name: syntheticsApiKeyObjectType,
+ hidden: true,
+ namespaceType: 'single',
+ mappings: {
+ dynamic: false,
+ properties: {
+ apiKey: {
+ type: 'binary',
+ },
+ /* 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.
+ id: {
+ type: 'keyword',
+ },
+ name: {
+ type: 'long',
+ },
+ */
+ },
+ },
+ management: {
+ importableAndExportable: false,
+ icon: 'uptimeApp',
+ getTitle: () =>
+ i18n.translate('xpack.uptime.synthetics.service.apiKey', {
+ defaultMessage: 'Synthetics service api key',
+ }),
+ },
+};
+
+export const getSyntheticsServiceAPIKey = async (client: EncryptedSavedObjectsClient) => {
+ try {
+ const obj = await client.getDecryptedAsInternalUser(
+ syntheticsServiceApiKey.name,
+ syntheticsApiKeyID
+ );
+ return obj?.attributes;
+ } catch (getErr) {
+ if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) {
+ return undefined;
+ }
+ throw getErr;
+ }
+};
+export const setSyntheticsServiceApiKey = async (
+ client: SavedObjectsClientContract,
+ apiKey: SyntheticsServiceApiKey
+) => {
+ await client.create(syntheticsServiceApiKey.name, apiKey, {
+ id: syntheticsApiKeyID,
+ overwrite: true,
+ });
+};
diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts
new file mode 100644
index 0000000000000..1d164f5dd5b62
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts
@@ -0,0 +1,90 @@
+/*
+ * 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 { getAPIKeyForSyntheticsService } from './get_api_key';
+import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks';
+import { securityMock } from '../../../../security/server/mocks';
+import { coreMock } from '../../../../../../src/core/server/mocks';
+import { syntheticsServiceApiKey } from '../saved_objects/service_api_key';
+import { KibanaRequest } from 'kibana/server';
+
+describe('getAPIKeyTest', function () {
+ const core = coreMock.createStart();
+ const security = securityMock.createStart();
+ const encryptedSavedObject = encryptedSavedObjectsMock.createStart();
+ const request = {} as KibanaRequest;
+
+ security.authc.apiKeys.areAPIKeysEnabled = jest.fn().mockReturnValue(true);
+ security.authc.apiKeys.create = jest.fn().mockReturnValue({
+ id: 'test',
+ name: 'service-api-key',
+ api_key: 'qwerty',
+ encoded: '@#$%^&',
+ });
+
+ it('should generate an api key and return it', async () => {
+ const apiKey = await getAPIKeyForSyntheticsService({
+ request,
+ security,
+ encryptedSavedObject,
+ savedObjectsClient: core.savedObjects.getScopedClient(request),
+ });
+
+ expect(security.authc.apiKeys.areAPIKeysEnabled).toHaveBeenCalledTimes(1);
+ expect(security.authc.apiKeys.create).toHaveBeenCalledTimes(1);
+ expect(security.authc.apiKeys.create).toHaveBeenCalledWith(
+ {},
+ {
+ name: 'synthetics-api-key',
+ role_descriptors: {
+ synthetics_writer: {
+ cluster: ['monitor', 'read_ilm', 'read_pipeline'],
+ index: [
+ {
+ names: ['synthetics-*'],
+ privileges: ['view_index_metadata', 'create_doc', 'auto_configure'],
+ },
+ ],
+ },
+ },
+ metadata: {
+ description:
+ 'Created for synthetics service to be passed to the heartbeat to communicate with ES',
+ },
+ }
+ );
+ expect(apiKey).toEqual({ apiKey: 'qwerty', id: 'test', name: 'service-api-key' });
+ });
+
+ it('should return existing api key', async () => {
+ const getObject = jest
+ .fn()
+ .mockReturnValue({ attributes: { apiKey: 'qwerty', id: 'test', name: 'service-api-key' } });
+
+ encryptedSavedObject.getClient = jest.fn().mockReturnValue({
+ getDecryptedAsInternalUser: getObject,
+ });
+ const apiKey = await getAPIKeyForSyntheticsService({
+ request,
+ security,
+ encryptedSavedObject,
+ savedObjectsClient: core.savedObjects.getScopedClient(request),
+ });
+
+ expect(apiKey).toEqual({ apiKey: 'qwerty', id: 'test', name: 'service-api-key' });
+
+ expect(encryptedSavedObject.getClient).toHaveBeenCalledTimes(1);
+ expect(getObject).toHaveBeenCalledTimes(1);
+ expect(encryptedSavedObject.getClient).toHaveBeenCalledWith({
+ includedHiddenTypes: [syntheticsServiceApiKey.name],
+ });
+ expect(getObject).toHaveBeenCalledWith(
+ 'uptime-synthetics-api-key',
+ 'ba997842-b0cf-4429-aa9d-578d9bf0d391'
+ );
+ });
+});
diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts
new file mode 100644
index 0000000000000..2a291c64ca2b2
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts
@@ -0,0 +1,85 @@
+/*
+ * 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 { KibanaRequest, SavedObjectsClientContract } from '../../../../../../src/core/server';
+import { EncryptedSavedObjectsPluginStart } from '../../../../encrypted_saved_objects/server';
+import { SecurityPluginStart } from '../../../../security/server';
+import {
+ getSyntheticsServiceAPIKey,
+ setSyntheticsServiceApiKey,
+ syntheticsServiceApiKey,
+} from '../saved_objects/service_api_key';
+import { SyntheticsServiceApiKey } from '../../../common/runtime_types/synthetics_service_api_key';
+
+export const getAPIKeyForSyntheticsService = async ({
+ encryptedSavedObject,
+ savedObjectsClient,
+ request,
+ security,
+}: {
+ encryptedSavedObject: EncryptedSavedObjectsPluginStart;
+ request: KibanaRequest;
+ security: SecurityPluginStart;
+ savedObjectsClient: SavedObjectsClientContract;
+}): Promise => {
+ const encryptedClient = encryptedSavedObject.getClient({
+ includedHiddenTypes: [syntheticsServiceApiKey.name],
+ });
+
+ const apiKey = await getSyntheticsServiceAPIKey(encryptedClient);
+ if (apiKey) {
+ return apiKey;
+ }
+ return await generateAndSaveAPIKey({ request, security, savedObjectsClient });
+};
+
+export const generateAndSaveAPIKey = async ({
+ security,
+ request,
+ savedObjectsClient,
+}: {
+ security: SecurityPluginStart;
+ request: KibanaRequest;
+ savedObjectsClient: SavedObjectsClientContract;
+}) => {
+ try {
+ const isApiKeysEnabled = await security.authc.apiKeys?.areAPIKeysEnabled();
+
+ if (!isApiKeysEnabled) {
+ return new Error('Please enable API keys in kibana to use synthetics service.');
+ }
+
+ const apiKeyResult = await security.authc.apiKeys?.create(request, {
+ name: 'synthetics-api-key',
+ role_descriptors: {
+ synthetics_writer: {
+ cluster: ['monitor', 'read_ilm', 'read_pipeline'],
+ index: [
+ {
+ names: ['synthetics-*'],
+ privileges: ['view_index_metadata', 'create_doc', 'auto_configure'],
+ },
+ ],
+ },
+ },
+ metadata: {
+ description:
+ 'Created for synthetics service to be passed to the heartbeat to communicate with ES',
+ },
+ });
+
+ if (apiKeyResult) {
+ const { id, name, api_key: apiKey } = apiKeyResult;
+ const apiKeyObject = { id, name, apiKey };
+ // discard decoded key and rest of the keys
+ await setSyntheticsServiceApiKey(savedObjectsClient, apiKeyObject);
+ return apiKeyObject;
+ }
+ } catch (e) {
+ throw e;
+ }
+};
diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.test.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.test.ts
new file mode 100644
index 0000000000000..f028d5e154a56
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.test.ts
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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 { getEsHosts } from './get_es_hosts';
+import { CloudSetup } from '../../../../cloud/server';
+
+describe('getEsHostsTest', () => {
+ const cloudSetup = {
+ cloudId:
+ 'TLS_Test:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJDI0ZDYwY2NjYmZjODRhZmZhNGRjYTQ3M2M2YjFlZDgwJGUxMjkyY2YzMTczZTRkNTViZDViM2NlNzYyZDg1NzY3',
+ isCloudEnabled: true,
+ } as CloudSetup;
+
+ it('should return expected host in cloud', function () {
+ const esHosts = getEsHosts({
+ cloud: cloudSetup,
+ config: {},
+ });
+
+ expect(esHosts).toEqual([
+ 'https://24d60cccbfc84affa4dca473c6b1ed80.us-central1.gcp.cloud.es.io:443',
+ ]);
+ });
+
+ it('should return expected host from config', function () {
+ const esHosts = getEsHosts({
+ config: {
+ unsafe: {
+ service: {
+ hosts: ['http://localhost:9200'],
+ },
+ },
+ },
+ });
+
+ expect(esHosts).toEqual(['http://localhost:9200']);
+ });
+ it('should return cloud hosts when both config and cloud are present', function () {
+ const esHosts = getEsHosts({
+ cloud: cloudSetup,
+ config: {
+ unsafe: {
+ service: {
+ hosts: ['http://localhost:9200'],
+ },
+ },
+ },
+ });
+
+ expect(esHosts).toEqual([
+ 'https://24d60cccbfc84affa4dca473c6b1ed80.us-central1.gcp.cloud.es.io:443',
+ ]);
+ });
+});
diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.ts
new file mode 100644
index 0000000000000..d0de73b73e23e
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.ts
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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 { CloudSetup } from '../../../../cloud/server';
+import { decodeCloudId } from '../../../../fleet/common';
+import { UptimeConfig } from '../../../common/config';
+
+export function getEsHosts({
+ cloud,
+ config,
+}: {
+ cloud?: CloudSetup;
+ config: UptimeConfig;
+}): string[] {
+ const cloudId = cloud?.isCloudEnabled && cloud.cloudId;
+ const cloudUrl = cloudId && decodeCloudId(cloudId)?.elasticsearchUrl;
+ const cloudHosts = cloudUrl ? [cloudUrl] : undefined;
+ if (cloudHosts && cloudHosts.length > 0) {
+ return cloudHosts;
+ }
+
+ const flagHosts = config?.unsafe?.service?.hosts;
+
+ if (flagHosts && flagHosts.length > 0) {
+ return flagHosts;
+ }
+
+ return [];
+}
diff --git a/x-pack/plugins/uptime/server/plugin.ts b/x-pack/plugins/uptime/server/plugin.ts
index b1b85eb943c81..4276497257111 100644
--- a/x-pack/plugins/uptime/server/plugin.ts
+++ b/x-pack/plugins/uptime/server/plugin.ts
@@ -12,27 +12,36 @@ import {
Plugin as PluginType,
ISavedObjectsRepository,
Logger,
+ SavedObjectsClient,
} from '../../../../src/core/server';
import { uptimeRuleFieldMap } from '../common/rules/uptime_rule_field_map';
import { initServerWithKibana } from './kibana.index';
-import { KibanaTelemetryAdapter, UptimeCorePlugins } from './lib/adapters';
+import {
+ KibanaTelemetryAdapter,
+ UptimeCorePluginsSetup,
+ UptimeCorePluginsStart,
+ UptimeCoreSetup,
+} from './lib/adapters';
import { registerUptimeSavedObjects, savedObjectsAdapter } from './lib/saved_objects/saved_objects';
import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map';
import { Dataset } from '../../rule_registry/server';
import { UptimeConfig } from '../common/config';
+import { installSyntheticsIndexTemplates } from './rest_api/synthetics_service/install_index_templates';
export type UptimeRuleRegistry = ReturnType['ruleRegistry'];
export class Plugin implements PluginType {
private savedObjectsClient?: ISavedObjectsRepository;
private initContext: PluginInitializerContext;
- private logger?: Logger;
+ private logger: Logger;
+ private server?: UptimeCoreSetup;
- constructor(_initializerContext: PluginInitializerContext) {
- this.initContext = _initializerContext;
+ constructor(initializerContext: PluginInitializerContext) {
+ this.initContext = initializerContext;
+ this.logger = initializerContext.logger.get();
}
- public setup(core: CoreSetup, plugins: UptimeCorePlugins) {
+ public setup(core: CoreSetup, plugins: UptimeCorePluginsSetup) {
const config = this.initContext.config.get();
savedObjectsAdapter.config = config;
@@ -53,14 +62,15 @@ export class Plugin implements PluginType {
],
});
- initServerWithKibana(
- { router: core.http.createRouter(), config },
- plugins,
- ruleDataClient,
- this.logger
- );
+ this.server = {
+ config,
+ router: core.http.createRouter(),
+ cloud: plugins.cloud,
+ } as UptimeCoreSetup;
+
+ initServerWithKibana(this.server, plugins, ruleDataClient, this.logger);
- registerUptimeSavedObjects(core.savedObjects, config);
+ registerUptimeSavedObjects(core.savedObjects, plugins.encryptedSavedObjects, config);
KibanaTelemetryAdapter.registerUsageCollector(
plugins.usageCollection,
@@ -72,8 +82,33 @@ export class Plugin implements PluginType {
};
}
- public start(core: CoreStart, _plugins: any) {
+ public start(core: CoreStart, plugins: UptimeCorePluginsStart) {
this.savedObjectsClient = core.savedObjects.createInternalRepository();
+ if (this.server) {
+ this.server.security = plugins.security;
+ this.server.fleet = plugins.fleet;
+ this.server.encryptedSavedObjects = plugins.encryptedSavedObjects;
+ }
+
+ if (this.server?.config?.unsafe?.service.enabled) {
+ const esClient = core.elasticsearch.client.asInternalUser;
+ installSyntheticsIndexTemplates({
+ esClient,
+ server: this.server,
+ savedObjectsClient: new SavedObjectsClient(core.savedObjects.createInternalRepository()),
+ }).then(
+ (result) => {
+ if (result.name === 'synthetics' && result.install_status === 'installed') {
+ this.logger.info('Installed synthetics index templates');
+ } else if (result.name === 'synthetics' && result.install_status === 'install_failed') {
+ this.logger.warn('Failed to install synthetics index templates');
+ }
+ },
+ () => {
+ this.logger.warn('Failed to install synthetics index templates');
+ }
+ );
+ }
}
public stop() {}
diff --git a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts
index 8b6add27f889a..c3d7c693ef00a 100644
--- a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts
+++ b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts
@@ -20,6 +20,7 @@ export const createRouteWithAuth = (
request,
response,
savedObjectsClient,
+ server,
}) => {
const { statusCode, message } = libs.license(context.licensing.license);
if (statusCode === 200) {
@@ -29,6 +30,7 @@ export const createRouteWithAuth = (
request,
response,
savedObjectsClient,
+ server,
});
}
switch (statusCode) {
diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts
index d5aadf079931d..344dd4d203d8d 100644
--- a/x-pack/plugins/uptime/server/rest_api/index.ts
+++ b/x-pack/plugins/uptime/server/rest_api/index.ts
@@ -27,6 +27,7 @@ import { createGetIndexStatusRoute } from './index_state';
import { createNetworkEventsRoute } from './network_events';
import { createJourneyFailedStepsRoute } from './pings/journeys';
import { createLastSuccessfulStepRoute } from './synthetics/last_successful_step';
+import { installIndexTemplatesRoute } from './synthetics_service/install_index_templates';
export * from './types';
export { createRouteWithAuth } from './create_route_with_auth';
@@ -51,4 +52,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [
createJourneyFailedStepsRoute,
createLastSuccessfulStepRoute,
createJourneyScreenshotBlocksRoute,
+ installIndexTemplatesRoute,
];
diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/install_index_templates.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/install_index_templates.ts
new file mode 100644
index 0000000000000..b40c6018f966b
--- /dev/null
+++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/install_index_templates.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server';
+import { UMRestApiRouteFactory } from '../types';
+import { API_URLS } from '../../../common/constants';
+import { UptimeCoreSetup } from '../../lib/adapters';
+
+export const installIndexTemplatesRoute: UMRestApiRouteFactory = () => ({
+ method: 'GET',
+ path: API_URLS.INDEX_TEMPLATES,
+ validate: {},
+ handler: async ({ server, request, savedObjectsClient, uptimeEsClient }): Promise => {
+ return installSyntheticsIndexTemplates({
+ server,
+ savedObjectsClient,
+ esClient: uptimeEsClient.baseESClient,
+ });
+ },
+});
+
+export async function installSyntheticsIndexTemplates({
+ esClient,
+ server,
+ savedObjectsClient,
+}: {
+ server: UptimeCoreSetup;
+ esClient: ElasticsearchClient;
+ savedObjectsClient: SavedObjectsClientContract;
+}) {
+ // no need to add error handling here since fleetSetupCompleted is already wrapped in try/catch and will log
+ // warning if setup fails to complete
+ await server.fleet.fleetSetupCompleted();
+
+ return await server.fleet.packageService.ensureInstalledPackage({
+ esClient,
+ savedObjectsClient,
+ pkgName: 'synthetics',
+ });
+}
diff --git a/x-pack/plugins/uptime/server/rest_api/types.ts b/x-pack/plugins/uptime/server/rest_api/types.ts
index ea083fc04174e..f8027cefd3f58 100644
--- a/x-pack/plugins/uptime/server/rest_api/types.ts
+++ b/x-pack/plugins/uptime/server/rest_api/types.ts
@@ -17,6 +17,7 @@ import {
} from 'kibana/server';
import { UMServerLibs, UptimeESClient } from '../lib/lib';
import type { UptimeRequestHandlerContext } from '../types';
+import { UptimeCoreSetup } from '../lib/adapters';
/**
* Defines the basic properties employed by Uptime routes.
@@ -58,7 +59,10 @@ export type UMRestApiRouteFactory = (libs: UMServerLibs) => UptimeRoute;
* Functions of this type accept our internal route format and output a route
* object that the Kibana platform can consume.
*/
-export type UMKibanaRouteWrapper = (uptimeRoute: UptimeRoute) => UMKibanaRoute;
+export type UMKibanaRouteWrapper = (
+ uptimeRoute: UptimeRoute,
+ server: UptimeCoreSetup
+) => UMKibanaRoute;
/**
* This is the contract we specify internally for route handling.
@@ -68,6 +72,7 @@ export type UMRouteHandler = ({
context,
request,
response,
+ server,
savedObjectsClient,
}: {
uptimeEsClient: UptimeESClient;
@@ -75,4 +80,5 @@ export type UMRouteHandler = ({
request: KibanaRequest, Record, Record>;
response: KibanaResponseFactory;
savedObjectsClient: SavedObjectsClientContract;
+ server: UptimeCoreSetup;
}) => IKibanaResponse | Promise>;
diff --git a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts
index ddde993cc9c70..cd25e0e742625 100644
--- a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts
+++ b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts
@@ -12,7 +12,7 @@ import { createUptimeESClient, inspectableEsQueriesMap } from '../lib/lib';
import { KibanaResponse } from '../../../../../src/core/server/http/router';
import { enableInspectEsQueries } from '../../../observability/common';
-export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({
+export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute, server) => ({
...uptimeRoute,
options: {
tags: ['access:uptime-read', ...(uptimeRoute?.writeAccess ? ['access:uptime-write'] : [])],
@@ -40,6 +40,7 @@ export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({
context,
request,
response,
+ server,
});
if (res instanceof KibanaResponse) {
diff --git a/x-pack/plugins/uptime/server/uptime_server.ts b/x-pack/plugins/uptime/server/uptime_server.ts
index ded76027a3c3a..ae606d7d4c3bf 100644
--- a/x-pack/plugins/uptime/server/uptime_server.ts
+++ b/x-pack/plugins/uptime/server/uptime_server.ts
@@ -9,7 +9,7 @@ import { Logger } from 'kibana/server';
import { createLifecycleRuleTypeFactory, IRuleDataClient } from '../../rule_registry/server';
import { UMServerLibs } from './lib/lib';
import { createRouteWithAuth, restApiRoutes, uptimeRouteWrapper } from './rest_api';
-import { UptimeCoreSetup, UptimeCorePlugins } from './lib/adapters';
+import { UptimeCoreSetup, UptimeCorePluginsSetup } from './lib/adapters';
import { statusCheckAlertFactory } from './lib/alerts/status_check';
import { tlsAlertFactory } from './lib/alerts/tls';
@@ -19,12 +19,12 @@ import { durationAnomalyAlertFactory } from './lib/alerts/duration_anomaly';
export const initUptimeServer = (
server: UptimeCoreSetup,
libs: UMServerLibs,
- plugins: UptimeCorePlugins,
+ plugins: UptimeCorePluginsSetup,
ruleDataClient: IRuleDataClient,
logger: Logger
) => {
restApiRoutes.forEach((route) =>
- libs.framework.registerRoute(uptimeRouteWrapper(createRouteWithAuth(libs, route)))
+ libs.framework.registerRoute(uptimeRouteWrapper(createRouteWithAuth(libs, route), server))
);
const {
diff --git a/x-pack/test/api_integration/apis/search/search.ts b/x-pack/test/api_integration/apis/search/search.ts
index 45e8933bf715f..d36121a102a28 100644
--- a/x-pack/test/api_integration/apis/search/search.ts
+++ b/x-pack/test/api_integration/apis/search/search.ts
@@ -250,7 +250,8 @@ export default function ({ getService }: FtrProviderContext) {
});
});
- describe('delete', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/119272
+ describe.skip('delete', () => {
it('should return 404 when no search id provided', async () => {
await supertest.delete(`/internal/search/ese`).set('kbn-xsrf', 'foo').send().expect(404);
});
diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
index 1873aeffb3884..1b1aa9abc831a 100644
--- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
+++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts
@@ -499,7 +499,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
});
- describe('no dashboard privileges', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/116881
+ describe.skip('no dashboard privileges', () => {
before(async () => {
await security.role.create('no_dashboard_privileges_role', {
elasticsearch: {
diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts
index dfb5ba1cba4fd..9c53ba20d38de 100644
--- a/x-pack/test/functional/apps/infra/home_page.ts
+++ b/x-pack/test/functional/apps/infra/home_page.ts
@@ -35,7 +35,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
- describe('with metrics present', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/119763
+ describe.skip('with metrics present', () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await pageObjects.common.navigateToApp('infraOps');
diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts
index 55a11e6ec2d20..756b43fdad604 100644
--- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts
+++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts
@@ -15,7 +15,8 @@ import {
sampleLogTestData,
} from './index_test_data';
-export default function ({ getService }: FtrProviderContext) {
+export default function ({ getPageObject, getService }: FtrProviderContext) {
+ const headerPage = getPageObject('header');
const esArchiver = getService('esArchiver');
const ml = getService('ml');
@@ -42,6 +43,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.dataVisualizerIndexBased.clickUseFullDataButton(
testData.expected.totalDocCountFormatted
);
+ await headerPage.waitUntilLoadingHasFinished();
await ml.testExecution.logTestStep(
`${testData.suiteTitle} displays elements in the doc count panel correctly`
@@ -166,8 +168,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.securityUI.loginAsMlPowerUser();
});
- // FLAKY: https://github.com/elastic/kibana/issues/118472
- describe.skip('with farequote', function () {
+ describe('with farequote', function () {
// Run tests on full farequote index.
it(`${farequoteDataViewTestData.suiteTitle} loads the data visualizer selector page`, async () => {
// Start navigation from the base of the ML app.
diff --git a/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js b/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js
index fce6fcfff7772..702a333999619 100644
--- a/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js
+++ b/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js
@@ -15,7 +15,7 @@ export const getLifecycleMethods = (getService, getPageObjects) => {
async setup(archive, { from, to, useSuperUser = false }) {
_archive = archive;
if (!useSuperUser) {
- await security.testUser.setRoles(['monitoring_user', 'kibana_admin']);
+ await security.testUser.setRoles(['monitoring_user', 'kibana_admin', 'test_monitoring']);
}
const kibanaServer = getService('kibanaServer');
diff --git a/x-pack/test/functional/apps/monitoring/cluster/list.js b/x-pack/test/functional/apps/monitoring/cluster/list.js
index 09361f88f5652..61b783efe3e68 100644
--- a/x-pack/test/functional/apps/monitoring/cluster/list.js
+++ b/x-pack/test/functional/apps/monitoring/cluster/list.js
@@ -13,6 +13,8 @@ export default function ({ getService, getPageObjects }) {
const clusterOverview = getService('monitoringClusterOverview');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['monitoring', 'header', 'common']);
+ const alertsService = getService('monitoringAlerts');
+ const browser = getService('browser');
describe('Cluster listing', () => {
describe('with trial license clusters', () => {
@@ -150,5 +152,29 @@ export default function ({ getService, getPageObjects }) {
});
});
});
+
+ describe('Alerts', () => {
+ const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects);
+
+ before(async () => {
+ await setup('x-pack/test/functional/es_archives/monitoring/multicluster', {
+ from: 'Aug 15, 2017 @ 21:00:00.000',
+ to: 'Aug 16, 2017 @ 00:00:00.000',
+ });
+ });
+
+ after(async () => {
+ await tearDown();
+
+ await alertsService.deleteAlerts();
+
+ await browser.clearLocalStorage();
+ });
+
+ it('should show a toast when alerts are created successfully', async () => {
+ await clusterList.acceptAlertsModal();
+ expect(await testSubjects.exists('alertsCreatedToast', { timeout: 10000 })).to.be(true);
+ });
+ });
});
}
diff --git a/x-pack/test/functional/apps/monitoring/cluster/overview.js b/x-pack/test/functional/apps/monitoring/cluster/overview.js
index 902c82f088152..25e52535a39b2 100644
--- a/x-pack/test/functional/apps/monitoring/cluster/overview.js
+++ b/x-pack/test/functional/apps/monitoring/cluster/overview.js
@@ -10,6 +10,11 @@ import { getLifecycleMethods } from '../_get_lifecycle_methods';
export default function ({ getService, getPageObjects }) {
const overview = getService('monitoringClusterOverview');
+ const testSubjects = getService('testSubjects');
+ const PageObjects = getPageObjects(['monitoring', 'common', 'timePicker']);
+ const alertsService = getService('monitoringAlerts');
+ const browser = getService('browser');
+ const setupMode = getService('monitoringSetupMode');
describe('Cluster overview', () => {
describe('for Green cluster with Gold license', () => {
@@ -159,5 +164,41 @@ export default function ({ getService, getPageObjects }) {
expect(await overview.doesLsPanelExist()).to.be(false);
});
});
+
+ describe('Alerts', () => {
+ const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects);
+
+ before(async () => {
+ await setup('x-pack/test/functional/es_archives/monitoring/singlecluster_green_gold', {
+ from: 'Aug 23, 2017 @ 21:29:35.267',
+ to: 'Aug 23, 2017 @ 21:47:25.556',
+ });
+ });
+
+ after(async () => {
+ await tearDown();
+ await alertsService.deleteAlerts();
+ await browser.clearLocalStorage();
+ });
+
+ describe('when create alerts options is selected in the alerts modal', () => {
+ before(async () => {
+ await overview.acceptAlertsModal();
+ });
+
+ it('should show a toast when alerts are created successfully', async () => {
+ expect(await testSubjects.exists('alertsCreatedToast', { timeout: 10000 })).to.be(true);
+ });
+
+ it('should show badges when entering setup mode', async () => {
+ await setupMode.clickSetupModeBtn();
+ await PageObjects.timePicker.startAutoRefresh(1);
+
+ expect(await testSubjects.exists('alertsBadge')).to.be(true);
+ await PageObjects.timePicker.pauseAutoRefresh();
+ await setupMode.clickExitSetupModeBtn();
+ });
+ });
+ });
});
}
diff --git a/x-pack/test/functional/apps/uptime/synthetics_integration.ts b/x-pack/test/functional/apps/uptime/synthetics_integration.ts
index 9346867a0f1db..83fc3c4079619 100644
--- a/x-pack/test/functional/apps/uptime/synthetics_integration.ts
+++ b/x-pack/test/functional/apps/uptime/synthetics_integration.ts
@@ -170,7 +170,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
});
- describe('create new policy', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/103390
+ describe.skip('create new policy', () => {
let version: string;
beforeEach(async () => {
diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js
index 2abd91fd0433a..de0c5dbd3699f 100644
--- a/x-pack/test/functional/config.js
+++ b/x-pack/test/functional/config.js
@@ -213,6 +213,11 @@ export default async function ({ readConfigFile }) {
},
security: {
roles: {
+ test_monitoring: {
+ elasticsearch: {
+ cluster: ['monitor'],
+ },
+ },
test_logstash_reader: {
elasticsearch: {
cluster: [],
diff --git a/x-pack/test/functional/services/index.ts b/x-pack/test/functional/services/index.ts
index 2f9259c16d4bf..10c68456e1262 100644
--- a/x-pack/test/functional/services/index.ts
+++ b/x-pack/test/functional/services/index.ts
@@ -37,6 +37,7 @@ import {
MonitoringKibanaInstanceProvider,
MonitoringKibanaSummaryStatusProvider,
MonitoringSetupModeProvider,
+ MonitoringAlertsProvider,
// @ts-ignore not ts yet
} from './monitoring';
// @ts-ignore not ts yet
@@ -101,6 +102,7 @@ export const services = {
monitoringKibanaInstance: MonitoringKibanaInstanceProvider,
monitoringKibanaSummaryStatus: MonitoringKibanaSummaryStatusProvider,
monitoringSetupMode: MonitoringSetupModeProvider,
+ monitoringAlerts: MonitoringAlertsProvider,
pipelineList: PipelineListProvider,
pipelineEditor: PipelineEditorProvider,
random: RandomProvider,
diff --git a/x-pack/test/functional/services/monitoring/alerts.js b/x-pack/test/functional/services/monitoring/alerts.js
new file mode 100644
index 0000000000000..c480cc0c45c03
--- /dev/null
+++ b/x-pack/test/functional/services/monitoring/alerts.js
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+export function MonitoringAlertsProvider({ getService }) {
+ const supertest = getService('supertest');
+
+ return new (class MonitoringAlerts {
+ async deleteAlerts() {
+ const apiResponse = await supertest.get('/api/alerts/_find?per_page=20');
+ const alerts = apiResponse.body.data.filter(({ consumer }) => consumer === 'monitoring');
+
+ return await Promise.all(
+ alerts.map(async (alert) =>
+ supertest.delete(`/api/alerts/alert/${alert.id}`).set('kbn-xsrf', 'true').expect(204)
+ )
+ );
+ }
+ })();
+}
diff --git a/x-pack/test/functional/services/monitoring/cluster_list.js b/x-pack/test/functional/services/monitoring/cluster_list.js
index bcf0e18ef4dd7..1873d191bca56 100644
--- a/x-pack/test/functional/services/monitoring/cluster_list.js
+++ b/x-pack/test/functional/services/monitoring/cluster_list.js
@@ -46,6 +46,10 @@ export function MonitoringClusterListProvider({ getService, getPageObjects }) {
return testSubjects.click(ALERTS_MODAL_BUTTON);
}
+ acceptAlertsModal() {
+ return testSubjects.click('alerts-modal-button');
+ }
+
getClusterLink(clusterUuid) {
return testSubjects.find(`${SUBJ_CLUSTER_ROW_PREFIX}${clusterUuid} > clusterLink`);
}
diff --git a/x-pack/test/functional/services/monitoring/index.js b/x-pack/test/functional/services/monitoring/index.js
index 5d337dc6ca822..e02280c52d2c0 100644
--- a/x-pack/test/functional/services/monitoring/index.js
+++ b/x-pack/test/functional/services/monitoring/index.js
@@ -30,3 +30,4 @@ export { MonitoringKibanaInstancesProvider } from './kibana_instances';
export { MonitoringKibanaInstanceProvider } from './kibana_instance';
export { MonitoringKibanaSummaryStatusProvider } from './kibana_summary_status';
export { MonitoringSetupModeProvider } from './setup_mode';
+export { MonitoringAlertsProvider } from './alerts';
diff --git a/x-pack/test/functional/services/monitoring/setup_mode.js b/x-pack/test/functional/services/monitoring/setup_mode.js
index 976b7b4214937..64139fbc5016f 100644
--- a/x-pack/test/functional/services/monitoring/setup_mode.js
+++ b/x-pack/test/functional/services/monitoring/setup_mode.js
@@ -13,6 +13,7 @@ export function MonitoringSetupModeProvider({ getService }) {
const SUBJ_SETUP_MODE_METRICBEAT_MIGRATION_TOOLTIP =
'monitoringSetupModeMetricbeatMigrationTooltip';
const SUBJ_SETUP_MODE_ALERTS_BADGE = 'monitoringSetupModeAlertBadges';
+ const SUBJ_EXIT_SETUP_MODE_BTN = 'exitSetupModeBtn';
return new (class SetupMode {
async doesSetupModeBtnAppear() {
@@ -34,5 +35,9 @@ export function MonitoringSetupModeProvider({ getService }) {
async doesAlertsTooltipAppear() {
return await testSubjects.exists(SUBJ_SETUP_MODE_ALERTS_BADGE);
}
+
+ async clickExitSetupModeBtn() {
+ return await testSubjects.click(SUBJ_EXIT_SETUP_MODE_BTN);
+ }
})();
}
diff --git a/yarn.lock b/yarn.lock
index 3d1ab34b02f85..44185306ca38f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12407,10 +12407,10 @@ elastic-apm-http-client@^10.3.0:
readable-stream "^3.4.0"
stream-chopper "^3.0.1"
-elastic-apm-node@^3.24.0:
- version "3.24.0"
- resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.24.0.tgz#d7acb3352f928a23c28ebabab2bd30098562814e"
- integrity sha512-Fmj/W2chWQa2zb1FfMYK2ypLB4TcnKNX+1klaJFbytRYDLgeSfo0EC7egvI3a+bLPZSRL5053PXOp7slVTPO6Q==
+elastic-apm-node@^3.25.0:
+ version "3.25.0"
+ resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.25.0.tgz#3207c936429739cd07f64cbf76d7b5b4b8e0da3e"
+ integrity sha512-3K+uUQkKeaJarjPb/pDY3fldP7QeppgPPx8nJOkOrW+BvQK5YBMiWbf4S9fdx0yUUkWsVX6K+CAc401+Y1COkg==
dependencies:
"@elastic/ecs-pino-format" "^1.2.0"
after-all-results "^2.0.0"