diff --git a/lib/api/listParts.js b/lib/api/listParts.js index 9ceec93e6e..ffff63ed0b 100644 --- a/lib/api/listParts.js +++ b/lib/api/listParts.js @@ -14,6 +14,7 @@ const { pushMetric } = require('../utapi/utilities'); const monitoring = require('../utilities/monitoringHandler'); const { data } = require('../data/wrapper'); const { setExpirationHeaders } = require('./apiUtils/object/expirationHeaders'); +const { algorithms } = require('./apiUtils/integrity/validateChecksums'); /* Format of xml response: @@ -64,6 +65,40 @@ function buildXML(xmlParams, xml, encodingFn) { }); } +function getPartNumber(item, splitter, splitterLen) { + // Metadata listings identify parts by key; AWS external listings are + // already normalized by Arsenal with a partNumber field. + if (item.key) { + // key form: {uploadId}{splitter}{partNumber} + const key = item.key; + const index = key.lastIndexOf(splitter); + if (index !== -1) { + return parseInt(key.substring(index + splitterLen), 10); + } + } + return item.partNumber; +} + +function getPartChecksum(item) { + return { + checksumAlgorithm: item.value.ChecksumAlgorithm, + checksumValue: item.value.ChecksumValue, + }; +} + +function getPartChecksumXML(checksumAlgorithm, checksumValue) { + if (!checksumAlgorithm || !checksumValue) { + return undefined; + } + const algorithm = checksumAlgorithm.toLowerCase(); + const xmlTag = algorithms[algorithm] && + algorithms[algorithm].getObjectAttributesXMLTag; + if (!xmlTag) { + return undefined; + } + return { tag: xmlTag, value: checksumValue }; +} + /** * listParts - List parts of an open multipart upload * @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info @@ -203,25 +238,15 @@ function listParts(authInfo, request, log, callback) { const isTruncated = storedParts.IsTruncated; const splitterLen = splitter.length; const partListing = storedParts.Contents.map(item => { - // key form: - // - {uploadId} - // - {splitter} - // - {partNumber} - let partNumber; - if (item.key) { - const index = item.key.lastIndexOf(splitter); - partNumber = - parseInt(item.key.substring(index + splitterLen), 10); - } else { - // if partListing came from real AWS backend, - // item.partNumber is present instead of item.key - partNumber = item.partNumber; - } + const value = item.value; + const partChecksum = getPartChecksum(item); return { - partNumber, - lastModified: item.value.LastModified, - ETag: item.value.ETag, - size: item.value.Size, + partNumber: getPartNumber(item, splitter, splitterLen), + lastModified: value.LastModified, + ETag: value.ETag, + size: value.Size, + checksumAlgorithm: partChecksum.checksumAlgorithm, + checksumValue: partChecksum.checksumValue, }; }); const lastPartShown = partListing.length > 0 ? @@ -246,6 +271,16 @@ function listParts(authInfo, request, log, callback) { { tag: 'Key', value: objectKey }, { tag: 'UploadId', value: uploadId }, ], xml, encodingFn); + const showChecksum = !mpuOverviewObj.checksumIsDefault && + mpuOverviewObj.checksumAlgorithm && + mpuOverviewObj.checksumType; + if (showChecksum) { + buildXML([ + { tag: 'ChecksumAlgorithm', + value: mpuOverviewObj.checksumAlgorithm.toUpperCase() }, + { tag: 'ChecksumType', value: mpuOverviewObj.checksumType }, + ], xml, encodingFn); + } xml.push(''); buildXML([ { tag: 'ID', value: mpuOverviewObj.initiatorID }, @@ -271,6 +306,10 @@ function listParts(authInfo, request, log, callback) { ], xml, encodingFn); partListing.forEach(part => { + const partChecksumXML = showChecksum ? + getPartChecksumXML( + part.checksumAlgorithm, part.checksumValue) : + undefined; xml.push(''); buildXML([ { tag: 'PartNumber', value: part.partNumber }, @@ -278,6 +317,9 @@ function listParts(authInfo, request, log, callback) { { tag: 'ETag', value: `"${part.ETag}"` }, { tag: 'Size', value: part.size }, ], xml, encodingFn); + if (partChecksumXML) { + buildXML([partChecksumXML], xml, encodingFn); + } xml.push(''); }); xml.push(''); diff --git a/lib/services.js b/lib/services.js index f08a00fc88..97116329a4 100644 --- a/lib/services.js +++ b/lib/services.js @@ -780,6 +780,9 @@ const services = { initiated: storedMetadata.initiated, controllingLocationConstraint: storedMetadata.controllingLocationConstraint, + checksumAlgorithm: storedMetadata.checksumAlgorithm, + checksumType: storedMetadata.checksumType, + checksumIsDefault: storedMetadata.checksumIsDefault, }; const tagging = storedMetadata['x-amz-tagging']; diff --git a/package.json b/package.json index d5cf1135d9..e2bfbe2f15 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@azure/storage-blob": "^12.28.0", "@hapi/joi": "^17.1.1", "@smithy/node-http-handler": "^3.0.0", - "arsenal": "git+https://github.com/scality/Arsenal#8.3.10", + "arsenal": "git+https://github.com/scality/Arsenal#8.3.11", "async": "2.6.4", "bucketclient": "scality/bucketclient#8.2.7", "bufferutil": "^4.0.8", diff --git a/tests/functional/aws-node-sdk/test/object/listPartsChecksum.js b/tests/functional/aws-node-sdk/test/object/listPartsChecksum.js new file mode 100644 index 0000000000..4c8d5a33dd --- /dev/null +++ b/tests/functional/aws-node-sdk/test/object/listPartsChecksum.js @@ -0,0 +1,174 @@ +const assert = require('assert'); +const { + AbortMultipartUploadCommand, + CreateBucketCommand, + CreateMultipartUploadCommand, + DeleteBucketCommand, + ListPartsCommand, + UploadPartCommand, +} = require('@aws-sdk/client-s3'); + +const withV4 = require('../support/withV4'); +const BucketUtility = require('../../lib/utility/bucket-util'); +const { algorithms } = require('../../../../../lib/api/apiUtils/integrity/validateChecksums'); + +const bucket = `list-parts-checksum-test-${Date.now()}`; +const defaultKey = 'default-no-checksum'; +const checksumBodies = [ + Buffer.from('first checksummed part', 'utf8'), + Buffer.from('second checksummed part', 'utf8'), +]; +const defaultBodies = [ + Buffer.from('first default checksum part', 'utf8'), + Buffer.from('second default checksum part', 'utf8'), +]; +const checksumFieldByAlgorithm = { + CRC32: 'ChecksumCRC32', + CRC32C: 'ChecksumCRC32C', + CRC64NVME: 'ChecksumCRC64NVME', + SHA1: 'ChecksumSHA1', + SHA256: 'ChecksumSHA256', +}; +const checksumTypesByAlgorithm = { + CRC32: ['COMPOSITE', 'FULL_OBJECT'], + CRC32C: ['COMPOSITE', 'FULL_OBJECT'], + CRC64NVME: ['FULL_OBJECT'], + SHA1: ['COMPOSITE'], + SHA256: ['COMPOSITE'], +}; + +function assertNoListPartsChecksum(partList) { + assert.strictEqual(partList.ChecksumAlgorithm, undefined); + assert.strictEqual(partList.ChecksumType, undefined); + partList.Parts.forEach(part => { + assert.strictEqual(part.ChecksumCRC32, undefined); + assert.strictEqual(part.ChecksumCRC32C, undefined); + assert.strictEqual(part.ChecksumCRC64NVME, undefined); + assert.strictEqual(part.ChecksumSHA1, undefined); + assert.strictEqual(part.ChecksumSHA256, undefined); + }); +} + +describe('ListParts checksum fields', () => + withV4(sigCfg => { + let bucketUtil; + let s3; + const openMPUs = []; + + async function abortUpload(key, uploadId) { + await s3.send(new AbortMultipartUploadCommand({ + Bucket: bucket, + Key: key, + UploadId: uploadId, + })); + const index = openMPUs.findIndex(upload => + upload.key === key && upload.uploadId === uploadId); + if (index !== -1) { + openMPUs.splice(index, 1); + } + } + + before(async () => { + bucketUtil = new BucketUtility('default', { + ...sigCfg, + requestChecksumCalculation: 'WHEN_REQUIRED', + }); + s3 = bucketUtil.s3; + await s3.send(new CreateBucketCommand({ Bucket: bucket })); + }); + + after(async () => { + await Promise.all(openMPUs.map(upload => + s3.send(new AbortMultipartUploadCommand({ + Bucket: bucket, + Key: upload.key, + UploadId: upload.uploadId, + })).catch(() => undefined))); + await bucketUtil.empty(bucket); + await s3.send(new DeleteBucketCommand({ Bucket: bucket })); + }); + + for (const [checksumAlgorithm, checksumTypes] of + Object.entries(checksumTypesByAlgorithm)) { + for (const checksumType of checksumTypes) { + it(`should include ${checksumAlgorithm}/${checksumType} root ` + + 'and part checksum fields', async () => { + const key = `explicit-${checksumAlgorithm}-${checksumType}`; + const checksumField = + checksumFieldByAlgorithm[checksumAlgorithm]; + const internalAlgorithm = checksumAlgorithm.toLowerCase(); + const { UploadId } = + await s3.send(new CreateMultipartUploadCommand({ + Bucket: bucket, + Key: key, + ChecksumAlgorithm: checksumAlgorithm, + ChecksumType: checksumType, + })); + openMPUs.push({ key, uploadId: UploadId }); + + const partChecksums = await Promise.all( + checksumBodies.map(body => + algorithms[internalAlgorithm].digest(body))); + + await Promise.all(checksumBodies.map((body, index) => + s3.send(new UploadPartCommand({ + Bucket: bucket, + Key: key, + UploadId, + PartNumber: index + 1, + Body: body, + [checksumField]: partChecksums[index], + })))); + + const partList = await s3.send(new ListPartsCommand({ + Bucket: bucket, + Key: key, + UploadId, + })); + + assert.strictEqual(partList.ChecksumAlgorithm, + checksumAlgorithm); + assert.strictEqual(partList.ChecksumType, checksumType); + assert.strictEqual(partList.Parts.length, + checksumBodies.length); + partList.Parts.forEach((part, index) => { + assert.strictEqual(part.PartNumber, index + 1); + assert.strictEqual(part[checksumField], + partChecksums[index]); + }); + + await abortUpload(key, UploadId); + }); + } + } + + it('should omit default checksum fields when no checksum headers are sent', + async () => { + const { UploadId } = await s3.send(new CreateMultipartUploadCommand({ + Bucket: bucket, + Key: defaultKey, + })); + openMPUs.push({ key: defaultKey, uploadId: UploadId }); + + await Promise.all(defaultBodies.map((body, index) => + s3.send(new UploadPartCommand({ + Bucket: bucket, + Key: defaultKey, + UploadId, + PartNumber: index + 1, + Body: body, + })))); + + const partList = await s3.send(new ListPartsCommand({ + Bucket: bucket, + Key: defaultKey, + UploadId, + })); + + assert.strictEqual(partList.Parts.length, defaultBodies.length); + assertNoListPartsChecksum(partList); + + await abortUpload(defaultKey, UploadId); + }); + }) +); diff --git a/tests/functional/aws-node-sdk/test/object/mpuUploadPartChecksum.js b/tests/functional/aws-node-sdk/test/object/mpuUploadPartChecksum.js index 4e44187b90..1bcf410912 100644 --- a/tests/functional/aws-node-sdk/test/object/mpuUploadPartChecksum.js +++ b/tests/functional/aws-node-sdk/test/object/mpuUploadPartChecksum.js @@ -5,6 +5,7 @@ const { AbortMultipartUploadCommand, UploadPartCommand, DeleteBucketCommand, + ListPartsCommand, } = require('@aws-sdk/client-s3'); const withV4 = require('../support/withV4'); @@ -42,6 +43,18 @@ before(async () => { } }); +async function assertPartChecksumStored(s3, uploadId, partNumber, + checksumHeader, expectedChecksum) { + const listRes = await s3.send(new ListPartsCommand({ + Bucket: bucket, + Key: key, + UploadId: uploadId, + })); + const found = listRes.Parts.find(part => part.PartNumber === partNumber); + assert(found, `Expected part ${partNumber} in ListParts response`); + assert.strictEqual(found[checksumHeader], expectedChecksum); +} + describe('UploadPart checksum validation', () => withV4(sigCfg => { let bucketUtil; @@ -82,12 +95,15 @@ describe('UploadPart checksum validation', () => }); it(`should accept ${mpuAlgo} with correct digest`, async () => { + const partNumber = 1; const res = await s3.send(new UploadPartCommand({ Bucket: bucket, Key: key, UploadId: uploadId, - PartNumber: 1, Body: partBody, + PartNumber: partNumber, Body: partBody, [checksumField[mpuAlgo]]: correctDigest[mpuAlgo], })); assert.strictEqual(res[checksumField[mpuAlgo]], correctDigest[mpuAlgo]); + await assertPartChecksumStored(s3, uploadId, partNumber, + checksumField[mpuAlgo], correctDigest[mpuAlgo]); }); it(`should reject ${mpuAlgo} with wrong digest (BadDigest)`, async () => { diff --git a/tests/unit/api/listParts.js b/tests/unit/api/listParts.js index 48a9668e11..edcab83cd3 100644 --- a/tests/unit/api/listParts.js +++ b/tests/unit/api/listParts.js @@ -7,6 +7,7 @@ const constants = require('../../../constants'); const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../helpers'); const { metadata: inMemMetadata } = require('arsenal').storage.metadata.inMemory.metadata; const listParts = require('../../../lib/api/listParts'); +const { data } = require('../../../lib/data/wrapper'); const metadata = require('../metadataswitch'); const log = new DummyRequestLogger(); @@ -31,6 +32,41 @@ const partTwoKey = '4db92ccc-d89d-49d3-9fa6-e9c2c1eb31b0' + const partThreeKey = `4db92ccc-d89d-49d3-9fa6-e9c2c1eb31b0${splitter}00003`; const partFourKey = `4db92ccc-d89d-49d3-9fa6-e9c2c1eb31b0${splitter}00004`; const partFiveKey = `4db92ccc-d89d-49d3-9fa6-e9c2c1eb31b0${splitter}00005`; +const partKeys = [partOneKey, partTwoKey, partThreeKey, partFourKey, + partFiveKey]; +const partChecksumValues = { + crc32: 'AAAAAA==', + crc32c: 'AQAAAA==', + crc64nvme: 'AAAAAAAAAAA=', + sha1: 'AAAAAAAAAAAAAAAAAAAAAAAAAAA=', + sha256: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', +}; + +function makeListRequest() { + return { + bucketName, + namespace, + objectKey: uploadKey, + url: `/${uploadKey}?uploadId=${uploadId}`, + headers: { host: `${bucketName}.s3.amazonaws.com` }, + query: { uploadId }, + actionImplicitDenies: false, + }; +} + +function setOverviewChecksum(checksumAlgorithm, checksumType, + checksumIsDefault) { + const overview = inMemMetadata.keyMaps.get(mpuBucket).get(overviewKey); + overview.checksumAlgorithm = checksumAlgorithm; + overview.checksumType = checksumType; + overview.checksumIsDefault = checksumIsDefault; +} + +function setPartChecksum(partKey, checksumAlgorithm, checksumValue) { + const part = inMemMetadata.keyMaps.get(mpuBucket).get(partKey); + part.checksumAlgorithm = checksumAlgorithm; + part.checksumValue = checksumValue; +} describe('List Parts API', () => { beforeEach(done => { @@ -107,15 +143,7 @@ describe('List Parts API', () => { }); it('should list all parts of a multipart upload', done => { - const listRequest = { - bucketName, - namespace, - objectKey: uploadKey, - url: `/${uploadKey}?uploadId=${uploadId}`, - headers: { host: `${bucketName}.s3.amazonaws.com` }, - query: { uploadId }, - actionImplicitDenies: false, - }; + const listRequest = makeListRequest(); listParts(authInfo, listRequest, log, (err, xml) => { assert.strictEqual(err, null); @@ -145,6 +173,165 @@ describe('List Parts API', () => { lastPieceETag); assert.strictEqual(json.ListPartsResult.Part[4].Size[0], '18'); assert.strictEqual(json.ListPartsResult.Part.length, 5); + assert.strictEqual(json.ListPartsResult.ChecksumAlgorithm, + undefined); + assert.strictEqual(json.ListPartsResult.ChecksumType, + undefined); + assert.strictEqual(json.ListPartsResult.Part[0].ChecksumSHA256, + undefined); + done(); + }); + }); + }); + + it('should include root and part checksum fields', done => { + setOverviewChecksum('sha256', 'COMPOSITE', false); + setPartChecksum(partOneKey, 'sha256', partChecksumValues.sha256); + + const listRequest = makeListRequest(); + + listParts(authInfo, listRequest, log, (err, xml) => { + assert.strictEqual(err, null); + parseString(xml, (err, json) => { + assert.strictEqual(err, null); + assert.strictEqual(json.ListPartsResult.ChecksumAlgorithm[0], + 'SHA256'); + assert.strictEqual(json.ListPartsResult.ChecksumType[0], + 'COMPOSITE'); + assert.strictEqual( + json.ListPartsResult.Part[0].ChecksumSHA256[0], + partChecksumValues.sha256); + assert.strictEqual(json.ListPartsResult.Part[1].ChecksumSHA256, + undefined); + done(); + }); + }); + }); + + it('should omit default CRC64NVME checksum fields', done => { + setOverviewChecksum('crc64nvme', 'FULL_OBJECT', true); + setPartChecksum(partOneKey, 'crc64nvme', + partChecksumValues.crc64nvme); + + const listRequest = makeListRequest(); + + listParts(authInfo, listRequest, log, (err, xml) => { + assert.strictEqual(err, null); + parseString(xml, (err, json) => { + assert.strictEqual(err, null); + assert.strictEqual(json.ListPartsResult.ChecksumAlgorithm, + undefined); + assert.strictEqual(json.ListPartsResult.ChecksumType, + undefined); + assert.strictEqual( + json.ListPartsResult.Part[0].ChecksumCRC64NVME, + undefined); + done(); + }); + }); + }); + + it('should omit root checksum fields if only checksumAlgorithm is set', + done => { + setOverviewChecksum('sha256', undefined, false); + + const listRequest = makeListRequest(); + + listParts(authInfo, listRequest, log, (err, xml) => { + assert.strictEqual(err, null); + parseString(xml, (err, json) => { + assert.strictEqual(err, null); + assert.strictEqual(json.ListPartsResult.ChecksumAlgorithm, + undefined); + assert.strictEqual(json.ListPartsResult.ChecksumType, + undefined); + done(); + }); + }); + }); + + it('should omit root checksum fields if only checksumType is set', done => { + setOverviewChecksum(undefined, 'COMPOSITE', false); + + const listRequest = makeListRequest(); + + listParts(authInfo, listRequest, log, (err, xml) => { + assert.strictEqual(err, null); + parseString(xml, (err, json) => { + assert.strictEqual(err, null); + assert.strictEqual(json.ListPartsResult.ChecksumAlgorithm, + undefined); + assert.strictEqual(json.ListPartsResult.ChecksumType, + undefined); + done(); + }); + }); + }); + + it('should render checksum tags for every supported part algorithm', + done => { + setOverviewChecksum('sha256', 'COMPOSITE', false); + + const checksumCases = [ + ['crc32', 'ChecksumCRC32'], + ['crc32c', 'ChecksumCRC32C'], + ['crc64nvme', 'ChecksumCRC64NVME'], + ['sha1', 'ChecksumSHA1'], + ['sha256', 'ChecksumSHA256'], + ]; + checksumCases.forEach(([algorithm], index) => { + setPartChecksum(partKeys[index], algorithm, + partChecksumValues[algorithm]); + }); + + const listRequest = makeListRequest(); + + listParts(authInfo, listRequest, log, (err, xml) => { + assert.strictEqual(err, null); + parseString(xml, (err, json) => { + assert.strictEqual(err, null); + checksumCases.forEach(([algorithm, xmlTag], index) => { + assert.strictEqual( + json.ListPartsResult.Part[index][xmlTag][0], + partChecksumValues[algorithm]); + }); + done(); + }); + }); + }); + + it('should render external backend checksum fields', done => { + setOverviewChecksum('sha256', 'COMPOSITE', false); + + const originalListParts = data.listParts; + data.listParts = (mpuInfo, request, lcCheckFn, log, callback) => + callback(null, { + IsTruncated: false, + Contents: [{ + partNumber: 1, + value: { + LastModified: '2015-11-30T22:41:18.658Z', + ETag: 'f3a9fb2071d3503b703938a74eb99846', + Size: 6000000, + ChecksumAlgorithm: 'sha256', + ChecksumValue: partChecksumValues.sha256, + }, + }], + }); + + const listRequest = makeListRequest(); + + listParts(authInfo, listRequest, log, (err, xml) => { + data.listParts = originalListParts; + assert.strictEqual(err, null); + parseString(xml, (err, json) => { + assert.strictEqual(err, null); + assert.strictEqual(json.ListPartsResult.Part.length, 1); + assert.strictEqual(json.ListPartsResult.Part[0].PartNumber[0], + '1'); + assert.strictEqual( + json.ListPartsResult.Part[0].ChecksumSHA256[0], + partChecksumValues.sha256); done(); }); }); diff --git a/tests/unit/lib/services.spec.js b/tests/unit/lib/services.spec.js index 025814bd3d..f76ee39502 100644 --- a/tests/unit/lib/services.spec.js +++ b/tests/unit/lib/services.spec.js @@ -5,7 +5,8 @@ const { versioning } = require('arsenal'); const services = require('../../../lib/services'); const metadata = require('../../../lib/metadata/wrapper'); const acl = require('../../../lib/metadata/acl'); -const { DummyRequestLogger } = require('../helpers'); +const constants = require('../../../constants'); +const { DummyRequestLogger, makeAuthInfo } = require('../helpers'); const { VersionId } = versioning.VersioningConstants; @@ -234,4 +235,87 @@ describe('services', () => { }); }); }); + + describe('metadataValidateMultipart checksum fields', () => { + const uploadId = 'test-upload-id'; + const authInfo = makeAuthInfo('accessKey1'); + const ownerID = authInfo.getCanonicalID(); + const mpuBucketName = `${constants.mpuBucketPrefix}${bucketName}`; + const mpuOverviewKey = `overview${constants.splitter}${objectKey}` + + `${constants.splitter}${uploadId}`; + const mpuBucket = { + getName: () => mpuBucketName, + getMdBucketModelVersion: () => 2, + getOwner: () => ownerID, + }; + const storedMetadata = { + key: objectKey, + id: uploadId, + eventualStorageBucket: bucketName, + initiator: { + ID: ownerID, + DisplayName: 'initiator', + }, + 'owner-id': ownerID, + 'owner-display-name': 'owner', + 'x-amz-storage-class': 'STANDARD', + initiated: '2026-04-23T00:00:00.000Z', + controllingLocationConstraint: 'us-east-1', + }; + + function validateMultipart(storedMetadataOverride, cb) { + sinon.stub(metadata, 'getBucket').callsFake((name, reqLog, done) => { + assert.strictEqual(name, mpuBucketName); + done(null, mpuBucket); + }); + sinon.stub(metadata, 'getObjectMD') + .callsFake((bucket, key, params, reqLog, done) => { + assert.strictEqual(bucket, mpuBucketName); + assert.strictEqual(key, mpuOverviewKey); + done(null, { + ...storedMetadata, + ...storedMetadataOverride, + }); + }); + + services.metadataValidateMultipart({ + bucketName, + objectKey, + uploadId, + authInfo, + requestType: 'listParts', + log, + }, cb); + } + + it('should expose checksum fields from stored MPU overview metadata', + done => { + validateMultipart({ + checksumAlgorithm: 'sha256', + checksumType: 'COMPOSITE', + checksumIsDefault: false, + }, (err, bucket, mpuOverview, returnedStoredMetadata) => { + assert.ifError(err); + assert.strictEqual(bucket, mpuBucket); + assert.strictEqual(returnedStoredMetadata.checksumAlgorithm, + 'sha256'); + assert.strictEqual(mpuOverview.checksumAlgorithm, 'sha256'); + assert.strictEqual(mpuOverview.checksumType, 'COMPOSITE'); + assert.strictEqual(mpuOverview.checksumIsDefault, false); + done(); + }); + }); + + it('should leave checksum fields undefined for legacy MPU overview metadata', + done => { + validateMultipart({}, (err, bucket, mpuOverview) => { + assert.ifError(err); + assert.strictEqual(bucket, mpuBucket); + assert.strictEqual(mpuOverview.checksumAlgorithm, undefined); + assert.strictEqual(mpuOverview.checksumType, undefined); + assert.strictEqual(mpuOverview.checksumIsDefault, undefined); + done(); + }); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index acde8a0a9f..ea0139a2e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3251,6 +3251,14 @@ httpagent "github:scality/httpagent#1.1.0" werelogs "github:scality/werelogs#8.2.2" +"@scality/hdclient@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@scality/hdclient/-/hdclient-1.3.2.tgz#544d08a5b88869a9c30107c05d774e13595966fb" + integrity sha512-voy67AlH1irNmaXno0KP/KpiEBYzzdW8EoGjBXsVztLFjGe+RQnNtAyzCn05secZZy43jBYLOrZ7032gorxvrg== + dependencies: + httpagent "github:scality/httpagent#1.1.0" + werelogs "github:scality/werelogs#8.2.2" + "@senx/warp10@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@senx/warp10/-/warp10-2.0.3.tgz#dcce3890d491c6380f2967abcf126909ed208969" @@ -6135,9 +6143,9 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.3.10": - version "8.3.10" - resolved "git+https://github.com/scality/Arsenal#600fb069bdfb82106513a897658a027fdb96afbd" +"arsenal@git+https://github.com/scality/Arsenal#8.3.11": + version "8.3.11" + resolved "git+https://github.com/scality/Arsenal#1acb14fd1d28afb4b62be99365f9da14c459b8bf" dependencies: "@aws-sdk/client-kms" "^3.975.0" "@aws-sdk/client-s3" "^3.975.0" @@ -6146,7 +6154,7 @@ arraybuffer.prototype.slice@^1.0.4: "@azure/identity" "^4.13.0" "@azure/storage-blob" "^12.31.0" "@js-sdsl/ordered-set" "^4.4.2" - "@scality/hdclient" "^1.3.1" + "@scality/hdclient" "^1.3.2" "@smithy/node-http-handler" "^4.3.0" "@smithy/protocol-http" "^5.3.5" JSONStream "^1.3.5"