Compare commits

...

6 Commits

Author SHA1 Message Date
Anurag Mittal 5149cd8f99
ARSN-416: bump package.json-to-7.10.46-2 2024-05-30 13:35:22 +02:00
Anurag Mittal 71243b6859
ARSN-412-test-relevant-errors
(cherry picked from commit c5c1db4568)
2024-05-30 13:34:52 +02:00
Anurag Mittal 7a9fe38957
VAULT-412-add-unit-test-for-conditions
(cherry picked from commit 58f4d3cb3a)
2024-05-30 13:34:47 +02:00
Anurag Mittal eff398ba0f
ARSN-412: add support for exists pre-condition
(cherry picked from commit b049f39e2a)
2024-05-30 13:34:41 +02:00
Nicolas Humbert da3aecdbe1 bump version 2023-12-07 12:09:04 +01:00
Nicolas Humbert 1042394f9a ARSN-376 Probe response logic should be handled in the handler
Currently, the probe response logic is distributed between Backbeat probe handlers and Arsenal's onRequest method.

This scattered approach causes confusion for developers and results in bugs.

The solution is to centralize the probe response logic exclusively within the Backbeat probe handlers.
2023-12-07 12:02:38 +01:00
5 changed files with 117 additions and 17 deletions

View File

@ -148,7 +148,7 @@ export class IndexTransaction {
'missing condition for conditional put' 'missing condition for conditional put'
); );
} }
if (typeof condition.notExists !== 'string') { if (typeof condition.notExists !== 'string' && typeof condition.exists !== 'string') {
throw propError( throw propError(
'unsupportedConditionalOperation', 'unsupportedConditionalOperation',
'missing key or supported condition' 'missing key or supported condition'

View File

@ -90,12 +90,6 @@ export class ProbeServer extends httpServer {
return; return;
} }
const probeResponse = this._handlers.get(req.url!)!(res, log); this._handlers.get(req.url!)!(res, log);
if (probeResponse !== undefined && probeResponse !== '') {
// Return an internal error with the response
errors.InternalError
.customizeDescription(probeResponse)
.writeResponse(res);
}
} }
} }

View File

