Compare commits
3 Commits
developmen
...
user/jonat
Author | SHA1 | Date |
---|---|---|
Jonathan Gramain | eae3f0b2fa | |
Jonathan Gramain | f989d5a1d1 | |
Jonathan Gramain | 80c62db053 |
186
lib/Config.js
186
lib/Config.js
|
@ -88,6 +88,47 @@ function parseSproxydConfig(configSproxyd) {
|
|||
return joi.attempt(configSproxyd, joiSchema, 'bad config');
|
||||
}
|
||||
|
||||
function parseRedisConfig(redisConfig) {
|
||||
const joiSchema = joi.object({
|
||||
password: joi.string().allow(''),
|
||||
host: joi.string(),
|
||||
port: joi.number(),
|
||||
retry: joi.object({
|
||||
connectBackoff: joi.object({
|
||||
min: joi.number().required(),
|
||||
max: joi.number().required(),
|
||||
jitter: joi.number().required(),
|
||||
factor: joi.number().required(),
|
||||
deadline: joi.number().required(),
|
||||
}),
|
||||
}),
|
||||
// sentinel config
|
||||
sentinels: joi.alternatives().try(
|
||||
joi.string()
|
||||
.pattern(/^[a-zA-Z0-9.-]+:[0-9]+(,[a-zA-Z0-9.-]+:[0-9]+)*$/)
|
||||
.custom(hosts => hosts.split(',').map(item => {
|
||||
const [host, port] = item.split(':');
|
||||
return { host, port: Number.parseInt(port, 10) };
|
||||
})),
|
||||
joi.array().items(
|
||||
joi.object({
|
||||
host: joi.string().required(),
|
||||
port: joi.number().required(),
|
||||
})
|
||||
).min(1),
|
||||
),
|
||||
name: joi.string(),
|
||||
sentinelPassword: joi.string().allow(''),
|
||||
})
|
||||
.and('host', 'port')
|
||||
.and('sentinels', 'name')
|
||||
.xor('host', 'sentinels')
|
||||
.without('sentinels', ['host', 'port'])
|
||||
.without('host', ['sentinels', 'sentinelPassword']);
|
||||
|
||||
return joi.attempt(redisConfig, joiSchema, 'bad config');
|
||||
}
|
||||
|
||||
function restEndpointsAssert(restEndpoints, locationConstraints) {
|
||||
assert(typeof restEndpoints === 'object',
|
||||
'bad config: restEndpoints must be an object of endpoints');
|
||||
|
@ -294,18 +335,17 @@ function parseUtapiReindex(config) {
|
|||
const {
|
||||
enabled,
|
||||
schedule,
|
||||
sentinel,
|
||||
redis,
|
||||
bucketd,
|
||||
onlyCountLatestWhenObjectLocked,
|
||||
} = config;
|
||||
assert(typeof enabled === 'boolean',
|
||||
'bad config: utapi.reindex.enabled must be a boolean');
|
||||
assert(typeof sentinel === 'object',
|
||||
'bad config: utapi.reindex.sentinel must be an object');
|
||||
assert(typeof sentinel.port === 'number',
|
||||
'bad config: utapi.reindex.sentinel.port must be a number');
|
||||
assert(typeof sentinel.name === 'string',
|
||||
'bad config: utapi.reindex.sentinel.name must be a string');
|
||||
'bad config: utapi.reindex.enabled must be a boolean');
|
||||
|
||||
const parsedRedis = parseRedisConfig(redis);
|
||||
assert(Array.isArray(parsedRedis.sentinels),
|
||||
'bad config: utapi reindex redis config requires a list of sentinels');
|
||||
|
||||
assert(typeof bucketd === 'object',
|
||||
'bad config: utapi.reindex.bucketd must be an object');
|
||||
assert(typeof bucketd.port === 'number',
|
||||
|
@ -323,6 +363,13 @@ function parseUtapiReindex(config) {
|
|||
'bad config: utapi.reindex.schedule must be a valid ' +
|
||||
`cron schedule. ${e.message}.`);
|
||||
}
|
||||
return {
|
||||
enabled,
|
||||
schedule,
|
||||
redis: parsedRedis,
|
||||
bucketd,
|
||||
onlyCountLatestWhenObjectLocked,
|
||||
};
|
||||
}
|
||||
|
||||
function requestsConfigAssert(requestsConfig) {
|
||||
|
@ -761,8 +808,7 @@ class Config extends EventEmitter {
|
|||
assert(typeof config.localCache.port === 'number',
|
||||
'config: invalid port for localCache. port must be a number');
|
||||
if (config.localCache.password !== undefined) {
|
||||
assert(
|
||||
this._verifyRedisPassword(config.localCache.password),
|
||||
assert(typeof config.localCache.password === 'string',
|
||||
'config: invalid password for localCache. password must' +
|
||||
' be a string');
|
||||
}
|
||||
|
@ -774,55 +820,7 @@ class Config extends EventEmitter {
|
|||
}
|
||||
|
||||
if (config.redis) {
|
||||
if (config.redis.sentinels) {
|
||||
this.redis = { sentinels: [], name: null };
|
||||
|
||||
assert(typeof config.redis.name === 'string',
|
||||
'bad config: redis sentinel name must be a string');
|
||||
this.redis.name = config.redis.name;
|
||||
assert(Array.isArray(config.redis.sentinels) ||
|
||||
typeof config.redis.sentinels === 'string',
|
||||
'bad config: redis sentinels must be an array or string');
|
||||
|
||||
if (typeof config.redis.sentinels === 'string') {
|
||||
config.redis.sentinels.split(',').forEach(item => {
|
||||
const [host, port] = item.split(':');
|
||||
this.redis.sentinels.push({ host,
|
||||
port: Number.parseInt(port, 10) });
|
||||
});
|
||||
} else if (Array.isArray(config.redis.sentinels)) {
|
||||
config.redis.sentinels.forEach(item => {
|
||||
const { host, port } = item;
|
||||
assert(typeof host === 'string',
|
||||
'bad config: redis sentinel host must be a string');
|
||||
assert(typeof port === 'number',
|
||||
'bad config: redis sentinel port must be a number');
|
||||
this.redis.sentinels.push({ host, port });
|
||||
});
|
||||
}
|
||||
|
||||
if (config.redis.sentinelPassword !== undefined) {
|
||||
assert(
|
||||
this._verifyRedisPassword(config.redis.sentinelPassword));
|
||||
this.redis.sentinelPassword = config.redis.sentinelPassword;
|
||||
}
|
||||
} else {
|
||||
// check for standalone configuration
|
||||
this.redis = {};
|
||||
assert(typeof config.redis.host === 'string',
|
||||
'bad config: redis.host must be a string');
|
||||
assert(typeof config.redis.port === 'number',
|
||||
'bad config: redis.port must be a number');
|
||||
this.redis.host = config.redis.host;
|
||||
this.redis.port = config.redis.port;
|
||||
}
|
||||
if (config.redis.password !== undefined) {
|
||||
assert(
|
||||
this._verifyRedisPassword(config.redis.password),
|
||||
'bad config: invalid password for redis. password must ' +
|
||||
'be a string');
|
||||
this.redis.password = config.redis.password;
|
||||
}
|
||||
this.redis = parseRedisConfig(config.redis);
|
||||
}
|
||||
if (config.utapi) {
|
||||
this.utapi = { component: 's3' };
|
||||
|
@ -851,65 +849,8 @@ class Config extends EventEmitter {
|
|||
this.utapi.localCache = config.localCache;
|
||||
assert(config.utapi.redis, 'missing required property of utapi ' +
|
||||
'configuration: redis');
|
||||
if (config.utapi.redis.sentinels) {
|
||||
this.utapi.redis = { sentinels: [], name: null };
|
||||
|
||||
assert(typeof config.utapi.redis.name === 'string',
|
||||
'bad config: redis sentinel name must be a string');
|
||||
this.utapi.redis.name = config.utapi.redis.name;
|
||||
|
||||
assert(Array.isArray(config.utapi.redis.sentinels),
|
||||
'bad config: redis sentinels must be an array');
|
||||
config.utapi.redis.sentinels.forEach(item => {
|
||||
const { host, port } = item;
|
||||
assert(typeof host === 'string',
|
||||
'bad config: redis sentinel host must be a string');
|
||||
assert(typeof port === 'number',
|
||||
'bad config: redis sentinel port must be a number');
|
||||
this.utapi.redis.sentinels.push({ host, port });
|
||||
});
|
||||
} else {
|
||||
// check for standalone configuration
|
||||
this.utapi.redis = {};
|
||||
assert(typeof config.utapi.redis.host === 'string',
|
||||
'bad config: redis.host must be a string');
|
||||
assert(typeof config.utapi.redis.port === 'number',
|
||||
'bad config: redis.port must be a number');
|
||||
this.utapi.redis.host = config.utapi.redis.host;
|
||||
this.utapi.redis.port = config.utapi.redis.port;
|
||||
}
|
||||
if (config.utapi.redis.password !== undefined) {
|
||||
assert(
|
||||
this._verifyRedisPassword(config.utapi.redis.password),
|
||||
'config: invalid password for utapi redis. password' +
|
||||
' must be a string');
|
||||
this.utapi.redis.password = config.utapi.redis.password;
|
||||
}
|
||||
if (config.utapi.redis.sentinelPassword !== undefined) {
|
||||
assert(
|
||||
this._verifyRedisPassword(config.utapi.redis.sentinelPassword),
|
||||
'config: invalid password for utapi redis. password' +
|
||||
' must be a string');
|
||||
this.utapi.redis.sentinelPassword =
|
||||
config.utapi.redis.sentinelPassword;
|
||||
}
|
||||
if (config.utapi.redis.retry !== undefined) {
|
||||
if (config.utapi.redis.retry.connectBackoff !== undefined) {
|
||||
const { min, max, jitter, factor, deadline } = config.utapi.redis.retry.connectBackoff;
|
||||
assert.strictEqual(typeof min, 'number',
|
||||
'utapi.redis.retry.connectBackoff: min must be a number');
|
||||
assert.strictEqual(typeof max, 'number',
|
||||
'utapi.redis.retry.connectBackoff: max must be a number');
|
||||
assert.strictEqual(typeof jitter, 'number',
|
||||
'utapi.redis.retry.connectBackoff: jitter must be a number');
|
||||
assert.strictEqual(typeof factor, 'number',
|
||||
'utapi.redis.retry.connectBackoff: factor must be a number');
|
||||
assert.strictEqual(typeof deadline, 'number',
|
||||
'utapi.redis.retry.connectBackoff: deadline must be a number');
|
||||
}
|
||||
|
||||
this.utapi.redis.retry = config.utapi.redis.retry;
|
||||
} else {
|
||||
this.utapi.redis = parseRedisConfig(config.utapi.redis);
|
||||
if (this.utapi.redis.retry === undefined) {
|
||||
this.utapi.redis.retry = {
|
||||
connectBackoff: {
|
||||
min: 10,
|
||||
|
@ -920,6 +861,7 @@ class Config extends EventEmitter {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (config.utapi.metrics) {
|
||||
this.utapi.metrics = config.utapi.metrics;
|
||||
}
|
||||
|
@ -988,8 +930,7 @@ class Config extends EventEmitter {
|
|||
}
|
||||
|
||||
if (config.utapi && config.utapi.reindex) {
|
||||
parseUtapiReindex(config.utapi.reindex);
|
||||
this.utapi.reindex = config.utapi.reindex;
|
||||
this.utapi.reindex = parseUtapiReindex(config.utapi.reindex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1430,10 +1371,6 @@ class Config extends EventEmitter {
|
|||
};
|
||||
}
|
||||
|
||||
_verifyRedisPassword(password) {
|
||||
return typeof password === 'string';
|
||||
}
|
||||
|
||||
setAuthDataAccounts(accounts) {
|
||||
this.authData.accounts = accounts;
|
||||
this.emit('authdata-update');
|
||||
|
@ -1547,6 +1484,7 @@ class Config extends EventEmitter {
|
|||
|
||||
module.exports = {
|
||||
parseSproxydConfig,
|
||||
parseRedisConfig,
|
||||
locationConstraintAssert,
|
||||
ConfigObject: Config,
|
||||
config: new Config(),
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"moment": "^2.26.0",
|
||||
"npm-run-all": "~4.1.5",
|
||||
"prom-client": "14.2.0",
|
||||
"utapi": "git+https://github.com/scality/utapi#7.70.4",
|
||||
"utapi": "git+https://github.com/scality/utapi#cbc28e8bca7bdcc650f4f9947fa56ad07a482d53",
|
||||
"utf8": "~2.1.1",
|
||||
"uuid": "^3.0.1",
|
||||
"vaultclient": "scality/vaultclient#7.10.13",
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
const assert = require('assert');
|
||||
const { parseRedisConfig } = require('../../../lib/Config');
|
||||
|
||||
describe('parseRedisConfig', () => {
|
||||
[
|
||||
{
|
||||
desc: 'with host and port',
|
||||
input: {
|
||||
host: 'localhost',
|
||||
port: 6479,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with host, port and password',
|
||||
input: {
|
||||
host: 'localhost',
|
||||
port: 6479,
|
||||
password: 'mypass',
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with host, port and an empty password',
|
||||
input: {
|
||||
host: 'localhost',
|
||||
port: 6479,
|
||||
password: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with host, port and an empty retry config',
|
||||
input: {
|
||||
host: 'localhost',
|
||||
port: 6479,
|
||||
retry: {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with host, port and a custom retry config',
|
||||
input: {
|
||||
host: 'localhost',
|
||||
port: 6479,
|
||||
retry: {
|
||||
connectBackoff: {
|
||||
min: 10,
|
||||
max: 1000,
|
||||
jitter: 0.1,
|
||||
factor: 1.5,
|
||||
deadline: 10000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a single sentinel and no sentinel password',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: [
|
||||
{
|
||||
host: 'localhost',
|
||||
port: 16479,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with two sentinels and a sentinel password',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: [
|
||||
{
|
||||
host: '10.20.30.40',
|
||||
port: 16479,
|
||||
},
|
||||
{
|
||||
host: '10.20.30.41',
|
||||
port: 16479,
|
||||
},
|
||||
],
|
||||
sentinelPassword: 'mypass',
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a sentinel and an empty sentinel password',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: [
|
||||
{
|
||||
host: '10.20.30.40',
|
||||
port: 16479,
|
||||
},
|
||||
],
|
||||
sentinelPassword: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a basic production-like config with sentinels',
|
||||
input: {
|
||||
name: 'scality-s3',
|
||||
password: '',
|
||||
sentinelPassword: '',
|
||||
sentinels: [
|
||||
{
|
||||
host: 'storage-1',
|
||||
port: 16379,
|
||||
},
|
||||
{
|
||||
host: 'storage-2',
|
||||
port: 16379,
|
||||
},
|
||||
{
|
||||
host: 'storage-3',
|
||||
port: 16379,
|
||||
},
|
||||
{
|
||||
host: 'storage-4',
|
||||
port: 16379,
|
||||
},
|
||||
{
|
||||
host: 'storage-5',
|
||||
port: 16379,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a single sentinel passed as a string',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: '10.20.30.40:16479',
|
||||
},
|
||||
output: {
|
||||
name: 'myname',
|
||||
sentinels: [
|
||||
{
|
||||
host: '10.20.30.40',
|
||||
port: 16479,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a list of sentinels passed as a string',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: '10.20.30.40:16479,another-host:16480,10.20.30.42:16481',
|
||||
sentinelPassword: 'mypass',
|
||||
},
|
||||
output: {
|
||||
name: 'myname',
|
||||
sentinels: [
|
||||
{
|
||||
host: '10.20.30.40',
|
||||
port: 16479,
|
||||
},
|
||||
{
|
||||
host: 'another-host',
|
||||
port: 16480,
|
||||
},
|
||||
{
|
||||
host: '10.20.30.42',
|
||||
port: 16481,
|
||||
},
|
||||
],
|
||||
sentinelPassword: 'mypass',
|
||||
},
|
||||
},
|
||||
].forEach(testCase => {
|
||||
it(`should parse a valid config ${testCase.desc}`, () => {
|
||||
const redisConfig = parseRedisConfig(testCase.input);
|
||||
assert.deepStrictEqual(redisConfig, testCase.output || testCase.input);
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
{
|
||||
desc: 'that is empty',
|
||||
input: {},
|
||||
},
|
||||
{
|
||||
desc: 'with only a host',
|
||||
input: {
|
||||
host: 'localhost',
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with only a port',
|
||||
input: {
|
||||
port: 6479,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a custom retry config with missing values',
|
||||
input: {
|
||||
host: 'localhost',
|
||||
port: 6479,
|
||||
retry: {
|
||||
connectBackoff: {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a sentinel but no name',
|
||||
input: {
|
||||
sentinels: [
|
||||
{
|
||||
host: 'localhost',
|
||||
port: 16479,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with a sentinel but an empty name',
|
||||
input: {
|
||||
name: '',
|
||||
sentinels: [
|
||||
{
|
||||
host: 'localhost',
|
||||
port: 16479,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with an empty list of sentinels',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with an empty list of sentinels passed as a string',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: 'with an invalid list of sentinels passed as a string (missing port)',
|
||||
input: {
|
||||
name: 'myname',
|
||||
sentinels: '10.20.30.40:16479,10.20.30.50',
|
||||
},
|
||||
},
|
||||
].forEach(testCase => {
|
||||
it(`should fail to parse an invalid config ${testCase.desc}`, () => {
|
||||
assert.throws(() => {
|
||||
parseRedisConfig(testCase.input);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5481,9 +5481,9 @@ user-home@^2.0.0:
|
|||
dependencies:
|
||||
os-homedir "^1.0.0"
|
||||
|
||||
"utapi@git+https://github.com/scality/utapi#7.70.4":
|
||||
"utapi@git+https://github.com/scality/utapi#cbc28e8bca7bdcc650f4f9947fa56ad07a482d53":
|
||||
version "7.70.4"
|
||||
resolved "git+https://github.com/scality/utapi#960d990e899bc6d90f9e835ac2befdb319f6ee0b"
|
||||
resolved "git+https://github.com/scality/utapi#cbc28e8bca7bdcc650f4f9947fa56ad07a482d53"
|
||||
dependencies:
|
||||
"@hapi/joi" "^17.1.1"
|
||||
"@senx/warp10" "^1.0.14"
|
||||
|
|
Loading…
Reference in New Issue