[APM] Add license check for significant terms (#88209)

* only shows significant terms under platinum license

* addressing PR comments

* addressing PR comments

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Cauê Marcondes 2021-01-15 09:48:57 +01:00 committed by GitHub
parent a0da8bda04
commit 6beef61f93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 277 additions and 127 deletions

View file

@ -0,0 +1,217 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { License } from '../../licensing/common/license';
import { isActiveGoldLicense, isActivePlatinumLicense } from './license_check';
describe('License check', () => {
describe('isActivePlatinumLicense', () => {
describe('with an expired license', () => {
it('returns false', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'platinum',
type: 'platinum',
status: 'expired',
},
signature: 'test signature',
});
expect(isActivePlatinumLicense(license)).toEqual(false);
});
});
describe('with a basic license', () => {
it('returns false', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'basic',
type: 'basic',
status: 'active',
},
signature: 'test signature',
});
expect(isActivePlatinumLicense(license)).toEqual(false);
});
});
describe('with a gold license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'gold',
type: 'gold',
status: 'active',
},
signature: 'test signature',
});
expect(isActivePlatinumLicense(license)).toEqual(false);
});
});
describe('with a platinum license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'platinum',
type: 'platinum',
status: 'active',
},
signature: 'test signature',
});
expect(isActivePlatinumLicense(license)).toEqual(true);
});
});
describe('with an enterprise license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'enterprise',
type: 'enterprise',
status: 'active',
},
signature: 'test signature',
});
expect(isActivePlatinumLicense(license)).toEqual(true);
});
});
describe('with a trial license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'trial',
type: 'trial',
status: 'active',
},
signature: 'test signature',
});
expect(isActivePlatinumLicense(license)).toEqual(true);
});
});
});
describe('isActiveGoldLicense', () => {
describe('with an expired license', () => {
it('returns false', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'gold',
type: 'gold',
status: 'expired',
},
signature: 'test signature',
});
expect(isActiveGoldLicense(license)).toEqual(false);
});
});
describe('with a basic license', () => {
it('returns false', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'basic',
type: 'basic',
status: 'active',
},
signature: 'test signature',
});
expect(isActiveGoldLicense(license)).toEqual(false);
});
});
describe('with a gold license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'gold',
type: 'gold',
status: 'active',
},
signature: 'test signature',
});
expect(isActiveGoldLicense(license)).toEqual(true);
});
});
describe('with a platinum license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'platinum',
type: 'platinum',
status: 'active',
},
signature: 'test signature',
});
expect(isActiveGoldLicense(license)).toEqual(true);
});
});
describe('with an enterprise license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'enterprise',
type: 'enterprise',
status: 'active',
},
signature: 'test signature',
});
expect(isActiveGoldLicense(license)).toEqual(true);
});
});
describe('with a trial license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'trial',
type: 'trial',
status: 'active',
},
signature: 'test signature',
});
expect(isActiveGoldLicense(license)).toEqual(true);
});
});
});
});

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ILicense, LicenseType } from '../../licensing/common/types';
function isActiveLicense(licenseType: LicenseType, license?: ILicense) {
return license && license.isActive && license.hasAtLeast(licenseType);
}
export function isActivePlatinumLicense(license?: ILicense) {
return isActiveLicense('platinum', license);
}
export function isActiveGoldLicense(license?: ILicense) {
return isActiveLicense('gold', license);
}

View file

@ -1,97 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { License } from '../../licensing/common/license';
import * as serviceMap from './service_map';
describe('service map helpers', () => {
describe('isActivePlatinumLicense', () => {
describe('with an expired license', () => {
it('returns false', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'platinum',
type: 'platinum',
status: 'expired',
},
signature: 'test signature',
});
expect(serviceMap.isActivePlatinumLicense(license)).toEqual(false);
});
});
describe('with a basic license', () => {
it('returns false', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'basic',
type: 'basic',
status: 'active',
},
signature: 'test signature',
});
expect(serviceMap.isActivePlatinumLicense(license)).toEqual(false);
});
});
describe('with a platinum license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'platinum',
type: 'platinum',
status: 'active',
},
signature: 'test signature',
});
expect(serviceMap.isActivePlatinumLicense(license)).toEqual(true);
});
});
describe('with an enterprise license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'enterprise',
type: 'enterprise',
status: 'active',
},
signature: 'test signature',
});
expect(serviceMap.isActivePlatinumLicense(license)).toEqual(true);
});
});
describe('with a trial license', () => {
it('returns true', () => {
const license = new License({
license: {
uid: 'test uid',
expiryDateInMillis: 0,
mode: 'trial',
type: 'trial',
status: 'active',
},
signature: 'test signature',
});
expect(serviceMap.isActivePlatinumLicense(license)).toEqual(true);
});
});
});
});

