Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions extensions/replication/ReplicationConfigValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ const { hostPortJoi, transportJoi, bootstrapListJoi, adminCredsJoi,
retryParamsJoi, probeServerJoi, probeServerPerSite,
stsConfigJoi } =
require('../../lib/config/configItems.joi');
const {
authTypeAccount,
authTypeAssumeRole,
authTypeRole,
authTypeService,
} = require('../../lib/constants');

const { MAX_QUEUED_DEFAULT } = require('../../lib/constants').backbeatConsumer;

Expand All @@ -18,30 +24,44 @@ const CRR_FAILURE_EXPIRY = 24 * 60 * 60; // Expire Redis keys after 24 hours.
const OBJECT_SIZE_METRICS = [66560, 8388608, 68157440];

const destinationAuthJoi = joi.object({
type: joi.alternatives().try('account', 'role', 'service', 'assumeRole')
.required(),
type: joi.alternatives().try(
authTypeAccount,
authTypeRole,
authTypeService,
authTypeAssumeRole
).required(),
account: joi.string()
.when('type', { is: 'account', then: joi.required() }),
.when('type', { is: authTypeAccount, then: joi.required() }),
vault: joi.object({
host: joi.string().optional(),
port: joi.number().greater(0).optional(),
adminPort: joi.number().greater(0).optional(),
adminCredentialsFile: joi.string().optional(),
}),
sts: stsConfigJoi
.when('type', { is: 'assumeRole', then: joi.required() }),
.keys({
// override port to make it optional, with default based on transport
port: joi.alternatives().try(
stsConfigJoi.extract('port'),
joi.string().optional().valid('', null).empty(['', null])
).default(joi.ref('....transport', { adjust: transport => transport === 'http' ? 80 : 443 })),
})
.when('type', { is: authTypeAssumeRole, then: joi.required() }),
});

const joiSchema = joi.object({
source: {
transport: transportJoi,
s3: hostPortJoi.required(),
auth: joi.object({
type: joi.alternatives().try('account', 'role', 'service').
required(),
type: joi.alternatives().try(
authTypeAccount,
authTypeRole,
authTypeService,
).required(),
account: joi.string()
.when('type', { is: 'account', then: joi.required() })
.when('type', { is: 'service', then: joi.required() }),
.when('type', { is: authTypeAccount, then: joi.required() })
.when('type', { is: authTypeService, then: joi.required() }),
vault: joi.object({
host: joi.string().required(),
port: joi.number().greater(0).required(),
Expand All @@ -51,7 +71,7 @@ const joiSchema = joi.object({
then: joi.required(),
}),
adminCredentialsFile: joi.string().optional(),
}).when('type', { is: 'role', then: joi.required() }),
}).when('type', { is: authTypeRole, then: joi.required() }),
}).required(),
},
destination: joi.object({
Expand Down
1 change: 1 addition & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const constants = {
statusTimedOut: 'TIMED_OUT',
authTypeAssumeRole: 'assumeRole',
authTypeAccount: 'account',
authTypeRole: 'role',
authTypeService: 'service',
authTypeNone: 'none',
services: {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backbeat",
"version": "9.1.3",
"version": "9.1.4",
"description": "Asynchronous queue and job manager",
"main": "index.js",
"scripts": {
Expand Down
73 changes: 59 additions & 14 deletions tests/unit/replication/ReplicationConfigValidator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const baseConfig = {
};

describe('ReplicationConfigValidator', () => {
it('should require all sites to have a config when destination auth is per site', () => {
it('should require all sites to have a config when destination auth is per site', () => {
const config = {
...baseConfig,
destination: {
Expand All @@ -108,8 +108,9 @@ describe('ReplicationConfigValidator', () => {
delete config.destination.auth;
assert.throws(() => configValidator({}, config),
err => err.message === 'missing destination configuration for sites: aws2,aws3');
});
it('should not require all sites to have a config when destination.auth is defined', () => {
});

it('should not require all sites to have a config when destination.auth is defined', () => {
const config = {
...baseConfig,
destination: {
Expand All @@ -125,8 +126,9 @@ describe('ReplicationConfigValidator', () => {
}
};
assert.doesNotThrow(() => configValidator({}, config));
});
it('should allow specifying a custom transport for a site', () => {
});

it('should allow specifying a custom transport for a site', () => {
const config = {
...baseConfig,
destination: {
Expand All @@ -139,8 +141,9 @@ describe('ReplicationConfigValidator', () => {
}
};
assert.doesNotThrow(() => configValidator({}, config));
});
it('should require destination auth to contain sts config when type is assumeRole', () => {
});

it('should require destination auth to contain sts config when type is assumeRole', () => {
const config = {
...baseConfig,
destination: {
Expand Down Expand Up @@ -169,8 +172,9 @@ describe('ReplicationConfigValidator', () => {
};
assert.throws(() => configValidator({}, config),
err => err.message === '"destination.sites.aws1.auth.sts" is required');
});
it('should validate new destination schema', () => {
});

it('should validate new destination schema', () => {
const config = {
...baseConfig,
destination: {
Expand Down Expand Up @@ -208,8 +212,48 @@ describe('ReplicationConfigValidator', () => {
delete config.destination.auth;
delete config.destination.transport;
assert.doesNotThrow(() => configValidator({}, config));
});
it('should validate old destination schema', () => {
});

[
{ transport: 'http', port: undefined, expected: 80 },
{ transport: 'https', port: null, expected: 443 },
{ transport: 'https', port: '', expected: 443 },
{ transport: 'https', port: 7841, expected: 7841 },
{ transport: 'https', port: '3426', expected: 3426 },
].forEach(({ transport, port, expected }) =>
it(`should use default sts port when ${transport} port is ${port === '' ? '""' : port}`, () => {
const config = {
...baseConfig,
destination: {
...baseConfig.destination,
bootstrapList: [
{ site: 'aws', type: 'aws_s3' },
],
sites: {
aws: {
transport,
auth: {
type: 'assumeRole',
sts: {
host: 'sts.enpoint.com',
port,
accessKey: 'accessKey',
secretKey: 'secretKey',
},
},
},
},
},
};
delete config.destination.auth;
delete config.destination.transport;

const result = configValidator({}, config);
assert.strictEqual(result.destination.sites.aws.auth.sts.port, expected);
})
);

it('should validate old destination schema', () => {
const config = {
...baseConfig,
destination: {
Expand All @@ -222,8 +266,9 @@ describe('ReplicationConfigValidator', () => {
}
};
assert.doesNotThrow(() => configValidator({}, config));
});
it('should load admin credentials from file when adminCredentialsFile is set', () => {
});

it('should load admin credentials from file when adminCredentialsFile is set', () => {
const config = {
...baseConfig,
source: {
Expand Down Expand Up @@ -285,5 +330,5 @@ describe('ReplicationConfigValidator', () => {
conf.destination.sites.aws1.auth.vault.adminCredentials,
adminCredentials,
);
});
});
});
Loading