From c44919d8ec555667a497ed3da835d90a953f4cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv?= Date: Sun, 16 Feb 2020 21:53:17 +0100 Subject: [PATCH 01/11] [APM] Stabilize agent configuration API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused `deleteByQuery` Fix issues with query param decoding Fix tests Fix tsc Use hashed service as id Revert "Use hashed service as id" This reverts commit 9e943ea6f22cbcdfeec6711818db535d09b31342. Ensure that overwriting only happens when updating configurations Add comment to explain “markAppliedByAgent” --- .../AddEditFlyout/DeleteButton.tsx | 10 +- .../AddEditFlyout/index.tsx | 2 +- .../AddEditFlyout/saveConfig.ts | 30 +-- x-pack/legacy/plugins/apm/readme.md | 22 ++ .../index.test.ts | 41 +++ .../agent_configuration_intake_rt/index.ts | 26 ++ .../apm/server/lib/helpers/es_client.ts | 7 +- .../__snapshots__/queries.test.ts.snap | 234 ++++++++++++------ .../configuration_types.d.ts | 19 +- .../create_or_update_configuration.ts | 22 +- .../find_exact_configuration.ts | 46 ++++ .../get_agent_name_by_service.ts | 2 +- .../agent_configuration/queries.test.ts | 146 +++++++---- .../{search.ts => search_configurations.ts} | 35 +-- .../apm/server/routes/create_apm_api.ts | 6 +- .../routes/settings/agent_configuration.ts | 137 +++++----- .../apis/apm/agent_configuration.ts | 118 ++++++--- .../apis/apm/feature_controls.ts | 27 +- x-pack/test/api_integration/apis/apm/index.ts | 2 +- x-pack/test/functional/apps/apm/index.ts | 2 +- 20 files changed, 612 insertions(+), 322 deletions(-) create mode 100644 x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts create mode 100644 x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts create mode 100644 x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts rename x-pack/plugins/apm/server/lib/settings/agent_configuration/{search.ts => search_configurations.ts} (68%) diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx index 496147b02589b..1564f1ae746a9 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/DeleteButton.tsx @@ -51,12 +51,18 @@ async function deleteConfig( ) { try { await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/{configurationId}', + pathname: '/api/apm/settings/agent-configuration', method: 'DELETE', params: { - path: { configurationId: selectedConfig.id } + body: { + service: { + name: selectedConfig.service.name, + environment: selectedConfig.service.environment + } + } } }); + toasts.addSuccess({ title: i18n.translate( 'xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededTitle', diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx index 653dedea733f2..db327270f0ce9 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx @@ -135,8 +135,8 @@ export function AddEditFlyout({ sampleRate, captureBody, transactionMaxSpans, - configurationId: selectedConfig ? selectedConfig.id : undefined, agentName, + existingConfig: Boolean(selectedConfig), toasts, trackApmEvent }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts index 19934cafb4694..0faaedbdfe829 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts @@ -27,8 +27,8 @@ export async function saveConfig({ sampleRate, captureBody, transactionMaxSpans, - configurationId, agentName, + existingConfig, toasts, trackApmEvent }: { @@ -38,8 +38,8 @@ export async function saveConfig({ sampleRate: string; captureBody: string; transactionMaxSpans: string; - configurationId?: string; agentName?: string; + existingConfig: boolean; toasts: NotificationsStart['toasts']; trackApmEvent: UiTracker; }) { @@ -64,24 +64,14 @@ export async function saveConfig({ settings }; - if (configurationId) { - await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/{configurationId}', - method: 'PUT', - params: { - path: { configurationId }, - body: configuration - } - }); - } else { - await callApmApi({ - pathname: '/api/apm/settings/agent-configuration/new', - method: 'POST', - params: { - body: configuration - } - }); - } + await callApmApi({ + pathname: '/api/apm/settings/agent-configuration', + method: 'PUT', + params: { + query: { overwrite: existingConfig }, + body: configuration + } + }); toasts.addSuccess({ title: i18n.translate( diff --git a/x-pack/legacy/plugins/apm/readme.md b/x-pack/legacy/plugins/apm/readme.md index 2106243d12aea..a513249c296db 100644 --- a/x-pack/legacy/plugins/apm/readme.md +++ b/x-pack/legacy/plugins/apm/readme.md @@ -71,6 +71,28 @@ node scripts/jest.js plugins/apm --watch node scripts/jest.js plugins/apm --updateSnapshot ``` +### Functional tests + +**Start server** +`node scripts/functional_tests_server --config x-pack/test/functional/config.js` + +**Run tests** +`node scripts/functional_test_runner --config x-pack/test/functional/config.js --grep='APM specs'` + +APM tests are located in `x-pack/test/functional/apps/apm`. +For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme) + +### API integration tests + +**Start server** +`node scripts/functional_tests_server --config x-pack/test/api_integration/config.js` + +**Run tests** +`node scripts/functional_test_runner --config x-pack/test/api_integration/config.js --grep='APM specs'` + +APM tests are located in `x-pack/test/api_integration/apis/apm`. +For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme) + ### Linting _Note: Run the following commands from `kibana/`._ diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts new file mode 100644 index 0000000000000..4c9dc78eb41e9 --- /dev/null +++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.test.ts @@ -0,0 +1,41 @@ +/* + * 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 { agentConfigurationIntakeRt } from './index'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('agentConfigurationIntakeRt', () => { + it('is valid when required parameters are given', () => { + const config = { + service: {}, + settings: {} + }; + + expect(isConfigValid(config)).toBe(true); + }); + + it('is valid when required and optional parameters are given', () => { + const config = { + service: { name: 'my-service', environment: 'my-environment' }, + settings: { + transaction_sample_rate: 0.5, + capture_body: 'foo', + transaction_max_spans: 10 + } + }; + + expect(isConfigValid(config)).toBe(true); + }); + + it('is invalid when required parameters are not given', () => { + const config = {}; + expect(isConfigValid(config)).toBe(false); + }); +}); + +function isConfigValid(config: any) { + return isRight(agentConfigurationIntakeRt.decode(config)); +} diff --git a/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts new file mode 100644 index 0000000000000..32a2832b5eaf3 --- /dev/null +++ b/x-pack/plugins/apm/common/runtime_types/agent_configuration_intake_rt/index.ts @@ -0,0 +1,26 @@ +/* + * 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 * as t from 'io-ts'; +import { transactionSampleRateRt } from '../transaction_sample_rate_rt'; +import { transactionMaxSpansRt } from '../transaction_max_spans_rt'; + +export const serviceRt = t.partial({ + name: t.string, + environment: t.string +}); + +export const agentConfigurationIntakeRt = t.intersection([ + t.partial({ agent_name: t.string }), + t.type({ + service: serviceRt, + settings: t.partial({ + transaction_sample_rate: transactionSampleRateRt, + capture_body: t.string, + transaction_max_spans: transactionMaxSpansRt + }) + }) +]); diff --git a/x-pack/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/plugins/apm/server/lib/helpers/es_client.ts index 8ada02d085631..86eb1dba507f0 100644 --- a/x-pack/plugins/apm/server/lib/helpers/es_client.ts +++ b/x-pack/plugins/apm/server/lib/helpers/es_client.ts @@ -7,9 +7,10 @@ /* eslint-disable no-console */ import { IndexDocumentParams, - IndicesCreateParams, IndicesDeleteParams, - SearchParams + SearchParams, + IndicesCreateParams, + DeleteDocumentResponse } from 'elasticsearch'; import { cloneDeep, isString, merge, uniqueId } from 'lodash'; import { KibanaRequest } from 'src/core/server'; @@ -188,7 +189,7 @@ export function getESClient( index: (params: APMIndexDocumentParams) => { return withTime(() => callMethod('index', params)); }, - delete: (params: IndicesDeleteParams) => { + delete: (params: IndicesDeleteParams): Promise => { return withTime(() => callMethod('delete', params)); }, indicesCreate: (params: IndicesCreateParams) => { diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap index 542fdd99e2635..db34b4d5d20b5 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/__snapshots__/queries.test.ts.snap @@ -1,6 +1,90 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`agent configuration queries fetches all environments 1`] = ` +exports[`agent configuration queries findExactConfiguration find configuration by service.environment 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "service.name", + }, + }, + ], + }, + }, + Object { + "term": Object { + "service.environment": "bar", + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries findExactConfiguration find configuration by service.name 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "service.environment", + }, + }, + ], + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries findExactConfiguration find configuration by service.name and service.environment 1`] = ` +Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + Object { + "term": Object { + "service.environment": "bar", + }, + }, + ], + }, + }, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries getAllEnvironments fetches all environments 1`] = ` Object { "body": Object { "aggs": Object { @@ -41,14 +125,79 @@ Object { } `; -exports[`agent configuration queries fetches configurations 1`] = ` +exports[`agent configuration queries getExistingEnvironmentsForService fetches unavailable environments 1`] = ` +Object { + "body": Object { + "aggs": Object { + "environments": Object { + "terms": Object { + "field": "service.environment", + "missing": "ALL_OPTION_VALUE", + "size": 50, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "service.name": "foo", + }, + }, + ], + }, + }, + "size": 0, + }, + "index": "myIndex", +} +`; + +exports[`agent configuration queries getServiceNames fetches service names 1`] = ` +Object { + "body": Object { + "aggs": Object { + "services": Object { + "terms": Object { + "field": "service.name", + "size": 50, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "terms": Object { + "processor.event": Array [ + "transaction", + "error", + "metric", + ], + }, + }, + ], + }, + }, + "size": 0, + }, + "index": Array [ + "myIndex", + "myIndex", + "myIndex", + ], +} +`; + +exports[`agent configuration queries listConfigurations fetches configurations 1`] = ` Object { "index": "myIndex", "size": 200, } `; -exports[`agent configuration queries fetches filtered configurations with an environment 1`] = ` +exports[`agent configuration queries searchConfigurations fetches filtered configurations with an environment 1`] = ` Object { "body": Object { "query": Object { @@ -60,9 +209,7 @@ Object { "boost": 2, "filter": Object { "term": Object { - "service.name": Object { - "value": "foo", - }, + "service.name": "foo", }, }, }, @@ -72,9 +219,7 @@ Object { "boost": 1, "filter": Object { "term": Object { - "service.environment": Object { - "value": "bar", - }, + "service.environment": "bar", }, }, }, @@ -109,7 +254,7 @@ Object { } `; -exports[`agent configuration queries fetches filtered configurations without an environment 1`] = ` +exports[`agent configuration queries searchConfigurations fetches filtered configurations without an environment 1`] = ` Object { "body": Object { "query": Object { @@ -121,9 +266,7 @@ Object { "boost": 2, "filter": Object { "term": Object { - "service.name": Object { - "value": "foo", - }, + "service.name": "foo", }, }, }, @@ -157,68 +300,3 @@ Object { "index": "myIndex", } `; - -exports[`agent configuration queries fetches service names 1`] = ` -Object { - "body": Object { - "aggs": Object { - "services": Object { - "terms": Object { - "field": "service.name", - "size": 50, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "terms": Object { - "processor.event": Array [ - "transaction", - "error", - "metric", - ], - }, - }, - ], - }, - }, - "size": 0, - }, - "index": Array [ - "myIndex", - "myIndex", - "myIndex", - ], -} -`; - -exports[`agent configuration queries fetches unavailable environments 1`] = ` -Object { - "body": Object { - "aggs": Object { - "environments": Object { - "terms": Object { - "field": "service.environment", - "missing": "ALL_OPTION_VALUE", - "size": 50, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "foo", - }, - }, - ], - }, - }, - "size": 0, - }, - "index": "myIndex", -} -`; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts index ea8f50c90c1d3..ddbe6892c5441 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/configuration_types.d.ts @@ -4,18 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface AgentConfiguration { +import t from 'io-ts'; +import { agentConfigurationIntakeRt } from '../../../../common/runtime_types/agent_configuration_intake_rt'; + +export type AgentConfigurationIntake = t.TypeOf< + typeof agentConfigurationIntakeRt +>; +export type AgentConfiguration = { '@timestamp': number; applied_by_agent?: boolean; etag?: string; agent_name?: string; - service: { - name?: string; - environment?: string; - }; - settings: { - transaction_sample_rate?: number; - capture_body?: string; - transaction_max_spans?: number; - }; -} +} & AgentConfigurationIntake; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts index 5a67f78de6f65..74fcc61dde863 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_or_update_configuration.ts @@ -6,19 +6,19 @@ import hash from 'object-hash'; import { Setup } from '../../helpers/setup_request'; -import { AgentConfiguration } from './configuration_types'; +import { + AgentConfiguration, + AgentConfigurationIntake +} from './configuration_types'; import { APMIndexDocumentParams } from '../../helpers/es_client'; export async function createOrUpdateConfiguration({ configurationId, - configuration, + configurationIntake, setup }: { configurationId?: string; - configuration: Omit< - AgentConfiguration, - '@timestamp' | 'applied_by_agent' | 'etag' - >; + configurationIntake: AgentConfigurationIntake; setup: Setup; }) { const { internalClient, indices } = setup; @@ -27,15 +27,15 @@ export async function createOrUpdateConfiguration({ refresh: true, index: indices.apmAgentConfigurationIndex, body: { - agent_name: configuration.agent_name, + agent_name: configurationIntake.agent_name, service: { - name: configuration.service.name, - environment: configuration.service.environment + name: configurationIntake.service.name, + environment: configurationIntake.service.environment }, - settings: configuration.settings, + settings: configurationIntake.settings, '@timestamp': Date.now(), applied_by_agent: false, - etag: hash(configuration) + etag: hash(configurationIntake) } }; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts new file mode 100644 index 0000000000000..eea409882f876 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/find_exact_configuration.ts @@ -0,0 +1,46 @@ +/* + * 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 { + SERVICE_NAME, + SERVICE_ENVIRONMENT +} from '../../../../common/elasticsearch_fieldnames'; +import { Setup } from '../../helpers/setup_request'; +import { AgentConfiguration } from './configuration_types'; +import { ESSearchHit } from '../../../../typings/elasticsearch'; + +export async function findExactConfiguration({ + service, + setup +}: { + service: AgentConfiguration['service']; + setup: Setup; +}) { + const { internalClient, indices } = setup; + + const serviceNameFilter = service.name + ? { term: { [SERVICE_NAME]: service.name } } + : { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } }; + + const environmentFilter = service.environment + ? { term: { [SERVICE_ENVIRONMENT]: service.environment } } + : { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } }; + + const params = { + index: indices.apmAgentConfigurationIndex, + body: { + query: { + bool: { filter: [serviceNameFilter, environmentFilter] } + } + } + }; + + const resp = await internalClient.search( + params + ); + + return resp.hits.hits[0] as ESSearchHit | undefined; +} diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts index dccf8b110d082..84be805b4dd98 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts @@ -51,5 +51,5 @@ export async function getAgentNameByService({ const agentName = aggregations?.agent_names.buckets[0].key as | string | undefined; - return { agentName }; + return agentName; } diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts index a82d148781ad8..b951b7f350eed 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts @@ -8,11 +8,12 @@ import { getAllEnvironments } from './get_environments/get_all_environments'; import { getExistingEnvironmentsForService } from './get_environments/get_existing_environments_for_service'; import { getServiceNames } from './get_service_names'; import { listConfigurations } from './list_configurations'; -import { searchConfigurations } from './search'; +import { searchConfigurations } from './search_configurations'; import { SearchParamsMock, inspectSearchParams } from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +import { findExactConfiguration } from './find_exact_configuration'; describe('agent configuration queries', () => { let mock: SearchParamsMock; @@ -21,68 +22,117 @@ describe('agent configuration queries', () => { mock.teardown(); }); - it('fetches all environments', async () => { - mock = await inspectSearchParams(setup => - getAllEnvironments({ - serviceName: 'foo', - setup - }) - ); + describe('getAllEnvironments', () => { + it('fetches all environments', async () => { + mock = await inspectSearchParams(setup => + getAllEnvironments({ + serviceName: 'foo', + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches unavailable environments', async () => { - mock = await inspectSearchParams(setup => - getExistingEnvironmentsForService({ - serviceName: 'foo', - setup - }) - ); + describe('getExistingEnvironmentsForService', () => { + it('fetches unavailable environments', async () => { + mock = await inspectSearchParams(setup => + getExistingEnvironmentsForService({ + serviceName: 'foo', + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches service names', async () => { - mock = await inspectSearchParams(setup => - getServiceNames({ - setup - }) - ); + describe('getServiceNames', () => { + it('fetches service names', async () => { + mock = await inspectSearchParams(setup => + getServiceNames({ + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches configurations', async () => { - mock = await inspectSearchParams(setup => - listConfigurations({ - setup - }) - ); + describe('listConfigurations', () => { + it('fetches configurations', async () => { + mock = await inspectSearchParams(setup => + listConfigurations({ + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches filtered configurations without an environment', async () => { - mock = await inspectSearchParams(setup => - searchConfigurations({ - serviceName: 'foo', - setup - }) - ); + describe('searchConfigurations', () => { + it('fetches filtered configurations without an environment', async () => { + mock = await inspectSearchParams(setup => + searchConfigurations({ + service: { + name: 'foo' + }, + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); + + it('fetches filtered configurations with an environment', async () => { + mock = await inspectSearchParams(setup => + searchConfigurations({ + service: { + name: 'foo', + environment: 'bar' + }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); }); - it('fetches filtered configurations with an environment', async () => { - mock = await inspectSearchParams(setup => - searchConfigurations({ - serviceName: 'foo', - environment: 'bar', - setup - }) - ); + describe('findExactConfiguration', () => { + it('find configuration by service.name', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { name: 'foo' }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); + + it('find configuration by service.environment', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { environment: 'bar' }, + setup + }) + ); + + expect(mock.params).toMatchSnapshot(); + }); + + it('find configuration by service.name and service.environment', async () => { + mock = await inspectSearchParams(setup => + findExactConfiguration({ + service: { name: 'foo', environment: 'bar' }, + setup + }) + ); - expect(mock.params).toMatchSnapshot(); + expect(mock.params).toMatchSnapshot(); + }); }); }); diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts similarity index 68% rename from x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts rename to x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts index 766baead006b6..9bbdc96a3a797 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/search.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/search_configurations.ts @@ -12,29 +12,39 @@ import { Setup } from '../../helpers/setup_request'; import { AgentConfiguration } from './configuration_types'; export async function searchConfigurations({ - serviceName, - environment, + service, setup }: { - serviceName: string; - environment?: string; + service: AgentConfiguration['service']; setup: Setup; }) { const { internalClient, indices } = setup; - const environmentFilter = environment + + // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring). + // Additionally a boost has been added to service.name to ensure it scores higher. + // If there is tie between a config with a matching service.name and a config with a matching environment, the config that matches service.name wins + const serviceNameFilter = service.name + ? [ + { + constant_score: { + filter: { term: { [SERVICE_NAME]: service.name } }, + boost: 2 + } + } + ] + : []; + + const environmentFilter = service.environment ? [ { constant_score: { - filter: { term: { [SERVICE_ENVIRONMENT]: { value: environment } } }, + filter: { term: { [SERVICE_ENVIRONMENT]: service.environment } }, boost: 1 } } ] : []; - // In the following `constant_score` is being used to disable IDF calculation (where frequency of a term influences scoring) - // Additionally a boost has been added to service.name to ensure it scores higher - // if there is tie between a config with a matching service.name and a config with a matching environment const params = { index: indices.apmAgentConfigurationIndex, body: { @@ -42,12 +52,7 @@ export async function searchConfigurations({ bool: { minimum_should_match: 2, should: [ - { - constant_score: { - filter: { term: { [SERVICE_NAME]: { value: serviceName } } }, - boost: 2 - } - }, + ...serviceNameFilter, ...environmentFilter, { bool: { must_not: [{ exists: { field: SERVICE_NAME } }] } }, { bool: { must_not: [{ exists: { field: SERVICE_ENVIRONMENT } }] } } diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index f65e271389938..21392edbb2c48 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -23,11 +23,10 @@ import { import { agentConfigurationRoute, agentConfigurationSearchRoute, - createAgentConfigurationRoute, deleteAgentConfigurationRoute, listAgentConfigurationEnvironmentsRoute, listAgentConfigurationServicesRoute, - updateAgentConfigurationRoute, + createOrUpdateAgentConfigurationRoute, agentConfigurationAgentNameRoute } from './settings/agent_configuration'; import { @@ -83,11 +82,10 @@ const createApmApi = () => { .add(agentConfigurationAgentNameRoute) .add(agentConfigurationRoute) .add(agentConfigurationSearchRoute) - .add(createAgentConfigurationRoute) .add(deleteAgentConfigurationRoute) .add(listAgentConfigurationEnvironmentsRoute) .add(listAgentConfigurationServicesRoute) - .add(updateAgentConfigurationRoute) + .add(createOrUpdateAgentConfigurationRoute) // APM indices .add(apmIndexSettingsRoute) diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts index ddd6a27025131..83b845b1fc436 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts @@ -9,15 +9,19 @@ import Boom from 'boom'; import { setupRequest } from '../../lib/helpers/setup_request'; import { getServiceNames } from '../../lib/settings/agent_configuration/get_service_names'; import { createOrUpdateConfiguration } from '../../lib/settings/agent_configuration/create_or_update_configuration'; -import { searchConfigurations } from '../../lib/settings/agent_configuration/search'; +import { searchConfigurations } from '../../lib/settings/agent_configuration/search_configurations'; +import { findExactConfiguration } from '../../lib/settings/agent_configuration/find_exact_configuration'; import { listConfigurations } from '../../lib/settings/agent_configuration/list_configurations'; import { getEnvironments } from '../../lib/settings/agent_configuration/get_environments'; import { deleteConfiguration } from '../../lib/settings/agent_configuration/delete_configuration'; import { createRoute } from '../create_route'; -import { transactionSampleRateRt } from '../../../common/runtime_types/transaction_sample_rate_rt'; -import { transactionMaxSpansRt } from '../../../common/runtime_types/transaction_max_spans_rt'; import { getAgentNameByService } from '../../lib/settings/agent_configuration/get_agent_name_by_service'; import { markAppliedByAgent } from '../../lib/settings/agent_configuration/mark_applied_by_agent'; +import { + serviceRt, + agentConfigurationIntakeRt +} from '../../../common/runtime_types/agent_configuration_intake_rt'; +import { jsonRt } from '../../../common/runtime_types/json_rt'; // get list of configurations export const agentConfigurationRoute = createRoute(core => ({ @@ -31,20 +35,34 @@ export const agentConfigurationRoute = createRoute(core => ({ // delete configuration export const deleteAgentConfigurationRoute = createRoute(() => ({ method: 'DELETE', - path: '/api/apm/settings/agent-configuration/{configurationId}', + path: '/api/apm/settings/agent-configuration', options: { tags: ['access:apm', 'access:apm_write'] }, params: { - path: t.type({ - configurationId: t.string + body: t.type({ + service: serviceRt }) }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const { configurationId } = context.params.path; + const { service } = context.params.body; + + const config = await findExactConfiguration({ service, setup }); + if (!config) { + context.logger.info( + `Config was not found for ${service.name}/${service.environment}` + ); + + throw Boom.notFound(); + } + + context.logger.info( + `Deleting config ${service.name}/${service.environment} (${config._id})` + ); + return await deleteConfiguration({ - configurationId, + configurationId: config._id, setup }); } @@ -62,23 +80,6 @@ export const listAgentConfigurationServicesRoute = createRoute(() => ({ } })); -const agentPayloadRt = t.intersection([ - t.partial({ agent_name: t.string }), - t.type({ - service: t.intersection([ - t.partial({ name: t.string }), - t.partial({ environment: t.string }) - ]) - }), - t.type({ - settings: t.intersection([ - t.partial({ transaction_sample_rate: transactionSampleRateRt }), - t.partial({ capture_body: t.string }), - t.partial({ transaction_max_spans: transactionMaxSpansRt }) - ]) - }) -]); - // get environments for service export const listAgentConfigurationEnvironmentsRoute = createRoute(() => ({ path: '/api/apm/settings/agent-configuration/environments', @@ -102,55 +103,47 @@ export const agentConfigurationAgentNameRoute = createRoute(() => ({ const setup = await setupRequest(context, request); const { serviceName } = context.params.query; const agentName = await getAgentNameByService({ serviceName, setup }); - return agentName; + return { agentName }; } })); -export const createAgentConfigurationRoute = createRoute(() => ({ - method: 'POST', - path: '/api/apm/settings/agent-configuration/new', - params: { - body: agentPayloadRt - }, +export const createOrUpdateAgentConfigurationRoute = createRoute(() => ({ + method: 'PUT', + path: '/api/apm/settings/agent-configuration', options: { tags: ['access:apm', 'access:apm_write'] }, + params: { + query: t.partial({ overwrite: jsonRt.pipe(t.boolean) }), + body: agentConfigurationIntakeRt + }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const configuration = context.params.body; + const { body, query } = context.params; - // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764) - context.logger.info( - `Hitting: /api/apm/settings/agent-configuration/new with ${configuration.service.name}/${configuration.service.environment}` - ); - const res = await createOrUpdateConfiguration({ - configuration, + // if the config already exists, it is fetched and updated + // this is to avoid creating two configs with identical service params + const config = await findExactConfiguration({ + service: body.service, setup }); - context.logger.info(`Created agent configuration`); - return res; - } -})); + // if the config exists ?overwrite=true is required + if (config && !query.overwrite) { + throw Boom.badRequest( + `A configuration already exists for "${body.service.name}/${body.service.environment}. Use ?overwrite=true to overwrite the existing configuration.` + ); + } + + context.logger.info( + `${config ? 'Updating' : 'Creating'} config ${body.service.name}/${ + body.service.environment + }` + ); -export const updateAgentConfigurationRoute = createRoute(() => ({ - method: 'PUT', - path: '/api/apm/settings/agent-configuration/{configurationId}', - options: { - tags: ['access:apm', 'access:apm_write'] - }, - params: { - path: t.type({ - configurationId: t.string - }), - body: agentPayloadRt - }, - handler: async ({ context, request }) => { - const setup = await setupRequest(context, request); - const { configurationId } = context.params.path; return await createOrUpdateConfiguration({ - configurationId, - configuration: context.params.body, + configurationId: config?._id, + configurationIntake: body, setup }); } @@ -162,41 +155,33 @@ export const agentConfigurationSearchRoute = createRoute(core => ({ path: '/api/apm/settings/agent-configuration/search', params: { body: t.type({ - service: t.intersection([ - t.type({ name: t.string }), - t.partial({ environment: t.string }) - ]), + service: serviceRt, etag: t.string }) }, handler: async ({ context, request }) => { - const { body } = context.params; - - // TODO: Remove logger. Only added temporarily to debug flaky test (https://github.com/elastic/kibana/issues/51764) - context.logger.info( - `Hitting: /api/apm/settings/agent-configuration/search for ${body.service.name}/${body.service.environment}` - ); + const { service, etag } = context.params.body; const setup = await setupRequest(context, request); const config = await searchConfigurations({ - serviceName: body.service.name, - environment: body.service.environment, + service, setup }); if (!config) { context.logger.info( - `Config was not found for ${body.service.name}/${body.service.environment}` + `Config was not found for ${service.name}/${service.environment}` ); - throw new Boom('Not found', { statusCode: 404 }); + throw Boom.notFound(); } context.logger.info( - `Config was found for ${body.service.name}/${body.service.environment}` + `Config was found for ${service.name}/${service.environment}` ); // update `applied_by_agent` field if etags match - if (body.etag === config._source.etag && !config._source.applied_by_agent) { + // this happens in the background and doesn't block the response + if (etag === config._source.etag && !config._source.applied_by_agent) { markAppliedByAgent({ id: config._id, body: config._source, setup }); } diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts index 12e08869fa586..00d844e0ba501 100644 --- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts +++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts @@ -6,6 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { AgentConfigurationIntake } from '../../../../legacy/plugins/apm/server/lib/settings/agent_configuration/configuration_types'; export default function agentConfigurationTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -18,74 +19,119 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo'); } - let createdConfigIds: any[] = []; - async function createConfiguration(configuration: any) { + let createdConfigs: AgentConfigurationIntake[] = []; + async function createConfiguration(config: any) { + log.debug('creating configuration', config.service); const res = await supertest - .post(`/api/apm/settings/agent-configuration/new`) - .send(configuration) + .put(`/api/apm/settings/agent-configuration`) + .send(config) .set('kbn-xsrf', 'foo'); - createdConfigIds.push(res.body._id); + if (res.statusCode !== 200) { + throw new Error( + `Could not create config ${JSON.stringify(config.service)}. Received statuscode ${ + res.statusCode + } and message: ${JSON.stringify(res.body)}` + ); + } + createdConfigs.push(config); return res; } - function deleteCreatedConfigurations() { - const promises = Promise.all(createdConfigIds.map(deleteConfiguration)); + async function updateConfiguration(config: any) { + log.debug('updating configuration', config.service); + const res = await supertest + .put(`/api/apm/settings/agent-configuration?overwrite=true`) + .send(config) + .set('kbn-xsrf', 'foo'); + + if (res.statusCode !== 200) { + throw new Error( + `Could not update config ${JSON.stringify(config.service)}. Received statuscode ${ + res.statusCode + } and message: ${JSON.stringify(res.body)}` + ); + } + + return res; + } + + async function deleteCreatedConfigurations() { + const promises = Promise.all(createdConfigs.map(deleteConfiguration)); return promises; } - function deleteConfiguration(configurationId: string) { - return supertest - .delete(`/api/apm/settings/agent-configuration/${configurationId}`) - .set('kbn-xsrf', 'foo') - .then((response: any) => { - createdConfigIds = createdConfigIds.filter(id => id === configurationId); - return response; - }); + async function deleteConfiguration({ service }: AgentConfigurationIntake) { + log.debug('deleting configuration', service); + const res = await supertest + .delete(`/api/apm/settings/agent-configuration`) + .send({ service }) + .set('kbn-xsrf', 'foo'); + + createdConfigs = createdConfigs.filter(c => { + const isMatch = + c.service.name === service.name && c.service.environment === service.environment; + + return !isMatch; + }); + + if (res.statusCode !== 200) { + throw new Error( + `Could not delete config ${JSON.stringify(service)}. Received statuscode ${ + res.statusCode + } and message: ${JSON.stringify(res.body)}` + ); + } + + return res; } describe('agent configuration', () => { describe('when creating one configuration', () => { - let createdConfigId: string; + const newConfig = { + service: {}, + settings: { transaction_sample_rate: 0.55 }, + }; - const parameters = { + const searchParams = { service: { name: 'myservice', environment: 'development' }, etag: '7312bdcc34999629a3d39df24ed9b2a7553c0c39', }; before(async () => { - log.debug('creating agent configuration'); - - // all / all - const { body } = await createConfiguration({ - service: {}, - settings: { transaction_sample_rate: 0.1 }, - }); - - createdConfigId = body._id; + await createConfiguration(newConfig); }); - it('returns the created configuration', async () => { - const { statusCode, body } = await searchConfigurations(parameters); + after(async () => { + await deleteCreatedConfigurations(); + }); + it('can find the created config', async () => { + const { statusCode, body } = await searchConfigurations(searchParams); expect(statusCode).to.equal(200); - expect(body._id).to.equal(createdConfigId); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: 0.55 }); }); - it('succesfully deletes the configuration', async () => { - await deleteConfiguration(createdConfigId); + it('can update the created config', async () => { + await updateConfiguration({ service: {}, settings: { transaction_sample_rate: 0.85 } }); - const { statusCode } = await searchConfigurations(parameters); + const { statusCode, body } = await searchConfigurations(searchParams); + expect(statusCode).to.equal(200); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: 0.85 }); + }); + it('can delete the created config', async () => { + await deleteConfiguration(newConfig); + const { statusCode } = await searchConfigurations(searchParams); expect(statusCode).to.equal(404); }); }); - describe('when creating four configurations', () => { + describe('when creating multiple configurations', () => { before(async () => { - log.debug('creating agent configuration'); - // all / all await createConfiguration({ service: {}, @@ -118,7 +164,6 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); after(async () => { - log.debug('deleting agent configurations'); await deleteCreatedConfigurations(); }); @@ -169,7 +214,6 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); after(async () => { - log.debug('deleting agent configurations'); await deleteCreatedConfigurations(); }); diff --git a/x-pack/test/api_integration/apis/apm/feature_controls.ts b/x-pack/test/api_integration/apis/apm/feature_controls.ts index ec2bbca23ddd2..f96edf3086802 100644 --- a/x-pack/test/api_integration/apis/apm/feature_controls.ts +++ b/x-pack/test/api_integration/apis/apm/feature_controls.ts @@ -14,7 +14,6 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const supertestWithoutAuth = getService('supertestWithoutAuth'); const security = getService('security'); const spaces = getService('spaces'); - const log = getService('log'); const es = getService('legacyEs'); const start = encodeURIComponent(new Date(Date.now() - 10000).toISOString()); @@ -33,7 +32,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) interface Endpoint { req: { url: string; - method?: 'get' | 'post' | 'delete'; + method?: 'get' | 'post' | 'delete' | 'put'; body?: any; }; expectForbidden: (result: any) => void; @@ -196,7 +195,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const { statusCode, req } = response; if (statusCode !== 200) { - log.debug(`Endpoint: ${req.method} ${req.path} + throw new Error(`Endpoint: ${req.method} ${req.path} Status code: ${statusCode} Response: ${response.body.message}`); } @@ -244,26 +243,28 @@ export default function featureControlsTests({ getService }: FtrProviderContext) } describe('apm feature controls', () => { - let res: any; + const config = { + service: { name: 'test-service' }, + settings: { transaction_sample_rate: 0.5 }, + }; before(async () => { console.log(`Creating agent configuration`); - res = await executeAsAdmin({ - method: 'post', - url: '/api/apm/settings/agent-configuration/new', - body: { - service: { name: 'test-service' }, - settings: { transaction_sample_rate: 0.5 }, - }, + await executeAsAdmin({ + method: 'put', + url: '/api/apm/settings/agent-configuration', + body: config, }); console.log(`Agent configuration created`); }); after(async () => { console.log('deleting agent configuration'); - const configurationId = res.body._id; await executeAsAdmin({ method: 'delete', - url: `/api/apm/settings/agent-configuration/${configurationId}`, + url: `/api/apm/settings/agent-configuration`, + body: { + service: config.service, + }, }); }); diff --git a/x-pack/test/api_integration/apis/apm/index.ts b/x-pack/test/api_integration/apis/apm/index.ts index c49d577537048..6f41f4abfecc3 100644 --- a/x-pack/test/api_integration/apis/apm/index.ts +++ b/x-pack/test/api_integration/apis/apm/index.ts @@ -7,7 +7,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('APM', () => { + describe('APM specs', () => { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./agent_configuration')); }); diff --git a/x-pack/test/functional/apps/apm/index.ts b/x-pack/test/functional/apps/apm/index.ts index 945af09183f03..bf254f9b9b419 100644 --- a/x-pack/test/functional/apps/apm/index.ts +++ b/x-pack/test/functional/apps/apm/index.ts @@ -6,7 +6,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ loadTestFile }: FtrProviderContext) { - describe('APM', function() { + describe('APM specs', function() { this.tags('ciGroup6'); loadTestFile(require.resolve('./feature_controls')); }); From 57b5f3a608d61cd430842899d8235be3c3deb27b Mon Sep 17 00:00:00 2001 From: Brandon Morelli Date: Tue, 18 Feb 2020 08:31:20 -0800 Subject: [PATCH 02/11] docs: APM API template --- docs/apm/api.asciidoc | 332 ++++++++++++++++++++++++++++++++++++++++ docs/apm/index.asciidoc | 2 + 2 files changed, 334 insertions(+) create mode 100644 docs/apm/api.asciidoc diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc new file mode 100644 index 0000000000000..8bae47303a3a6 --- /dev/null +++ b/docs/apm/api.asciidoc @@ -0,0 +1,332 @@ +[role="xpack"] +[[apm-api]] +== API + +Some APM app features are provided via a REST API: + +* <> + +TIP: Kibana provides additional <>, +and general information on <>. + +//// +******************************************************* +//// + +[[agent-config-api]] +=== Agent Configuration API + +The Agent configuration API allows you to fine-tune your APM agent configuration, +without needing to redeploy your application. + +[source,console] +-------------------------------------------------- +POST /api/apm/settings/agent-configuration/new +-------------------------------------------------- + +The following Agent configuration APIs are available: + +* <> to create a new Agent configuration. +* <> to delete an Agent configuration. +* <> to update a preexisting Agent configuration. +* <> to list all Agent configurations. +* <> to search for an Agent configuration. + +//// +******************************************************* +//// + + +[[apm-create-config]] +==== Create configuration + +//// +Add a description. +Link to related APIs if appropriate. + +Guidelines for parameter documentation +*************************************** +* Use a definition list. +* End each definition with a period. +* Include whether the parameter is Optional or Required and the data type. +* Include default values as the last sentence of the first paragraph. +* Include a range of valid values, if applicable. +* If the parameter requires a specific delimiter for multiple values, say so. +* If the parameter supports wildcards, ditto. +* For large or nested objects, consider linking to a separate definition list. +*************************************** +//// + +[[apm-create-config-req]] +===== Request +//// +This section show the basic endpoint, without the body or optional parameters. +Variables should use <...> syntax. +If an API supports both PUT and POST, include both here. +//// + +`POST /api/apm/settings/agent-configuration/new` + +[[apm-create-config-pre]] +===== Prerequisites +//// +Optional list of prerequisites. + +For example: + +* A snapshot of an index created in 5.x can be restored to 6.x. You must... +* If the {es} {security-features} are enabled, you must have `write`, `monitor`, +and `manage_follow_index` index privileges... +//// + +[[apm-create-config-params]] +===== Parameters +//// +A list of all the parameters within the path of the endpoint (before the query string (?)). + +For example: +``:: +(Required, string) Name of the follower index +//// + + +[[apm-create-config-query]] +===== Query parameters +//// +A list of the parameters in the query string of the endpoint (after the ?). + +For example: +`wait_for_active_shards`:: +(Optional, integer) Specifies the number of shards to wait on being active before +responding. A shard must be restored from the leader index being active. +Restoring a follower shard requires transferring all the remote Lucene segment +files to the follower index. The default is `0`, which means waiting on none of +the shards to be active. +//// + + +[[apm-create-config-req-body]] +===== Request body +//// +A list of the properties you can specify in the body of the request. + +For example: +`remote_cluster`:: +(Required, string) The <> that contains +the leader index. + +`leader_index`:: +(Required, string) The name of the index in the leader cluster to follow. +//// + + +[[apm-create-config-body]] +===== Response body +//// +Response body is only required for detailed responses. + +For example: +`auto_follow_stats`:: + (object) An object representing stats for the auto-follow coordinator. This + object consists of the following fields: + +`auto_follow_stats.number_of_successful_follow_indices`::: + (long) the number of indices that the auto-follow coordinator successfully + followed +... + +//// + + +[[apm-create-config-codes]] +===== Response code +//// +Response codes are only required when needed to understand the response body. + +For example: +`200`:: +Indicates all listed indices or index aliases exist. + + `404`:: +Indicates one or more listed indices or index aliases **do not** exist. +//// + + +[[apm-create-config-example]] +===== Example +//// +Optional additional examples. +DO NOT repeat the basic example used earlier. + + +[source,console] +---- +PUT /follower_index/_ccr/follow?wait_for_active_shards=1 +{ + "remote_cluster" : "remote_cluster", + "leader_index" : "leader_index", + "max_read_request_operation_count" : 1024, + "max_outstanding_read_requests" : 16, + "max_read_request_size" : "1024k", + "max_write_request_operation_count" : 32768, + "max_write_request_size" : "16k", + "max_outstanding_write_requests" : 8, + "max_write_buffer_count" : 512, + "max_write_buffer_size" : "512k", + "max_retry_delay" : "10s", + "read_poll_timeout" : "30s" +} +---- +// TEST[setup:remote_cluster_and_leader_index] + +The API returns the following result: + +[source,console-result] +---- +{ + "follow_index_created" : true, + "follow_index_shards_acked" : true, + "index_following_started" : true +} +---- +//// + +//// +******************************************************* +//// + + +[[apm-delete-config]] +==== Delete configuration + +[[apm-delete-config-req]] +===== Request + +`DELETE /api/apm/settings/agent-configuration/{configurationId}` + +[[apm-delete-config-pre]] +===== Prerequisites + +[[apm-delete-config-params]] +===== Parameters + +[[apm-delete-config-query]] +===== Query parameters + +[[apm-delete-config-req-body]] +===== Request body + +[[apm-delete-config-body]] +===== Response body + +[[apm-delete-config-codes]] +===== Response code + +[[apm-delete-config-example]] +===== Example + +//// +******************************************************* +//// + + +[[apm-update-config]] +==== Update configuration + +[[apm-update-config-req]] +===== Request + +`PUT /api/apm/settings/agent-configuration/{configurationId}` + +[[apm-update-config-pre]] +===== Prerequisites + +[[apm-update-config-params]] +===== Parameters + +[[apm-update-config-query]] +===== Query parameters + +[[apm-update-config-req-body]] +===== Request body + +[[apm-update-config-body]] +===== Response body + +[[apm-update-config-codes]] +===== Response code + +[[apm-update-config-example]] +===== Example + +//// +******************************************************* +//// + + +[[apm-list-config]] +==== List configuration + +[[apm-list-config-req]] +===== Request + +`GET /api/apm/settings/agent-configuration` + +[[apm-list-config-pre]] +===== Prerequisites + +[[apm-list-config-params]] +===== Parameters + +[[apm-list-config-query]] +===== Query parameters + +[[apm-list-config-req-body]] +===== Request body + +[[apm-list-config-body]] +===== Response body + +[[apm-list-config-codes]] +===== Response code + +[[apm-list-config-example]] +===== Example + +//// +******************************************************* +//// + + +[[apm-search-config]] +==== Search configuration + +[[apm-search-config-req]] +===== Request + +`POST /api/apm/settings/agent-configuration/search` + +[[apm-search-config-pre]] +===== Prerequisites + +[[apm-search-config-params]] +===== Parameters + +[[apm-search-config-query]] +===== Query parameters + +[[apm-search-config-req-body]] +===== Request body + +[[apm-search-config-body]] +===== Response body + +[[apm-search-config-codes]] +===== Response code + +[[apm-search-config-example]] +===== Example + +//// +******************************************************* +//// diff --git a/docs/apm/index.asciidoc b/docs/apm/index.asciidoc index 7eb7278cf0358..d3f0dc5b7f11f 100644 --- a/docs/apm/index.asciidoc +++ b/docs/apm/index.asciidoc @@ -24,3 +24,5 @@ include::getting-started.asciidoc[] include::bottlenecks.asciidoc[] include::using-the-apm-ui.asciidoc[] + +include::api.asciidoc[] From 6f2d9264b3824348311500e2a49569c5e0b547bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv?= Date: Wed, 19 Feb 2020 15:04:04 +0100 Subject: [PATCH 03/11] Fill out agent config docs --- docs/apm/api.asciidoc | 350 ++++++++++++++++-------------------------- 1 file changed, 135 insertions(+), 215 deletions(-) diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index 8bae47303a3a6..625cee355fc34 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -19,16 +19,10 @@ and general information on <>. The Agent configuration API allows you to fine-tune your APM agent configuration, without needing to redeploy your application. -[source,console] --------------------------------------------------- -POST /api/apm/settings/agent-configuration/new --------------------------------------------------- - The following Agent configuration APIs are available: -* <> to create a new Agent configuration. +* <> to create or update an Agent configuration * <> to delete an Agent configuration. -* <> to update a preexisting Agent configuration. * <> to list all Agent configurations. * <> to search for an Agent configuration. @@ -36,160 +30,51 @@ The following Agent configuration APIs are available: ******************************************************* //// +[[apm-update-config]] +==== Create or update configuration -[[apm-create-config]] -==== Create configuration - -//// -Add a description. -Link to related APIs if appropriate. - -Guidelines for parameter documentation -*************************************** -* Use a definition list. -* End each definition with a period. -* Include whether the parameter is Optional or Required and the data type. -* Include default values as the last sentence of the first paragraph. -* Include a range of valid values, if applicable. -* If the parameter requires a specific delimiter for multiple values, say so. -* If the parameter supports wildcards, ditto. -* For large or nested objects, consider linking to a separate definition list. -*************************************** -//// - -[[apm-create-config-req]] +[[apm-update-config-req]] ===== Request -//// -This section show the basic endpoint, without the body or optional parameters. -Variables should use <...> syntax. -If an API supports both PUT and POST, include both here. -//// - -`POST /api/apm/settings/agent-configuration/new` - -[[apm-create-config-pre]] -===== Prerequisites -//// -Optional list of prerequisites. -For example: +`PUT /api/apm/settings/agent-configuration` -* A snapshot of an index created in 5.x can be restored to 6.x. You must... -* If the {es} {security-features} are enabled, you must have `write`, `monitor`, -and `manage_follow_index` index privileges... -//// - -[[apm-create-config-params]] -===== Parameters -//// -A list of all the parameters within the path of the endpoint (before the query string (?)). - -For example: -``:: -(Required, string) Name of the follower index -//// - - -[[apm-create-config-query]] +[[apm-update-config-query]] ===== Query parameters -//// -A list of the parameters in the query string of the endpoint (after the ?). - -For example: -`wait_for_active_shards`:: -(Optional, integer) Specifies the number of shards to wait on being active before -responding. A shard must be restored from the leader index being active. -Restoring a follower shard requires transferring all the remote Lucene segment -files to the follower index. The default is `0`, which means waiting on none of -the shards to be active. -//// - -[[apm-create-config-req-body]] +[[apm-update-config-req-body]] ===== Request body -//// -A list of the properties you can specify in the body of the request. -For example: -`remote_cluster`:: -(Required, string) The <> that contains -the leader index. +`service`:: +(required) Service object containing `name` and `environment` on the configuration that should be deleted. -`leader_index`:: -(Required, string) The name of the index in the leader cluster to follow. -//// +`settings`:: +(required) Key/value object with settings and their corresponding value. +`agent_name`:: +(optional) The agent name is used by the UI to determine which settings to display. -[[apm-create-config-body]] +[[apm-update-config-body]] ===== Response body -//// -Response body is only required for detailed responses. - -For example: -`auto_follow_stats`:: - (object) An object representing stats for the auto-follow coordinator. This - object consists of the following fields: - -`auto_follow_stats.number_of_successful_follow_indices`::: - (long) the number of indices that the auto-follow coordinator successfully - followed -... - -//// - - -[[apm-create-config-codes]] -===== Response code -//// -Response codes are only required when needed to understand the response body. -For example: -`200`:: -Indicates all listed indices or index aliases exist. - - `404`:: -Indicates one or more listed indices or index aliases **do not** exist. -//// - - -[[apm-create-config-example]] +[[apm-update-config-example]] ===== Example -//// -Optional additional examples. -DO NOT repeat the basic example used earlier. - [source,console] ----- -PUT /follower_index/_ccr/follow?wait_for_active_shards=1 -{ - "remote_cluster" : "remote_cluster", - "leader_index" : "leader_index", - "max_read_request_operation_count" : 1024, - "max_outstanding_read_requests" : 16, - "max_read_request_size" : "1024k", - "max_write_request_operation_count" : 32768, - "max_write_request_size" : "16k", - "max_outstanding_write_requests" : 8, - "max_write_buffer_count" : 512, - "max_write_buffer_size" : "512k", - "max_retry_delay" : "10s", - "read_poll_timeout" : "30s" -} ----- -// TEST[setup:remote_cluster_and_leader_index] - -The API returns the following result: - -[source,console-result] ----- +-------------------------------------------------- +PUT /api/apm/settings/agent-configuration { - "follow_index_created" : true, - "follow_index_shards_acked" : true, - "index_following_started" : true + "agent_name": "nodejs" + "service" : { + "name" : "frontend", + "environment" : "production" + }, + "settings" : { + "transaction_sample_rate" : 0.4, + "capture_body" : "off", + "transaction_max_spans" : 500 + } } ----- -//// +-------------------------------------------------- //// ******************************************************* @@ -199,65 +84,41 @@ The API returns the following result: [[apm-delete-config]] ==== Delete configuration +[source,console] +-------------------------------------------------- +POST /api/apm/settings/agent-configuration +-------------------------------------------------- + [[apm-delete-config-req]] ===== Request -`DELETE /api/apm/settings/agent-configuration/{configurationId}` +`DELETE /api/apm/settings/agent-configuration` -[[apm-delete-config-pre]] -===== Prerequisites - -[[apm-delete-config-params]] -===== Parameters - -[[apm-delete-config-query]] -===== Query parameters [[apm-delete-config-req-body]] ===== Request body + +`service`:: +(required) Service object containing `name` and `environment` on the configuration that should be deleted. + [[apm-delete-config-body]] ===== Response body -[[apm-delete-config-codes]] -===== Response code [[apm-delete-config-example]] ===== Example -//// -******************************************************* -//// - - -[[apm-update-config]] -==== Update configuration - -[[apm-update-config-req]] -===== Request - -`PUT /api/apm/settings/agent-configuration/{configurationId}` - -[[apm-update-config-pre]] -===== Prerequisites - -[[apm-update-config-params]] -===== Parameters - -[[apm-update-config-query]] -===== Query parameters - -[[apm-update-config-req-body]] -===== Request body - -[[apm-update-config-body]] -===== Response body - -[[apm-update-config-codes]] -===== Response code - -[[apm-update-config-example]] -===== Example +[source,console] +-------------------------------------------------- +DELETE /api/apm/settings/agent-configuration +{ + "service" : { + "name" : "frontend", + "environment": "production" + } +} +-------------------------------------------------- //// ******************************************************* @@ -267,31 +128,64 @@ The API returns the following result: [[apm-list-config]] ==== List configuration +[source,console] +-------------------------------------------------- +GET /api/apm/settings/agent-configuration +-------------------------------------------------- + [[apm-list-config-req]] ===== Request `GET /api/apm/settings/agent-configuration` -[[apm-list-config-pre]] -===== Prerequisites - -[[apm-list-config-params]] -===== Parameters - -[[apm-list-config-query]] -===== Query parameters - -[[apm-list-config-req-body]] -===== Request body - [[apm-list-config-body]] ===== Response body -[[apm-list-config-codes]] -===== Response code - -[[apm-list-config-example]] -===== Example +``` +[ + { + "agent_name": "go", + "service": { + "name": "opbeans-go", + "environment": "production" + }, + "settings": { + "transaction_sample_rate": 1, + "capture_body": "off", + "transaction_max_spans": 200 + }, + "@timestamp": 1581934104843, + "applied_by_agent": false, + "etag": "1e58c178efeebae15c25c539da740d21dee422fc" + }, + { + "agent_name": "go", + "service": { + "name": "opbeans-go" + }, + "settings": { + "transaction_sample_rate": 1, + "capture_body": "off", + "transaction_max_spans": 300 + }, + "@timestamp": 1581934111727, + "applied_by_agent": false, + "etag": "3eed916d3db434d9fb7f039daa681c7a04539a64" + }, + { + "agent_name": "nodejs", + "service": { + "name": "frontend" + }, + "settings": { + "transaction_sample_rate": 1, + }, + "@timestamp": 1582031336265, + "applied_by_agent": false, + "etag": "5080ed25785b7b19f32713681e79f46996801a5b" + } +] +``` //// ******************************************************* @@ -306,27 +200,53 @@ The API returns the following result: `POST /api/apm/settings/agent-configuration/search` -[[apm-search-config-pre]] -===== Prerequisites - -[[apm-search-config-params]] -===== Parameters - -[[apm-search-config-query]] -===== Query parameters - [[apm-search-config-req-body]] ===== Request body +`service`:: +(required) Service object containing `name` and `environment` on the configuration that should be deleted. + +`etag`:: +(required) etag is sent by the agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`. + [[apm-search-config-body]] ===== Response body -[[apm-search-config-codes]] -===== Response code +``` +{ + "_index": ".apm-agent-configuration", + "_id": "CIaqXXABmQCdPphWj8EJ", + "_score": 2, + "_source": { + "agent_name": "nodejs", + "service": { + "name": "frontend" + }, + "settings": { + "transaction_sample_rate": 1, + }, + "@timestamp": 1582031336265, + "applied_by_agent": false, + "etag": "5080ed25785b7b19f32713681e79f46996801a5b" + } +} +``` [[apm-search-config-example]] ===== Example +[source,console] +-------------------------------------------------- +POST /api/apm/settings/agent-configuration/search +{ + "etag" : "1e58c178efeebae15c25c539da740d21dee422fc", + "service" : { + "name" : "frontend", + "environment": "production" + } +} +-------------------------------------------------- + //// ******************************************************* //// From be840f521a1367cf98759f0c1e78c37bbbb05283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Fri, 21 Feb 2020 11:16:17 +0100 Subject: [PATCH 04/11] Doc formatting fixes --- docs/apm/api.asciidoc | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index 625cee355fc34..f7c459c6debca 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -33,13 +33,10 @@ The following Agent configuration APIs are available: [[apm-update-config]] ==== Create or update configuration -[[apm-update-config-req]] -===== Request - -`PUT /api/apm/settings/agent-configuration` - -[[apm-update-config-query]] -===== Query parameters +[source,console] +-------------------------------------------------- +PUT /api/apm/settings/agent-configuration +-------------------------------------------------- [[apm-update-config-req-body]] ===== Request body @@ -63,7 +60,6 @@ The following Agent configuration APIs are available: -------------------------------------------------- PUT /api/apm/settings/agent-configuration { - "agent_name": "nodejs" "service" : { "name" : "frontend", "environment" : "production" @@ -72,7 +68,8 @@ PUT /api/apm/settings/agent-configuration "transaction_sample_rate" : 0.4, "capture_body" : "off", "transaction_max_spans" : 500 - } + }, + "agent_name": "nodejs" } -------------------------------------------------- @@ -86,15 +83,9 @@ PUT /api/apm/settings/agent-configuration [source,console] -------------------------------------------------- -POST /api/apm/settings/agent-configuration +DELETE /api/apm/settings/agent-configuration -------------------------------------------------- -[[apm-delete-config-req]] -===== Request - -`DELETE /api/apm/settings/agent-configuration` - - [[apm-delete-config-req-body]] ===== Request body @@ -133,15 +124,12 @@ DELETE /api/apm/settings/agent-configuration GET /api/apm/settings/agent-configuration -------------------------------------------------- -[[apm-list-config-req]] -===== Request - -`GET /api/apm/settings/agent-configuration` - [[apm-list-config-body]] ===== Response body -``` + +[source,js] +-------------------------------------------------- [ { "agent_name": "go", @@ -185,7 +173,7 @@ GET /api/apm/settings/agent-configuration "etag": "5080ed25785b7b19f32713681e79f46996801a5b" } ] -``` +-------------------------------------------------- //// ******************************************************* @@ -212,7 +200,8 @@ GET /api/apm/settings/agent-configuration [[apm-search-config-body]] ===== Response body -``` +[source,js] +-------------------------------------------------- { "_index": ".apm-agent-configuration", "_id": "CIaqXXABmQCdPphWj8EJ", @@ -230,7 +219,7 @@ GET /api/apm/settings/agent-configuration "etag": "5080ed25785b7b19f32713681e79f46996801a5b" } } -``` +-------------------------------------------------- [[apm-search-config-example]] ===== Example From 3a527d579dc6f13abea8c72fdff428feeb3428c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Fri, 21 Feb 2020 11:24:20 +0100 Subject: [PATCH 05/11] Format objects --- docs/apm/api.asciidoc | 60 +++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index f7c459c6debca..eee96bc937b3b 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -33,16 +33,22 @@ The following Agent configuration APIs are available: [[apm-update-config]] ==== Create or update configuration -[source,console] --------------------------------------------------- -PUT /api/apm/settings/agent-configuration --------------------------------------------------- +[[apm-update-config-req]] +===== Request + +`PUT /api/apm/settings/agent-configuration` [[apm-update-config-req-body]] ===== Request body `service`:: -(required) Service object containing `name` and `environment` on the configuration that should be deleted. +(required, object) Service identifying the configuration to create or update. + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service `settings`:: (required) Key/value object with settings and their corresponding value. @@ -81,20 +87,21 @@ PUT /api/apm/settings/agent-configuration [[apm-delete-config]] ==== Delete configuration -[source,console] --------------------------------------------------- -DELETE /api/apm/settings/agent-configuration --------------------------------------------------- +[[apm-delete-config-req]] +===== Request + +`DELETE /api/apm/settings/agent-configuration` [[apm-delete-config-req-body]] ===== Request body - - `service`:: -(required) Service object containing `name` and `environment` on the configuration that should be deleted. +(required, object) Service identifying the configuration to delete -[[apm-delete-config-body]] -===== Response body +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service [[apm-delete-config-example]] @@ -119,10 +126,11 @@ DELETE /api/apm/settings/agent-configuration [[apm-list-config]] ==== List configuration -[source,console] --------------------------------------------------- -GET /api/apm/settings/agent-configuration --------------------------------------------------- + +[[apm-list-config-req]] +===== Request + +`GET /api/apm/settings/agent-configuration` [[apm-list-config-body]] ===== Response body @@ -175,6 +183,14 @@ GET /api/apm/settings/agent-configuration ] -------------------------------------------------- +[[apm-list-config-example]] +===== Example + +[source,console] +-------------------------------------------------- +GET /api/apm/settings/agent-configuration +-------------------------------------------------- + //// ******************************************************* //// @@ -192,7 +208,13 @@ GET /api/apm/settings/agent-configuration ===== Request body `service`:: -(required) Service object containing `name` and `environment` on the configuration that should be deleted. +(required, object) Service identifying the configuration. + +`name` ::: + (required, string) Name of service + +`environment` ::: + (optional, string) Environment of service `etag`:: (required) etag is sent by the agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`. From 46399da16ac5d0309579549639a92e6974ef0152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Fri, 21 Feb 2020 11:53:20 +0100 Subject: [PATCH 06/11] Remove empty section --- docs/apm/api.asciidoc | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index eee96bc937b3b..b520cc46bef8d 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -56,8 +56,6 @@ The following Agent configuration APIs are available: `agent_name`:: (optional) The agent name is used by the UI to determine which settings to display. -[[apm-update-config-body]] -===== Response body [[apm-update-config-example]] ===== Example @@ -135,7 +133,6 @@ DELETE /api/apm/settings/agent-configuration [[apm-list-config-body]] ===== Response body - [source,js] -------------------------------------------------- [ From 9cc75fbfe48e92b3c52d1982074d822d478fac25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 24 Feb 2020 13:01:20 +0100 Subject: [PATCH 07/11] Avoid null pointer if bucket is empty --- .../apm/server/lib/services/get_service_node_metadata.ts | 4 ++-- .../agent_configuration/get_agent_name_by_service.ts | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts index 7120d3bca6c25..ccd8b123e23e2 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts @@ -58,8 +58,8 @@ export async function getServiceNodeMetadata({ const response = await client.search(query); return { - host: response.aggregations?.host.buckets[0].key || NOT_AVAILABLE_LABEL, + host: response.aggregations?.host.buckets[0]?.key || NOT_AVAILABLE_LABEL, containerId: - response.aggregations?.containerId.buckets[0].key || NOT_AVAILABLE_LABEL + response.aggregations?.containerId.buckets[0]?.key || NOT_AVAILABLE_LABEL }; } diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts index 84be805b4dd98..a9af1f6174fd5 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/get_agent_name_by_service.ts @@ -48,8 +48,6 @@ export async function getAgentNameByService({ }; const { aggregations } = await client.search(params); - const agentName = aggregations?.agent_names.buckets[0].key as - | string - | undefined; - return agentName; + const agentName = aggregations?.agent_names.buckets[0]?.key; + return agentName as string | undefined; } From 2cf31257e8647c8c0aaa518969ff9935f665e337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 24 Feb 2020 13:06:54 +0100 Subject: [PATCH 08/11] Rename to `isExistingConfig` --- .../Settings/AgentConfigurations/AddEditFlyout/index.tsx | 2 +- .../AgentConfigurations/AddEditFlyout/saveConfig.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx index db327270f0ce9..c77617fbb424f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/index.tsx @@ -136,7 +136,7 @@ export function AddEditFlyout({ captureBody, transactionMaxSpans, agentName, - existingConfig: Boolean(selectedConfig), + isExistingConfig: Boolean(selectedConfig), toasts, trackApmEvent }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts index 0faaedbdfe829..d36120a054795 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AddEditFlyout/saveConfig.ts @@ -28,7 +28,7 @@ export async function saveConfig({ captureBody, transactionMaxSpans, agentName, - existingConfig, + isExistingConfig, toasts, trackApmEvent }: { @@ -39,7 +39,7 @@ export async function saveConfig({ captureBody: string; transactionMaxSpans: string; agentName?: string; - existingConfig: boolean; + isExistingConfig: boolean; toasts: NotificationsStart['toasts']; trackApmEvent: UiTracker; }) { @@ -68,7 +68,7 @@ export async function saveConfig({ pathname: '/api/apm/settings/agent-configuration', method: 'PUT', params: { - query: { overwrite: existingConfig }, + query: { overwrite: isExistingConfig }, body: configuration } }); From 95d0b6daa02b7d9290b298467b595a258d49b2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 24 Feb 2020 15:44:07 +0100 Subject: [PATCH 09/11] Clean up API test --- .../apis/apm/agent_configuration.ts | 70 +++++++------------ 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts index 00d844e0ba501..c567601034870 100644 --- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts +++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts @@ -5,8 +5,8 @@ */ import expect from '@kbn/expect'; +import { AgentConfigurationIntake } from '../../../../plugins/apm/server/lib/settings/agent_configuration/configuration_types'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { AgentConfigurationIntake } from '../../../../legacy/plugins/apm/server/lib/settings/agent_configuration/configuration_types'; export default function agentConfigurationTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -19,7 +19,6 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo'); } - let createdConfigs: AgentConfigurationIntake[] = []; async function createConfiguration(config: any) { log.debug('creating configuration', config.service); const res = await supertest @@ -35,7 +34,6 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte ); } - createdConfigs.push(config); return res; } @@ -57,11 +55,6 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte return res; } - async function deleteCreatedConfigurations() { - const promises = Promise.all(createdConfigs.map(deleteConfiguration)); - return promises; - } - async function deleteConfiguration({ service }: AgentConfigurationIntake) { log.debug('deleting configuration', service); const res = await supertest @@ -69,13 +62,6 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte .send({ service }) .set('kbn-xsrf', 'foo'); - createdConfigs = createdConfigs.filter(c => { - const isMatch = - c.service.name === service.name && c.service.environment === service.environment; - - return !isMatch; - }); - if (res.statusCode !== 200) { throw new Error( `Could not delete config ${JSON.stringify(service)}. Received statuscode ${ @@ -103,10 +89,6 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte await createConfiguration(newConfig); }); - after(async () => { - await deleteCreatedConfigurations(); - }); - it('can find the created config', async () => { const { statusCode, body } = await searchConfigurations(searchParams); expect(statusCode).to.equal(200); @@ -131,40 +113,35 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); describe('when creating multiple configurations', () => { - before(async () => { - // all / all - await createConfiguration({ + const configs = [ + { service: {}, settings: { transaction_sample_rate: 0.1 }, - }); - - // my_service / all - await createConfiguration({ + }, + { service: { name: 'my_service' }, settings: { transaction_sample_rate: 0.2 }, - }); - - // all / production - await createConfiguration({ + }, + { service: { environment: 'production' }, settings: { transaction_sample_rate: 0.3 }, - }); - - // all / production - await createConfiguration({ + }, + { service: { environment: 'development' }, settings: { transaction_sample_rate: 0.4 }, - }); - - // my_service / production - await createConfiguration({ + }, + { service: { name: 'my_service', environment: 'development' }, settings: { transaction_sample_rate: 0.5 }, - }); + }, + ]; + + before(async () => { + await Promise.all(configs.map(config => createConfiguration(config))); }); after(async () => { - await deleteCreatedConfigurations(); + await Promise.all(configs.map(config => deleteConfiguration(config))); }); const agentsRequests = [ @@ -204,17 +181,18 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte }); describe('when an agent retrieves a configuration', () => { + const config = { + service: { name: 'myservice', environment: 'development' }, + settings: { transaction_sample_rate: 0.9 }, + }; + before(async () => { log.debug('creating agent configuration'); - - await createConfiguration({ - service: { name: 'myservice', environment: 'development' }, - settings: { transaction_sample_rate: 0.9 }, - }); + await createConfiguration(config); }); after(async () => { - await deleteCreatedConfigurations(); + await deleteConfiguration(config); }); it(`should have 'applied_by_agent=false' on first request`, async () => { From 73798184cb89476c4a2a8068c97ae976c0192009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 24 Feb 2020 16:54:06 +0100 Subject: [PATCH 10/11] Add `throwOnError ` --- .../apis/apm/agent_configuration.ts | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts index c567601034870..8a962b7811ffc 100644 --- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts +++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts @@ -19,38 +19,26 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte .set('kbn-xsrf', 'foo'); } - async function createConfiguration(config: any) { + async function createConfiguration(config: AgentConfigurationIntake) { log.debug('creating configuration', config.service); const res = await supertest .put(`/api/apm/settings/agent-configuration`) .send(config) .set('kbn-xsrf', 'foo'); - if (res.statusCode !== 200) { - throw new Error( - `Could not create config ${JSON.stringify(config.service)}. Received statuscode ${ - res.statusCode - } and message: ${JSON.stringify(res.body)}` - ); - } + throwOnError(res); return res; } - async function updateConfiguration(config: any) { + async function updateConfiguration(config: AgentConfigurationIntake) { log.debug('updating configuration', config.service); const res = await supertest .put(`/api/apm/settings/agent-configuration?overwrite=true`) .send(config) .set('kbn-xsrf', 'foo'); - if (res.statusCode !== 200) { - throw new Error( - `Could not update config ${JSON.stringify(config.service)}. Received statuscode ${ - res.statusCode - } and message: ${JSON.stringify(res.body)}` - ); - } + throwOnError(res); return res; } @@ -62,17 +50,22 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte .send({ service }) .set('kbn-xsrf', 'foo'); - if (res.statusCode !== 200) { - throw new Error( - `Could not delete config ${JSON.stringify(service)}. Received statuscode ${ - res.statusCode - } and message: ${JSON.stringify(res.body)}` - ); - } + throwOnError(res); return res; } + function throwOnError(res: any) { + const { statusCode, req, body } = res; + if (statusCode !== 200) { + throw new Error(` + Endpoint: ${req.method} ${req.path} + Service: ${JSON.stringify(res.request._data.service)} + Status code: ${statusCode} + Response: ${body.message}`); + } + } + describe('agent configuration', () => { describe('when creating one configuration', () => { const newConfig = { From 3f46e63e31d2f774300c6ba9fce8d7de1add0cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 24 Feb 2020 17:05:56 +0100 Subject: [PATCH 11/11] Re-order specs --- .../apis/apm/agent_configuration.ts | 16 ++++++++++------ .../api_integration/apis/apm/feature_controls.ts | 15 +++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/x-pack/test/api_integration/apis/apm/agent_configuration.ts b/x-pack/test/api_integration/apis/apm/agent_configuration.ts index 8a962b7811ffc..959a0c97acfa3 100644 --- a/x-pack/test/api_integration/apis/apm/agent_configuration.ts +++ b/x-pack/test/api_integration/apis/apm/agent_configuration.ts @@ -116,15 +116,15 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte settings: { transaction_sample_rate: 0.2 }, }, { - service: { environment: 'production' }, + service: { name: 'my_service', environment: 'development' }, settings: { transaction_sample_rate: 0.3 }, }, { - service: { environment: 'development' }, + service: { environment: 'production' }, settings: { transaction_sample_rate: 0.4 }, }, { - service: { name: 'my_service', environment: 'development' }, + service: { environment: 'development' }, settings: { transaction_sample_rate: 0.5 }, }, ]; @@ -142,20 +142,24 @@ export default function agentConfigurationTests({ getService }: FtrProviderConte service: { name: 'non_existing_service', environment: 'non_existing_env' }, expectedSettings: { transaction_sample_rate: 0.1 }, }, + { + service: { name: 'my_service', environment: 'non_existing_env' }, + expectedSettings: { transaction_sample_rate: 0.2 }, + }, { service: { name: 'my_service', environment: 'production' }, expectedSettings: { transaction_sample_rate: 0.2 }, }, { - service: { name: 'non_existing_service', environment: 'production' }, + service: { name: 'my_service', environment: 'development' }, expectedSettings: { transaction_sample_rate: 0.3 }, }, { - service: { name: 'non_existing_service', environment: 'development' }, + service: { name: 'non_existing_service', environment: 'production' }, expectedSettings: { transaction_sample_rate: 0.4 }, }, { - service: { name: 'my_service', environment: 'development' }, + service: { name: 'non_existing_service', environment: 'development' }, expectedSettings: { transaction_sample_rate: 0.5 }, }, ]; diff --git a/x-pack/test/api_integration/apis/apm/feature_controls.ts b/x-pack/test/api_integration/apis/apm/feature_controls.ts index f96edf3086802..3c5314d0d3261 100644 --- a/x-pack/test/api_integration/apis/apm/feature_controls.ts +++ b/x-pack/test/api_integration/apis/apm/feature_controls.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -/* eslint-disable no-console */ - import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -15,6 +13,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const security = getService('security'); const spaces = getService('spaces'); const es = getService('legacyEs'); + const log = getService('log'); const start = encodeURIComponent(new Date(Date.now() - 10000).toISOString()); const end = encodeURIComponent(new Date().toISOString()); @@ -147,7 +146,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) index: '.apm-agent-configuration', }); - console.warn(JSON.stringify(res, null, 2)); + log.error(JSON.stringify(res, null, 2)); }, }, ]; @@ -215,9 +214,9 @@ export default function featureControlsTests({ getService }: FtrProviderContext) spaceId?: string; }) { for (const endpoint of endpoints) { - console.log(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`); + log.info(`Requesting: ${endpoint.req.url}. Expecting: ${expectation}`); const result = await executeAsUser(endpoint.req, username, password, spaceId); - console.log(`Responded: ${endpoint.req.url}`); + log.info(`Responded: ${endpoint.req.url}`); try { if (expectation === 'forbidden') { @@ -248,17 +247,17 @@ export default function featureControlsTests({ getService }: FtrProviderContext) settings: { transaction_sample_rate: 0.5 }, }; before(async () => { - console.log(`Creating agent configuration`); + log.info(`Creating agent configuration`); await executeAsAdmin({ method: 'put', url: '/api/apm/settings/agent-configuration', body: config, }); - console.log(`Agent configuration created`); + log.info(`Agent configuration created`); }); after(async () => { - console.log('deleting agent configuration'); + log.info('deleting agent configuration'); await executeAsAdmin({ method: 'delete', url: `/api/apm/settings/agent-configuration`,