View file

@ -6,7 +6,6 @@
import { i18n } from '@kbn/i18n';
import cytoscape from 'cytoscape';
import { ILicense } from '../../licensing/common/types';
import {
AGENT_NAME,
SERVICE_ENVIRONMENT,
@ -61,10 +60,6 @@ export interface ServiceNodeStats {
avgErrorRate: number | null;
}
export function isActivePlatinumLicense(license: ILicense) {
return license.isActive && license.hasAtLeast('platinum');
}
export const invalidLicenseMessage = i18n.translate(
'xpack.apm.serviceMap.invalidLicenseMessage',
{

View file

@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
export const enableCorrelations = 'apm:enableCorrelations';
export const enableSignificantTerms = 'apm:enableSignificantTerms';
export const enableServiceOverview = 'apm:enableServiceOverview';

View file

@ -19,19 +19,25 @@ import {
} from '@elastic/eui';
import { useHistory } from 'react-router-dom';
import { EuiSpacer } from '@elastic/eui';
import { enableCorrelations } from '../../../../common/ui_settings_keys';
import { isActivePlatinumLicense } from '../../../../common/license_check';
import { enableSignificantTerms } from '../../../../common/ui_settings_keys';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
import { LatencyCorrelations } from './LatencyCorrelations';
import { ErrorCorrelations } from './ErrorCorrelations';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { createHref } from '../../shared/Links/url_helpers';
import { useLicenseContext } from '../../../context/license/use_license_context';
export function Correlations() {
const { uiSettings } = useApmPluginContext().core;
const { urlParams } = useUrlParams();
const license = useLicenseContext();
const history = useHistory();
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
if (!uiSettings.get(enableCorrelations)) {
if (
!uiSettings.get(enableSignificantTerms) ||
!isActivePlatinumLicense(license)
) {
return null;
}

View file

@ -7,10 +7,10 @@
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import React, { PropsWithChildren, ReactNode } from 'react';
import styled from 'styled-components';
import { isActivePlatinumLicense } from '../../../../common/license_check';
import { useTrackPageview } from '../../../../../observability/public';
import {
invalidLicenseMessage,
isActivePlatinumLicense,
SERVICE_MAP_TIMEOUT_ERROR,
} from '../../../../common/service_map';
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';

View file

@ -6,11 +6,11 @@
import { Logger } from 'kibana/server';
import moment from 'moment';
import { isActivePlatinumLicense } from '../../../common/license_check';
import { APMConfig } from '../..';
import { KibanaRequest } from '../../../../../../src/core/server';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/common';
import { ESFilter } from '../../../../../typings/elasticsearch';
import { isActivePlatinumLicense } from '../../../common/service_map';
import { UIFilters } from '../../../typings/ui_filters';
import { APMRequestHandlerContext } from '../../routes/typings';
import {

View file

@ -4,12 +4,23 @@
* you may not use this file except in compliance with the Elastic License.
*/
import Boom from '@hapi/boom';
import { i18n } from '@kbn/i18n';
import * as t from 'io-ts';
import { rangeRt } from './default_api_types';
import { getCorrelationsForSlowTransactions } from '../lib/correlations/get_correlations_for_slow_transactions';
import { isActivePlatinumLicense } from '../../common/license_check';
import { getCorrelationsForFailedTransactions } from '../lib/correlations/get_correlations_for_failed_transactions';
import { createRoute } from './create_route';
import { getCorrelationsForSlowTransactions } from '../lib/correlations/get_correlations_for_slow_transactions';
import { setupRequest } from '../lib/helpers/setup_request';
import { createRoute } from './create_route';
import { rangeRt } from './default_api_types';
const INVALID_LICENSE = i18n.translate(
'xpack.apm.significanTerms.license.text',
{
defaultMessage:
'To use the correlations API, you must be subscribed to an Elastic Platinum license.',
}
);
export const correlationsForSlowTransactionsRoute = createRoute({
endpoint: 'GET /api/apm/correlations/slow_transactions',
@ -30,6 +41,9 @@ export const correlationsForSlowTransactionsRoute = createRoute({
}),
options: { tags: ['access:apm'] },
handler: async ({ context, request }) => {
if (!isActivePlatinumLicense(context.licensing.license)) {
throw Boom.forbidden(INVALID_LICENSE);
}
const setup = await setupRequest(context, request);
const {
serviceName,
@ -68,6 +82,9 @@ export const correlationsForFailedTransactionsRoute = createRoute({
}),
options: { tags: ['access:apm'] },
handler: async ({ context, request }) => {
if (!isActivePlatinumLicense(context.licensing.license)) {
throw Boom.forbidden(INVALID_LICENSE);
}
const setup = await setupRequest(context, request);
const {
serviceName,

View file

@ -6,10 +6,7 @@
import Boom from '@hapi/boom';
import * as t from 'io-ts';
import {
invalidLicenseMessage,
isActivePlatinumLicense,
} from '../../common/service_map';
import { invalidLicenseMessage } from '../../common/service_map';
import { setupRequest } from '../lib/helpers/setup_request';
import { getServiceMap } from '../lib/service_map/get_service_map';
import { getServiceMapServiceNodeInfo } from '../lib/service_map/get_service_map_service_node_info';
@ -17,6 +14,7 @@ import { createRoute } from './create_route';
import { rangeRt, uiFiltersRt } from './default_api_types';
import { notifyFeatureUsage } from '../feature';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { isActivePlatinumLicense } from '../../common/license_check';
export const serviceMapRoute = createRoute({
endpoint: 'GET /api/apm/service-map',

View file

@ -6,7 +6,7 @@
import * as t from 'io-ts';
import Boom from '@hapi/boom';
import { isActivePlatinumLicense } from '../../../common/service_map';
import { isActivePlatinumLicense } from '../../../common/license_check';
import { ML_ERRORS } from '../../../common/anomaly_detection';
import { createRoute } from '../create_route';
import { getAnomalyDetectionJobs } from '../../lib/anomaly_detection/get_anomaly_detection_jobs';

View file

@ -7,8 +7,8 @@
import Boom from '@hapi/boom';
import * as t from 'io-ts';
import { pick } from 'lodash';
import { isActiveGoldLicense } from '../../../common/license_check';
import { INVALID_LICENSE } from '../../../common/custom_link';
import { ILicense } from '../../../../licensing/common/types';
import { FILTER_OPTIONS } from '../../../common/custom_link/custom_link_filter_options';
import { notifyFeatureUsage } from '../../feature';
import { setupRequest } from '../../lib/helpers/setup_request';
@ -22,10 +22,6 @@ import { getTransaction } from '../../lib/settings/custom_link/get_transaction';
import { listCustomLinks } from '../../lib/settings/custom_link/list_custom_links';
import { createRoute } from '../create_route';
function isActiveGoldLicense(license: ILicense) {
return license.isActive && license.hasAtLeast('gold');
}
export const customLinkTransactionRoute = createRoute({
endpoint: 'GET /api/apm/settings/custom_links/transaction',
options: { tags: ['access:apm'] },

View file

@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
import { UiSettingsParams } from '../../../../src/core/types';
import {
enableCorrelations,
enableSignificantTerms,
enableServiceOverview,
} from '../common/ui_settings_keys';
@ -16,10 +16,10 @@ import {
* uiSettings definitions for APM.
*/
export const uiSettings: Record<string, UiSettingsParams<boolean>> = {
[enableCorrelations]: {
[enableSignificantTerms]: {
category: ['observability'],
name: i18n.translate('xpack.apm.enableCorrelationsExperimentName', {
defaultMessage: 'APM Significant terms',
defaultMessage: 'APM Significant terms (Platinum required)',
}),
value: false,
description: i18n.translate(

View file

@ -68,9 +68,5 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont
describe('Metrics', function () {
loadTestFile(require.resolve('./metrics_charts/metrics_charts'));
});
describe('Correlations', function () {
loadTestFile(require.resolve('./correlations/slow_transactions'));
});
});
}

View file

@ -42,5 +42,9 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr
loadTestFile(require.resolve('./csm/has_rum_data.ts'));
loadTestFile(require.resolve('./csm/page_load_dist.ts'));
});
describe('Correlations', function () {
loadTestFile(require.resolve('./correlations/slow_transactions'));
});
});
}