diff --git a/package-lock.json b/package-lock.json index e8751dcd..bb157b5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4328,7 +4328,7 @@ "dev": true }, "protobufjs": { - "version": "github:jawid-h/protobuf.js#8b91c72dca68fd6c418078fd2358c4969425dcdc", + "version": "github:jawid-h/protobuf.js#264b99b2ab6e097ff5350a57055d754d44d8e703", "from": "github:jawid-h/protobuf.js#fix/buffer-conversion", "requires": { "@protobufjs/aspromise": "^1.1.2", diff --git a/src/config/ConfigCollection.js b/src/config/ConfigCollection.js index f4a83993..270d34ac 100644 --- a/src/config/ConfigCollection.js +++ b/src/config/ConfigCollection.js @@ -8,7 +8,7 @@ class ConfigCollection { * @param {Config[]} [configs] * @param {string|null} [currentConfigName=null] */ - constructor(configs = [], currentConfigName = null) { + constructor(configs = [], currentConfigName = null, currentConfigFormatVersion) { this.configsMap = configs.reduce((configsMap, config) => { // eslint-disable-next-line no-param-reassign configsMap[config.getName()] = config; @@ -17,6 +17,7 @@ class ConfigCollection { }, {}); this.setDefaultConfigName(currentConfigName); + this.setConfigFormatVersion(currentConfigFormatVersion); } /** @@ -53,6 +54,27 @@ class ConfigCollection { return this.defaultConfigName; } + /** + * Set current config format version + * + * @param {string} version + * @returns {ConfigCollection} + */ + setConfigFormatVersion(version) { + this.configFormatVersion = version; + + return this; + } + + /** + * Get current config format version if set + * + * @returns {string|null} + */ + getConfigFormatVersion() { + return this.configFormatVersion; + } + /** * Get current config if set * diff --git a/src/config/configFile/ConfigJsonFileRepository.js b/src/config/configFile/ConfigJsonFileRepository.js index 3269e04d..254f21f5 100644 --- a/src/config/configFile/ConfigJsonFileRepository.js +++ b/src/config/configFile/ConfigJsonFileRepository.js @@ -10,12 +10,15 @@ const configFileJsonSchema = require('./configFileJsonSchema'); const ConfigFileNotFoundError = require('../errors/ConfigFileNotFoundError'); const InvalidConfigFileFormatError = require('../errors/InvalidConfigFileFormatError'); +const packageJson = require('../../../package.json'); + class ConfigJsonFileRepository { /** * @param configFilePath */ - constructor(configFilePath) { + constructor(configFilePath, migrateConfigOptions) { this.configFilePath = configFilePath; + this.migrateConfigOptions = migrateConfigOptions; this.ajv = new Ajv(); } @@ -49,12 +52,21 @@ class ConfigJsonFileRepository { let configs; try { configs = Object.entries(configFileData.configs) - .map(([name, options]) => new Config(name, options)); + .map(([name, options]) => { + const migratedOptions = this.migrateConfigOptions( + name, + options, + configFileData.configFormatVersion, + packageJson.version, + ); + + return new Config(name, migratedOptions); + }); } catch (e) { throw new InvalidConfigFileFormatError(this.configFilePath, e); } - return new ConfigCollection(configs, configFileData.defaultConfigName); + return new ConfigCollection(configs, configFileData.defaultConfigName, packageJson.version); } /** @@ -66,6 +78,7 @@ class ConfigJsonFileRepository { async write(configCollection) { const configFileData = { defaultConfigName: configCollection.getDefaultConfigName(), + configFormatVersion: configCollection.getConfigFormatVersion(), }; configFileData.configs = configCollection.getAllConfigs().reduce((configsMap, config) => { diff --git a/src/config/configFile/configFileJsonSchema.js b/src/config/configFile/configFileJsonSchema.js index 5eee16dc..e2903f85 100644 --- a/src/config/configFile/configFileJsonSchema.js +++ b/src/config/configFile/configFileJsonSchema.js @@ -2,6 +2,9 @@ module.exports = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { + configFormatVersion: { + type: 'string', + }, defaultConfigName: { type: ['string', 'null'], }, @@ -9,6 +12,6 @@ module.exports = { type: 'object', }, }, - required: ['defaultConfigName', 'configs'], + required: ['configFormatVersion', 'defaultConfigName', 'configs'], additionalProperties: false, }; diff --git a/src/config/configOptionMigrations.js b/src/config/configOptionMigrations.js new file mode 100644 index 00000000..9a21ebc2 --- /dev/null +++ b/src/config/configOptionMigrations.js @@ -0,0 +1,29 @@ +const lodashGet = require('lodash.get'); +const lodashSet = require('lodash.set'); + +const systemConfigs = require('./systemConfigs/systemConfigs'); + +module.exports = { + '0.17.0-dev.12': (name, options) => { + // Rename tendermint to tenderdash + lodashSet(options, 'platform.drive.tenderdash', options.platform.drive.tendermint); + // eslint-disable-next-line no-param-reassign + delete options.platform.drive.tendermint; + + // Copy new configs from system defaults + const sourceConfigName = name in systemConfigs ? name : 'base'; + const paths = [ + 'platform.dapi.nginx.rateLimiter.enable', + 'platform.dapi.nginx.rateLimiter.burst', + 'platform.dapi.nginx.rateLimiter.rate', + 'platform.drive.tenderdash.validatorKey', + 'platform.drive.tenderdash.nodeKey', + ]; + + paths.forEach((path) => { + lodashSet(options, path, lodashGet(systemConfigs[sourceConfigName], path)); + }); + + return options; + }, +}; diff --git a/src/config/migrateConfigOptions.js b/src/config/migrateConfigOptions.js new file mode 100644 index 00000000..480c3b80 --- /dev/null +++ b/src/config/migrateConfigOptions.js @@ -0,0 +1,19 @@ +const semver = require('semver'); + +const configOptionMigrations = require('./configOptionMigrations'); + +function migrateConfigOptions(name, options, fromVersion, toVersion) { + if (fromVersion === toVersion) { + return options; + } + + return Object.keys(configOptionMigrations) + .filter((version) => (semver.gt(version, fromVersion) && semver.lte(version, toVersion))) + .sort(semver.compare) + .reduce((migratedOptions, version) => { + const migrationFunction = configOptionMigrations[version]; + return migrationFunction(name, migratedOptions); + }, options); +} + +module.exports = migrateConfigOptions; diff --git a/src/config/systemConfigs/createSystemConfigsFactory.js b/src/config/systemConfigs/createSystemConfigsFactory.js index e9bffcff..860ef743 100644 --- a/src/config/systemConfigs/createSystemConfigsFactory.js +++ b/src/config/systemConfigs/createSystemConfigsFactory.js @@ -2,6 +2,8 @@ const Config = require('../Config'); const ConfigCollection = require('../ConfigCollection'); +const packageJson = require('../../../package.json'); + /** * @param {Object} systemConfigs * @return {createSystemConfigs} @@ -16,7 +18,7 @@ function createSystemConfigsFactory(systemConfigs) { new Config(name, options) )); - return new ConfigCollection(configs, 'base'); + return new ConfigCollection(configs, 'base', packageJson.version); } return createSystemConfigs; diff --git a/src/createDIContainer.js b/src/createDIContainer.js index 66a7379b..b71e3908 100644 --- a/src/createDIContainer.js +++ b/src/createDIContainer.js @@ -15,6 +15,7 @@ const ensureHomeDirFactory = require('./config/configFile/ensureHomeDirFactory') const ConfigJsonFileRepository = require('./config/configFile/ConfigJsonFileRepository'); const createSystemConfigsFactory = require('./config/systemConfigs/createSystemConfigsFactory'); const resetSystemConfigFactory = require('./config/systemConfigs/resetSystemConfigFactory'); +const migrateConfigOptions = require('./config/migrateConfigOptions'); const systemConfigs = require('./config/systemConfigs/systemConfigs'); const renderServiceTemplatesFactory = require('./templates/renderServiceTemplatesFactory'); @@ -67,6 +68,7 @@ async function createDIContainer(options) { systemConfigs: asValue(systemConfigs), createSystemConfigs: asFunction(createSystemConfigsFactory), resetSystemConfig: asFunction(resetSystemConfigFactory), + migrateConfigOptions: asValue(migrateConfigOptions), // `configCollection` and `config` are registering on command init });