"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.CENTRALIZED_SERVICE_BASE_CONFIG = exports.ApmConfiguration = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _path = require("path"); var _lodash = require("lodash"); var _child_process = require("child_process"); var _utils = require("@kbn/utils"); var _fs = require("fs"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ // deep import to avoid loading the whole package // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html const DEFAULT_CONFIG = { active: true, contextPropagationOnly: true, environment: 'development', logUncaughtExceptions: true, globalLabels: {} }; const CENTRALIZED_SERVICE_BASE_CONFIG = { 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 // it makes it public. This is not a security/privacy issue. Normally we'd // instead disable the need for a secretToken in the APM Server config where // the data is transmitted to, but due to how it's being hosted, it's easier, // for now, to simply leave it in. secretToken: 'JpBCcOQxN81D5yucs2', breakdownMetrics: true, captureSpanStackTraces: false, centralConfig: false, metricsInterval: '30s', propagateTracestate: true, transactionSampleRate: 1.0 }; exports.CENTRALIZED_SERVICE_BASE_CONFIG = CENTRALIZED_SERVICE_BASE_CONFIG; const CENTRALIZED_SERVICE_DIST_CONFIG = { breakdownMetrics: false, captureBody: 'off', captureHeaders: false, metricsInterval: '120s', transactionSampleRate: 0.1 }; class ApmConfiguration { constructor(rootDir, rawKibanaConfig, isDistributable) { (0, _defineProperty2.default)(this, "baseConfig", void 0); (0, _defineProperty2.default)(this, "kibanaVersion", void 0); (0, _defineProperty2.default)(this, "pkgBuild", void 0); this.rootDir = rootDir; this.rawKibanaConfig = rawKibanaConfig; this.isDistributable = isDistributable; // eslint-disable-next-line @typescript-eslint/no-var-requires const { version, build } = require((0, _path.join)(this.rootDir, 'package.json')); this.kibanaVersion = version; this.pkgBuild = build; } getConfig(serviceName) { return { ...this.getBaseConfig(), serviceName }; } getBaseConfig() { if (!this.baseConfig) { var _this$baseConfig; const configFromSources = this.getConfigFromAllSources(); this.baseConfig = (0, _lodash.merge)({ serviceVersion: this.kibanaVersion }, DEFAULT_CONFIG, this.getUuidConfig(), this.getGitConfig(), this.getCiConfig(), configFromSources); /** * When the user doesn't override the serverUrl we define our central APM service * as the serverUrl along with a few other overrides to prevent potentially * sensitive data from being sent to this service. */ const centralizedConfig = this.isDistributable ? (0, _lodash.merge)({}, CENTRALIZED_SERVICE_BASE_CONFIG, CENTRALIZED_SERVICE_DIST_CONFIG) : CENTRALIZED_SERVICE_BASE_CONFIG; if (!((_this$baseConfig = this.baseConfig) !== null && _this$baseConfig !== void 0 && _this$baseConfig.serverUrl) || this.baseConfig.serverUrl === centralizedConfig.serverUrl) { this.baseConfig = (0, _lodash.merge)(this.baseConfig, centralizedConfig); } } return this.baseConfig; } /** * Override some config values when specific environment variables are used */ getConfigFromEnv(configFromKibanaConfig) { const config = {}; if (process.env.ELASTIC_APM_ACTIVE === 'true') { config.active = true; } if (process.env.ELASTIC_APM_CONTEXT_PROPAGATION_ONLY === 'true') { config.contextPropagationOnly = true; } else if (process.env.ELASTIC_APM_CONTEXT_PROPAGATION_ONLY === 'false') { config.contextPropagationOnly = false; } if (process.env.ELASTIC_APM_ENVIRONMENT) { config.environment = process.env.ELASTIC_APM_ENVIRONMENT; } else { // We check NODE_ENV in a different way so that, unlike // ELASTIC_APM_ENVIRONMENT, it does not override any explicit value set // in the config file. if (!configFromKibanaConfig.environment && process.env.NODE_ENV) { config.environment = process.env.NODE_ENV; } } if (process.env.ELASTIC_APM_TRANSACTION_SAMPLE_RATE) { config.transactionSampleRate = parseFloat(process.env.ELASTIC_APM_TRANSACTION_SAMPLE_RATE); } if (process.env.ELASTIC_APM_SERVER_URL) { 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 => { const [key, ...val] = p.split('='); return [key, val.join('=')]; })); } return config; } /** * Get the elastic.apm configuration from the --config file, supersedes the * default config. */ getConfigFromKibanaConfig() { var _this$rawKibanaConfig, _this$rawKibanaConfig2, _this$rawKibanaConfig3; return (_this$rawKibanaConfig = (_this$rawKibanaConfig2 = this.rawKibanaConfig) === null || _this$rawKibanaConfig2 === void 0 ? void 0 : (_this$rawKibanaConfig3 = _this$rawKibanaConfig2.elastic) === null || _this$rawKibanaConfig3 === void 0 ? void 0 : _this$rawKibanaConfig3.apm) !== null && _this$rawKibanaConfig !== void 0 ? _this$rawKibanaConfig : {}; } /** * Determine the Kibana UUID, initialized the value of `globalLabels.kibana_uuid` * when the UUID can be determined. */ getUuidConfig() { var _this$rawKibanaConfig4, _this$rawKibanaConfig5, _this$rawKibanaConfig6, _this$rawKibanaConfig7; // try to access the `server.uuid` value from the config file first. // if not manually defined, we will then read the value from the `{DATA_FOLDER}/uuid` file. // note that as the file is created by the platform AFTER apm init, the file // will not be present at first startup, but there is nothing we can really do about that. const uuidFromConfig = (_this$rawKibanaConfig4 = this.rawKibanaConfig) === null || _this$rawKibanaConfig4 === void 0 ? void 0 : (_this$rawKibanaConfig5 = _this$rawKibanaConfig4.server) === null || _this$rawKibanaConfig5 === void 0 ? void 0 : _this$rawKibanaConfig5.uuid; if (uuidFromConfig) { return { globalLabels: { kibana_uuid: uuidFromConfig } }; } const dataPath = ((_this$rawKibanaConfig6 = this.rawKibanaConfig) === null || _this$rawKibanaConfig6 === void 0 ? void 0 : (_this$rawKibanaConfig7 = _this$rawKibanaConfig6.path) === null || _this$rawKibanaConfig7 === void 0 ? void 0 : _this$rawKibanaConfig7.data) || (0, _utils.getDataPath)(); try { const filename = (0, _path.join)(dataPath, 'uuid'); const uuid = (0, _fs.readFileSync)(filename, 'utf-8'); if (!uuid) { return {}; } return { globalLabels: { kibana_uuid: uuid } }; } catch (e) { if (e.code === 'ENOENT') { return {}; } throw e; } } /** * When running Kibana with ELASTIC_APM_ENVIRONMENT=ci we attempt to grab * some environment variables we populate in CI related to the build under test */ getCiConfig() { if (process.env.ELASTIC_APM_ENVIRONMENT !== 'ci') { return {}; } const isPr = !!process.env.BUILDKITE_PULL_REQUEST && process.env.BUILDKITE_PULL_REQUEST !== 'false'; return { globalLabels: { branch: process.env.GIT_BRANCH || '', targetBranch: process.env.GITHUB_PR_TARGET_BRANCH || '', ciBuildNumber: process.env.BUILDKITE_BUILD_NUMBER || '', ciBuildId: process.env.BUILDKITE_BUILD_ID || '', ciBuildJobId: process.env.BUILDKITE_JOB_ID || '', isPr, prId: isPr ? process.env.BUILDKITE_PULL_REQUEST : '' } }; } /** * When running from the distributable pull the build sha from the package.json * file. Otherwise attempt to read the current HEAD sha using `git`. */ getGitConfig() { if (this.isDistributable) { return { globalLabels: { git_rev: this.pkgBuild.sha } }; } try { return { globalLabels: { git_rev: (0, _child_process.execSync)('git rev-parse --short HEAD', { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() } }; } catch { return {}; } } /** * Reads APM configuration from different sources and merges them together. */ getConfigFromAllSources() { const configFromKibanaConfig = this.getConfigFromKibanaConfig(); const configFromEnv = this.getConfigFromEnv(configFromKibanaConfig); const config = (0, _lodash.merge)({}, configFromKibanaConfig, configFromEnv); if (config.active === false && config.contextPropagationOnly !== false) { throw new Error('APM is disabled, but context propagation is enabled. Please disable context propagation with contextPropagationOnly:false'); } if (config.active === true) { var _config$contextPropag; config.contextPropagationOnly = (_config$contextPropag = config.contextPropagationOnly) !== null && _config$contextPropag !== void 0 ? _config$contextPropag : false; } return config; } } exports.ApmConfiguration = ApmConfiguration;