Skip to content

Commit

Permalink
Override config (#462)
Browse files Browse the repository at this point in the history
* allowing override config to extend / replace elements in base

* fixing eslint

* adding env variable support for configuration
  • Loading branch information
Magesh Chandramouli authored and absrivastava committed Jan 24, 2019
1 parent f44f43e commit 97a1a85
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 3 deletions.
15 changes: 12 additions & 3 deletions server/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,24 @@

const _ = require('lodash');
const baseConfiguration = require('../config/base');
const override = require('./override');

let finalConfiguration = _.merge({}, baseConfiguration);

// if an override configuration file is provided, extend the base config with
// the provided one. This is not a recursive merge, just a top level extend with
// the overriden config
if (process.env.HAYSTACK_OVERRIDES_CONFIG_PATH) {
let overridesConfigration = process.env.HAYSTACK_OVERRIDES_CONFIG_PATH;
if (!overridesConfigration.startsWith('/')) {
overridesConfigration = `${process.cwd()}/${overridesConfigration}`;
}
// eslint-disable-next-line global-require, import/no-dynamic-require
const environmentSpecificConfiguration = require(overridesConfigration);
module.exports = _.extend({}, baseConfiguration, environmentSpecificConfiguration);
} else {
module.exports = baseConfiguration;
finalConfiguration = _.extend({}, finalConfiguration, environmentSpecificConfiguration);
}

// if there are environment variables, read them as objects and merge them
// into the current configuration
const overrideObject = override.readOverrides(process.env);
module.exports = _.merge({}, finalConfiguration, overrideObject);
56 changes: 56 additions & 0 deletions server/config/override.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2018 Expedia Group
*
* Licensed 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.
*
*/

const _ = require('lodash');

const camelize = str => str.replace(/\W+(.)/g, (match, chr) => chr.toUpperCase());

// this code will tokenize incoming keys to produce json objects
// For example, a key like 'HAYSTACK_CONNECTORS_TRACES_ZIPKIN__URL'; with value 'foo'
// will turn into '{ connectors: { traces: { zipkinUrl: 'foo' } } }'
// Note: a single _ splits the token into words and a __ combines them
// to a camelCase
module.exports = {

readOverrides: (collection) => {
const overrideData = {};
_.each(collection, (value, key) => {
if (key.startsWith('HAYSTACK_')) {
const parts = key.toLowerCase().replace(/__/g, ' ').split('_');
parts.shift();

let configObject = overrideData;
let part = parts.shift();
// console.log(`${key} [${parts}] ${value}`);

while (part) {
part = camelize(part);
if (parts.length) {
if (configObject[part] == null) {
configObject[part] = {};
}
configObject = configObject[part];
} else {
configObject[part] = value;
}
part = parts.shift();
}
}
});
return overrideData;
}
};
69 changes: 69 additions & 0 deletions test/server/config/override.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2018 Expedia Group
*
* Licensed 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.
*
*/

const {expect} = require('chai');
const override = require('../../../server/config/override');

describe('override configuration reader', () => {
it('iterates over environment variables and returns an empty object if no matching variable is found', () => {
const envVariables = {
rvm_bin_path: '/Users/mchandramouli/.rvm/bin',
GEM_HOME: '/Users/mchandramouli/.rvm/gems/ruby-2.6.0',
NVM_CD_FLAGS: '',
TERM: 'xterm-256color'
};

const actual = override.readOverrides(envVariables);
expect(actual).to.deep.equal({});
});

it('iterates over environment variables and returns an object split by _', () => {
const envVariables = {
HAYSTACK_CONNECTORS_TRACES_ENABLED: 'true',
HAYSTACK_CONNECTORS_TRACES_PROVIDER: 'haystack',
TERM: 'xterm-256color'
};

const actual = override.readOverrides(envVariables);
expect(actual).to.deep.equal({
connectors: {
traces: {
enabled: 'true',
provider: 'haystack'
}
}
});
});

it('iterates over environment variables and returns an object split by _ and camelCase words split by __', () => {
const envVariables = {
HAYSTACK_CONNECTORS_TRENDS_CONNECTOR__NAME: 'haystack',
HAYSTACK_CONNECTORS_TRENDS_METRIC__TANK__URL: 'http://localhost:6000',
TERM: 'xterm-256color'
};

const actual = override.readOverrides(envVariables);
expect(actual).to.deep.equal({
connectors: {
trends: {
connectorName: 'haystack',
metricTankUrl: 'http://localhost:6000'
}
}
});
});
});

0 comments on commit 97a1a85

Please sign in to comment.