@ -3,7 +3,7 @@
"engines": { "engines": {
"node": ">=16" "node": ">=16"
}, },
"version": "7.10.46", "version": "7.10.46-2",
"description": "Common utilities for the S3 project components", "description": "Common utilities for the S3 project components",
"main": "build/index.js", "main": "build/index.js",
"repository": { "repository": {

View File

@ -53,12 +53,21 @@ function checkKeyNotExistsInDB(db, key, cb) {
return cb(err); return cb(err);
} }
if (value) { if (value) {
return cb(errors.PreconditionFailed); return cb(errors.EntityAlreadyExists);
} }
return cb(); return cb();
}); });
} }
function checkKeyExistsInDB(db, key, callback) {
return db.get(key, err => {
if (err) {
return callback(err.notFound ? errors.NoSuchEntity : err);
}
return callback();
});
}
class ConditionalLevelDB { class ConditionalLevelDB {
constructor() { constructor() {
this.db = createDb(); this.db = createDb();
@ -70,6 +79,9 @@ class ConditionalLevelDB {
case ('notExists' in cond): case ('notExists' in cond):
checkKeyNotExistsInDB(this.db, cond.notExists, asyncCallback); checkKeyNotExistsInDB(this.db, cond.notExists, asyncCallback);
break; break;
case ('exists' in cond):
checkKeyExistsInDB(this.db, cond.exists, asyncCallback);
break;
default: default:
asyncCallback(new Error('unsupported conditional operation')); asyncCallback(new Error('unsupported conditional operation'));
} }
@ -425,7 +437,7 @@ describe('IndexTransaction', () => {
value: value3, value: value3,
}); });
return transaction.commit(err => { return transaction.commit(err => {
if (!err || !err.is.PreconditionFailed) { if (!err || !err.is.EntityAlreadyExists) {
return done(new Error('should not be able to conditional put for duplicate key')); return done(new Error('should not be able to conditional put for duplicate key'));
} }
return async.parallel([ return async.parallel([
@ -457,11 +469,87 @@ describe('IndexTransaction', () => {
it('should not allow batch operation with unsupported condition', done => { it('should not allow batch operation with unsupported condition', done => {
const transaction = new IndexTransaction(); const transaction = new IndexTransaction();
try { try {
transaction.addCondition({ exists: key1 }); transaction.addCondition({ like: key1 });
done(new Error('should fail for unsupported condition, currently supported - notExists')); done(new Error('should fail for unsupported condition, currently supported - notExists'));
} catch (err) { } catch (err) {
assert.strictEqual(err.unsupportedConditionalOperation, true); assert.strictEqual(err.unsupportedConditionalOperation, true);
done(); done();
} }
}); });
it('should allow batch operation with key specified in exists condition is present in db', done => {
const db = new ConditionalLevelDB();
const { client } = db;
let transaction = new IndexTransaction(db);
transaction.put(key1, value1);
return async.series([
next => transaction.commit(next),
next => client.get(key1, next),
], err => {
assert.ifError(err);
// create new transaction as previous transaction is already committed
transaction = new IndexTransaction(db);
transaction.addCondition({ exists: key1 });
transaction.push({
type: 'put',
key: key1,
value: value2,
});
return async.series([
next => transaction.commit(next),
next => client.get(key1, next),
], (err, res) => {
assert.ifError(err);
assert.strictEqual(res[1], value2);
return done();
});
});
});
it('should not allow batch operation with key specified in exists condition is not in db', done => {
const db = new ConditionalLevelDB();
const { client } = db;
const transaction = new IndexTransaction(db);
transaction.addCondition({ exists: key1 });
transaction.push({
type: 'put',
key: key1,
value: value1,
});
return transaction.commit(err => {
assert.strictEqual(err && err.NoSuchEntity, true);
return checkKeyNotExistsInDB(client, key1, done);
});
});
it('should handle batch operations with multiple conditions correctly', done => {
const db = new ConditionalLevelDB();
const { client } = db;
let transaction = new IndexTransaction(db);
transaction.put(key1, value1);
return async.series([
next => transaction.commit(next),
next => client.get(key1, next),
], err => {
assert.ifError(err);
// create new transaction as previous transaction is already committed
transaction = new IndexTransaction(db);
transaction.addCondition({ exists: key1 });
transaction.addCondition({ notExists: key2 });
transaction.push({
type: 'put',
key: key1,
value: value2,
});
return async.series([
next => transaction.commit(next),
next => client.get(key1, next),
], (err, res) => {
assert.ifError(err);
assert.strictEqual(res[1], value2);
return done();
});
});
});
}); });

View File

@ -89,16 +89,34 @@ describe('network.probe.ProbeServer', () => {
}); });
it('500 response on bad probe', done => { it('500 response on bad probe', done => {
server.addHandler('/check', () => 'check failed'); const failedMessage = 'failed_message';
server.addHandler('/check', res => {
res.writeHead(500);
res.end(failedMessage);
});
makeRequest('GET', '/check', (err, res) => { makeRequest('GET', '/check', (err, res) => {
assert.ifError(err); assert.ifError(err);
assert.strictEqual(res.statusCode, 500); assert.strictEqual(res.statusCode, 500);
res.setEncoding('utf8'); res.setEncoding('utf8');
res.on('data', body => { res.on('data', body => {
assert.strictEqual( assert.strictEqual(body, failedMessage);
body, done();
'{"errorType":"InternalError","errorMessage":"check failed"}', });
); });
});
it('500 response on bad async probe', done => {
const failedMessage = 'failed_message';
server.addHandler('/check', async res => {
res.writeHead(500);
res.end(failedMessage);
});
makeRequest('GET', '/check', (err, res) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 500);
res.setEncoding('utf8');
res.on('data', body => {
assert.strictEqual(body, failedMessage);
done(); done();
}); });
}); });