Skip to content

Commit

Permalink
add prod/dev config handling + loader tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pgayvallet committed Sep 23, 2020
1 parent 3469894 commit ee3280c
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 21 deletions.
51 changes: 34 additions & 17 deletions packages/kbn-apm-config-loader/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,28 @@ import { execSync } from 'child_process';
// deep import to avoid loading the whole package
import { getDataPath } from '@kbn/utils/target/path';
import { readFileSync } from 'fs';
import { ApmAgentConfig } from './types';

const defaultConfig = {
active: false,
serverUrl: 'https://f1542b814f674090afd914960583265f.apm.us-central1.gcp.cloud.es.io:443',
// 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: 'R0Gjg46pE9K9wGestd',
globalLabels: {},
breakdownMetrics: true,
centralConfig: false,
logUncaughtExceptions: true,
const getDefaultConfig = (isDistributable: boolean): ApmAgentConfig => {
if (isDistributable) {
return {
active: false,
};
}
return {
active: false,
serverUrl: 'https://f1542b814f674090afd914960583265f.apm.us-central1.gcp.cloud.es.io:443',
// 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: 'R0Gjg46pE9K9wGestd',
globalLabels: {},
breakdownMetrics: true,
centralConfig: false,
logUncaughtExceptions: true,
};
};

export class ApmConfiguration {
Expand All @@ -45,14 +53,15 @@ export class ApmConfiguration {

constructor(
private readonly rootDir: string,
private readonly rawKibanaConfig: Record<string, any>
private readonly rawKibanaConfig: Record<string, any>,
private readonly isDistributable: boolean
) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { version } = require(join(this.rootDir, 'package.json'));
this.kibanaVersion = version.replace(/\./g, '_');
}

public getConfig(serviceName: string) {
public getConfig(serviceName: string): ApmAgentConfig {
return {
...this.getBaseConfig(),
serviceName: `${serviceName}-${this.kibanaVersion}`,
Expand All @@ -61,7 +70,11 @@ export class ApmConfiguration {

private getBaseConfig() {
if (!this.baseConfig) {
const apmConfig = merge(defaultConfig, this.getDevConfig());
const apmConfig = merge(
getDefaultConfig(this.isDistributable),
this.getConfigFromKibanaConfig(),
this.getDevConfig()
);

const rev = this.getGitRev();
if (rev !== null) {
Expand All @@ -78,6 +91,10 @@ export class ApmConfiguration {
return this.baseConfig;
}

private getConfigFromKibanaConfig(): ApmAgentConfig {
return get(this.rawKibanaConfig, 'elastic.apm', {});
}

private getKibanaUuid() {
// 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.
Expand All @@ -94,7 +111,7 @@ export class ApmConfiguration {
} catch (e) {} // eslint-disable-line no-empty
}

private getDevConfig() {
private getDevConfig(): ApmAgentConfig {
try {
const apmDevConfigPath = join(this.rootDir, 'config', 'apm.dev.js');
return require(apmDevConfigPath);
Expand Down
45 changes: 45 additions & 0 deletions packages/kbn-apm-config-loader/src/config_loader.test.mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export const getConfigurationFilePathsMock = jest.fn();
jest.doMock('./utils/get_config_file_paths', () => ({
getConfigurationFilePaths: getConfigurationFilePathsMock,
}));

export const getConfigFromFilesMock = jest.fn();
jest.doMock('./utils/read_config', () => ({
getConfigFromFiles: getConfigFromFilesMock,
}));

export const applyConfigOverridesMock = jest.fn();
jest.doMock('./utils/apply_config_overrides', () => ({
applyConfigOverrides: applyConfigOverridesMock,
}));

export const ApmConfigurationMock = jest.fn();
jest.doMock('./config', () => ({
ApmConfiguration: ApmConfigurationMock,
}));

export const resetAllMocks = () => {
getConfigurationFilePathsMock.mockReset();
getConfigFromFilesMock.mockReset();
applyConfigOverridesMock.mockReset();
ApmConfigurationMock.mockReset();
};
75 changes: 75 additions & 0 deletions packages/kbn-apm-config-loader/src/config_loader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import {
ApmConfigurationMock,
applyConfigOverridesMock,
getConfigFromFilesMock,
getConfigurationFilePathsMock,
resetAllMocks,
} from './config_loader.test.mocks';

import { loadConfiguration } from './config_loader';

describe('loadConfiguration', () => {
const argv = ['some', 'arbitrary', 'args'];
const rootDir = '/root/dir';
const isDistributable = false;

afterEach(() => {
resetAllMocks();
});

it('calls `getConfigurationFilePaths` with the correct arguments', () => {
loadConfiguration(argv, rootDir, isDistributable);
expect(getConfigurationFilePathsMock).toHaveBeenCalledTimes(1);
expect(getConfigurationFilePathsMock).toHaveBeenCalledWith(argv);
});

it('calls `getConfigFromFiles` with the correct arguments', () => {
const configPaths = ['/path/to/config', '/path/to/other/config'];
getConfigurationFilePathsMock.mockReturnValue(configPaths);

loadConfiguration(argv, rootDir, isDistributable);
expect(getConfigFromFilesMock).toHaveBeenCalledTimes(1);
expect(getConfigFromFilesMock).toHaveBeenCalledWith(configPaths);
});

it('calls `applyConfigOverrides` with the correct arguments', () => {
const config = { server: { uuid: 'uuid' } };
getConfigFromFilesMock.mockReturnValue(config);

loadConfiguration(argv, rootDir, isDistributable);
expect(applyConfigOverridesMock).toHaveBeenCalledTimes(1);
expect(applyConfigOverridesMock).toHaveBeenCalledWith(config, argv);
});

it('creates and return an `ApmConfiguration` instance', () => {
const apmInstance = { apmInstance: true };
ApmConfigurationMock.mockImplementation(() => apmInstance);

const config = { server: { uuid: 'uuid' } };
getConfigFromFilesMock.mockReturnValue(config);

const instance = loadConfiguration(argv, rootDir, isDistributable);
expect(ApmConfigurationMock).toHaveBeenCalledTimes(1);
expect(ApmConfigurationMock).toHaveBeenCalledWith(rootDir, config, isDistributable);
expect(instance).toBe(apmInstance);
});
});
10 changes: 7 additions & 3 deletions packages/kbn-apm-config-loader/src/config_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,22 @@
*/

import { getConfigurationFilePaths, getConfigFromFiles, applyConfigOverrides } from './utils';

import { ApmConfiguration } from './config';

/**
* Load the APM configuration.
*
* @param argv the `process.argv` arguments
* @param rootDir The root directory of kibana (where the sources and the `package.json` file are)
* @param production true for production builds, false otherwise
*/
export const loadConfiguration = (argv: string[], rootDir: string): ApmConfiguration => {
export const loadConfiguration = (
argv: string[],
rootDir: string,
isDistributable: boolean
): ApmConfiguration => {
const configPaths = getConfigurationFilePaths(argv);
const rawConfiguration = getConfigFromFiles(configPaths);
applyConfigOverrides(rawConfiguration, argv);
return new ApmConfiguration(rootDir, rawConfiguration);
return new ApmConfiguration(rootDir, rawConfiguration, isDistributable);
};
1 change: 1 addition & 0 deletions packages/kbn-apm-config-loader/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@

export { loadConfiguration } from './config_loader';
export type { ApmConfiguration } from './config';
export type { ApmAgentConfig } from './types';
24 changes: 24 additions & 0 deletions packages/kbn-apm-config-loader/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// There is an (incomplete) `AgentConfigOptions` type declared in node_modules/elastic-apm-node/index.d.ts
// but it's not exported, and using ts tricks to retrieve the type via Parameters<ApmAgent['start']>[0]
// causes errors in the generated .d.ts file because of esModuleInterop and the fact that the apm module
// is just exporting an instance of the `ApmAgent` type.
export type ApmAgentConfig = Record<string, any>;
2 changes: 1 addition & 1 deletion src/apm.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module.exports = function (serviceName = name) {
return;
}

apmConfig = loadConfiguration(process.argv, ROOT_DIR);
apmConfig = loadConfiguration(process.argv, ROOT_DIR, isKibanaDistributable);
const conf = apmConfig.getConfig(serviceName);
require('elastic-apm-node').start(conf);
};
Expand Down

0 comments on commit ee3280c

Please sign in to comment.