Compare commits
14 Commits
developmen
...
hotfix/7.1
Author | SHA1 | Date |
---|---|---|
bert-e | b16a796433 | |
bert-e | 2c1189f76b | |
Ronnie | b3b733970c | |
Ronnie Smith | c98195a835 | |
Ronnie Smith | a118ac89c3 | |
Taylor McKinnon | 7a2e1389fe | |
Jonathan Gramain | 25c5cca38c | |
Jonathan Gramain | 474413afc0 | |
Jonathan Gramain | 5f13af45f3 | |
Jonathan Gramain | abfccc41eb | |
Jonathan Gramain | 57dbc39755 | |
Jonathan Gramain | 0f46d861ef | |
Jonathan Gramain | e4dd8b6d68 | |
Jonathan Gramain | 088fb9484d |
|
@ -85,6 +85,15 @@ function abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
|
|||
}
|
||||
return next(null, mpuBucket, destBucket, false);
|
||||
},
|
||||
function sendAbortPut(mpuBucket, destBucket, skipDataDelete, next) {
|
||||
services.sendAbortMPUPut(bucketName, objectKey, uploadId, log,
|
||||
err => {
|
||||
if (err) {
|
||||
return next(err, destBucket);
|
||||
}
|
||||
return next(null, mpuBucket, destBucket, skipDataDelete);
|
||||
});
|
||||
},
|
||||
function getPartLocations(mpuBucket, destBucket, skipDataDelete,
|
||||
next) {
|
||||
services.getMPUparts(mpuBucket.getName(), uploadId, log,
|
||||
|
|
|
@ -253,6 +253,7 @@ function createAndStoreObject(bucketName, bucketMD, objectKey, objMD, authInfo,
|
|||
metadataStoreParams.versioning = options.versioning;
|
||||
metadataStoreParams.isNull = options.isNull;
|
||||
metadataStoreParams.nullVersionId = options.nullVersionId;
|
||||
metadataStoreParams.nullUploadId = options.nullUploadId;
|
||||
return _storeInMDandDeleteData(bucketName, infoArr,
|
||||
cipherBundle, metadataStoreParams,
|
||||
options.dataToDelete, requestLogger, requestMethod, next);
|
||||
|
|
|
@ -129,7 +129,23 @@ function _deleteNullVersionMD(bucketName, objKey, options, mst, log, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
function processVersioningState(mst, vstat, cb) {
|
||||
/**
|
||||
* Process state from the master version of an object and the bucket
|
||||
* versioning configuration, return a set of options objects
|
||||
*
|
||||
* @param {object} mst - state of master version, as returned by
|
||||
* getMasterState()
|
||||
* @param {string} vstat - bucket versioning status: 'Enabled' or 'Suspended'
|
||||
*
|
||||
* @return {object} result object with the following attributes:
|
||||
* - {object} options: versioning-related options to pass to the
|
||||
services.metadataStoreObject() call
|
||||
* - {object} [storeOptions]: options for metadata to create a new
|
||||
null version key, if needed
|
||||
* - {object} [delOptions]: options for metadata to delete the null
|
||||
version key, if needed
|
||||
*/
|
||||
function processVersioningState(mst, vstat) {
|
||||
const options = {};
|
||||
const storeOptions = {};
|
||||
const delOptions = {};
|
||||
|
@ -143,9 +159,12 @@ function processVersioningState(mst, vstat, cb) {
|
|||
// if null version exists, clean it up prior to put
|
||||
if (mst.isNull) {
|
||||
delOptions.versionId = mst.versionId;
|
||||
return cb(null, options, null, delOptions);
|
||||
if (mst.uploadId) {
|
||||
delOptions.replayId = mst.uploadId;
|
||||
}
|
||||
return cb(null, options);
|
||||
return { options, delOptions };
|
||||
}
|
||||
return { options };
|
||||
}
|
||||
// versioning is enabled, create a new version
|
||||
options.versioning = true;
|
||||
|
@ -155,9 +174,14 @@ function processVersioningState(mst, vstat, cb) {
|
|||
storeOptions.versionId = versionId;
|
||||
storeOptions.isNull = true;
|
||||
options.nullVersionId = versionId;
|
||||
return cb(null, options, storeOptions);
|
||||
// non-versioned (non-null) MPU objects don't have a
|
||||
// replay ID, so don't reference their uploadId
|
||||
if (mst.isNull && mst.uploadId) {
|
||||
options.nullUploadId = mst.uploadId;
|
||||
}
|
||||
return cb(null, options);
|
||||
return { options, storeOptions };
|
||||
}
|
||||
return { options };
|
||||
}
|
||||
// master is versioned and is not a null version
|
||||
const nullVersionId = mst.nullVersionId;
|
||||
|
@ -166,17 +190,36 @@ function processVersioningState(mst, vstat, cb) {
|
|||
options.versionId = '';
|
||||
options.isNull = true;
|
||||
if (nullVersionId === undefined) {
|
||||
return cb(null, options);
|
||||
return { options };
|
||||
}
|
||||
delOptions.versionId = nullVersionId;
|
||||
return cb(null, options, null, delOptions);
|
||||
if (mst.nullUploadId) {
|
||||
delOptions.replayId = mst.nullUploadId;
|
||||
}
|
||||
return { options, delOptions };
|
||||
}
|
||||
// versioning is enabled, put the new version
|
||||
options.versioning = true;
|
||||
options.nullVersionId = nullVersionId;
|
||||
return cb(null, options);
|
||||
if (mst.nullUploadId) {
|
||||
options.nullUploadId = mst.nullUploadId;
|
||||
}
|
||||
return { options };
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the state of the master version from its object metadata
|
||||
*
|
||||
* @param {object} objMD - object metadata parsed from JSON
|
||||
*
|
||||
* @return {object} state of master version, with the following attributes:
|
||||
* - {boolean} exists - true if the object exists (i.e. if `objMD` is truish)
|
||||
* - {string} versionId - version ID of the master key
|
||||
* - {boolean} isNull - whether the master version is a null version
|
||||
* - {string} nullVersionId - if not a null version, reference to the
|
||||
* null version ID
|
||||
* - {array} objLocation - array of data locations
|
||||
*/
|
||||
function getMasterState(objMD) {
|
||||
if (!objMD) {
|
||||
return {};
|
||||
|
@ -184,8 +227,10 @@ function getMasterState(objMD) {
|
|||
const mst = {
|
||||
exists: true,
|
||||
versionId: objMD.versionId,
|
||||
uploadId: objMD.uploadId,
|
||||
isNull: objMD.isNull,
|
||||
nullVersionId: objMD.nullVersionId,
|
||||
nullUploadId: objMD.nullUploadId,
|
||||
};
|
||||
if (objMD.location) {
|
||||
mst.objLocation = Array.isArray(objMD.location) ?
|
||||
|
@ -213,35 +258,29 @@ function getMasterState(objMD) {
|
|||
*/
|
||||
function versioningPreprocessing(bucketName, bucketMD, objectKey, objMD,
|
||||
log, callback) {
|
||||
const options = {};
|
||||
const mst = getMasterState(objMD);
|
||||
const vCfg = bucketMD.getVersioningConfiguration();
|
||||
// bucket is not versioning configured
|
||||
if (!vCfg) {
|
||||
options.dataToDelete = mst.objLocation;
|
||||
const options = { dataToDelete: mst.objLocation };
|
||||
return process.nextTick(callback, null, options);
|
||||
}
|
||||
// bucket is versioning configured
|
||||
return async.waterfall([
|
||||
function processState(next) {
|
||||
processVersioningState(mst, vCfg.Status,
|
||||
(err, options, storeOptions, delOptions) => {
|
||||
process.nextTick(next, err, options, storeOptions,
|
||||
delOptions);
|
||||
});
|
||||
},
|
||||
function storeVersion(options, storeOptions, delOptions, next) {
|
||||
const { options, storeOptions, delOptions } =
|
||||
processVersioningState(mst, vCfg.Status);
|
||||
return async.series([
|
||||
function storeVersion(next) {
|
||||
if (!storeOptions) {
|
||||
return process.nextTick(next, null, options, delOptions);
|
||||
return process.nextTick(next);
|
||||
}
|
||||
const versionMD = Object.assign({}, objMD, storeOptions);
|
||||
const params = { versionId: storeOptions.versionId };
|
||||
return _storeNullVersionMD(bucketName, objectKey, versionMD,
|
||||
params, log, err => next(err, options, delOptions));
|
||||
params, log, next);
|
||||
},
|
||||
function deleteNullVersion(options, delOptions, next) {
|
||||
function deleteNullVersion(next) {
|
||||
if (!delOptions) {
|
||||
return process.nextTick(next, null, options);
|
||||
return process.nextTick(next);
|
||||
}
|
||||
return _deleteNullVersionMD(bucketName, objectKey, delOptions, mst,
|
||||
log, (err, nullDataToDelete) => {
|
||||
|
@ -259,10 +298,10 @@ function versioningPreprocessing(bucketName, bucketMD, objectKey, objMD,
|
|||
return next(errors.InternalError);
|
||||
}
|
||||
Object.assign(options, { dataToDelete: nullDataToDelete });
|
||||
return next(null, options);
|
||||
return next();
|
||||
});
|
||||
},
|
||||
], (err, options) => callback(err, options));
|
||||
], err => callback(err, options));
|
||||
}
|
||||
|
||||
/** preprocessingVersioningDelete - return versioning information for S3 to
|
||||
|
@ -291,6 +330,9 @@ function preprocessingVersioningDelete(bucketName, bucketMD, objectMD,
|
|||
// deleting a specific version
|
||||
options.deleteData = true;
|
||||
options.versionId = reqVersionId;
|
||||
if (objectMD.uploadId) {
|
||||
options.replayId = objectMD.uploadId;
|
||||
}
|
||||
return callback(null, options);
|
||||
}
|
||||
if (reqVersionId) {
|
||||
|
@ -298,18 +340,26 @@ function preprocessingVersioningDelete(bucketName, bucketMD, objectMD,
|
|||
if (objectMD.versionId === undefined) {
|
||||
// object is not versioned, deleting it
|
||||
options.deleteData = true;
|
||||
// non-versioned (non-null) MPU objects don't have a
|
||||
// replay ID, so don't reference their uploadId
|
||||
return callback(null, options);
|
||||
}
|
||||
if (objectMD.isNull) {
|
||||
// master is the null version
|
||||
options.deleteData = true;
|
||||
options.versionId = objectMD.versionId;
|
||||
if (objectMD.uploadId) {
|
||||
options.replayId = objectMD.uploadId;
|
||||
}
|
||||
return callback(null, options);
|
||||
}
|
||||
if (objectMD.nullVersionId) {
|
||||
// null version exists, deleting it
|
||||
options.deleteData = true;
|
||||
options.versionId = objectMD.nullVersionId;
|
||||
if (objectMD.nullUploadId) {
|
||||
options.replayId = objectMD.nullUploadId;
|
||||
}
|
||||
return callback(null, options);
|
||||
}
|
||||
// null version does not exist, no deletion
|
||||
|
@ -324,6 +374,8 @@ module.exports = {
|
|||
decodeVersionId,
|
||||
getVersionIdResHeader,
|
||||
checkQueryVersionId,
|
||||
processVersioningState,
|
||||
getMasterState,
|
||||
versioningPreprocessing,
|
||||
preprocessingVersioningDelete,
|
||||
};
|
||||
|
|
|
@ -336,6 +336,7 @@ function completeMultipartUpload(authInfo, request, log, callback) {
|
|||
metaStoreParams.versioning = options.versioning;
|
||||
metaStoreParams.isNull = options.isNull;
|
||||
metaStoreParams.nullVersionId = options.nullVersionId;
|
||||
metaStoreParams.nullUploadId = options.nullUploadId;
|
||||
return next(null, destBucket, dataLocations,
|
||||
metaStoreParams, mpuBucket, keysToDelete, aggregateETag,
|
||||
objMD, extraPartLocations, pseudoCipherBundle,
|
||||
|
|
|
@ -5,6 +5,7 @@ const getMetaHeaders = s3middleware.userMetadata.getMetaHeaders;
|
|||
const convertToXml = s3middleware.convertToXml;
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const { hasNonPrintables } = require('../utilities/stringChecks');
|
||||
const { cleanUpBucket } = require('./apiUtils/bucket/bucketCreation');
|
||||
const constants = require('../../constants');
|
||||
const services = require('../services');
|
||||
|
@ -49,6 +50,13 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
|
|||
log.debug('processing request', { method: 'initiateMultipartUpload' });
|
||||
const bucketName = request.bucketName;
|
||||
const objectKey = request.objectKey;
|
||||
|
||||
if (hasNonPrintables(objectKey)) {
|
||||
return callback(errors.InvalidInput.customizeDescription(
|
||||
'object keys cannot contain non-printable characters'
|
||||
));
|
||||
}
|
||||
|
||||
// Note that we are using the string set forth in constants.js
|
||||
// to split components in the storage
|
||||
// of each MPU. AWS does not restrict characters in object keys so
|
||||
|
|
|
@ -435,6 +435,8 @@ function objectCopy(authInfo, request, sourceBucket,
|
|||
storeMetadataParams.isNull = options.isNull;
|
||||
// eslint-disable-next-line
|
||||
storeMetadataParams.nullVersionId = options.nullVersionId;
|
||||
// eslint-disable-next-line
|
||||
storeMetadataParams.nullUploadId = options.nullUploadId;
|
||||
const dataToDelete = options.dataToDelete;
|
||||
return next(null, storeMetadataParams, destDataGetInfoArr,
|
||||
destObjMD, serverSideEncryption, destBucketMD,
|
||||
|
|
|
@ -10,6 +10,7 @@ const { checkQueryVersionId } = require('./apiUtils/object/versioning');
|
|||
const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const { validateHeaders } = require('./apiUtils/object/objectLockHelpers');
|
||||
const { hasNonPrintables } = require('../utilities/stringChecks');
|
||||
const kms = require('../kms/wrapper');
|
||||
const { config } = require('../Config');
|
||||
|
||||
|
@ -58,6 +59,13 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
const requestType = 'objectPut';
|
||||
const valParams = { authInfo, bucketName, objectKey, requestType, request };
|
||||
const canonicalID = authInfo.getCanonicalID();
|
||||
|
||||
if (hasNonPrintables(objectKey)) {
|
||||
return callback(errors.InvalidInput.customizeDescription(
|
||||
'object keys can not contain non-printable characters'
|
||||
));
|
||||
}
|
||||
|
||||
log.trace('owner canonicalID to send to data', { canonicalID });
|
||||
|
||||
return metadataValidateBucketAndObj(valParams, log,
|
||||
|
|
|
@ -190,6 +190,10 @@ class BucketFileInterface {
|
|||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
// Ignore the PUT done by AbortMPU
|
||||
if (params && params.isAbort) {
|
||||
return cb();
|
||||
}
|
||||
db.withRequestLogger(log)
|
||||
.put(objName, JSON.stringify(objVal), params, (err, data) => {
|
||||
if (err) {
|
||||
|
|
|
@ -79,7 +79,11 @@ const metastore = {
|
|||
|
||||
putObject: (bucketName, objName, objVal, params, log, cb) => {
|
||||
process.nextTick(() => {
|
||||
metastore.getBucketAttributes(bucketName, log, err => {
|
||||
// Ignore the PUT done by AbortMPU
|
||||
if (params && params.isAbort) {
|
||||
return cb(null);
|
||||
}
|
||||
return metastore.getBucketAttributes(bucketName, log, err => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
|
|
@ -96,7 +96,8 @@ const services = {
|
|||
const { objectKey, authInfo, size, contentMD5, metaHeaders,
|
||||
contentType, cacheControl, contentDisposition, contentEncoding,
|
||||
expires, multipart, headers, overrideMetadata, log,
|
||||
lastModifiedDate, versioning, versionId, tagging, taggingCopy,
|
||||
lastModifiedDate, versioning, versionId, uploadId,
|
||||
tagging, taggingCopy,
|
||||
replicationInfo, defaultRetention, dataStoreName,
|
||||
retentionMode, retentionDate, legalHold, originOp } = params;
|
||||
log.trace('storing object in metadata');
|
||||
|
@ -157,11 +158,16 @@ const services = {
|
|||
if (versionId || versionId === '') {
|
||||
options.versionId = versionId;
|
||||
}
|
||||
if (uploadId) {
|
||||
md.setUploadId(uploadId);
|
||||
options.replayId = uploadId;
|
||||
}
|
||||
// information to store about the version and the null version id
|
||||
// in the object metadata
|
||||
const { isNull, nullVersionId, isDeleteMarker } = params;
|
||||
const { isNull, nullVersionId, nullUploadId, isDeleteMarker } = params;
|
||||
md.setIsNull(isNull)
|
||||
.setNullVersionId(nullVersionId)
|
||||
.setNullUploadId(nullUploadId)
|
||||
.setIsDeleteMarker(isDeleteMarker);
|
||||
if (versionId && versionId !== 'null') {
|
||||
md.setVersionId(versionId);
|
||||
|
@ -735,6 +741,23 @@ const services = {
|
|||
metadata.deleteObjectMD(mpuBucketName, key, {}, log, callback);
|
||||
}, err => cb(err));
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} bucketName - MPU destination bucket name
|
||||
* @param {string} objectKey - MPU destination key
|
||||
* @param {string} uploadId - MPU uploadId
|
||||
* @param {object} log - werelogs logger
|
||||
* @param {Function} cb - callback called with possible error
|
||||
* @returns {undefined} -
|
||||
*/
|
||||
sendAbortMPUPut(bucketName, objectKey, uploadId, log, cb) {
|
||||
const storeParams = {
|
||||
isAbort: true,
|
||||
replayId: uploadId,
|
||||
};
|
||||
metadata.putObjectMD(bucketName, objectKey, {}, storeParams, log, cb);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = services;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* hasNonPrintables returns whether or not the given value
|
||||
* includes characters that are non-printable.
|
||||
*
|
||||
* @param {string} value - value to check for non-printables
|
||||
* @returns {Boolean} whether or not the value has non-printables
|
||||
*/
|
||||
function hasNonPrintables(value) {
|
||||
for (const char of value) {
|
||||
const codePoint = char.codePointAt(0);
|
||||
if (codePoint < 32 || codePoint === 127) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hasNonPrintables,
|
||||
};
|
|
@ -20,7 +20,7 @@
|
|||
"homepage": "https://github.com/scality/S3#readme",
|
||||
"dependencies": {
|
||||
"@hapi/joi": "^17.1.0",
|
||||
"arsenal": "github:scality/Arsenal#7.10.2",
|
||||
"arsenal": "github:scality/Arsenal#5772c37",
|
||||
"async": "~2.5.0",
|
||||
"aws-sdk": "2.905.0",
|
||||
"azure-storage": "^2.1.0",
|
||||
|
@ -34,7 +34,6 @@
|
|||
"level-mem": "^5.0.1",
|
||||
"moment": "^2.26.0",
|
||||
"npm-run-all": "~4.1.5",
|
||||
"sinon": "^9.0.2",
|
||||
"sproxydclient": "scality/sproxydclient#8.0.2",
|
||||
"utapi": "scality/utapi#7.10.2",
|
||||
"utf8": "~2.1.1",
|
||||
|
@ -56,6 +55,7 @@
|
|||
"mocha-junit-reporter": "^1.23.1",
|
||||
"mocha-multi-reporters": "^1.1.7",
|
||||
"node-mocks-http": "1.5.2",
|
||||
"sinon": "^13.0.1",
|
||||
"tv4": "^1.2.7"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -0,0 +1,437 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const { errors, versioning } = require('arsenal');
|
||||
const { config } = require('../../../../lib/Config');
|
||||
const INF_VID = versioning.VersionID.getInfVid(config.replicationGroupId);
|
||||
|
||||
const { processVersioningState, getMasterState,
|
||||
preprocessingVersioningDelete } =
|
||||
require('../../../../lib/api/apiUtils/object/versioning');
|
||||
|
||||
describe('versioning helpers', () => {
|
||||
describe('getMasterState+processVersioningState', () => {
|
||||
[
|
||||
{
|
||||
description: 'no prior version exists',
|
||||
objMD: null,
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'prior non-null object version exists',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'prior MPU object non-null version exists',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
uploadId: 'fooUploadId',
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'prior null object version exists',
|
||||
objMD: {
|
||||
versionId: 'vnull',
|
||||
isNull: true,
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
// instruct to first copy the null version onto a
|
||||
// newly created version key preserving the version ID
|
||||
storeOptions: {
|
||||
isNull: true,
|
||||
versionId: 'vnull',
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
delOptions: {
|
||||
versionId: 'vnull',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'prior MPU object null version exists',
|
||||
objMD: {
|
||||
versionId: 'vnull',
|
||||
isNull: true,
|
||||
uploadId: 'fooUploadId',
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
nullVersionId: 'vnull',
|
||||
nullUploadId: 'fooUploadId',
|
||||
},
|
||||
// instruct to first copy the null version onto a
|
||||
// newly created version key preserving the version ID
|
||||
storeOptions: {
|
||||
isNull: true,
|
||||
versionId: 'vnull',
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
delOptions: {
|
||||
versionId: 'vnull',
|
||||
replayId: 'fooUploadId',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'prior object exists, put before versioning was first enabled',
|
||||
objMD: {},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
nullVersionId: INF_VID,
|
||||
},
|
||||
// instruct to first copy the null version onto a
|
||||
// newly created version key as the oldest version
|
||||
storeOptions: {
|
||||
isNull: true,
|
||||
versionId: INF_VID,
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'prior MPU object exists, put before versioning ' +
|
||||
'was first enabled',
|
||||
objMD: {
|
||||
uploadId: 'fooUploadId',
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
nullVersionId: INF_VID,
|
||||
},
|
||||
// instruct to first copy the null version onto a
|
||||
// newly created version key as the oldest version
|
||||
storeOptions: {
|
||||
isNull: true,
|
||||
versionId: INF_VID,
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'prior non-null object version exists with ref to null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
delOptions: {
|
||||
versionId: 'vnull',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'prior MPU object non-null version exists with ' +
|
||||
'ref to null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
uploadId: 'fooUploadId',
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
delOptions: {
|
||||
versionId: 'vnull',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'prior object non-null version exists with ' +
|
||||
'ref to MPU null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
nullVersionId: 'vnull',
|
||||
nullUploadId: 'nullFooUploadId',
|
||||
},
|
||||
versioningEnabledExpectedRes: {
|
||||
options: {
|
||||
versioning: true,
|
||||
nullVersionId: 'vnull',
|
||||
nullUploadId: 'nullFooUploadId',
|
||||
},
|
||||
},
|
||||
versioningSuspendedExpectedRes: {
|
||||
options: {
|
||||
isNull: true,
|
||||
versionId: '',
|
||||
},
|
||||
delOptions: {
|
||||
versionId: 'vnull',
|
||||
replayId: 'nullFooUploadId',
|
||||
},
|
||||
},
|
||||
},
|
||||
].forEach(testCase =>
|
||||
['Enabled', 'Suspended'].forEach(versioningStatus => it(
|
||||
`${testCase.description}, versioning Status=${versioningStatus}`,
|
||||
() => {
|
||||
const mst = getMasterState(testCase.objMD);
|
||||
// stringify and parse to get rid of the "undefined"
|
||||
// properties, artifacts of how the function builds the
|
||||
// result
|
||||
const res = JSON.parse(
|
||||
JSON.stringify(
|
||||
processVersioningState(mst, versioningStatus)
|
||||
)
|
||||
);
|
||||
const expectedRes =
|
||||
testCase[`versioning${versioningStatus}ExpectedRes`];
|
||||
assert.deepStrictEqual(res, expectedRes);
|
||||
})));
|
||||
});
|
||||
|
||||
describe('preprocessingVersioningDelete', () => {
|
||||
[
|
||||
{
|
||||
description: 'no reqVersionId: no delete action',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
},
|
||||
expectedRes: {},
|
||||
},
|
||||
{
|
||||
description: 'delete non-null object version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
},
|
||||
reqVersionId: 'v1',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'v1',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'delete MPU object non-null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
uploadId: 'fooUploadId',
|
||||
},
|
||||
reqVersionId: 'v1',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'v1',
|
||||
replayId: 'fooUploadId',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'delete null object version',
|
||||
objMD: {
|
||||
versionId: 'vnull',
|
||||
isNull: true,
|
||||
},
|
||||
reqVersionId: 'null',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'vnull',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'delete MPU object null version',
|
||||
objMD: {
|
||||
versionId: 'vnull',
|
||||
isNull: true,
|
||||
uploadId: 'fooUploadId',
|
||||
},
|
||||
reqVersionId: 'null',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'vnull',
|
||||
replayId: 'fooUploadId',
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'delete object put before versioning was first enabled',
|
||||
objMD: {},
|
||||
reqVersionId: 'null',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'delete MPU object put before versioning was first enabled',
|
||||
objMD: {
|
||||
uploadId: 'fooUploadId',
|
||||
},
|
||||
reqVersionId: 'null',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'delete non-null object version with ref to null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
reqVersionId: 'v1',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'v1',
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'delete MPU object non-null version with ref to null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
uploadId: 'fooUploadId',
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
reqVersionId: 'v1',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'v1',
|
||||
replayId: 'fooUploadId',
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'delete non-null object version with ref to MPU null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
nullVersionId: 'vnull',
|
||||
nullUploadId: 'nullFooUploadId',
|
||||
},
|
||||
reqVersionId: 'v1',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'v1',
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'delete null object version from ref to null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
nullVersionId: 'vnull',
|
||||
},
|
||||
reqVersionId: 'null',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'vnull',
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'delete MPU object null version from ref to null version',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
nullVersionId: 'vnull',
|
||||
nullUploadId: 'nullFooUploadId',
|
||||
},
|
||||
reqVersionId: 'null',
|
||||
expectedRes: {
|
||||
deleteData: true,
|
||||
versionId: 'vnull',
|
||||
replayId: 'nullFooUploadId',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'delete null version that does not exist',
|
||||
objMD: {
|
||||
versionId: 'v1',
|
||||
},
|
||||
reqVersionId: 'null',
|
||||
expectedError: errors.NoSuchKey,
|
||||
},
|
||||
].forEach(testCase => it(testCase.description, done => {
|
||||
const mockBucketMD = {
|
||||
getVersioningConfiguration: () => ({ Status: 'Enabled' }),
|
||||
};
|
||||
preprocessingVersioningDelete(
|
||||
'foobucket', mockBucketMD, testCase.objMD,
|
||||
testCase.reqVersionId, null, (err, options) => {
|
||||
if (testCase.expectedError) {
|
||||
assert.strictEqual(err, testCase.expectedError);
|
||||
} else {
|
||||
assert.ifError(err);
|
||||
assert.deepStrictEqual(options, testCase.expectedRes);
|
||||
}
|
||||
done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -1,11 +1,13 @@
|
|||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
const { parseString } = require('xml2js');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const { errors } = require('arsenal');
|
||||
|
||||
const { cleanup, DummyRequestLogger } = require('../helpers');
|
||||
const { config } = require('../../../lib/Config');
|
||||
const services = require('../../../lib/services');
|
||||
const DummyRequest = require('../DummyRequest');
|
||||
const { bucketPut } = require('../../../lib/api/bucketPut');
|
||||
const initiateMultipartUpload
|
||||
|
@ -40,6 +42,7 @@ function _createAndAbortMpu(usEastSetting, fakeUploadID, locationConstraint,
|
|||
callback) {
|
||||
config.locationConstraints['us-east-1'].legacyAwsBehavior =
|
||||
usEastSetting;
|
||||
let uploadId;
|
||||
const post = '<?xml version="1.0" encoding="UTF-8"?>' +
|
||||
'<CreateBucketConfiguration ' +
|
||||
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
|
@ -54,7 +57,7 @@ function _createAndAbortMpu(usEastSetting, fakeUploadID, locationConstraint,
|
|||
(json, next) => {
|
||||
// use uploadId parsed from initiateMpu request to construct
|
||||
// uploadPart and deleteMpu requests
|
||||
const uploadId =
|
||||
uploadId =
|
||||
json.InitiateMultipartUploadResult.UploadId[0];
|
||||
const partBody = Buffer.from('I am a part\n', 'utf8');
|
||||
const partRequest = new DummyRequest({
|
||||
|
@ -89,7 +92,7 @@ function _createAndAbortMpu(usEastSetting, fakeUploadID, locationConstraint,
|
|||
}),
|
||||
(deleteMpuRequest, next) =>
|
||||
multipartDelete(authInfo, deleteMpuRequest, log, next),
|
||||
], callback);
|
||||
], err => callback(err, uploadId));
|
||||
}
|
||||
|
||||
describe('Multipart Delete API', () => {
|
||||
|
@ -136,4 +139,15 @@ describe('Multipart Delete API', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a PUT to bucketd with `isAbort` and `replayId`', done => {
|
||||
const spy = sinon.spy(services, 'sendAbortMPUPut');
|
||||
_createAndAbortMpu(true, false, eastLocation, (err, uploadId) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(spy.calledOnce, true);
|
||||
assert.strictEqual(
|
||||
spy.calledOnceWith(bucketName, objectKey, uploadId), true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -557,6 +557,7 @@ describe('Multipart Upload API', () => {
|
|||
assert(MD);
|
||||
assert.strictEqual(MD['x-amz-meta-stuff'],
|
||||
'I am some user metadata');
|
||||
assert.strictEqual(MD.uploadId, testUploadId);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -1375,7 +1376,7 @@ describe('Multipart Upload API', () => {
|
|||
});
|
||||
|
||||
it('should return no error if attempt to abort/delete ' +
|
||||
'a multipart upload that does not exist and not using' +
|
||||
'a multipart upload that does not exist and not using ' +
|
||||
'legacyAWSBehavior', done => {
|
||||
async.waterfall([
|
||||
next => bucketPut(authInfo, bucketPutRequest, log, next),
|
||||
|
@ -1629,7 +1630,7 @@ describe('Multipart Upload API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should throw an error on put of an object part with an invalid' +
|
||||
it('should throw an error on put of an object part with an invalid ' +
|
||||
'uploadId', done => {
|
||||
const testUploadId = 'invalidUploadID';
|
||||
const partRequest = new DummyRequest({
|
||||
|
@ -1793,12 +1794,14 @@ describe('complete mpu with versioning', () => {
|
|||
versioningTestUtils.createBucketPutVersioningReq(bucketName, 'Enabled');
|
||||
const suspendVersioningRequest = versioningTestUtils
|
||||
.createBucketPutVersioningReq(bucketName, 'Suspended');
|
||||
const testPutObjectRequests = objData.slice(0, 2).map(data =>
|
||||
versioningTestUtils.createPutObjectRequest(bucketName, objectKey,
|
||||
data));
|
||||
let testPutObjectRequests;
|
||||
|
||||
beforeEach(done => {
|
||||
cleanup();
|
||||
testPutObjectRequests = objData
|
||||
.slice(0, 2)
|
||||
.map(data => versioningTestUtils.createPutObjectRequest(
|
||||
bucketName, objectKey, data));
|
||||
bucketPut(authInfo, bucketPutRequest, log, done);
|
||||
});
|
||||
|
||||
|
@ -1808,7 +1811,58 @@ describe('complete mpu with versioning', () => {
|
|||
});
|
||||
|
||||
it('should delete null version when creating new null version, ' +
|
||||
'even when null version is not the latest version', done => {
|
||||
'when null version is the latest version', done => {
|
||||
async.waterfall([
|
||||
next => bucketPutVersioning(authInfo,
|
||||
suspendVersioningRequest, log, err => next(err)),
|
||||
next => initiateMultipartUpload(
|
||||
authInfo, initiateRequest, log, next),
|
||||
(result, corsHeaders, next) => parseString(result, next),
|
||||
(json, next) => {
|
||||
const partBody = objData[2];
|
||||
const testUploadId =
|
||||
json.InitiateMultipartUploadResult.UploadId[0];
|
||||
const partRequest = _createPutPartRequest(testUploadId, 1,
|
||||
partBody);
|
||||
objectPutPart(authInfo, partRequest, undefined, log,
|
||||
(err, eTag) => next(err, eTag, testUploadId));
|
||||
},
|
||||
(eTag, testUploadId, next) => {
|
||||
const origPutObject = metadataBackend.putObject;
|
||||
metadataBackend.putObject =
|
||||
(bucketName, objName, objVal, params, log, cb) => {
|
||||
assert.strictEqual(params.replayId, testUploadId);
|
||||
metadataBackend.putObject = origPutObject;
|
||||
metadataBackend.putObject(
|
||||
bucketName, objName, objVal, params, log, cb);
|
||||
};
|
||||
const parts = [{ partNumber: 1, eTag }];
|
||||
const completeRequest = _createCompleteMpuRequest(testUploadId,
|
||||
parts);
|
||||
completeMultipartUpload(authInfo, completeRequest, log,
|
||||
err => next(err, testUploadId));
|
||||
},
|
||||
(testUploadId, next) => {
|
||||
const origDeleteObject = metadataBackend.deleteObject;
|
||||
metadataBackend.deleteObject =
|
||||
(bucketName, objName, params, log, cb) => {
|
||||
assert.strictEqual(params.replayId, testUploadId);
|
||||
metadataBackend.deleteObject = origDeleteObject;
|
||||
metadataBackend.deleteObject(
|
||||
bucketName, objName, params, log, cb);
|
||||
};
|
||||
// overwrite null version with a non-MPU object
|
||||
objectPut(authInfo, testPutObjectRequests[1],
|
||||
undefined, log, err => next(err));
|
||||
},
|
||||
], err => {
|
||||
assert.ifError(err, `Unexpected err: ${err}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete null version when creating new null version, ' +
|
||||
'when null version is not the latest version', done => {
|
||||
async.waterfall([
|
||||
// putting null version: put obj before versioning configured
|
||||
next => objectPut(authInfo, testPutObjectRequests[0],
|
||||
|
@ -1836,18 +1890,39 @@ describe('complete mpu with versioning', () => {
|
|||
(err, eTag) => next(err, eTag, testUploadId));
|
||||
},
|
||||
(eTag, testUploadId, next) => {
|
||||
const origPutObject = metadataBackend.putObject;
|
||||
metadataBackend.putObject =
|
||||
(bucketName, objName, objVal, params, log, cb) => {
|
||||
assert.strictEqual(params.replayId, testUploadId);
|
||||
metadataBackend.putObject = origPutObject;
|
||||
metadataBackend.putObject(
|
||||
bucketName, objName, objVal, params, log, cb);
|
||||
};
|
||||
const parts = [{ partNumber: 1, eTag }];
|
||||
const completeRequest = _createCompleteMpuRequest(testUploadId,
|
||||
parts);
|
||||
completeMultipartUpload(authInfo, completeRequest, log, next);
|
||||
completeMultipartUpload(authInfo, completeRequest, log,
|
||||
err => next(err, testUploadId));
|
||||
},
|
||||
(testUploadId, next) => {
|
||||
versioningTestUtils.assertDataStoreValues(
|
||||
ds, [undefined, objData[1], objData[2]]);
|
||||
|
||||
const origDeleteObject = metadataBackend.deleteObject;
|
||||
metadataBackend.deleteObject =
|
||||
(bucketName, objName, params, log, cb) => {
|
||||
assert.strictEqual(params.replayId, testUploadId);
|
||||
metadataBackend.deleteObject = origDeleteObject;
|
||||
metadataBackend.deleteObject(
|
||||
bucketName, objName, params, log, cb);
|
||||
};
|
||||
// overwrite null version with a non-MPU object
|
||||
objectPut(authInfo, testPutObjectRequests[1],
|
||||
undefined, log, err => next(err));
|
||||
},
|
||||
], err => {
|
||||
assert.ifError(err, `Unexpected err: ${err}`);
|
||||
process.nextTick(() => {
|
||||
versioningTestUtils.assertDataStoreValues(ds, [undefined,
|
||||
objData[1], objData[2]]);
|
||||
done(err);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
const crypto = require('crypto');
|
||||
const { errors } = require('arsenal');
|
||||
const xml2js = require('xml2js');
|
||||
|
||||
const { bucketPut } = require('../../../lib/api/bucketPut');
|
||||
const bucketPutACL = require('../../../lib/api/bucketPutACL');
|
||||
|
@ -8,6 +11,11 @@ const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../helpers');
|
|||
const objectPut = require('../../../lib/api/objectPut');
|
||||
const objectDelete = require('../../../lib/api/objectDelete');
|
||||
const objectGet = require('../../../lib/api/objectGet');
|
||||
const initiateMultipartUpload
|
||||
= require('../../../lib/api/initiateMultipartUpload');
|
||||
const objectPutPart = require('../../../lib/api/objectPutPart');
|
||||
const completeMultipartUpload
|
||||
= require('../../../lib/api/completeMultipartUpload');
|
||||
const DummyRequest = require('../DummyRequest');
|
||||
|
||||
const log = new DummyRequestLogger();
|
||||
|
@ -69,6 +77,14 @@ describe('objectDelete API', () => {
|
|||
url: `/${bucketName}/${objectKey}`,
|
||||
});
|
||||
|
||||
const initiateMPURequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: `/${objectKey}?uploads`,
|
||||
};
|
||||
|
||||
it('should delete an object', done => {
|
||||
bucketPut(authInfo, testBucketPutRequest, log, () => {
|
||||
objectPut(authInfo, testPutObjectRequest,
|
||||
|
@ -112,6 +128,57 @@ describe('objectDelete API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should delete a multipart upload', done => {
|
||||
const partBody = Buffer.from('I am a part\n', 'utf8');
|
||||
let testUploadId;
|
||||
let calculatedHash;
|
||||
async.waterfall([
|
||||
next => bucketPut(authInfo, testBucketPutRequest, log, next),
|
||||
(corsHeaders, next) => initiateMultipartUpload(authInfo,
|
||||
initiateMPURequest, log, next),
|
||||
(result, corsHeaders, next) => xml2js.parseString(result, next),
|
||||
(json, next) => {
|
||||
testUploadId = json.InitiateMultipartUploadResult.UploadId[0];
|
||||
const md5Hash = crypto.createHash('md5').update(partBody);
|
||||
calculatedHash = md5Hash.digest('hex');
|
||||
const partRequest = new DummyRequest({
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: `/${objectKey}?partNumber=1&uploadId=${testUploadId}`,
|
||||
query: {
|
||||
partNumber: '1',
|
||||
uploadId: testUploadId,
|
||||
},
|
||||
calculatedHash,
|
||||
}, partBody);
|
||||
objectPutPart(authInfo, partRequest, undefined, log, next);
|
||||
},
|
||||
(hexDigest, corsHeaders, next) => {
|
||||
const completeBody = '<CompleteMultipartUpload>' +
|
||||
'<Part>' +
|
||||
'<PartNumber>1</PartNumber>' +
|
||||
`<ETag>"${calculatedHash}"</ETag>` +
|
||||
'</Part>' +
|
||||
'</CompleteMultipartUpload>';
|
||||
const completeRequest = {
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey,
|
||||
parsedHost: 's3.amazonaws.com',
|
||||
url: `/${objectKey}?uploadId=${testUploadId}`,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
query: { uploadId: testUploadId },
|
||||
post: completeBody,
|
||||
};
|
||||
completeMultipartUpload(authInfo, completeRequest, log, next);
|
||||
},
|
||||
(result, resHeaders, next) =>
|
||||
objectDelete(authInfo, testDeleteRequest, log, next),
|
||||
], done);
|
||||
});
|
||||
|
||||
it('should prevent anonymous user deleteObject API access', done => {
|
||||
const publicAuthInfo = makeAuthInfo(constants.publicId);
|
||||
bucketPut(authInfo, testBucketPutRequest, log, () => {
|
||||
|
|
72
yarn.lock
72
yarn.lock
|
@ -105,24 +105,31 @@
|
|||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1"
|
||||
integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==
|
||||
|
||||
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1":
|
||||
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0":
|
||||
version "1.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b"
|
||||
integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==
|
||||
dependencies:
|
||||
type-detect "4.0.8"
|
||||
|
||||
"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
|
||||
integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==
|
||||
"@sinonjs/commons@^1.8.3":
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
|
||||
integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
|
||||
dependencies:
|
||||
type-detect "4.0.8"
|
||||
|
||||
"@sinonjs/fake-timers@>=5", "@sinonjs/fake-timers@^9.0.0":
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.0.tgz#8c92c56f195e0bed4c893ba59c8e3d55831ca0df"
|
||||
integrity sha512-M8vapsv9qQupMdzrVzkn5rb9jG7aUTEPAZdMtME2PuBaefksFZVE2C1g4LBRTkF/k3nRDNbDc5tp5NFC1PEYxA==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@sinonjs/samsam@^5.3.1":
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f"
|
||||
integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==
|
||||
"@sinonjs/samsam@^6.1.1":
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.1.1.tgz#627f7f4cbdb56e6419fa2c1a3e4751ce4f6a00b1"
|
||||
integrity sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.6.0"
|
||||
lodash.get "^4.4.2"
|
||||
|
@ -629,9 +636,9 @@ arraybuffer.slice@~0.0.7:
|
|||
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
|
||||
integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==
|
||||
|
||||
"arsenal@github:scality/Arsenal#7.10.2":
|
||||
version "7.10.2"
|
||||
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/07a110ff86a67884d9f992e4570fddd529435729"
|
||||
"arsenal@github:scality/Arsenal#5772c37":
|
||||
version "7.10.4"
|
||||
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/5772c37b00505ba99eb339c161f8299c2e60449a"
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.1.0"
|
||||
JSONStream "^1.0.0"
|
||||
|
@ -1825,11 +1832,16 @@ diff@1.4.0:
|
|||
resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf"
|
||||
integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8=
|
||||
|
||||
diff@^4.0.1, diff@^4.0.2:
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||
|
||||
diff@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
|
||||
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
|
||||
|
||||
diskusage@1.1.3, diskusage@^1.1.1, diskusage@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/diskusage/-/diskusage-1.1.3.tgz#680d7dbf1b679168a195c9240eb3552cbd2c067b"
|
||||
|
@ -4538,13 +4550,13 @@ nice-try@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
nise@^4.0.4:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6"
|
||||
integrity sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==
|
||||
nise@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.1.tgz#ac4237e0d785ecfcb83e20f389185975da5c31f3"
|
||||
integrity sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
"@sinonjs/fake-timers" "^6.0.0"
|
||||
"@sinonjs/commons" "^1.8.3"
|
||||
"@sinonjs/fake-timers" ">=5"
|
||||
"@sinonjs/text-encoding" "^0.7.1"
|
||||
just-extend "^4.0.2"
|
||||
path-to-regexp "^1.7.0"
|
||||
|
@ -5657,17 +5669,17 @@ simple-swizzle@^0.2.2:
|
|||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
sinon@^9.0.2:
|
||||
version "9.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b"
|
||||
integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==
|
||||
sinon@^13.0.1:
|
||||
version "13.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sinon/-/sinon-13.0.1.tgz#2a568beca2084c48985dd98e276e065c81738e3c"
|
||||
integrity sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.8.1"
|
||||
"@sinonjs/fake-timers" "^6.0.1"
|
||||
"@sinonjs/samsam" "^5.3.1"
|
||||
diff "^4.0.2"
|
||||
nise "^4.0.4"
|
||||
supports-color "^7.1.0"
|
||||
"@sinonjs/commons" "^1.8.3"
|
||||
"@sinonjs/fake-timers" "^9.0.0"
|
||||
"@sinonjs/samsam" "^6.1.1"
|
||||
diff "^5.0.0"
|
||||
nise "^5.1.1"
|
||||
supports-color "^7.2.0"
|
||||
|
||||
slice-ansi@0.0.4:
|
||||
version "0.0.4"
|
||||
|
@ -6397,7 +6409,7 @@ supports-color@^5.3.0:
|
|||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^7.1.0:
|
||||
supports-color@^7.1.0, supports-color@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
|
||||
|
|
Loading…
Reference in New Issue