diff --git a/x-pack/plugins/infra/server/mocks.ts b/x-pack/plugins/infra/server/mocks.ts new file mode 100644 index 0000000000000..5b587a1fe80d5 --- /dev/null +++ b/x-pack/plugins/infra/server/mocks.ts @@ -0,0 +1,34 @@ +/* + * 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 { + createLogViewsServiceSetupMock, + createLogViewsServiceStartMock, +} from './services/log_views/log_views_service.mock'; +import { InfraPluginSetup, InfraPluginStart } from './types'; + +const createInfraSetupMock = () => { + const infraSetupMock: jest.Mocked = { + defineInternalSourceConfiguration: jest.fn(), + logViews: createLogViewsServiceSetupMock(), + }; + + return infraSetupMock; +}; + +const createInfraStartMock = () => { + const infraStartMock: jest.Mocked = { + getMetricIndices: jest.fn(), + logViews: createLogViewsServiceStartMock(), + }; + return infraStartMock; +}; + +export const infraPluginMock = { + createSetupContract: createInfraSetupMock, + createStartContract: createInfraStartMock, +}; diff --git a/x-pack/plugins/infra/server/services/log_views/log_views_service.mock.ts b/x-pack/plugins/infra/server/services/log_views/log_views_service.mock.ts index becd5a015b2ec..e472e30fae2b4 100644 --- a/x-pack/plugins/infra/server/services/log_views/log_views_service.mock.ts +++ b/x-pack/plugins/infra/server/services/log_views/log_views_service.mock.ts @@ -6,7 +6,11 @@ */ import { createLogViewsClientMock } from './log_views_client.mock'; -import { LogViewsServiceStart } from './types'; +import { LogViewsServiceSetup, LogViewsServiceStart } from './types'; + +export const createLogViewsServiceSetupMock = (): jest.Mocked => ({ + defineInternalLogView: jest.fn(), +}); export const createLogViewsServiceStartMock = (): jest.Mocked => ({ getClient: jest.fn((_savedObjectsClient: any, _elasticsearchClient: any) => diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/setup/index.js b/x-pack/plugins/monitoring/common/http_api/cluster/index.ts similarity index 52% rename from x-pack/plugins/monitoring/server/routes/api/v1/setup/index.js rename to x-pack/plugins/monitoring/common/http_api/cluster/index.ts index f450fc906d076..af53ade67f610 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/setup/index.js +++ b/x-pack/plugins/monitoring/common/http_api/cluster/index.ts @@ -5,6 +5,5 @@ * 2.0. */ -export { clusterSetupStatusRoute } from './cluster_setup_status'; -export { nodeSetupStatusRoute } from './node_setup_status'; -export { disableElasticsearchInternalCollectionRoute } from './disable_elasticsearch_internal_collection'; +export * from './post_cluster'; +export * from './post_clusters'; diff --git a/x-pack/plugins/monitoring/common/http_api/cluster/post_cluster.ts b/x-pack/plugins/monitoring/common/http_api/cluster/post_cluster.ts new file mode 100644 index 0000000000000..faa26989fec37 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/cluster/post_cluster.ts @@ -0,0 +1,29 @@ +/* + * 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 * as rt from 'io-ts'; +import { ccsRT, clusterUuidRT, timeRangeRT } from '../shared'; + +export const postClusterRequestParamsRT = rt.type({ + clusterUuid: clusterUuidRT, +}); + +export const postClusterRequestPayloadRT = rt.intersection([ + rt.partial({ + ccs: ccsRT, + }), + rt.type({ + timeRange: timeRangeRT, + codePaths: rt.array(rt.string), + }), +]); + +export type PostClusterRequestPayload = rt.TypeOf; + +export const postClusterResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/cluster/post_clusters.ts b/x-pack/plugins/monitoring/common/http_api/cluster/post_clusters.ts new file mode 100644 index 0000000000000..ad3214c354bc5 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/cluster/post_clusters.ts @@ -0,0 +1,20 @@ +/* + * 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 * as rt from 'io-ts'; +import { timeRangeRT } from '../shared'; + +export const postClustersRequestPayloadRT = rt.type({ + timeRange: timeRangeRT, + codePaths: rt.array(rt.string), +}); + +export type PostClustersRequestPayload = rt.TypeOf; + +export const postClustersResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/setup/index.ts b/x-pack/plugins/monitoring/common/http_api/setup/index.ts new file mode 100644 index 0000000000000..33cce5833c3c5 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/setup/index.ts @@ -0,0 +1,10 @@ +/* + * 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 * from './post_cluster_setup_status'; +export * from './post_node_setup_status'; +export * from './post_disable_internal_collection'; diff --git a/x-pack/plugins/monitoring/common/http_api/setup/post_cluster_setup_status.ts b/x-pack/plugins/monitoring/common/http_api/setup/post_cluster_setup_status.ts new file mode 100644 index 0000000000000..2c4f1293fb89e --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/setup/post_cluster_setup_status.ts @@ -0,0 +1,44 @@ +/* + * 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 * as rt from 'io-ts'; +import { + booleanFromStringRT, + ccsRT, + clusterUuidRT, + createLiteralValueFromUndefinedRT, + timeRangeRT, +} from '../shared'; + +export const postClusterSetupStatusRequestParamsRT = rt.partial({ + clusterUuid: clusterUuidRT, +}); + +export const postClusterSetupStatusRequestQueryRT = rt.partial({ + // This flag is not intended to be used in production. It was introduced + // as a way to ensure consistent API testing - the typical data source + // for API tests are archived data, where the cluster configuration and data + // are consistent from environment to environment. However, this endpoint + // also attempts to retrieve data from the running stack products (ES and Kibana) + // which will vary from environment to environment making it difficult + // to write tests against. Therefore, this flag exists and should only be used + // in our testing environment. + skipLiveData: rt.union([booleanFromStringRT, createLiteralValueFromUndefinedRT(false)]), +}); + +export const postClusterSetupStatusRequestPayloadRT = rt.partial({ + ccs: ccsRT, + timeRange: timeRangeRT, +}); + +export type PostClusterSetupStatusRequestPayload = rt.TypeOf< + typeof postClusterSetupStatusRequestPayloadRT +>; + +export const postClusterSetupStatusResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/setup/post_disable_internal_collection.ts b/x-pack/plugins/monitoring/common/http_api/setup/post_disable_internal_collection.ts new file mode 100644 index 0000000000000..d44794d7e1829 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/setup/post_disable_internal_collection.ts @@ -0,0 +1,14 @@ +/* + * 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 * as rt from 'io-ts'; +import { clusterUuidRT } from '../shared'; + +export const postDisableInternalCollectionRequestParamsRT = rt.partial({ + // the cluster uuid seems to be required but never used + clusterUuid: clusterUuidRT, +}); diff --git a/x-pack/plugins/monitoring/common/http_api/setup/post_node_setup_status.ts b/x-pack/plugins/monitoring/common/http_api/setup/post_node_setup_status.ts new file mode 100644 index 0000000000000..1d51d36ae4477 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/setup/post_node_setup_status.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 * as rt from 'io-ts'; +import { + booleanFromStringRT, + ccsRT, + createLiteralValueFromUndefinedRT, + timeRangeRT, +} from '../shared'; + +export const postNodeSetupStatusRequestParamsRT = rt.type({ + nodeUuid: rt.string, +}); + +export const postNodeSetupStatusRequestQueryRT = rt.partial({ + // This flag is not intended to be used in production. It was introduced + // as a way to ensure consistent API testing - the typical data source + // for API tests are archived data, where the cluster configuration and data + // are consistent from environment to environment. However, this endpoint + // also attempts to retrieve data from the running stack products (ES and Kibana) + // which will vary from environment to environment making it difficult + // to write tests against. Therefore, this flag exists and should only be used + // in our testing environment. + skipLiveData: rt.union([booleanFromStringRT, createLiteralValueFromUndefinedRT(false)]), +}); + +export const postNodeSetupStatusRequestPayloadRT = rt.partial({ + ccs: ccsRT, + timeRange: timeRangeRT, +}); + +export type PostNodeSetupStatusRequestPayload = rt.TypeOf< + typeof postNodeSetupStatusRequestPayloadRT +>; + +export const postNodeSetupStatusResponsePayloadRT = rt.type({ + // TODO: add payload entries +}); diff --git a/x-pack/plugins/monitoring/common/http_api/shared/literal_value.test.ts b/x-pack/plugins/monitoring/common/http_api/shared/literal_value.test.ts new file mode 100644 index 0000000000000..3d70e86620602 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/shared/literal_value.test.ts @@ -0,0 +1,30 @@ +/* + * 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 { either } from 'fp-ts'; +import * as rt from 'io-ts'; +import { createLiteralValueFromUndefinedRT } from './literal_value'; + +describe('LiteralValueFromUndefined runtime type', () => { + it('decodes undefined to a given literal value', () => { + expect(createLiteralValueFromUndefinedRT('SOME_VALUE').decode(undefined)).toEqual( + either.right('SOME_VALUE') + ); + }); + + it('can be used to define default values when decoding', () => { + expect( + rt.union([rt.boolean, createLiteralValueFromUndefinedRT(true)]).decode(undefined) + ).toEqual(either.right(true)); + }); + + it('rejects other values', () => { + expect( + either.isLeft(createLiteralValueFromUndefinedRT('SOME_VALUE').decode('DEFINED')) + ).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/monitoring/common/http_api/shared/query_string_boolean.test.ts b/x-pack/plugins/monitoring/common/http_api/shared/query_string_boolean.test.ts new file mode 100644 index 0000000000000..1801c6746feb2 --- /dev/null +++ b/x-pack/plugins/monitoring/common/http_api/shared/query_string_boolean.test.ts @@ -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. + */ + +import { either } from 'fp-ts'; +import { booleanFromStringRT } from './query_string_boolean'; + +describe('BooleanFromString runtime type', () => { + it('decodes string "true" to a boolean', () => { + expect(booleanFromStringRT.decode('true')).toEqual(either.right(true)); + }); + + it('decodes string "false" to a boolean', () => { + expect(booleanFromStringRT.decode('false')).toEqual(either.right(false)); + }); + + it('rejects other strings', () => { + expect(either.isLeft(booleanFromStringRT.decode('maybe'))).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/monitoring/server/debug_logger.ts b/x-pack/plugins/monitoring/server/debug_logger.ts index 0add1f12f0304..cce00f834cbb2 100644 --- a/x-pack/plugins/monitoring/server/debug_logger.ts +++ b/x-pack/plugins/monitoring/server/debug_logger.ts @@ -4,18 +4,19 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { RouteMethod } from '@kbn/core/server'; import fs from 'fs'; import { MonitoringConfig } from './config'; -import { RouteDependencies } from './types'; +import { LegacyRequest, MonitoringCore, MonitoringRouteConfig, RouteDependencies } from './types'; export function decorateDebugServer( - _server: any, + server: MonitoringCore, config: MonitoringConfig, logger: RouteDependencies['logger'] -) { +): MonitoringCore { // bail if the proper config value is not set (extra protection) if (!config.ui.debug_mode) { - return _server; + return server; } // create a debug logger that will either write to file (if debug_log_path exists) or log out via logger @@ -23,14 +24,16 @@ export function decorateDebugServer( return { // maintain the rest of _server untouched - ..._server, + ...server, // TODO: replace any - route: (options: any) => { + route: ( + options: MonitoringRouteConfig + ) => { const apiPath = options.path; - return _server.route({ + return server.route({ ...options, // TODO: replace any - handler: async (req: any) => { + handler: async (req: LegacyRequest): Promise => { const { elasticsearch: cached } = req.server.plugins; const apiRequestHeaders = req.headers; req.server.plugins.elasticsearch = { diff --git a/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.ts b/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.ts index 80d17a8ad0627..f93c3f8ad7590 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.ts +++ b/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.ts @@ -6,13 +6,18 @@ */ import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants'; +import { TimeRange } from '../../../common/http_api/shared'; import { ElasticsearchResponse } from '../../../common/types/es'; -import { LegacyRequest, Cluster } from '../../types'; -import { getNewIndexPatterns } from './get_index_patterns'; import { Globals } from '../../static_globals'; +import { Cluster, LegacyRequest } from '../../types'; +import { getNewIndexPatterns } from './get_index_patterns'; + +export interface FindSupportClusterRequestPayload { + timeRange: TimeRange; +} async function findSupportedBasicLicenseCluster( - req: LegacyRequest, + req: LegacyRequest, clusters: Cluster[], ccs: string, kibanaUuid: string, @@ -53,7 +58,7 @@ async function findSupportedBasicLicenseCluster( }, }, { term: { 'kibana_stats.kibana.uuid': kibanaUuid } }, - { range: { timestamp: { gte, lte, format: 'strict_date_optional_time' } } }, + { range: { timestamp: { gte, lte, format: 'epoch_millis' } } }, ], }, }, @@ -86,7 +91,10 @@ async function findSupportedBasicLicenseCluster( * Non-Basic license clusters and any cluster in a single-cluster environment * are also flagged as supported in this method. */ -export function flagSupportedClusters(req: LegacyRequest, ccs: string) { +export function flagSupportedClusters( + req: LegacyRequest, + ccs: string +) { const serverLog = (message: string) => req.getLogger('supported-clusters').debug(message); const flagAllSupported = (clusters: Cluster[]) => { clusters.forEach((cluster) => { diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_index_patterns.ts b/x-pack/plugins/monitoring/server/lib/cluster/get_index_patterns.ts index 7d470857dfe5a..2ebf4fe6b480e 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_index_patterns.ts +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_index_patterns.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { LegacyServer } from '../../types'; import { prefixIndexPatternWithCcs } from '../../../common/ccs_utils'; import { INDEX_PATTERN_ELASTICSEARCH, @@ -20,14 +19,13 @@ import { INDEX_PATTERN_ENTERPRISE_SEARCH, CCS_REMOTE_PATTERN, } from '../../../common/constants'; -import { MonitoringConfig } from '../..'; +import { MonitoringConfig } from '../../config'; export function getIndexPatterns( - server: LegacyServer, + config: MonitoringConfig, additionalPatterns: Record = {}, ccs: string = CCS_REMOTE_PATTERN ) { - const config = server.config; const esIndexPattern = prefixIndexPatternWithCcs(config, INDEX_PATTERN_ELASTICSEARCH, ccs); const kbnIndexPattern = prefixIndexPatternWithCcs(config, INDEX_PATTERN_KIBANA, ccs); const lsIndexPattern = prefixIndexPatternWithCcs(config, INDEX_PATTERN_LOGSTASH, ccs); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_monitoring_auth.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_monitoring_auth.ts index 3bd9f6d2265dc..a5ee876012c1d 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_monitoring_auth.ts +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_monitoring_auth.ts @@ -19,7 +19,7 @@ import { LegacyRequest } from '../../types'; */ // TODO: replace LegacyRequest with current request object + plugin retrieval -export async function verifyMonitoringAuth(req: LegacyRequest) { +export async function verifyMonitoringAuth(req: LegacyRequest) { const xpackInfo = get(req.server.plugins.monitoring, 'info'); if (xpackInfo) { @@ -42,7 +42,7 @@ export async function verifyMonitoringAuth(req: LegacyRequest) { */ // TODO: replace LegacyRequest with current request object + plugin retrieval -async function verifyHasPrivileges(req: LegacyRequest) { +async function verifyHasPrivileges(req: LegacyRequest): Promise { const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); let response; diff --git a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.js b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.ts similarity index 79% rename from x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.js rename to x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.ts index 214e8d5907443..ed92948be8e3b 100644 --- a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.js +++ b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.test.ts @@ -5,49 +5,50 @@ * 2.0. */ -import { getCollectionStatus } from '.'; +import { featuresPluginMock } from '@kbn/features-plugin/server/mocks'; +import { infraPluginMock } from '@kbn/infra-plugin/server/mocks'; +import { loggerMock } from '@kbn/logging-mocks'; +import { usageCollectionPluginMock } from '@kbn/usage-collection-plugin/server/mocks'; +import { configSchema, createConfig } from '../../../config'; +import { monitoringPluginMock } from '../../../mocks'; +import { LegacyRequest } from '../../../types'; import { getIndexPatterns } from '../../cluster/get_index_patterns'; +import { getCollectionStatus } from './get_collection_status'; const liveClusterUuid = 'a12'; const mockReq = ( - searchResult = {}, - securityEnabled = true, - userHasPermissions = true, - securityErrorMessage = null -) => { + searchResult: object = {}, + securityEnabled: boolean = true, + userHasPermissions: boolean = true, + securityErrorMessage: string | null = null +): LegacyRequest => { + const usageCollectionSetup = usageCollectionPluginMock.createSetupContract(); + const licenseService = monitoringPluginMock.createLicenseServiceMock(); + licenseService.getSecurityFeature.mockReturnValue({ + isAvailable: securityEnabled, + isEnabled: securityEnabled, + }); + const logger = loggerMock.create(); + return { server: { instanceUuid: 'kibana-1234', newPlatform: { setup: { plugins: { - usageCollection: { - getCollectorByType: () => ({ - isReady: () => false, - }), - }, + usageCollection: usageCollectionSetup, + features: featuresPluginMock.createSetup(), + infra: infraPluginMock.createSetupContract(), }, }, }, - config: { ui: { ccs: { enabled: false } } }, - usage: { - collectorSet: { - getCollectorByType: () => ({ - isReady: () => false, - }), - }, - }, + config: createConfig(configSchema.validate({ ui: { ccs: { enabled: false } } })), + log: logger, + route: jest.fn(), plugins: { monitoring: { info: { - getLicenseService: () => ({ - getSecurityFeature: () => { - return { - isAvailable: securityEnabled, - isEnabled: securityEnabled, - }; - }, - }), + getLicenseService: () => licenseService, }, }, elasticsearch: { @@ -86,6 +87,17 @@ const mockReq = ( }, }, }, + logger, + getLogger: () => logger, + params: {}, + payload: {}, + query: {}, + headers: {}, + getKibanaStatsCollector: () => null, + getUiSettingsService: () => null, + getActionTypeRegistry: () => null, + getRulesClient: () => null, + getActionsClient: () => null, }; }; @@ -124,7 +136,7 @@ describe('getCollectionStatus', () => { }, }); - const result = await getCollectionStatus(req, getIndexPatterns(req.server)); + const result = await getCollectionStatus(req, getIndexPatterns(req.server.config)); expect(result.kibana.totalUniqueInstanceCount).toBe(1); expect(result.kibana.totalUniqueFullyMigratedCount).toBe(0); @@ -173,7 +185,7 @@ describe('getCollectionStatus', () => { }, }); - const result = await getCollectionStatus(req, getIndexPatterns(req.server)); + const result = await getCollectionStatus(req, getIndexPatterns(req.server.config)); expect(result.kibana.totalUniqueInstanceCount).toBe(1); expect(result.kibana.totalUniqueFullyMigratedCount).toBe(1); @@ -229,7 +241,7 @@ describe('getCollectionStatus', () => { }, }); - const result = await getCollectionStatus(req, getIndexPatterns(req.server)); + const result = await getCollectionStatus(req, getIndexPatterns(req.server.config)); expect(result.kibana.totalUniqueInstanceCount).toBe(2); expect(result.kibana.totalUniqueFullyMigratedCount).toBe(1); @@ -251,7 +263,11 @@ describe('getCollectionStatus', () => { it('should detect products based on other indices', async () => { const req = mockReq({ hits: { total: { value: 1 } } }); - const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); + const result = await getCollectionStatus( + req, + getIndexPatterns(req.server.config), + liveClusterUuid + ); expect(result.kibana.detected.doesExist).toBe(true); expect(result.elasticsearch.detected.doesExist).toBe(true); @@ -261,13 +277,21 @@ describe('getCollectionStatus', () => { it('should work properly when security is disabled', async () => { const req = mockReq({ hits: { total: { value: 1 } } }, false); - const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); + const result = await getCollectionStatus( + req, + getIndexPatterns(req.server.config), + liveClusterUuid + ); expect(result.kibana.detected.doesExist).toBe(true); }); it('should work properly with an unknown security message', async () => { const req = mockReq({ hits: { total: { value: 1 } } }, true, true, 'foobar'); - const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); + const result = await getCollectionStatus( + req, + getIndexPatterns(req.server.config), + liveClusterUuid + ); expect(result._meta.hasPermissions).toBe(false); }); @@ -278,7 +302,11 @@ describe('getCollectionStatus', () => { true, 'no handler found for uri [/_security/user/_has_privileges] and method [POST]' ); - const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); + const result = await getCollectionStatus( + req, + getIndexPatterns(req.server.config), + liveClusterUuid + ); expect(result.kibana.detected.doesExist).toBe(true); }); @@ -289,13 +317,21 @@ describe('getCollectionStatus', () => { true, 'Invalid index name [_security]' ); - const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); + const result = await getCollectionStatus( + req, + getIndexPatterns(req.server.config), + liveClusterUuid + ); expect(result.kibana.detected.doesExist).toBe(true); }); it('should not work if the user does not have the necessary permissions', async () => { const req = mockReq({ hits: { total: { value: 1 } } }, true, false); - const result = await getCollectionStatus(req, getIndexPatterns(req.server), liveClusterUuid); + const result = await getCollectionStatus( + req, + getIndexPatterns(req.server.config), + liveClusterUuid + ); expect(result._meta.hasPermissions).toBe(false); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.ts b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.ts index b06b74fd255f4..568b8bbaef567 100644 --- a/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.ts +++ b/x-pack/plugins/monitoring/server/lib/setup/collection/get_collection_status.ts @@ -5,17 +5,18 @@ * 2.0. */ -import { get, uniq } from 'lodash'; import { CollectorFetchContext, UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; +import { get, uniq } from 'lodash'; import { - METRICBEAT_INDEX_NAME_UNIQUE_TOKEN, - ELASTICSEARCH_SYSTEM_ID, APM_SYSTEM_ID, - KIBANA_SYSTEM_ID, BEATS_SYSTEM_ID, - LOGSTASH_SYSTEM_ID, + ELASTICSEARCH_SYSTEM_ID, KIBANA_STATS_TYPE_MONITORING, + KIBANA_SYSTEM_ID, + LOGSTASH_SYSTEM_ID, + METRICBEAT_INDEX_NAME_UNIQUE_TOKEN, } from '../../../../common/constants'; +import { TimeRange } from '../../../../common/http_api/shared'; import { LegacyRequest } from '../../../types'; import { getLivesNodes } from '../../elasticsearch/nodes/get_nodes/get_live_nodes'; @@ -31,7 +32,7 @@ interface Bucket { const NUMBER_OF_SECONDS_AGO_TO_LOOK = 30; const getRecentMonitoringDocuments = async ( - req: LegacyRequest, + req: LegacyRequest, indexPatterns: Record, clusterUuid?: string, nodeUuid?: string, @@ -300,7 +301,7 @@ function isBeatFromAPM(bucket: Bucket) { return get(beatType, 'buckets[0].key') === 'apm-server'; } -async function hasNecessaryPermissions(req: LegacyRequest) { +async function hasNecessaryPermissions(req: LegacyRequest) { const licenseService = await req.server.plugins.monitoring.info.getLicenseService(); const securityFeature = licenseService.getSecurityFeature(); if (!securityFeature.isAvailable || !securityFeature.isEnabled) { @@ -366,7 +367,7 @@ async function getLiveKibanaInstance(usageCollection?: UsageCollectionSetup) { ); } -async function getLiveElasticsearchClusterUuid(req: LegacyRequest) { +async function getLiveElasticsearchClusterUuid(req: LegacyRequest) { const params = { path: '/_cluster/state/cluster_uuid', method: 'GET', @@ -377,7 +378,9 @@ async function getLiveElasticsearchClusterUuid(req: LegacyRequest) { return clusterUuid; } -async function getLiveElasticsearchCollectionEnabled(req: LegacyRequest) { +async function getLiveElasticsearchCollectionEnabled( + req: LegacyRequest +) { const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin'); const response = await callWithRequest(req, 'transport.request', { method: 'GET', @@ -425,7 +428,7 @@ async function getLiveElasticsearchCollectionEnabled(req: LegacyRequest) { * @param {*} skipLiveData Optional and will not make any live api calls if set to true */ export const getCollectionStatus = async ( - req: LegacyRequest, + req: LegacyRequest, indexPatterns: Record, clusterUuid?: string, nodeUuid?: string, diff --git a/x-pack/plugins/monitoring/server/mocks.ts b/x-pack/plugins/monitoring/server/mocks.ts new file mode 100644 index 0000000000000..5adeae22acfc0 --- /dev/null +++ b/x-pack/plugins/monitoring/server/mocks.ts @@ -0,0 +1,25 @@ +/* + * 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 { ILicense } from '@kbn/licensing-plugin/server'; +import { Subject } from 'rxjs'; +import { MonitoringLicenseService } from './types'; + +const createLicenseServiceMock = (): jest.Mocked => ({ + refresh: jest.fn(), + license$: new Subject(), + getMessage: jest.fn(), + getWatcherFeature: jest.fn(), + getMonitoringFeature: jest.fn(), + getSecurityFeature: jest.fn(), + stop: jest.fn(), +}); + +// this might be incomplete and is added to as needed +export const monitoringPluginMock = { + createLicenseServiceMock, +}; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts index 9188215137565..b773e25b81152 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts @@ -8,15 +8,15 @@ // @ts-ignore import { ActionResult } from '@kbn/actions-plugin/common'; import { RuleTypeParams, SanitizedRule } from '@kbn/alerting-plugin/common'; -import { handleError } from '../../../../lib/errors'; -import { AlertsFactory } from '../../../../alerts'; -import { LegacyServer, RouteDependencies } from '../../../../types'; import { ALERT_ACTION_TYPE_LOG } from '../../../../../common/constants'; +import { AlertsFactory } from '../../../../alerts'; import { disableWatcherClusterAlerts } from '../../../../lib/alerts/disable_watcher_cluster_alerts'; +import { handleError } from '../../../../lib/errors'; +import { MonitoringCore, RouteDependencies } from '../../../../types'; const DEFAULT_SERVER_LOG_NAME = 'Monitoring: Write to Kibana log'; -export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependencies) { +export function enableAlertsRoute(server: MonitoringCore, npRoute: RouteDependencies) { npRoute.router.post( { path: '/api/monitoring/v1/alerts/enable', diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/index.ts index 11782c73d9b55..c2511e1d24c0a 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/index.ts @@ -5,5 +5,11 @@ * 2.0. */ -export { enableAlertsRoute } from './enable'; -export { alertStatusRoute } from './status'; +import { MonitoringCore, RouteDependencies } from '../../../../types'; +import { enableAlertsRoute } from './enable'; +import { alertStatusRoute } from './status'; + +export function registerV1AlertRoutes(server: MonitoringCore, npRoute: RouteDependencies) { + alertStatusRoute(npRoute); + enableAlertsRoute(server, npRoute); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts index a145d92921634..a9efc14c8c458 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts @@ -6,13 +6,12 @@ */ import { schema } from '@kbn/config-schema'; -// @ts-ignore +import { CommonAlertFilter } from '../../../../../common/types/alerts'; +import { fetchStatus } from '../../../../lib/alerts/fetch_status'; import { handleError } from '../../../../lib/errors'; import { RouteDependencies } from '../../../../types'; -import { fetchStatus } from '../../../../lib/alerts/fetch_status'; -import { CommonAlertFilter } from '../../../../../common/types/alerts'; -export function alertStatusRoute(server: any, npRoute: RouteDependencies) { +export function alertStatusRoute(npRoute: RouteDependencies) { npRoute.router.post( { path: '/api/monitoring/v1/alert/{clusterUuid}/status', diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/apm/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/apm/index.ts index 0fb4dd78c9be6..97d9a2f9789d7 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/apm/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/apm/index.ts @@ -5,6 +5,13 @@ * 2.0. */ -export { apmInstanceRoute } from './instance'; -export { apmInstancesRoute } from './instances'; -export { apmOverviewRoute } from './overview'; +import { MonitoringCore } from '../../../../types'; +import { apmInstanceRoute } from './instance'; +import { apmInstancesRoute } from './instances'; +import { apmOverviewRoute } from './overview'; + +export function registerV1ApmRoutes(server: MonitoringCore) { + apmInstanceRoute(server); + apmInstancesRoute(server); + apmOverviewRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/beats/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/beats/index.ts index 57423052760bf..935ca35c3a384 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/beats/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/beats/index.ts @@ -5,6 +5,13 @@ * 2.0. */ -export { beatsOverviewRoute } from './overview'; -export { beatsListingRoute } from './beats'; -export { beatsDetailRoute } from './beat_detail'; +import { MonitoringCore } from '../../../../types'; +import { beatsListingRoute } from './beats'; +import { beatsDetailRoute } from './beat_detail'; +import { beatsOverviewRoute } from './overview'; + +export function registerV1BeatsRoutes(server: MonitoringCore) { + beatsDetailRoute(server); + beatsListingRoute(server); + beatsOverviewRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/check_access/check_access.ts b/x-pack/plugins/monitoring/server/routes/api/v1/check_access/check_access.ts index 450872049a3de..2db7481882b89 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/check_access/check_access.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/check_access/check_access.ts @@ -7,18 +7,19 @@ import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth'; import { handleError } from '../../../../lib/errors'; -import { LegacyRequest, LegacyServer } from '../../../../types'; +import { LegacyRequest, MonitoringCore } from '../../../../types'; /* * API for checking read privilege on Monitoring Data * Used for the "Access Denied" page as something to auto-retry with. */ -// TODO: Replace this LegacyServer call with the "new platform" core Kibana route method -export function checkAccessRoute(server: LegacyServer) { +// TODO: Replace this legacy route registration with the "new platform" core Kibana route method +export function checkAccessRoute(server: MonitoringCore) { server.route({ - method: 'GET', + method: 'get', path: '/api/monitoring/v1/check_access', + validate: {}, handler: async (req: LegacyRequest) => { const response: { has_access?: boolean } = {}; try { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/check_access/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/check_access/index.ts index 0fb8228f82442..5209ec8b92e9a 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/check_access/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/check_access/index.ts @@ -5,4 +5,9 @@ * 2.0. */ -export { checkAccessRoute } from './check_access'; +import { MonitoringCore } from '../../../../types'; +import { checkAccessRoute } from './check_access'; + +export function registerV1CheckAccessRoutes(server: MonitoringCore) { + checkAccessRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/cluster/cluster.ts b/x-pack/plugins/monitoring/server/routes/api/v1/cluster/cluster.ts index 30749f2e95c9f..6bd0a19d79c5f 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/cluster/cluster.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/cluster/cluster.ts @@ -5,39 +5,36 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; +import { + postClusterRequestParamsRT, + postClusterRequestPayloadRT, + postClusterResponsePayloadRT, +} from '../../../../../common/http_api/cluster'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; import { getClustersFromRequest } from '../../../../lib/cluster/get_clusters_from_request'; -// @ts-ignore -import { handleError } from '../../../../lib/errors'; import { getIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; -import { LegacyRequest, LegacyServer } from '../../../../types'; +import { handleError } from '../../../../lib/errors'; +import { MonitoringCore } from '../../../../types'; -export function clusterRoute(server: LegacyServer) { +export function clusterRoute(server: MonitoringCore) { /* * Cluster Overview */ + + const validateParams = createValidationFunction(postClusterRequestParamsRT); + const validateBody = createValidationFunction(postClusterRequestPayloadRT); + server.route({ - method: 'POST', + method: 'post', path: '/api/monitoring/v1/clusters/{clusterUuid}', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - }), - body: schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - codePaths: schema.arrayOf(schema.string()), - }), - }, + validate: { + params: validateParams, + body: validateBody, }, - handler: async (req: LegacyRequest) => { + handler: async (req) => { const config = server.config; - const indexPatterns = getIndexPatterns(server, { + const indexPatterns = getIndexPatterns(config, { filebeatIndexPattern: config.ui.logs.index, }); const options = { @@ -47,13 +44,12 @@ export function clusterRoute(server: LegacyServer) { codePaths: req.payload.codePaths, }; - let clusters = []; try { - clusters = await getClustersFromRequest(req, indexPatterns, options); + const clusters = await getClustersFromRequest(req, indexPatterns, options); + return postClusterResponsePayloadRT.encode(clusters); } catch (err) { throw handleError(err, req); } - return clusters; }, }); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/cluster/clusters.ts b/x-pack/plugins/monitoring/server/routes/api/v1/cluster/clusters.ts index 81acd0e53f319..9591dda205487 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/cluster/clusters.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/cluster/clusters.ts @@ -5,36 +5,33 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; -import { LegacyRequest, LegacyServer } from '../../../../types'; +import { + postClustersRequestPayloadRT, + postClustersResponsePayloadRT, +} from '../../../../../common/http_api/cluster'; import { getClustersFromRequest } from '../../../../lib/cluster/get_clusters_from_request'; +import { getIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth'; import { handleError } from '../../../../lib/errors'; -import { getIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; +import { MonitoringCore } from '../../../../types'; -export function clustersRoute(server: LegacyServer) { +export function clustersRoute(server: MonitoringCore) { /* * Monitoring Home * Route Init (for checking license and compatibility for multi-cluster monitoring */ + const validateBody = createValidationFunction(postClustersRequestPayloadRT); + // TODO switch from the LegacyServer route() method to the "new platform" route methods server.route({ - method: 'POST', + method: 'post', path: '/api/monitoring/v1/clusters', - config: { - validate: { - body: schema.object({ - timeRange: schema.object({ - min: schema.string(), - max: schema.string(), - }), - codePaths: schema.arrayOf(schema.string()), - }), - }, + validate: { + body: validateBody, }, - handler: async (req: LegacyRequest) => { - let clusters = []; + handler: async (req) => { const config = server.config; // NOTE using try/catch because checkMonitoringAuth is expected to throw @@ -42,17 +39,16 @@ export function clustersRoute(server: LegacyServer) { // the monitoring data. `try/catch` makes it a little more explicit. try { await verifyMonitoringAuth(req); - const indexPatterns = getIndexPatterns(server, { + const indexPatterns = getIndexPatterns(config, { filebeatIndexPattern: config.ui.logs.index, }); - clusters = await getClustersFromRequest(req, indexPatterns, { - codePaths: req.payload.codePaths as string[], // TODO remove this cast when we can properly type req by using the right route handler + const clusters = await getClustersFromRequest(req, indexPatterns, { + codePaths: req.payload.codePaths, }); + return postClustersResponsePayloadRT.encode(clusters); } catch (err) { throw handleError(err, req); } - - return clusters; }, }); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/cluster/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/cluster/index.ts index 769f315480d9c..9534398db52c1 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/cluster/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/cluster/index.ts @@ -5,5 +5,11 @@ * 2.0. */ -export { clusterRoute } from './cluster'; -export { clustersRoute } from './clusters'; +import { clusterRoute } from './cluster'; +import { clustersRoute } from './clusters'; +import { MonitoringCore } from '../../../../types'; + +export function registerV1ClusterRoutes(server: MonitoringCore) { + clusterRoute(server); + clustersRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.ts index b2d432a5e35b5..e706dc61c0a41 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/index.ts @@ -5,11 +5,23 @@ * 2.0. */ -export { esIndexRoute } from './index_detail'; -export { esIndicesRoute } from './indices'; -export { esNodeRoute } from './node_detail'; -export { esNodesRoute } from './nodes'; -export { esOverviewRoute } from './overview'; -export { mlJobRoute } from './ml_jobs'; -export { ccrRoute } from './ccr'; -export { ccrShardRoute } from './ccr_shard'; +import { MonitoringCore } from '../../../../types'; +import { ccrRoute } from './ccr'; +import { ccrShardRoute } from './ccr_shard'; +import { esIndexRoute } from './index_detail'; +import { esIndicesRoute } from './indices'; +import { mlJobRoute } from './ml_jobs'; +import { esNodesRoute } from './nodes'; +import { esNodeRoute } from './node_detail'; +import { esOverviewRoute } from './overview'; + +export function registerV1ElasticsearchRoutes(server: MonitoringCore) { + esIndexRoute(server); + esIndicesRoute(server); + esNodeRoute(server); + esNodesRoute(server); + esOverviewRoute(server); + mlJobRoute(server); + ccrRoute(server); + ccrShardRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts index 11e0eec3f08f0..f8742144b28f8 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts @@ -19,7 +19,7 @@ import { } from '../../../../../../common/http_api/elasticsearch_settings'; import { createValidationFunction } from '../../../../../lib/create_route_validation_function'; import { handleError } from '../../../../../lib/errors'; -import { LegacyServer, RouteDependencies } from '../../../../../types'; +import { MonitoringCore, RouteDependencies } from '../../../../../types'; const queryBody = { size: 0, @@ -72,7 +72,7 @@ const checkLatestMonitoringIsLegacy = async (context: RequestHandlerContext, ind return counts; }; -export function internalMonitoringCheckRoute(server: LegacyServer, npRoute: RouteDependencies) { +export function internalMonitoringCheckRoute(server: MonitoringCore, npRoute: RouteDependencies) { const validateBody = createValidationFunction( postElasticsearchSettingsInternalMonitoringRequestPayloadRT ); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.ts index 61bb1ba804a5a..dfc68068bf80d 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/index.ts @@ -5,8 +5,20 @@ * 2.0. */ -export { clusterSettingsCheckRoute } from './check/cluster'; -export { internalMonitoringCheckRoute } from './check/internal_monitoring'; -export { nodesSettingsCheckRoute } from './check/nodes'; -export { setCollectionEnabledRoute } from './set/collection_enabled'; -export { setCollectionIntervalRoute } from './set/collection_interval'; +import { MonitoringCore, RouteDependencies } from '../../../../types'; +import { clusterSettingsCheckRoute } from './check/cluster'; +import { internalMonitoringCheckRoute } from './check/internal_monitoring'; +import { nodesSettingsCheckRoute } from './check/nodes'; +import { setCollectionEnabledRoute } from './set/collection_enabled'; +import { setCollectionIntervalRoute } from './set/collection_interval'; + +export function registerV1ElasticsearchSettingsRoutes( + server: MonitoringCore, + npRoute: RouteDependencies +) { + clusterSettingsCheckRoute(server); + internalMonitoringCheckRoute(server, npRoute); + nodesSettingsCheckRoute(server); + setCollectionEnabledRoute(server); + setCollectionIntervalRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/index.ts new file mode 100644 index 0000000000000..e0f5e55c6c128 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { registerV1AlertRoutes } from './alerts'; +export { registerV1ApmRoutes } from './apm'; +export { registerV1BeatsRoutes } from './beats'; +export { registerV1CheckAccessRoutes } from './check_access'; +export { registerV1ClusterRoutes } from './cluster'; +export { registerV1ElasticsearchRoutes } from './elasticsearch'; +export { registerV1ElasticsearchSettingsRoutes } from './elasticsearch_settings'; +export { registerV1LogstashRoutes } from './logstash'; +export { registerV1SetupRoutes } from './setup'; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/logstash/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/logstash/index.ts index b267c17fc3346..a4975726cf0a1 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/logstash/index.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/logstash/index.ts @@ -5,10 +5,21 @@ * 2.0. */ -export { logstashNodesRoute } from './nodes'; -export { logstashNodeRoute } from './node'; -export { logstashOverviewRoute } from './overview'; -export { logstashPipelineRoute } from './pipeline'; -export { logstashNodePipelinesRoute } from './pipelines/node_pipelines'; -export { logstashClusterPipelinesRoute } from './pipelines/cluster_pipelines'; -export { logstashClusterPipelineIdsRoute } from './pipelines/cluster_pipeline_ids'; +import { MonitoringCore } from '../../../../types'; +import { logstashNodeRoute } from './node'; +import { logstashNodesRoute } from './nodes'; +import { logstashOverviewRoute } from './overview'; +import { logstashPipelineRoute } from './pipeline'; +import { logstashClusterPipelinesRoute } from './pipelines/cluster_pipelines'; +import { logstashClusterPipelineIdsRoute } from './pipelines/cluster_pipeline_ids'; +import { logstashNodePipelinesRoute } from './pipelines/node_pipelines'; + +export function registerV1LogstashRoutes(server: MonitoringCore) { + logstashClusterPipelineIdsRoute(server); + logstashClusterPipelinesRoute(server); + logstashNodePipelinesRoute(server); + logstashNodeRoute(server); + logstashNodesRoute(server); + logstashOverviewRoute(server); + logstashPipelineRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/setup/cluster_setup_status.js b/x-pack/plugins/monitoring/server/routes/api/v1/setup/cluster_setup_status.js deleted file mode 100644 index bc8b722d22214..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/setup/cluster_setup_status.js +++ /dev/null @@ -1,72 +0,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 { schema } from '@kbn/config-schema'; -import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth'; -import { handleError } from '../../../../lib/errors'; -import { getCollectionStatus } from '../../../../lib/setup/collection'; -import { getIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; - -export function clusterSetupStatusRoute(server) { - /* - * Monitoring Home - * Route Init (for checking license and compatibility for multi-cluster monitoring - */ - server.route({ - method: 'POST', - path: '/api/monitoring/v1/setup/collection/cluster/{clusterUuid?}', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.maybe(schema.string()), - }), - query: schema.object({ - // This flag is not intended to be used in production. It was introduced - // as a way to ensure consistent API testing - the typical data source - // for API tests are archived data, where the cluster configuration and data - // are consistent from environment to environment. However, this endpoint - // also attempts to retrieve data from the running stack products (ES and Kibana) - // which will vary from environment to environment making it difficult - // to write tests against. Therefore, this flag exists and should only be used - // in our testing environment. - skipLiveData: schema.boolean({ defaultValue: false }), - }), - body: schema.nullable( - schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.object({ - min: schema.string({ defaultValue: '' }), - max: schema.string({ defaultValue: '' }), - }), - }) - ), - }, - }, - handler: async (req) => { - let status = null; - - // NOTE using try/catch because checkMonitoringAuth is expected to throw - // an error when current logged-in user doesn't have permission to read - // the monitoring data. `try/catch` makes it a little more explicit. - try { - await verifyMonitoringAuth(req); - const indexPatterns = getIndexPatterns(server, {}, req.payload.ccs); - status = await getCollectionStatus( - req, - indexPatterns, - req.params.clusterUuid, - null, - req.query.skipLiveData - ); - } catch (err) { - throw handleError(err, req); - } - - return status; - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/setup/cluster_setup_status.ts b/x-pack/plugins/monitoring/server/routes/api/v1/setup/cluster_setup_status.ts new file mode 100644 index 0000000000000..370947df46b42 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/setup/cluster_setup_status.ts @@ -0,0 +1,62 @@ +/* + * 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 { + postClusterSetupStatusRequestParamsRT, + postClusterSetupStatusRequestPayloadRT, + postClusterSetupStatusRequestQueryRT, + postClusterSetupStatusResponsePayloadRT, +} from '../../../../../common/http_api/setup'; +import { getIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth'; +import { handleError } from '../../../../lib/errors'; +import { getCollectionStatus } from '../../../../lib/setup/collection'; +import { MonitoringCore } from '../../../../types'; + +export function clusterSetupStatusRoute(server: MonitoringCore) { + /* + * Monitoring Home + * Route Init (for checking license and compatibility for multi-cluster monitoring + */ + + const validateParams = createValidationFunction(postClusterSetupStatusRequestParamsRT); + const validateQuery = createValidationFunction(postClusterSetupStatusRequestQueryRT); + const validateBody = createValidationFunction(postClusterSetupStatusRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/setup/collection/cluster/{clusterUuid?}', + validate: { + params: validateParams, + query: validateQuery, + body: validateBody, + }, + handler: async (req) => { + const clusterUuid = req.params.clusterUuid; + const skipLiveData = req.query.skipLiveData; + + // NOTE using try/catch because checkMonitoringAuth is expected to throw + // an error when current logged-in user doesn't have permission to read + // the monitoring data. `try/catch` makes it a little more explicit. + try { + await verifyMonitoringAuth(req); + const indexPatterns = getIndexPatterns(server.config, {}, req.payload.ccs); + const status = await getCollectionStatus( + req, + indexPatterns, + clusterUuid, + undefined, + skipLiveData + ); + return postClusterSetupStatusResponsePayloadRT.encode(status); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/setup/disable_elasticsearch_internal_collection.js b/x-pack/plugins/monitoring/server/routes/api/v1/setup/disable_elasticsearch_internal_collection.ts similarity index 74% rename from x-pack/plugins/monitoring/server/routes/api/v1/setup/disable_elasticsearch_internal_collection.js rename to x-pack/plugins/monitoring/server/routes/api/v1/setup/disable_elasticsearch_internal_collection.ts index 9590d91c357ee..cdecf346bae9d 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/setup/disable_elasticsearch_internal_collection.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/setup/disable_elasticsearch_internal_collection.ts @@ -5,21 +5,19 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; +import { postDisableInternalCollectionRequestParamsRT } from '../../../../../common/http_api/setup'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth'; -import { handleError } from '../../../../lib/errors'; import { setCollectionDisabled } from '../../../../lib/elasticsearch_settings/set/collection_disabled'; +import { handleError } from '../../../../lib/errors'; +import { MonitoringCore } from '../../../../types'; -export function disableElasticsearchInternalCollectionRoute(server) { +export function disableElasticsearchInternalCollectionRoute(server: MonitoringCore) { server.route({ - method: 'POST', + method: 'post', path: '/api/monitoring/v1/setup/collection/{clusterUuid}/disable_internal_collection', - config: { - validate: { - params: schema.object({ - clusterUuid: schema.string(), - }), - }, + validate: { + params: createValidationFunction(postDisableInternalCollectionRequestParamsRT), }, handler: async (req) => { // NOTE using try/catch because checkMonitoringAuth is expected to throw diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/setup/index.ts b/x-pack/plugins/monitoring/server/routes/api/v1/setup/index.ts new file mode 100644 index 0000000000000..6a8ecac8597a8 --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/setup/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { MonitoringCore } from '../../../../types'; +import { clusterSetupStatusRoute } from './cluster_setup_status'; +import { disableElasticsearchInternalCollectionRoute } from './disable_elasticsearch_internal_collection'; +import { nodeSetupStatusRoute } from './node_setup_status'; + +export function registerV1SetupRoutes(server: MonitoringCore) { + clusterSetupStatusRoute(server); + disableElasticsearchInternalCollectionRoute(server); + nodeSetupStatusRoute(server); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/setup/node_setup_status.js b/x-pack/plugins/monitoring/server/routes/api/v1/setup/node_setup_status.js deleted file mode 100644 index 1f93e92843ea8..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/setup/node_setup_status.js +++ /dev/null @@ -1,74 +0,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 { schema } from '@kbn/config-schema'; -import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth'; -import { handleError } from '../../../../lib/errors'; -import { getCollectionStatus } from '../../../../lib/setup/collection'; -import { getIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; - -export function nodeSetupStatusRoute(server) { - /* - * Monitoring Home - * Route Init (for checking license and compatibility for multi-cluster monitoring - */ - server.route({ - method: 'POST', - path: '/api/monitoring/v1/setup/collection/node/{nodeUuid}', - config: { - validate: { - params: schema.object({ - nodeUuid: schema.string(), - }), - query: schema.object({ - // This flag is not intended to be used in production. It was introduced - // as a way to ensure consistent API testing - the typical data source - // for API tests are archived data, where the cluster configuration and data - // are consistent from environment to environment. However, this endpoint - // also attempts to retrieve data from the running stack products (ES and Kibana) - // which will vary from environment to environment making it difficult - // to write tests against. Therefore, this flag exists and should only be used - // in our testing environment. - skipLiveData: schema.boolean({ defaultValue: false }), - }), - body: schema.nullable( - schema.object({ - ccs: schema.maybe(schema.string()), - timeRange: schema.maybe( - schema.object({ - min: schema.string(), - max: schema.string(), - }) - ), - }) - ), - }, - }, - handler: async (req) => { - let status = null; - - // NOTE using try/catch because checkMonitoringAuth is expected to throw - // an error when current logged-in user doesn't have permission to read - // the monitoring data. `try/catch` makes it a little more explicit. - try { - await verifyMonitoringAuth(req); - const indexPatterns = getIndexPatterns(server, {}, req.payload.ccs); - status = await getCollectionStatus( - req, - indexPatterns, - null, - req.params.nodeUuid, - req.query.skipLiveData - ); - } catch (err) { - throw handleError(err, req); - } - - return status; - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/setup/node_setup_status.ts b/x-pack/plugins/monitoring/server/routes/api/v1/setup/node_setup_status.ts new file mode 100644 index 0000000000000..327b741a0e64a --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/setup/node_setup_status.ts @@ -0,0 +1,64 @@ +/* + * 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 { + postNodeSetupStatusRequestParamsRT, + postNodeSetupStatusRequestPayloadRT, + postNodeSetupStatusRequestQueryRT, + postNodeSetupStatusResponsePayloadRT, +} from '../../../../../common/http_api/setup'; +import { getIndexPatterns } from '../../../../lib/cluster/get_index_patterns'; +import { createValidationFunction } from '../../../../lib/create_route_validation_function'; +import { verifyMonitoringAuth } from '../../../../lib/elasticsearch/verify_monitoring_auth'; +import { handleError } from '../../../../lib/errors'; +import { getCollectionStatus } from '../../../../lib/setup/collection'; +import { MonitoringCore } from '../../../../types'; + +export function nodeSetupStatusRoute(server: MonitoringCore) { + /* + * Monitoring Home + * Route Init (for checking license and compatibility for multi-cluster monitoring + */ + + const validateParams = createValidationFunction(postNodeSetupStatusRequestParamsRT); + const validateQuery = createValidationFunction(postNodeSetupStatusRequestQueryRT); + const validateBody = createValidationFunction(postNodeSetupStatusRequestPayloadRT); + + server.route({ + method: 'post', + path: '/api/monitoring/v1/setup/collection/node/{nodeUuid}', + validate: { + params: validateParams, + query: validateQuery, + body: validateBody, + }, + handler: async (req) => { + const nodeUuid = req.params.nodeUuid; + const skipLiveData = req.query.skipLiveData; + const ccs = req.payload.ccs; + + // NOTE using try/catch because checkMonitoringAuth is expected to throw + // an error when current logged-in user doesn't have permission to read + // the monitoring data. `try/catch` makes it a little more explicit. + try { + await verifyMonitoringAuth(req); + const indexPatterns = getIndexPatterns(server.config, {}, ccs); + const status = await getCollectionStatus( + req, + indexPatterns, + undefined, + nodeUuid, + skipLiveData + ); + + return postNodeSetupStatusResponsePayloadRT.encode(status); + } catch (err) { + throw handleError(err, req); + } + }, + }); +} diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/ui.js b/x-pack/plugins/monitoring/server/routes/api/v1/ui.js deleted file mode 100644 index 618d12afedef7..0000000000000 --- a/x-pack/plugins/monitoring/server/routes/api/v1/ui.js +++ /dev/null @@ -1,42 +0,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. - */ - -// all routes for the app -export { checkAccessRoute } from './check_access'; -export * from './alerts'; -export { beatsDetailRoute, beatsListingRoute, beatsOverviewRoute } from './beats'; -export { clusterRoute, clustersRoute } from './cluster'; -export { - esIndexRoute, - esIndicesRoute, - esNodeRoute, - esNodesRoute, - esOverviewRoute, - mlJobRoute, - ccrRoute, - ccrShardRoute, -} from './elasticsearch'; -export { - internalMonitoringCheckRoute, - clusterSettingsCheckRoute, - nodesSettingsCheckRoute, - setCollectionEnabledRoute, - setCollectionIntervalRoute, -} from './elasticsearch_settings'; -export { kibanaInstanceRoute, kibanaInstancesRoute, kibanaOverviewRoute } from './kibana'; -export { apmInstanceRoute, apmInstancesRoute, apmOverviewRoute } from './apm'; -export { - logstashClusterPipelinesRoute, - logstashNodePipelinesRoute, - logstashNodeRoute, - logstashNodesRoute, - logstashOverviewRoute, - logstashPipelineRoute, - logstashClusterPipelineIdsRoute, -} from './logstash'; -export { entSearchOverviewRoute } from './enterprise_search'; -export * from './setup'; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/ui.ts b/x-pack/plugins/monitoring/server/routes/api/v1/ui.ts new file mode 100644 index 0000000000000..7aaa6591e868e --- /dev/null +++ b/x-pack/plugins/monitoring/server/routes/api/v1/ui.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ + +// these are the remaining routes not yet converted to TypeScript +// all others are registered through index.ts + +// @ts-expect-error +export { kibanaInstanceRoute, kibanaInstancesRoute, kibanaOverviewRoute } from './kibana'; +// @ts-expect-error +export { entSearchOverviewRoute } from './enterprise_search'; diff --git a/x-pack/plugins/monitoring/server/routes/index.ts b/x-pack/plugins/monitoring/server/routes/index.ts index 05a8de96b4c07..f38612d5a42da 100644 --- a/x-pack/plugins/monitoring/server/routes/index.ts +++ b/x-pack/plugins/monitoring/server/routes/index.ts @@ -8,22 +8,43 @@ import { MonitoringConfig } from '../config'; import { decorateDebugServer } from '../debug_logger'; -import { RouteDependencies } from '../types'; -// @ts-ignore -import * as uiRoutes from './api/v1/ui'; // namespace import +import { MonitoringCore, RouteDependencies } from '../types'; +import { + registerV1AlertRoutes, + registerV1ApmRoutes, + registerV1BeatsRoutes, + registerV1CheckAccessRoutes, + registerV1ClusterRoutes, + registerV1ElasticsearchRoutes, + registerV1ElasticsearchSettingsRoutes, + registerV1LogstashRoutes, + registerV1SetupRoutes, +} from './api/v1'; +import * as uiRoutes from './api/v1/ui'; export function requireUIRoutes( - _server: any, + server: MonitoringCore, config: MonitoringConfig, npRoute: RouteDependencies ) { const routes = Object.keys(uiRoutes); - const server = config.ui.debug_mode - ? decorateDebugServer(_server, config, npRoute.logger) - : _server; + const decoratedServer = config.ui.debug_mode + ? decorateDebugServer(server, config, npRoute.logger) + : server; routes.forEach((route) => { + // @ts-expect-error const registerRoute = uiRoutes[route]; // computed reference to module objects imported via namespace registerRoute(server, npRoute); }); + + registerV1AlertRoutes(decoratedServer, npRoute); + registerV1ApmRoutes(server); + registerV1BeatsRoutes(server); + registerV1CheckAccessRoutes(server); + registerV1ClusterRoutes(server); + registerV1ElasticsearchRoutes(server); + registerV1ElasticsearchSettingsRoutes(server, npRoute); + registerV1LogstashRoutes(server); + registerV1SetupRoutes(server); } diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 86447a24fdf04..64931f5888514 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -34,7 +34,7 @@ import { LicensingPluginStart } from '@kbn/licensing-plugin/server'; import { PluginSetupContract as FeaturesPluginSetupContract } from '@kbn/features-plugin/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { CloudSetup } from '@kbn/cloud-plugin/server'; -import { RouteConfig, RouteMethod } from '@kbn/core/server'; +import { RouteConfig, RouteMethod, Headers } from '@kbn/core/server'; import { ElasticsearchModifiedSource } from '../common/types/es'; import { RulesByType } from '../common/types/alerts'; import { configSchema, MonitoringConfig } from './config'; @@ -124,6 +124,7 @@ export interface LegacyRequest { payload: Body; params: Params; query: Query; + headers: Headers; getKibanaStatsCollector: () => any; getUiSettingsService: () => any; getActionTypeRegistry: () => any;