Skip to content

Commit fae1475

Browse files
committed
Merge branch 'feature/CLDSRV-823/async-await-start' into q/9.3
2 parents 47f03c2 + b6e1ad0 commit fae1475

3 files changed

Lines changed: 139 additions & 112 deletions

File tree

lib/api/bucketGet.js

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const { promisify } = require('util');
12
const querystring = require('querystring');
23
const { errors, errorInstances, versioning, s3middleware } = require('arsenal');
34
const constants = require('../../constants');
@@ -280,8 +281,7 @@ function processOptionalAttributes(item, optionalAttributes) {
280281
return xml;
281282
}
282283

283-
function handleResult(listParams, requestMaxKeys, encoding, authInfo,
284-
bucketName, list, corsHeaders, log, callback) {
284+
function handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, log) {
285285
// eslint-disable-next-line no-param-reassign
286286
listParams.maxKeys = requestMaxKeys;
287287
// eslint-disable-next-line no-param-reassign
@@ -297,7 +297,7 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
297297
pushMetric('listBucket', log, { authInfo, bucket: bucketName });
298298
monitoring.promMetrics('GET', bucketName, '200', 'listBucket');
299299

300-
return callback(null, res, corsHeaders);
300+
return res;
301301
}
302302

303303
/**
@@ -306,11 +306,17 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
306306
* requester's info
307307
* @param {object} request - http request object
308308
* @param {function} log - Werelogs request logger
309-
* @param {function} callback - callback to respond to http request
310-
* with either error code or xml response body
311-
* @return {undefined}
309+
* @param {function} callback - callback optional to keep backward compatibility
310+
* @return {Promise<object>} - object containing xml and additionalResHeaders
311+
* @throws {Error}
312312
*/
313-
function bucketGet(authInfo, request, log, callback) {
313+
async function bucketGet(authInfo, request, log, callback) {
314+
if (callback) {
315+
return bucketGet(authInfo, request, log)
316+
.then(result => callback(null, ...result))
317+
.catch(err => callback(err, null, err.additionalResHeaders));
318+
}
319+
314320
const params = request.query;
315321
const bucketName = request.bucketName;
316322
const v2 = params['list-type'];
@@ -322,15 +328,13 @@ function bucketGet(authInfo, request, log, callback) {
322328
.map(attr => attr !== 'RestoreStatus' ? attr.toLowerCase() : attr)
323329
?? [];
324330
if (optionalAttributes.some(attr => !attr.startsWith('x-amz-meta-') && attr != 'RestoreStatus')) {
325-
return callback(
326-
errorInstances.InvalidArgument.customizeDescription('Invalid attribute name specified')
327-
);
331+
throw errorInstances.InvalidArgument.customizeDescription('Invalid attribute name specified');
328332
}
329333

330334
if (v2 !== undefined && Number.parseInt(v2, 10) !== 2) {
331-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
332-
'List Type specified in Request'));
335+
throw errorInstances.InvalidArgument.customizeDescription('Invalid List Type specified in Request');
333336
}
337+
334338
if (v2) {
335339
log.addDefaultFields({ action: 'ListObjectsV2' });
336340
if (request.serverAccessLog) {
@@ -348,18 +352,14 @@ function bucketGet(authInfo, request, log, callback) {
348352
const encoding = params['encoding-type'];
349353
if (encoding !== undefined && encoding !== 'url') {
350354
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
351-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
352-
'Encoding Method specified in Request'));
355+
throw errorInstances.InvalidArgument.customizeDescription('Invalid Encoding Method specified in Request');
353356
}
354357

355358
const requestMaxKeys = params['max-keys'] ? Number.parseInt(params['max-keys'], 10) : 1000;
356359
if (Number.isNaN(requestMaxKeys) || requestMaxKeys < 0) {
357360
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
358-
return callback(errors.InvalidArgument);
361+
throw errors.InvalidArgument;
359362
}
360-
// AWS only returns 1000 keys even if max keys are greater.
361-
// Max keys stated in response xml can be greater than actual
362-
// keys returned.
363363
const actualMaxKeys = Math.min(constants.listingHardLimit, requestMaxKeys);
364364

365365
const metadataValParams = {
@@ -388,47 +388,51 @@ function bucketGet(authInfo, request, log, callback) {
388388
listParams.marker = params.marker;
389389
}
390390

391-
standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
392-
const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
391+
let bucket;
393392

394-
if (err) {
395-
log.debug('error processing request', { error: err });
396-
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
397-
return callback(err, null, corsHeaders);
398-
}
393+
try {
394+
const standardMetadataValidateBucketPromised = promisify(standardMetadataValidateBucket);
395+
bucket = await standardMetadataValidateBucketPromised(metadataValParams, request.actionImplicitDenies, log);
396+
} catch (err) {
397+
log.debug('error processing request', { err });
398+
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
399+
throw err;
400+
}
399401

400-
if (params.versions !== undefined) {
401-
listParams.listingType = 'DelimiterVersions';
402-
delete listParams.marker;
403-
listParams.keyMarker = params['key-marker'];
404-
listParams.versionIdMarker = params['version-id-marker'] ?
405-
versionIdUtils.decode(params['version-id-marker']) :
406-
undefined;
407-
}
402+
const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
408403

409-
if (!requestMaxKeys) {
410-
const emptyList = {
411-
CommonPrefixes: [],
412-
Contents: [],
413-
Versions: [],
414-
IsTruncated: false,
415-
};
416-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
417-
bucketName, emptyList, corsHeaders, log, callback);
418-
}
404+
if (params.versions !== undefined) {
405+
listParams.listingType = 'DelimiterVersions';
406+
delete listParams.marker;
407+
listParams.keyMarker = params['key-marker'];
408+
listParams.versionIdMarker = params['version-id-marker'] ?
409+
versionIdUtils.decode(params['version-id-marker']) :
410+
undefined;
411+
}
412+
if (!requestMaxKeys) {
413+
const emptyList = {
414+
CommonPrefixes: [],
415+
Contents: [],
416+
Versions: [],
417+
IsTruncated: false,
418+
};
419+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, emptyList, log);
420+
return [res, corsHeaders];
421+
}
419422

420-
return services.getObjectListing(bucketName, listParams, log, (err, list) => {
421-
if (err) {
422-
log.debug('error processing request', { error: err });
423-
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
424-
return callback(err, null, corsHeaders);
425-
}
423+
let list;
424+
try {
425+
list = await promisify(services.getObjectListing)(bucketName, listParams, log);
426+
} catch (err) {
427+
log.debug('error processing request', { error: err });
428+
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
426429

427-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
428-
bucketName, list, corsHeaders, log, callback);
429-
});
430-
});
431-
return undefined;
430+
err.additionalResHeaders = corsHeaders;
431+
throw err;
432+
}
433+
434+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, log);
435+
return [res, corsHeaders];
432436
}
433437

