From fe7abcb6148297ccd36c762c0f1cbc221ae7236c Mon Sep 17 00:00:00 2001 From: spalger Date: Thu, 7 Apr 2016 23:53:31 -0700 Subject: [PATCH 01/10] [cli/serve] accept mulitple --config flags --- src/cli/serve/__tests__/read_yaml_config.js | 7 ++ src/cli/serve/read_yaml_config.js | 71 +++++++++++---------- src/cli/serve/serve.js | 23 ++++--- 3 files changed, 59 insertions(+), 42 deletions(-) create mode 100644 src/cli/serve/__tests__/read_yaml_config.js diff --git a/src/cli/serve/__tests__/read_yaml_config.js b/src/cli/serve/__tests__/read_yaml_config.js new file mode 100644 index 0000000000000..7b17ede02e91e --- /dev/null +++ b/src/cli/serve/__tests__/read_yaml_config.js @@ -0,0 +1,7 @@ +describe('cli/serve/read_yaml_config', function () { + it('merges together multiple files'); + it('accepts a single config file'); + it('warns about deprecated settings'); + it('warns about legacy config value usage'); + it('only warns once about legacy and deprecated settings'); +}); diff --git a/src/cli/serve/read_yaml_config.js b/src/cli/serve/read_yaml_config.js index cb6f88a04a2ce..0ef2a1745f3f1 100644 --- a/src/cli/serve/read_yaml_config.js +++ b/src/cli/serve/read_yaml_config.js @@ -1,9 +1,14 @@ import _ from 'lodash'; -import fs from 'fs'; -import yaml from 'js-yaml'; +import ansicolors from 'ansicolors'; +import { readFileSync as read } from 'fs'; +import { safeLoad } from 'js-yaml'; import { fromRoot } from '../../utils'; +const warnAboutDeprecation = _.memoize(function (key, message) { + console.log(ansicolors.red('WARNING:'), message); +}); + let legacySettingMap = { // server port: 'server.port', @@ -38,38 +43,40 @@ const deprecatedSettings = { 'server.xsrf.token': 'server.xsrf.token is deprecated. It is no longer used when providing xsrf protection.' }; -module.exports = function (path) { - if (!path) return {}; - - let file = yaml.safeLoad(fs.readFileSync(path, 'utf8')); - - function apply(config, val, key) { - if (_.isPlainObject(val)) { - _.forOwn(val, function (subVal, subKey) { - apply(config, subVal, key + '.' + subKey); - }); - } - else if (_.isArray(val)) { - config[key] = []; - val.forEach((subVal, i) => { - apply(config, subVal, key + '.' + i); - }); - } - else { - _.set(config, key, val); - } - } +export default function (paths) { + if (!paths) return {}; - _.each(deprecatedSettings, function (message, setting) { - if (_.has(file, setting)) console.error(message); + const files = [].concat(paths).map(path => { + return safeLoad(read(path, 'utf8')); }); - // transform legeacy options into new namespaced versions - return _.transform(file, function (config, val, key) { - if (legacySettingMap.hasOwnProperty(key)) { - key = legacySettingMap[key]; - } + return _.transform(files, function applyConfigFile(config, file) { + _.forOwn(file, function apply(val, key) { + // transform legeacy options into new namespaced versions + if (legacySettingMap.hasOwnProperty(key)) { + const replacement = legacySettingMap[key]; + warnAboutDeprecation(key, `Config key "${key}" is deprecated. It has been replaced with "${replacement}"`); + key = replacement; + } - apply(config, val, key); + if (_.isPlainObject(val)) { + _.forOwn(val, function (subVal, subKey) { + apply(subVal, key + '.' + subKey); + }); + } + else if (_.isArray(val)) { + _.set(config, key, []); + val.forEach((subVal, i) => { + apply(subVal, key + '.' + i); + }); + } + else { + if (deprecatedSettings[key]) { + warnAboutDeprecation(key, deprecatedSettings[key]); + } + + _.set(config, key, val); + } + }); }, {}); -}; +} diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index b47a011db5b5d..5981133ed45ec 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -1,10 +1,12 @@ import _ from 'lodash'; -const { isWorker } = require('cluster'); -const { resolve } = require('path'); +import { isWorker } from 'cluster'; +import { resolve } from 'path'; -const cwd = process.cwd(); +import readYamlConfig from './read_yaml_config'; import { fromRoot } from '../../utils'; +const cwd = process.cwd(); + let canCluster; try { require.resolve('../cluster/cluster_manager'); @@ -21,22 +23,17 @@ const pathCollector = function () { }; }; +const configPathCollector = pathCollector(); const pluginDirCollector = pathCollector(); const pluginPathCollector = pathCollector(); function initServerSettings(opts, extraCliOptions) { - const readYamlConfig = require('./read_yaml_config'); const settings = readYamlConfig(opts.config); const set = _.partial(_.set, settings); const get = _.partial(_.get, settings); const has = _.partial(_.has, settings); const merge = _.partial(_.merge, settings); - if (opts.dev) { - try { merge(readYamlConfig(fromRoot('config/kibana.dev.yml'))); } - catch (e) { null; } - } - if (opts.dev) { set('env', 'development'); set('optimize.lazy', true); @@ -80,7 +77,9 @@ module.exports = function (program) { .option( '-c, --config ', 'Path to the config file, can be changed with the CONFIG_PATH environment variable as well', - process.env.CONFIG_PATH || fromRoot('config/kibana.yml')) + configPathCollector, + [ process.env.CONFIG_PATH || fromRoot('config/kibana.yml') ] + ) .option('-p, --port ', 'The port to bind to', parseInt) .option('-q, --quiet', 'Prevent all logging except errors') .option('-Q, --silent', 'Prevent all logging') @@ -116,6 +115,10 @@ module.exports = function (program) { command .action(async function (opts) { + if (opts.dev) { + opts.config.push(fromRoot('config/kibana.dev.yml')); + } + const settings = initServerSettings(opts, this.getUnknownOptions()); if (canCluster && opts.dev && !isWorker) { From 3410b09f33f6c22f4ce9e4dbbc4e840c92edbd59 Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 8 Apr 2016 01:01:42 -0700 Subject: [PATCH 02/10] [cli/serve] tests for readYamlConfig --- .../serve/__tests__/fixtures/deprecated.yml | 1 + src/cli/serve/__tests__/fixtures/legacy.yml | 1 + src/cli/serve/__tests__/fixtures/one.yml | 2 + src/cli/serve/__tests__/fixtures/two.yml | 2 + src/cli/serve/__tests__/read_yaml_config.js | 105 +++++++++++++++++- 5 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 src/cli/serve/__tests__/fixtures/deprecated.yml create mode 100644 src/cli/serve/__tests__/fixtures/legacy.yml create mode 100644 src/cli/serve/__tests__/fixtures/one.yml create mode 100644 src/cli/serve/__tests__/fixtures/two.yml diff --git a/src/cli/serve/__tests__/fixtures/deprecated.yml b/src/cli/serve/__tests__/fixtures/deprecated.yml new file mode 100644 index 0000000000000..748197e8957f6 --- /dev/null +++ b/src/cli/serve/__tests__/fixtures/deprecated.yml @@ -0,0 +1 @@ +server.xsrf.token: token diff --git a/src/cli/serve/__tests__/fixtures/legacy.yml b/src/cli/serve/__tests__/fixtures/legacy.yml new file mode 100644 index 0000000000000..080a80941646c --- /dev/null +++ b/src/cli/serve/__tests__/fixtures/legacy.yml @@ -0,0 +1 @@ +kibana_index: indexname diff --git a/src/cli/serve/__tests__/fixtures/one.yml b/src/cli/serve/__tests__/fixtures/one.yml new file mode 100644 index 0000000000000..e577d50638d5f --- /dev/null +++ b/src/cli/serve/__tests__/fixtures/one.yml @@ -0,0 +1,2 @@ +foo: 1 +bar: true diff --git a/src/cli/serve/__tests__/fixtures/two.yml b/src/cli/serve/__tests__/fixtures/two.yml new file mode 100644 index 0000000000000..aef807fcaebe9 --- /dev/null +++ b/src/cli/serve/__tests__/fixtures/two.yml @@ -0,0 +1,2 @@ +foo: 2 +baz: bonkers diff --git a/src/cli/serve/__tests__/read_yaml_config.js b/src/cli/serve/__tests__/read_yaml_config.js index 7b17ede02e91e..29b620b27dbb0 100644 --- a/src/cli/serve/__tests__/read_yaml_config.js +++ b/src/cli/serve/__tests__/read_yaml_config.js @@ -1,7 +1,102 @@ +import expect from 'expect.js'; +import { join, relative, resolve } from 'path'; +import readYamlConfig from '../read_yaml_config'; +import sinon from 'auto-release-sinon'; + +function fixture(name) { + return resolve(__dirname, 'fixtures', name); +} + describe('cli/serve/read_yaml_config', function () { - it('merges together multiple files'); - it('accepts a single config file'); - it('warns about deprecated settings'); - it('warns about legacy config value usage'); - it('only warns once about legacy and deprecated settings'); + it('reads a single config file', function () { + const config = readYamlConfig(fixture('one.yml')); + + expect(readYamlConfig(fixture('one.yml'))).to.eql({ + foo: 1, + bar: true, + }); + }); + + it('reads and merged mulitple config file', function () { + const config = readYamlConfig([ + fixture('one.yml'), + fixture('two.yml') + ]); + + expect(config).to.eql({ + foo: 2, + bar: true, + baz: 'bonkers' + }); + }); + + context('different cwd()', function () { + const oldCwd = process.cwd(); + const newCwd = join(oldCwd, '..'); + + before(function () { + process.chdir(newCwd); + }); + + it('resolves relative files based on the cwd', function () { + const relativePath = relative(newCwd, fixture('one.yml')); + const config = readYamlConfig(relativePath); + expect(config).to.eql({ + foo: 1, + bar: true, + }); + }); + + it('fails to load relative paths, not found because of the cwd', function () { + expect(function () { + readYamlConfig(relative(oldCwd, fixture('one.yml'))); + }).to.throwException(/ENOENT/); + }); + + after(function () { + process.chdir(oldCwd); + }); + }); + + context('stubbed stdout', function () { + let stub; + + beforeEach(function () { + stub = sinon.stub(process.stdout, 'write'); + }); + + context('deprecated settings', function () { + it('warns about deprecated settings', function () { + readYamlConfig(fixture('deprecated.yml')); + sinon.assert.calledOnce(stub); + expect(stub.firstCall.args[0]).to.match(/deprecated/); + stub.restore(); + }); + + it('only warns once about deprecated settings', function () { + readYamlConfig(fixture('deprecated.yml')); + readYamlConfig(fixture('deprecated.yml')); + readYamlConfig(fixture('deprecated.yml')); + sinon.assert.notCalled(stub); // already logged in previous test + stub.restore(); + }); + }); + + context('legacy settings', function () { + it('warns about deprecated settings', function () { + readYamlConfig(fixture('legacy.yml')); + sinon.assert.calledOnce(stub); + expect(stub.firstCall.args[0]).to.match(/has been replaced/); + stub.restore(); + }); + + it('only warns once about legacy settings', function () { + readYamlConfig(fixture('legacy.yml')); + readYamlConfig(fixture('legacy.yml')); + readYamlConfig(fixture('legacy.yml')); + sinon.assert.notCalled(stub); // already logged in previous test + stub.restore(); + }); + }); + }); }); From b7738f18b665cf47f0942634acc900dc2fa60611 Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 11 Apr 2016 12:41:20 -0700 Subject: [PATCH 03/10] [cli/server] split up file merging and deprecated settings logic --- src/cli/serve/deprecated_config.js | 62 ++++++++++++++++++++ src/cli/serve/read_yaml_config.js | 91 ++++++++---------------------- 2 files changed, 84 insertions(+), 69 deletions(-) create mode 100644 src/cli/serve/deprecated_config.js diff --git a/src/cli/serve/deprecated_config.js b/src/cli/serve/deprecated_config.js new file mode 100644 index 0000000000000..57c30afed31be --- /dev/null +++ b/src/cli/serve/deprecated_config.js @@ -0,0 +1,62 @@ +import { forOwn, has, transform } from 'lodash'; +import { red } from 'ansicolors'; +import { memoize } from 'lodash'; + +const logWarning = memoize(function (key, message) { + console.log(red('WARNING:'), message); +}); + +const legacySettings = { + // server + port: 'server.port', + host: 'server.host', + pid_file: 'pid.file', + ssl_cert_file: 'server.ssl.cert', + ssl_key_file: 'server.ssl.key', + + // logging + log_file: 'logging.dest', + + // kibana + kibana_index: 'kibana.index', + default_app_id: 'kibana.defaultAppId', + + // es + ca: 'elasticsearch.ssl.ca', + elasticsearch_preserve_host: 'elasticsearch.preserveHost', + elasticsearch_url: 'elasticsearch.url', + kibana_elasticsearch_client_crt: 'elasticsearch.ssl.cert', + kibana_elasticsearch_client_key: 'elasticsearch.ssl.key', + kibana_elasticsearch_password: 'elasticsearch.password', + kibana_elasticsearch_username: 'elasticsearch.username', + ping_timeout: 'elasticsearch.pingTimeout', + request_timeout: 'elasticsearch.requestTimeout', + shard_timeout: 'elasticsearch.shardTimeout', + startup_timeout: 'elasticsearch.startupTimeout', + verify_ssl: 'elasticsearch.ssl.verify', +}; + +const deprecatedSettings = { + 'server.xsrf.token': 'server.xsrf.token is deprecated. It is no longer used when providing xsrf protection.' +}; + +// transform legeacy options into new namespaced versions +export function rewriteDeprecatedConfig(object) { + const rewritten = transform(object, (clone, val, key) => { + if (legacySettings.hasOwnProperty(key)) { + const replacement = legacySettings[key]; + logWarning(key, `Config key "${key}" is deprecated. It has been replaced with "${replacement}"`); + clone[replacement] = val; + } else { + clone[key] = val; + } + }); + + forOwn(deprecatedSettings, (msg, key) => { + if (has(rewritten, key)) { + logWarning(key, msg); + } + }); + + return rewritten; +} diff --git a/src/cli/serve/read_yaml_config.js b/src/cli/serve/read_yaml_config.js index 0ef2a1745f3f1..6f6ee8c1854a3 100644 --- a/src/cli/serve/read_yaml_config.js +++ b/src/cli/serve/read_yaml_config.js @@ -1,82 +1,35 @@ -import _ from 'lodash'; -import ansicolors from 'ansicolors'; +import { chain, isArray, isPlainObject, forOwn, set, transform } from 'lodash'; import { readFileSync as read } from 'fs'; import { safeLoad } from 'js-yaml'; import { fromRoot } from '../../utils'; +import { rewriteDeprecatedConfig } from './deprecated_config'; -const warnAboutDeprecation = _.memoize(function (key, message) { - console.log(ansicolors.red('WARNING:'), message); -}); - -let legacySettingMap = { - // server - port: 'server.port', - host: 'server.host', - pid_file: 'pid.file', - ssl_cert_file: 'server.ssl.cert', - ssl_key_file: 'server.ssl.key', - - // logging - log_file: 'logging.dest', - - // kibana - kibana_index: 'kibana.index', - default_app_id: 'kibana.defaultAppId', - - // es - ca: 'elasticsearch.ssl.ca', - elasticsearch_preserve_host: 'elasticsearch.preserveHost', - elasticsearch_url: 'elasticsearch.url', - kibana_elasticsearch_client_crt: 'elasticsearch.ssl.cert', - kibana_elasticsearch_client_key: 'elasticsearch.ssl.key', - kibana_elasticsearch_password: 'elasticsearch.password', - kibana_elasticsearch_username: 'elasticsearch.username', - ping_timeout: 'elasticsearch.pingTimeout', - request_timeout: 'elasticsearch.requestTimeout', - shard_timeout: 'elasticsearch.shardTimeout', - startup_timeout: 'elasticsearch.startupTimeout', - verify_ssl: 'elasticsearch.ssl.verify', -}; - -const deprecatedSettings = { - 'server.xsrf.token': 'server.xsrf.token is deprecated. It is no longer used when providing xsrf protection.' -}; - -export default function (paths) { - if (!paths) return {}; - - const files = [].concat(paths).map(path => { - return safeLoad(read(path, 'utf8')); - }); - - return _.transform(files, function applyConfigFile(config, file) { - _.forOwn(file, function apply(val, key) { - // transform legeacy options into new namespaced versions - if (legacySettingMap.hasOwnProperty(key)) { - const replacement = legacySettingMap[key]; - warnAboutDeprecation(key, `Config key "${key}" is deprecated. It has been replaced with "${replacement}"`); - key = replacement; - } - - if (_.isPlainObject(val)) { - _.forOwn(val, function (subVal, subKey) { +export function merge(sources) { + return transform(sources, (merged, source) => { + forOwn(source, function apply(val, key) { + if (isPlainObject(val)) { + forOwn(val, function (subVal, subKey) { apply(subVal, key + '.' + subKey); }); + return; } - else if (_.isArray(val)) { - _.set(config, key, []); - val.forEach((subVal, i) => { - apply(subVal, key + '.' + i); - }); - } - else { - if (deprecatedSettings[key]) { - warnAboutDeprecation(key, deprecatedSettings[key]); - } - _.set(config, key, val); + if (isArray(val)) { + set(merged, key, []); + val.forEach((subVal, i) => apply(subVal, key + '.' + i)); + return; } + + set(merged, key, val); }); }, {}); } + +export default function (paths) { + const files = [].concat(paths || []) + .map(path => safeLoad(read(path, 'utf8'))) + .map(rewriteDeprecatedConfig); + + return merge(files); +} From 9a6379f65cda88e8741a87dc7db189adcbdf279c Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 11 Apr 2016 12:45:41 -0700 Subject: [PATCH 04/10] [cli/serve] add note to --config help text --- src/cli/serve/serve.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 5981133ed45ec..c7f1614434dda 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -76,7 +76,8 @@ module.exports = function (program) { .option('-e, --elasticsearch ', 'Elasticsearch instance') .option( '-c, --config ', - 'Path to the config file, can be changed with the CONFIG_PATH environment variable as well', + 'Path to the config file, can be changed with the CONFIG_PATH environment variable as well. ' + + 'Use mulitple --config flags to include multiple config files.', configPathCollector, [ process.env.CONFIG_PATH || fromRoot('config/kibana.yml') ] ) From b21793817521ddd40141c371efe9fa9515ba3f32 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 13 Apr 2016 10:21:49 -0700 Subject: [PATCH 05/10] [cli/serve] fix typo --- src/cli/serve/deprecated_config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/serve/deprecated_config.js b/src/cli/serve/deprecated_config.js index 57c30afed31be..16eb1a22791b9 100644 --- a/src/cli/serve/deprecated_config.js +++ b/src/cli/serve/deprecated_config.js @@ -40,7 +40,7 @@ const deprecatedSettings = { 'server.xsrf.token': 'server.xsrf.token is deprecated. It is no longer used when providing xsrf protection.' }; -// transform legeacy options into new namespaced versions +// transform legacy options into new namespaced versions export function rewriteDeprecatedConfig(object) { const rewritten = transform(object, (clone, val, key) => { if (legacySettings.hasOwnProperty(key)) { From 869fa10d3fff78967b18c3ead8af45002bc7d1b2 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 13 Apr 2016 10:30:23 -0700 Subject: [PATCH 06/10] [cli/server] flags do not have values --- src/cli/serve/serve.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index c7f1614434dda..eb1945f952acb 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -77,7 +77,7 @@ module.exports = function (program) { .option( '-c, --config ', 'Path to the config file, can be changed with the CONFIG_PATH environment variable as well. ' + - 'Use mulitple --config flags to include multiple config files.', + 'Use mulitple --config args to include multiple config files.', configPathCollector, [ process.env.CONFIG_PATH || fromRoot('config/kibana.yml') ] ) From 448848cf40815ba3b1781b6db4e0da6cd3d41e32 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 13 Apr 2016 12:03:08 -0700 Subject: [PATCH 07/10] [cli/serve] simplify testing by receiving the log function --- src/cli/serve/deprecated_config.js | 14 ++++---------- src/cli/serve/read_yaml_config.js | 9 +++++++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/cli/serve/deprecated_config.js b/src/cli/serve/deprecated_config.js index 16eb1a22791b9..76a60a7abf484 100644 --- a/src/cli/serve/deprecated_config.js +++ b/src/cli/serve/deprecated_config.js @@ -1,10 +1,4 @@ -import { forOwn, has, transform } from 'lodash'; -import { red } from 'ansicolors'; -import { memoize } from 'lodash'; - -const logWarning = memoize(function (key, message) { - console.log(red('WARNING:'), message); -}); +import { forOwn, has, noop, transform } from 'lodash'; const legacySettings = { // server @@ -41,16 +35,16 @@ const deprecatedSettings = { }; // transform legacy options into new namespaced versions -export function rewriteDeprecatedConfig(object) { +export function rewriteDeprecatedConfig(object, log = noop) { const rewritten = transform(object, (clone, val, key) => { if (legacySettings.hasOwnProperty(key)) { const replacement = legacySettings[key]; - logWarning(key, `Config key "${key}" is deprecated. It has been replaced with "${replacement}"`); + log(`Config key "${key}" is deprecated. It has been replaced with "${replacement}"`); clone[replacement] = val; } else { clone[key] = val; } - }); + }, {}); forOwn(deprecatedSettings, (msg, key) => { if (has(rewritten, key)) { diff --git a/src/cli/serve/read_yaml_config.js b/src/cli/serve/read_yaml_config.js index 6f6ee8c1854a3..e7733417162fb 100644 --- a/src/cli/serve/read_yaml_config.js +++ b/src/cli/serve/read_yaml_config.js @@ -1,10 +1,15 @@ -import { chain, isArray, isPlainObject, forOwn, set, transform } from 'lodash'; +import { chain, isArray, isPlainObject, forOwn, memoize, set, transform } from 'lodash'; import { readFileSync as read } from 'fs'; import { safeLoad } from 'js-yaml'; +import { red } from 'ansicolors'; import { fromRoot } from '../../utils'; import { rewriteDeprecatedConfig } from './deprecated_config'; +const log = memoize(function (message) { + console.log(red('WARNING:'), message); +}); + export function merge(sources) { return transform(sources, (merged, source) => { forOwn(source, function apply(val, key) { @@ -29,7 +34,7 @@ export function merge(sources) { export default function (paths) { const files = [].concat(paths || []) .map(path => safeLoad(read(path, 'utf8'))) - .map(rewriteDeprecatedConfig); + .map(file => rewriteDeprecatedConfig(file, log)); return merge(files); } From a02bbd5e40e29894b504dcd6caf43f1e866782d1 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 13 Apr 2016 12:04:07 -0700 Subject: [PATCH 08/10] [cli/serve] flatten keys before checking for deprecated ones --- src/cli/serve/deprecated_config.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/cli/serve/deprecated_config.js b/src/cli/serve/deprecated_config.js index 76a60a7abf484..b657ca93ac6ce 100644 --- a/src/cli/serve/deprecated_config.js +++ b/src/cli/serve/deprecated_config.js @@ -46,11 +46,20 @@ export function rewriteDeprecatedConfig(object, log = noop) { } }, {}); - forOwn(deprecatedSettings, (msg, key) => { - if (has(rewritten, key)) { - logWarning(key, msg); - } - }); + // walk through the object to find all nested keys, + // join them with simple string concatenation so that + // compound keys don't get considered a single path segment + (function recurse(obj, parent = []) { + forOwn(obj, (val, leaf) => { + const path = parent.concat(leaf); + const key = path.join('.'); + if (deprecatedSettings.hasOwnProperty(key)) { + log(deprecatedSettings[key]); + } else if (typeof val === 'object') { + recurse(val, path); + } + }); + }(rewritten)); return rewritten; } From 23e4902a367fe075d93462f016ecee6e91e1509e Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 13 Apr 2016 12:04:25 -0700 Subject: [PATCH 09/10] [cli/serve/deprecated_config] added tests --- src/cli/serve/__tests__/deprecated_config.js | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/cli/serve/__tests__/deprecated_config.js diff --git a/src/cli/serve/__tests__/deprecated_config.js b/src/cli/serve/__tests__/deprecated_config.js new file mode 100644 index 0000000000000..1e55e5e62da6b --- /dev/null +++ b/src/cli/serve/__tests__/deprecated_config.js @@ -0,0 +1,81 @@ +import expect from 'expect.js'; +import { rewriteDeprecatedConfig } from '../deprecated_config'; +import sinon from 'auto-release-sinon'; + +describe('cli/serve/deprecated_config', function () { + it('returns a clone of the input', function () { + const file = {}; + const output = rewriteDeprecatedConfig(file); + expect(output).to.not.be(file); + }); + + describe('legacy config values', function () { + it('rewrites legacy config values with literal path replacement', function () { + const file = { port: 4000, host: 'kibana.com' }; + const output = rewriteDeprecatedConfig(file); + expect(output).to.not.be(file); + expect(output).to.eql({ + 'server.port': 4000, + 'server.host': 'kibana.com', + }); + }); + + it('logs warnings when legacy config properties are encountered', function () { + const log = sinon.stub(); + rewriteDeprecatedConfig({ port: 5555 }, log); + sinon.assert.calledOnce(log); + expect(log.firstCall.args[0]).to.match(/port.+deprecated.+server\.port/); + }); + }); + + describe('deprecated config values', function () { + it('logs warnings about deprecated config values', function () { + const log = sinon.stub(); + rewriteDeprecatedConfig({ 'server.xsrf.token': 'xxtokenxx' }, log); + sinon.assert.calledOnce(log); + expect(log.firstCall.args[0]).to.match(/server\.xsrf\.token.+deprecated/); + }); + + it('passes deprecated values through', function () { + const prop = 'server.xsrf.token'; + const file = { [prop]: 'xxtokenxx' }; + const output = rewriteDeprecatedConfig(file); + expect(output).to.not.be(file); + expect(output).to.have.property(prop, file[prop]); + }); + + context('defined in a mixture of path keys and objects', function () { + it('detects nested propertie', function () { + const log = sinon.stub(); + rewriteDeprecatedConfig({ + server: { + xsrf: { + token: 'x' + } + } + }, log); + sinon.assert.calledOnce(log); + }); + + it('detects compound properties inside an object', function () { + const log = sinon.stub(); + rewriteDeprecatedConfig({ + server: { + 'xsrf.token': 'x' + } + }, log); + sinon.assert.calledOnce(log); + }); + + it('detects a property under a compound property', function () { + const log = sinon.stub(); + rewriteDeprecatedConfig({ + 'server.xsrf': { + token: 'x' + } + }, log); + sinon.assert.calledOnce(log); + }); + }); + }); +}); From 5a100b332d51a24c579d038fadffc2bb57459a5f Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 13 Apr 2016 14:27:42 -0700 Subject: [PATCH 10/10] [cli/serve] simplify deprecated config check by doing it after merge() --- src/cli/serve/__tests__/deprecated_config.js | 97 +++++++------------- src/cli/serve/__tests__/legacy_config.js | 28 ++++++ src/cli/serve/deprecated_config.js | 79 +++------------- src/cli/serve/legacy_config.js | 47 ++++++++++ src/cli/serve/read_yaml_config.js | 12 +-- 5 files changed, 128 insertions(+), 135 deletions(-) create mode 100644 src/cli/serve/__tests__/legacy_config.js create mode 100644 src/cli/serve/legacy_config.js diff --git a/src/cli/serve/__tests__/deprecated_config.js b/src/cli/serve/__tests__/deprecated_config.js index 1e55e5e62da6b..c132e814dcd12 100644 --- a/src/cli/serve/__tests__/deprecated_config.js +++ b/src/cli/serve/__tests__/deprecated_config.js @@ -1,81 +1,48 @@ import expect from 'expect.js'; -import { rewriteDeprecatedConfig } from '../deprecated_config'; +import { set } from 'lodash'; +import { checkForDeprecatedConfig } from '../deprecated_config'; import sinon from 'auto-release-sinon'; describe('cli/serve/deprecated_config', function () { - it('returns a clone of the input', function () { - const file = {}; - const output = rewriteDeprecatedConfig(file); - expect(output).to.not.be(file); + it('passes original config through', function () { + const config = {}; + set(config, 'server.xsrf.token', 'xxtokenxx'); + const output = checkForDeprecatedConfig(config); + expect(output).to.be(config); + expect(output.server).to.be(config.server); + expect(output.server.xsrf).to.be(config.server.xsrf); + expect(output.server.xsrf.token).to.be(config.server.xsrf.token); }); - describe('legacy config values', function () { - it('rewrites legacy config values with literal path replacement', function () { - const file = { port: 4000, host: 'kibana.com' }; - const output = rewriteDeprecatedConfig(file); - expect(output).to.not.be(file); - expect(output).to.eql({ - 'server.port': 4000, - 'server.host': 'kibana.com', - }); - }); - - it('logs warnings when legacy config properties are encountered', function () { - const log = sinon.stub(); - rewriteDeprecatedConfig({ port: 5555 }, log); - sinon.assert.calledOnce(log); - expect(log.firstCall.args[0]).to.match(/port.+deprecated.+server\.port/); - }); + it('logs warnings about deprecated config values', function () { + const log = sinon.stub(); + const config = {}; + set(config, 'server.xsrf.token', 'xxtokenxx'); + checkForDeprecatedConfig(config, log); + sinon.assert.calledOnce(log); + expect(log.firstCall.args[0]).to.match(/server\.xsrf\.token.+deprecated/); }); - describe('deprecated config values', function () { - it('logs warnings about deprecated config values', function () { + describe('does not support compound.keys', function () { + it('ignores fully compound keys', function () { const log = sinon.stub(); - rewriteDeprecatedConfig({ 'server.xsrf.token': 'xxtokenxx' }, log); - sinon.assert.calledOnce(log); - expect(log.firstCall.args[0]).to.match(/server\.xsrf\.token.+deprecated/); + const config = { 'server.xsrf.token': 'xxtokenxx' }; + checkForDeprecatedConfig(config, log); + sinon.assert.notCalled(log); }); - it('passes deprecated values through', function () { - const prop = 'server.xsrf.token'; - const file = { [prop]: 'xxtokenxx' }; - const output = rewriteDeprecatedConfig(file); - expect(output).to.not.be(file); - expect(output).to.have.property(prop, file[prop]); + it('ignores partially compound keys', function () { + const log = sinon.stub(); + const config = { server: { 'xsrf.token': 'xxtokenxx' } }; + checkForDeprecatedConfig(config, log); + sinon.assert.notCalled(log); }); - context('defined in a mixture of path keys and objects', function () { - it('detects nested propertie', function () { - const log = sinon.stub(); - rewriteDeprecatedConfig({ - server: { - xsrf: { - token: 'x' - } - } - }, log); - sinon.assert.calledOnce(log); - }); - - it('detects compound properties inside an object', function () { - const log = sinon.stub(); - rewriteDeprecatedConfig({ - server: { - 'xsrf.token': 'x' - } - }, log); - sinon.assert.calledOnce(log); - }); - - it('detects a property under a compound property', function () { - const log = sinon.stub(); - rewriteDeprecatedConfig({ - 'server.xsrf': { - token: 'x' - } - }, log); - sinon.assert.calledOnce(log); - }); + it('ignores partially compound keys', function () { + const log = sinon.stub(); + const config = { 'server.xsrf': { token: 'xxtokenxx' } }; + checkForDeprecatedConfig(config, log); + sinon.assert.notCalled(log); }); }); }); diff --git a/src/cli/serve/__tests__/legacy_config.js b/src/cli/serve/__tests__/legacy_config.js new file mode 100644 index 0000000000000..a380ae9e485c9 --- /dev/null +++ b/src/cli/serve/__tests__/legacy_config.js @@ -0,0 +1,28 @@ +import expect from 'expect.js'; +import { rewriteLegacyConfig } from '../legacy_config'; +import sinon from 'auto-release-sinon'; + +describe('cli/serve/legacy_config', function () { + it('returns a clone of the input', function () { + const file = {}; + const output = rewriteLegacyConfig(file); + expect(output).to.not.be(file); + }); + + it('rewrites legacy config values with literal path replacement', function () { + const file = { port: 4000, host: 'kibana.com' }; + const output = rewriteLegacyConfig(file); + expect(output).to.not.be(file); + expect(output).to.eql({ + 'server.port': 4000, + 'server.host': 'kibana.com', + }); + }); + + it('logs warnings when legacy config properties are encountered', function () { + const log = sinon.stub(); + rewriteLegacyConfig({ port: 5555 }, log); + sinon.assert.calledOnce(log); + expect(log.firstCall.args[0]).to.match(/port.+deprecated.+server\.port/); + }); +}); diff --git a/src/cli/serve/deprecated_config.js b/src/cli/serve/deprecated_config.js index b657ca93ac6ce..d0ec271a8cee8 100644 --- a/src/cli/serve/deprecated_config.js +++ b/src/cli/serve/deprecated_config.js @@ -1,65 +1,16 @@ -import { forOwn, has, noop, transform } from 'lodash'; - -const legacySettings = { - // server - port: 'server.port', - host: 'server.host', - pid_file: 'pid.file', - ssl_cert_file: 'server.ssl.cert', - ssl_key_file: 'server.ssl.key', - - // logging - log_file: 'logging.dest', - - // kibana - kibana_index: 'kibana.index', - default_app_id: 'kibana.defaultAppId', - - // es - ca: 'elasticsearch.ssl.ca', - elasticsearch_preserve_host: 'elasticsearch.preserveHost', - elasticsearch_url: 'elasticsearch.url', - kibana_elasticsearch_client_crt: 'elasticsearch.ssl.cert', - kibana_elasticsearch_client_key: 'elasticsearch.ssl.key', - kibana_elasticsearch_password: 'elasticsearch.password', - kibana_elasticsearch_username: 'elasticsearch.username', - ping_timeout: 'elasticsearch.pingTimeout', - request_timeout: 'elasticsearch.requestTimeout', - shard_timeout: 'elasticsearch.shardTimeout', - startup_timeout: 'elasticsearch.startupTimeout', - verify_ssl: 'elasticsearch.ssl.verify', -}; - -const deprecatedSettings = { - 'server.xsrf.token': 'server.xsrf.token is deprecated. It is no longer used when providing xsrf protection.' -}; - -// transform legacy options into new namespaced versions -export function rewriteDeprecatedConfig(object, log = noop) { - const rewritten = transform(object, (clone, val, key) => { - if (legacySettings.hasOwnProperty(key)) { - const replacement = legacySettings[key]; - log(`Config key "${key}" is deprecated. It has been replaced with "${replacement}"`); - clone[replacement] = val; - } else { - clone[key] = val; - } - }, {}); - - // walk through the object to find all nested keys, - // join them with simple string concatenation so that - // compound keys don't get considered a single path segment - (function recurse(obj, parent = []) { - forOwn(obj, (val, leaf) => { - const path = parent.concat(leaf); - const key = path.join('.'); - if (deprecatedSettings.hasOwnProperty(key)) { - log(deprecatedSettings[key]); - } else if (typeof val === 'object') { - recurse(val, path); - } - }); - }(rewritten)); - - return rewritten; +import { forOwn, has, noop } from 'lodash'; + +// deprecated settings are still allowed, but will be removed at a later time. They +// are checked for after the config object is prepared and known, so legacySettings +// will have already been transformed. +export const deprecatedSettings = new Map([ + [['server', 'xsrf', 'token'], 'server.xsrf.token is deprecated. It is no longer used when providing xsrf protection.'] +]); + +// check for and warn about deprecated settings +export function checkForDeprecatedConfig(object, log = noop) { + for (const [key, msg] of deprecatedSettings.entries()) { + if (has(object, key)) log(msg); + } + return object; } diff --git a/src/cli/serve/legacy_config.js b/src/cli/serve/legacy_config.js new file mode 100644 index 0000000000000..75fbb4e407eac --- /dev/null +++ b/src/cli/serve/legacy_config.js @@ -0,0 +1,47 @@ +import { noop, transform } from 'lodash'; + +// legacySettings allow kibana 4.2+ to accept the same config file that people +// used for kibana 4.0 and 4.1. These settings are transformed to their modern +// equivalents at the very begining of the process +export const legacySettings = { + // server + port: 'server.port', + host: 'server.host', + pid_file: 'pid.file', + ssl_cert_file: 'server.ssl.cert', + ssl_key_file: 'server.ssl.key', + + // logging + log_file: 'logging.dest', + + // kibana + kibana_index: 'kibana.index', + default_app_id: 'kibana.defaultAppId', + + // es + ca: 'elasticsearch.ssl.ca', + elasticsearch_preserve_host: 'elasticsearch.preserveHost', + elasticsearch_url: 'elasticsearch.url', + kibana_elasticsearch_client_crt: 'elasticsearch.ssl.cert', + kibana_elasticsearch_client_key: 'elasticsearch.ssl.key', + kibana_elasticsearch_password: 'elasticsearch.password', + kibana_elasticsearch_username: 'elasticsearch.username', + ping_timeout: 'elasticsearch.pingTimeout', + request_timeout: 'elasticsearch.requestTimeout', + shard_timeout: 'elasticsearch.shardTimeout', + startup_timeout: 'elasticsearch.startupTimeout', + verify_ssl: 'elasticsearch.ssl.verify', +}; + +// transform legacy options into new namespaced versions +export function rewriteLegacyConfig(object, log = noop) { + return transform(object, (clone, val, key) => { + if (legacySettings.hasOwnProperty(key)) { + const replacement = legacySettings[key]; + log(`Config key "${key}" is deprecated. It has been replaced with "${replacement}"`); + clone[replacement] = val; + } else { + clone[key] = val; + } + }, {}); +} diff --git a/src/cli/serve/read_yaml_config.js b/src/cli/serve/read_yaml_config.js index e7733417162fb..18e3a4520e875 100644 --- a/src/cli/serve/read_yaml_config.js +++ b/src/cli/serve/read_yaml_config.js @@ -4,7 +4,8 @@ import { safeLoad } from 'js-yaml'; import { red } from 'ansicolors'; import { fromRoot } from '../../utils'; -import { rewriteDeprecatedConfig } from './deprecated_config'; +import { rewriteLegacyConfig } from './legacy_config'; +import { checkForDeprecatedConfig } from './deprecated_config'; const log = memoize(function (message) { console.log(red('WARNING:'), message); @@ -32,9 +33,8 @@ export function merge(sources) { } export default function (paths) { - const files = [].concat(paths || []) - .map(path => safeLoad(read(path, 'utf8'))) - .map(file => rewriteDeprecatedConfig(file, log)); - - return merge(files); + const files = [].concat(paths || []); + const yamls = files.map(path => safeLoad(read(path, 'utf8'))); + const config = merge(yamls.map(file => rewriteLegacyConfig(file, log))); + return checkForDeprecatedConfig(config, log); }