Skip to content

Commit 97a1a85

Browse files
Magesh Chandramouliabsrivastava
authored andcommitted
Override config (#462)
* allowing override config to extend / replace elements in base * fixing eslint * adding env variable support for configuration
1 parent f44f43e commit 97a1a85

3 files changed

Lines changed: 137 additions & 3 deletions

File tree

server/config/config.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,24 @@
1919

2020
const _ = require('lodash');
2121
const baseConfiguration = require('../config/base');
22+
const override = require('./override');
2223

24+
let finalConfiguration = _.merge({}, baseConfiguration);
25+
26+
// if an override configuration file is provided, extend the base config with
27+
// the provided one. This is not a recursive merge, just a top level extend with
28+
// the overriden config
2329
if (process.env.HAYSTACK_OVERRIDES_CONFIG_PATH) {
2430
let overridesConfigration = process.env.HAYSTACK_OVERRIDES_CONFIG_PATH;
2531
if (!overridesConfigration.startsWith('/')) {
2632
overridesConfigration = `${process.cwd()}/${overridesConfigration}`;
2733
}
2834
// eslint-disable-next-line global-require, import/no-dynamic-require
2935
const environmentSpecificConfiguration = require(overridesConfigration);
30-
module.exports = _.extend({}, baseConfiguration, environmentSpecificConfiguration);
31-
} else {
32-
module.exports = baseConfiguration;
36+
finalConfiguration = _.extend({}, finalConfiguration, environmentSpecificConfiguration);
3337
}
38+
39+
// if there are environment variables, read them as objects and merge them
40+
// into the current configuration
41+
const overrideObject = override.readOverrides(process.env);
42+
module.exports = _.merge({}, finalConfiguration, overrideObject);

server/config/override.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2018 Expedia Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the License);
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an AS IS BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
const _ = require('lodash');
19+
20+
const camelize = str => str.replace(/\W+(.)/g, (match, chr) => chr.toUpperCase());
21+
22+
// this code will tokenize incoming keys to produce json objects
23+
// For example, a key like 'HAYSTACK_CONNECTORS_TRACES_ZIPKIN__URL'; with value 'foo'
24+
// will turn into '{ connectors: { traces: { zipkinUrl: 'foo' } } }'
25+
// Note: a single _ splits the token into words and a __ combines them
26+
// to a camelCase
27+
module.exports = {
28+
29+
readOverrides: (collection) => {
30+
const overrideData = {};
31+
_.each(collection, (value, key) => {
32+
if (key.startsWith('HAYSTACK_')) {
33+
const parts = key.toLowerCase().replace(/__/g, ' ').split('_');
34+
parts.shift();
35+
36+
let configObject = overrideData;
37+
let part = parts.shift();
38+
// console.log(`${key} [${parts}] ${value}`);
39+
40+
while (part) {
41+
part = camelize(part);
42+
if (parts.length) {
43+
if (configObject[part] == null) {
44+
configObject[part] = {};
45+
}
46+
configObject = configObject[part];
47+
} else {
48+
configObject[part] = value;
49+
}
50+
part = parts.shift();
51+
}
52+
}
53+
});
54+
return overrideData;
55+
}
56+
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2018 Expedia Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the License);
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an AS IS BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
const {expect} = require('chai');
19+
const override = require('../../../server/config/override');
20+
21+
describe('override configuration reader', () => {
22+
it('iterates over environment variables and returns an empty object if no matching variable is found', () => {
23+
const envVariables = {
24+
rvm_bin_path: '/Users/mchandramouli/.rvm/bin',
25+
GEM_HOME: '/Users/mchandramouli/.rvm/gems/ruby-2.6.0',
26+
NVM_CD_FLAGS: '',
27+
TERM: 'xterm-256color'
28+
};
29+
30+
const actual = override.readOverrides(envVariables);
31+
expect(actual).to.deep.equal({});
32+
});
33+
34+
it('iterates over environment variables and returns an object split by _', () => {
35+
const envVariables = {
36+
HAYSTACK_CONNECTORS_TRACES_ENABLED: 'true',
37+
HAYSTACK_CONNECTORS_TRACES_PROVIDER: 'haystack',
38+
TERM: 'xterm-256color'
39+
};
40+
41+
const actual = override.readOverrides(envVariables);
42+
expect(actual).to.deep.equal({
43+
connectors: {
44+
traces: {
45+
enabled: 'true',
46+
provider: 'haystack'
47+
}
48+
}
49+
});
50+
});
51+
52+
it('iterates over environment variables and returns an object split by _ and camelCase words split by __', () => {
53+
const envVariables = {
54+
HAYSTACK_CONNECTORS_TRENDS_CONNECTOR__NAME: 'haystack',
55+
HAYSTACK_CONNECTORS_TRENDS_METRIC__TANK__URL: 'http://localhost:6000',
56+
TERM: 'xterm-256color'
57+
};
58+
59+
const actual = override.readOverrides(envVariables);
60+
expect(actual).to.deep.equal({
61+
connectors: {
62+
trends: {
63+
connectorName: 'haystack',
64+
metricTankUrl: 'http://localhost:6000'
65+
}
66+
}
67+
});
68+
});
69+
});

0 commit comments

Comments
 (0)