434438
module.exports = {

lib/api/objectGetLegalHold.js

Lines changed: 72 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const async = require('async');
1+
const { promisify } = require('util');
22
const { errors, errorInstances, s3middleware } = require('arsenal');
33

44
const { decodeVersionId, getVersionIdResHeader }
@@ -15,18 +15,23 @@ const { convertToXml } = s3middleware.objectLegalHold;
1515
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
1616
* @param {object} request - http request object
1717
* @param {object} log - Werelogs logger
18-
* @param {function} callback - callback to server
19-
* @return {undefined}
18+
* @return {Promise<object>} - object containing xml and additionalResHeaders
2019
*/
21-
function objectGetLegalHold(authInfo, request, log, callback) {
20+
async function objectGetLegalHold(authInfo, request, log, callback) {
21+
if (callback) {
22+
return objectGetLegalHold(authInfo, request, log)
23+
.then(result => callback(null, ...result))
24+
.catch(err => callback(err, null, err.additionalResHeaders));
25+
}
26+
2227
log.debug('processing request', { method: 'objectGetLegalHold' });
2328

2429
const { bucketName, objectKey, query } = request;
2530

2631
const decodedVidResult = decodeVersionId(query);
2732
if (decodedVidResult instanceof Error) {
2833
log.trace('invalid versionId query', { versionId: query.versionId, error: decodedVidResult });
29-
return process.nextTick(() => callback(decodedVidResult));
34+
throw decodedVidResult;
3035
}
3136
const versionId = decodedVidResult;
3237

@@ -40,60 +45,69 @@ function objectGetLegalHold(authInfo, request, log, callback) {
4045
request,
4146
};
4247

43-
return async.waterfall([
44-
next => standardMetadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
45-
(err, bucket, objectMD) => {
46-
if (err) {
47-
log.trace('request authorization failed', { method: 'objectGetLegalHold', error: err });
48-
return next(err);
49-
}
50-
if (!objectMD) {
51-
const err = versionId ? errors.NoSuchVersion : errors.NoSuchKey;
52-
log.trace('error no object metadata found', { method: 'objectGetLegalHold', error: err });
53-
return next(err, bucket);
54-
}
55-
if (objectMD.isDeleteMarker) {
56-
if (versionId) {
57-
log.trace('requested version is delete marker', { method: 'objectGetLegalHold' });
58-
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
59-
return next(errors.MethodNotAllowed);
60-
}
61-
log.trace('most recent version is delete marker', { method: 'objectGetLegalHold' });
62-
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
63-
return next(errors.NoSuchKey);
64-
}
65-
if (!bucket.isObjectLockEnabled()) {
66-
log.trace('object lock not enabled on bucket', { method: 'objectGetRetention' });
67-
return next(errorInstances.InvalidRequest.customizeDescription(
68-
'Bucket is missing Object Lock Configuration'));
69-
}
70-
return next(null, bucket, objectMD);
71-
}),
72-
(bucket, objectMD, next) => {
73-
const { legalHold } = objectMD;
74-
const xml = convertToXml(legalHold);
75-
if (xml === '') {
76-
return next(errors.NoSuchObjectLockConfiguration);
77-
}
78-
return next(null, bucket, xml, objectMD);
79-
},
80-
], (err, bucket, xml, objectMD) => {
81-
const additionalResHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
82-
if (err) {
83-
log.trace('error processing request', { error: err, method: 'objectGetLegalHold' });
84-
} else {
85-
pushMetric('getObjectLegalHold', log, {
86-
authInfo,
87-
bucket: bucketName,
88-
keys: [objectKey],
89-
versionId: objectMD ? objectMD.versionId : undefined,
90-
location: objectMD ? objectMD.dataStoreName : undefined,
91-
});
92-
const verCfg = bucket.getVersioningConfiguration();
93-
additionalResHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
48+
let bucket, objectMD;
49+
50+
try {
51+
const standardMetadataValidateBucketAndObjPromised = promisify(standardMetadataValidateBucketAndObj);
52+
({ bucket, objectMD } = await standardMetadataValidateBucketAndObjPromised(
53+
metadataValParams,
54+
request.actionImplicitDenies,
55+
log,
56+
));
57+
} catch (err) {
58+
log.trace('request authorization failed', { method: 'objectGetLegalHold', error: err });
59+
throw err;
60+
}
61+
62+
if (!objectMD) {
63+
const err = versionId ? errors.NoSuchVersion : errors.NoSuchKey;
64+
log.trace('error no object metadata found', { method: 'objectGetLegalHold', error: err });
65+
throw err;
66+
}
67+
68+
if (objectMD.isDeleteMarker) {
69+
if (versionId) {
70+
log.trace('requested version is delete marker', { method: 'objectGetLegalHold' });
71+
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
72+
throw errors.MethodNotAllowed;
9473
}
95-
return callback(err, xml, additionalResHeaders);
74+
75+
log.trace('most recent version is delete marker', { method: 'objectGetLegalHold' });
76+
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
77+
throw errors.NoSuchKey;
78+
}
79+
80+
if (!bucket.isObjectLockEnabled()) {
81+
log.trace('object lock not enabled on bucket', { method: 'objectGetRetention' });
82+
throw errorInstances.InvalidRequest.customizeDescription('Bucket is missing Object Lock Configuration');
83+
}
84+
85+
const { legalHold } = objectMD;
86+
const xml = convertToXml(legalHold);
87+
if (xml === '') {
88+
throw errors.NoSuchObjectLockConfiguration;
89+
}
90+
91+
const additionalResHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
92+
93+
pushMetric('getObjectLegalHold', log, {
94+
authInfo,
95+
bucket: bucketName,
96+
keys: [objectKey],
97+
versionId: objectMD ? objectMD.versionId : undefined,
98+
location: objectMD ? objectMD.dataStoreName : undefined,
9699
});
100+
const verCfg = bucket.getVersioningConfiguration();
101+
additionalResHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
102+
103+
return [xml, additionalResHeaders];
97104
}
98105

99-
module.exports = objectGetLegalHold;
106+
module.exports = (...args) => {
107+
const callback = args.at(-1);
108+
const argsWithoutCallback = args.slice(0, -1);
109+
110+
objectGetLegalHold(...argsWithoutCallback)
111+
.then(result => callback(null, ...result))
112+
.catch(err => callback(err, null, err.additionalResHeaders));
113+
};

lib/metadata/metadataUtils.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const { promisify } = require('util');
12
const async = require('async');
23
const { errors } = require('arsenal');
34

@@ -429,6 +430,14 @@ function standardMetadataValidateBucketAndObj(params, actionImplicitDenies, log,
429430
return callback(null, bucket, objMD);
430431
});
431432
}
433+
standardMetadataValidateBucketAndObj[promisify.custom] = (...args) =>
434+
new Promise((resolve, reject) => {
435+
standardMetadataValidateBucketAndObj(...args, (err, bucket, objectMD) => {
436+
if (err) {return reject(err);}
437+
return resolve({ bucket, objectMD });
438+
});
439+
});
440+
432441
/** standardMetadataValidateBucket - retrieve bucket from metadata and check if user
433442
* is authorized to access it
434443
* @param {object} params - function parameters

0 commit comments

Comments
 (0)