From c97bfc8e3a539db6a3dc07d69f64b35dc419fdf2 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Tue, 22 Mar 2022 14:56:37 +0200 Subject: [PATCH] Debugging with apm - fixes and tutorial (#127892) * fixes + tutorial * cors config * omit secretToken so its not sent to FE Add config tests * lint * empty * swallow errors when parsing configs * read config test adjustment * apm docs review * new line * doc review Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- dev_docs/tutorials/apm_ui.png | Bin 0 -> 44173 bytes dev_docs/tutorials/debugging.mdx | 46 +++++++ .../src/config.test.mocks.ts | 8 -- .../kbn-apm-config-loader/src/config.test.ts | 124 +++++++----------- packages/kbn-apm-config-loader/src/config.ts | 30 +---- .../__snapshots__/read_config.test.ts.snap | 22 ++++ .../src/utils/get_config_file_paths.test.ts | 6 +- .../src/utils/get_config_file_paths.ts | 6 +- .../src/utils/read_config.test.ts | 11 +- .../src/utils/read_config.ts | 8 +- .../http_resources/get_apm_config.test.ts | 10 ++ .../server/http_resources/get_apm_config.ts | 12 +- 12 files changed, 169 insertions(+), 114 deletions(-) create mode 100644 dev_docs/tutorials/apm_ui.png diff --git a/dev_docs/tutorials/apm_ui.png b/dev_docs/tutorials/apm_ui.png new file mode 100644 index 0000000000000000000000000000000000000000..889fb6695e574e48b24fc67227bab813a5dd9f33 GIT binary patch literal 44173 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 z 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 + elastic.apm.secretToken: + ``` + 6. Once you run kibana and start using it, two new services (kibana, kibana-frontend) should appear under the APM UI on the APM deployment. + ![APM UI](./apm_ui.png) + +### Enabling APM via environment variables + +It is possible to enable APM via environment variables as well. +They take precedence over any values defined in `kibana.yml` or `kibana.dev.yml` + +Set the following environment variables to enable APM: + + * ELASTIC_APM_ACTIVE + * ELASTIC_APM_SERVER_URL + * ELASTIC_APM_SECRET_TOKEN diff --git a/packages/kbn-apm-config-loader/src/config.test.mocks.ts b/packages/kbn-apm-config-loader/src/config.test.mocks.ts index 4e148cbd325295..040d26deeba5df 100644 --- a/packages/kbn-apm-config-loader/src/config.test.mocks.ts +++ b/packages/kbn-apm-config-loader/src/config.test.mocks.ts @@ -17,13 +17,6 @@ export const packageMock = { }; jest.doMock(join(mockedRootDir, 'package.json'), () => packageMock.raw, { virtual: true }); -export const devConfigMock = { - raw: {} as any, -}; -jest.doMock(join(mockedRootDir, 'config', 'apm.dev.js'), () => devConfigMock.raw, { - virtual: true, -}); - export const gitRevExecMock = jest.fn(); jest.doMock('child_process', () => ({ ...childProcessModule, @@ -48,7 +41,6 @@ jest.doMock('fs', () => ({ export const resetAllMocks = () => { packageMock.raw = {}; - devConfigMock.raw = {}; gitRevExecMock.mockReset(); readUuidFileMock.mockReset(); jest.resetModules(); diff --git a/packages/kbn-apm-config-loader/src/config.test.ts b/packages/kbn-apm-config-loader/src/config.test.ts index 265362fa8f1669..5bc723a27590b1 100644 --- a/packages/kbn-apm-config-loader/src/config.test.ts +++ b/packages/kbn-apm-config-loader/src/config.test.ts @@ -5,23 +5,21 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { Labels } from 'elastic-apm-node'; +import type { AgentConfigOptions, Labels } from 'elastic-apm-node'; import { packageMock, mockedRootDir, gitRevExecMock, - devConfigMock, readUuidFileMock, resetAllMocks, } from './config.test.mocks'; -import { ApmConfiguration } from './config'; +import { ApmConfiguration, CENTRALIZED_SERVICE_BASE_CONFIG } from './config'; describe('ApmConfiguration', () => { beforeEach(() => { // start with an empty env to avoid CI from spoiling snapshots, env is unique for each jest file process.env = {}; - devConfigMock.raw = {}; packageMock.raw = { version: '8.0.0', build: { @@ -150,82 +148,58 @@ describe('ApmConfiguration', () => { ); }); - it('loads the configuration from the dev config is present', () => { - devConfigMock.raw = { - active: true, - serverUrl: 'https://dev-url.co', - }; - const config = new ApmConfiguration(mockedRootDir, {}, false); - expect(config.getConfig('serviceName')).toEqual( - expect.objectContaining({ - active: true, - serverUrl: 'https://dev-url.co', - }) - ); - }); - - it('does not load the configuration from the dev config in distributable', () => { - devConfigMock.raw = { - active: false, - }; - const config = new ApmConfiguration(mockedRootDir, {}, true); - expect(config.getConfig('serviceName')).toEqual( - expect.objectContaining({ - active: true, - }) - ); - }); + describe('env vars', () => { + beforeEach(() => { + delete process.env.ELASTIC_APM_ENVIRONMENT; + delete process.env.ELASTIC_APM_SECRET_TOKEN; + delete process.env.ELASTIC_APM_SERVER_URL; + delete process.env.NODE_ENV; + }); - it('overwrites the standard config file with the dev config', () => { - const kibanaConfig = { - elastic: { - apm: { - active: true, - serverUrl: 'https://url', - secretToken: 'secret', - }, - }, - }; - devConfigMock.raw = { - active: true, - serverUrl: 'https://dev-url.co', - }; - const config = new ApmConfiguration(mockedRootDir, kibanaConfig, false); - expect(config.getConfig('serviceName')).toEqual( - expect.objectContaining({ - active: true, - serverUrl: 'https://dev-url.co', - secretToken: 'secret', - }) - ); - }); + it('correctly sets environment by reading env vars', () => { + let config = new ApmConfiguration(mockedRootDir, {}, false); + expect(config.getConfig('serviceName')).toEqual( + expect.objectContaining({ + environment: 'development', + }) + ); - it('correctly sets environment by reading env vars', () => { - delete process.env.ELASTIC_APM_ENVIRONMENT; - delete process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + config = new ApmConfiguration(mockedRootDir, {}, false); + expect(config.getConfig('serviceName')).toEqual( + expect.objectContaining({ + environment: 'production', + }) + ); - let config = new ApmConfiguration(mockedRootDir, {}, false); - expect(config.getConfig('serviceName')).toEqual( - expect.objectContaining({ - environment: 'development', - }) - ); + process.env.ELASTIC_APM_ENVIRONMENT = 'ci'; + config = new ApmConfiguration(mockedRootDir, {}, false); + expect(config.getConfig('serviceName')).toEqual( + expect.objectContaining({ + environment: 'ci', + }) + ); + }); - process.env.NODE_ENV = 'production'; - config = new ApmConfiguration(mockedRootDir, {}, false); - expect(config.getConfig('serviceName')).toEqual( - expect.objectContaining({ - environment: 'production', - }) - ); + it('uses default config if serverUrl is not set', () => { + process.env.ELASTIC_APM_SECRET_TOKEN = 'banana'; + const config = new ApmConfiguration(mockedRootDir, {}, false); + const serverConfig = config.getConfig('serviceName'); + expect(serverConfig).toHaveProperty( + 'secretToken', + (CENTRALIZED_SERVICE_BASE_CONFIG as AgentConfigOptions).secretToken + ); + expect(serverConfig).toHaveProperty('serverUrl', CENTRALIZED_SERVICE_BASE_CONFIG.serverUrl); + }); - process.env.ELASTIC_APM_ENVIRONMENT = 'ci'; - config = new ApmConfiguration(mockedRootDir, {}, false); - expect(config.getConfig('serviceName')).toEqual( - expect.objectContaining({ - environment: 'ci', - }) - ); + it('uses env vars config if serverUrl is set', () => { + process.env.ELASTIC_APM_SECRET_TOKEN = 'banana'; + process.env.ELASTIC_APM_SERVER_URL = 'http://banana.com/'; + const config = new ApmConfiguration(mockedRootDir, {}, false); + const serverConfig = config.getConfig('serviceName'); + expect(serverConfig).toHaveProperty('secretToken', process.env.ELASTIC_APM_SECRET_TOKEN); + expect(serverConfig).toHaveProperty('serverUrl', process.env.ELASTIC_APM_SERVER_URL); + }); }); describe('contextPropagationOnly', () => { diff --git a/packages/kbn-apm-config-loader/src/config.ts b/packages/kbn-apm-config-loader/src/config.ts index 303ec931eeda0e..1094b966201450 100644 --- a/packages/kbn-apm-config-loader/src/config.ts +++ b/packages/kbn-apm-config-loader/src/config.ts @@ -24,7 +24,7 @@ const DEFAULT_CONFIG: AgentConfigOptions = { globalLabels: {}, }; -const CENTRALIZED_SERVICE_BASE_CONFIG: AgentConfigOptions | RUMAgentConfigOptions = { +export const CENTRALIZED_SERVICE_BASE_CONFIG: AgentConfigOptions | RUMAgentConfigOptions = { serverUrl: 'https://kibana-cloud-apm.apm.us-east-1.aws.found.io', // The secretToken below is intended to be hardcoded in this file even though @@ -136,6 +136,10 @@ export class ApmConfiguration { config.serverUrl = process.env.ELASTIC_APM_SERVER_URL; } + if (process.env.ELASTIC_APM_SECRET_TOKEN) { + config.secretToken = process.env.ELASTIC_APM_SECRET_TOKEN; + } + if (process.env.ELASTIC_APM_GLOBAL_LABELS) { config.globalLabels = Object.fromEntries( process.env.ELASTIC_APM_GLOBAL_LABELS.split(',').map((p) => { @@ -156,23 +160,6 @@ export class ApmConfiguration { return this.rawKibanaConfig?.elastic?.apm ?? {}; } - /** - * Get the configuration from the apm.dev.js file, supersedes config - * from the --config file, disabled when running the distributable - */ - private getDevConfig(): AgentConfigOptions { - if (this.isDistributable) { - return {}; - } - - try { - const apmDevConfigPath = join(this.rootDir, 'config', 'apm.dev.js'); - return require(apmDevConfigPath); - } catch (e) { - return {}; - } - } - /** * Determine the Kibana UUID, initialized the value of `globalLabels.kibana_uuid` * when the UUID can be determined. @@ -266,12 +253,7 @@ export class ApmConfiguration { * Reads APM configuration from different sources and merges them together. */ private getConfigFromAllSources(): AgentConfigOptions { - const config = merge( - {}, - this.getConfigFromKibanaConfig(), - this.getDevConfig(), - this.getConfigFromEnv() - ); + const config = merge({}, this.getConfigFromKibanaConfig(), this.getConfigFromEnv()); if (config.active === false && config.contextPropagationOnly !== false) { throw new Error( diff --git a/packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap b/packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap index afdce4e76d3f51..ee7b6751e2c6cb 100644 --- a/packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap +++ b/packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap @@ -74,6 +74,28 @@ Object { } `; +exports[`reads yaml files from file system and parses to json, even if one is missing 1`] = ` +Object { + "abc": Object { + "def": "test", + "qwe": 1, + "zyx": Object { + "val": 1, + }, + }, + "bar": true, + "empty_arr": Array [], + "foo": 1, + "pom": Object { + "bom": 3, + }, + "xyz": Array [ + "1", + "2", + ], +} +`; + exports[`returns a deep object 1`] = ` Object { "pid": Object { diff --git a/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts index 3b361a1c6958e5..41ba8b07b66b49 100644 --- a/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts +++ b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts @@ -23,6 +23,10 @@ describe('getConfigurationFilePaths', () => { }); it('fallbacks to `getConfigPath` value', () => { - expect(getConfigurationFilePaths([])).toEqual([getConfigPath()]); + const path = getConfigPath(); + expect(getConfigurationFilePaths([])).toEqual([ + path, + path.replace('kibana.yml', 'kibana.dev.yml'), + ]); }); }); diff --git a/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts index dc5a55935a90d4..d2cb7fb75258cf 100644 --- a/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts +++ b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts @@ -22,5 +22,9 @@ export const getConfigurationFilePaths = (argv: string[]): string[] => { if (rawPaths.length) { return rawPaths.map((path) => resolve(process.cwd(), path)); } - return [getConfigPath()]; + + const configPath = getConfigPath(); + + // Pick up settings from dev.yml as well + return [configPath, configPath.replace('kibana.yml', 'kibana.dev.yml')]; }; diff --git a/packages/kbn-apm-config-loader/src/utils/read_config.test.ts b/packages/kbn-apm-config-loader/src/utils/read_config.test.ts index 2838738c0ab6c2..157cebf122faf3 100644 --- a/packages/kbn-apm-config-loader/src/utils/read_config.test.ts +++ b/packages/kbn-apm-config-loader/src/utils/read_config.test.ts @@ -29,6 +29,12 @@ test('reads and merges multiple yaml files from file system and parses to json', expect(config).toMatchSnapshot(); }); +test('reads yaml files from file system and parses to json, even if one is missing', () => { + const config = getConfigFromFiles([fixtureFile('one.yml'), fixtureFile('boo.yml')]); + + expect(config).toMatchSnapshot(); +}); + test('should inject an environment variable value when setting a value with ${ENV_VAR}', () => { process.env.KBN_ENV_VAR1 = 'val1'; process.env.KBN_ENV_VAR2 = 'val2'; @@ -61,8 +67,9 @@ describe('different cwd()', () => { expect(config).toMatchSnapshot(); }); - test('fails to load relative paths, not found because of the cwd', () => { + test('ignores errors loading relative paths', () => { const relativePath = relative(resolve(__dirname, '..', '..'), fixtureFile('one.yml')); - expect(() => getConfigFromFiles([relativePath])).toThrowError(/ENOENT/); + const config = getConfigFromFiles([relativePath]); + expect(config).toStrictEqual({}); }); }); diff --git a/packages/kbn-apm-config-loader/src/utils/read_config.ts b/packages/kbn-apm-config-loader/src/utils/read_config.ts index 4371c7983515c9..0d6fce88b0532e 100644 --- a/packages/kbn-apm-config-loader/src/utils/read_config.ts +++ b/packages/kbn-apm-config-loader/src/utils/read_config.ts @@ -13,7 +13,13 @@ import { set } from '@elastic/safer-lodash-set'; import { isPlainObject } from 'lodash'; import { ensureDeepObject } from './ensure_deep_object'; -const readYaml = (path: string) => safeLoad(readFileSync(path, 'utf8')); +const readYaml = (path: string) => { + try { + return safeLoad(readFileSync(path, 'utf8')); + } catch (e) { + /* tslint:disable:no-empty */ + } +}; function replaceEnvVarRefs(val: string) { return val.replace(/\$\{(\w+)\}/g, (match, envVarName) => { diff --git a/src/core/server/http_resources/get_apm_config.test.ts b/src/core/server/http_resources/get_apm_config.test.ts index 9552a91da97b19..5e4e7b9b9525c1 100644 --- a/src/core/server/http_resources/get_apm_config.test.ts +++ b/src/core/server/http_resources/get_apm_config.test.ts @@ -56,6 +56,16 @@ describe('getApmConfig', () => { ); }); + it('omits secret token', () => { + getConfigurationMock.mockReturnValue({ + ...defaultApmConfig, + secretToken: 'smurfs', + }); + const config = getApmConfig('/some-other-path'); + + expect(config).not.toHaveProperty('secretToken'); + }); + it('enhance the configuration with values from the current server-side transaction', () => { agentMock.currentTransaction = { sampled: 'sampled', diff --git a/src/core/server/http_resources/get_apm_config.ts b/src/core/server/http_resources/get_apm_config.ts index 3e7be65f966527..881efe261d79fa 100644 --- a/src/core/server/http_resources/get_apm_config.ts +++ b/src/core/server/http_resources/get_apm_config.ts @@ -6,11 +6,19 @@ * Side Public License, v 1. */ -import agent from 'elastic-apm-node'; +import agent, { AgentConfigOptions } from 'elastic-apm-node'; import { getConfiguration, shouldInstrumentClient } from '@kbn/apm-config-loader'; +const OMIT_APM_CONFIG: Array = ['secretToken']; + export const getApmConfig = (requestPath: string) => { - const baseConfig = getConfiguration('kibana-frontend'); + const baseConfig = getConfiguration('kibana-frontend') || {}; + + // Omit configs not used by RUM agent. + OMIT_APM_CONFIG.forEach((config) => { + delete baseConfig[config]; + }); + if (!shouldInstrumentClient(baseConfig)) { return null; }