CLDSRV-898: CompleteMultipartUpload checksums #6166
CLDSRV-898: CompleteMultipartUpload checksums #6166leif-scality wants to merge 9 commits intodevelopment/9.4from
Conversation
leif-scality
commented
May 7, 2026
- Calculate and stores the final checksum when FULL_OBJECT (COMPOSITE are going to be stored by https://scality.atlassian.net/browse/S3C-10399)
- Calculate and compare the final object checksum with the one sent by the headers
- Check that all parts have the correct checksum and checksum type
Hello leif-scality,My role is to assist you with the merge of this Available options
Available commands
Status report is not available. |
Incorrect fix versionThe
Considering where you are trying to merge, I ignored possible hotfix versions and I expected to find:
Please check the |
| ]; | ||
|
|
||
| describe(label, () => { | ||
| it('accepts when every part includes the matching checksum', () => { |
There was a problem hiding this comment.
Test names using it() should start with should (e.g. should accept when every part includes the matching checksum). Multiple tests in this file use verb-first naming like accepts..., returns..., rejects..., does not....
— Claude Code
| assert(got.value.endsWith('-3'), | ||
| `expected -N suffix, got ${got.value}`); | ||
| // computeCompositeMPUChecksum's deterministic output for these | ||
| // exact placeholder digests: |
There was a problem hiding this comment.
Move require('crypto') and require('.../validateChecksums') to the top of the file. crypto is already imported at line 2 and algorithms at line 18 — these inner requires are unnecessary duplicates.
— Claude Code
| }); | ||
| }); | ||
| }); | ||
| }); No newline at end of file |
There was a problem hiding this comment.
Missing newline at end of file.
— Claude Code
| module.exports = completeMultipartUpload; | ||
| module.exports.validatePerPartChecksums = validatePerPartChecksums; | ||
| module.exports.computeFinalChecksum = computeFinalChecksum; | ||
| module.exports.validateExpectedFinalChecksum = validateExpectedFinalChecksum; No newline at end of file |
There was a problem hiding this comment.
Missing newline at end of file.
— Claude Code
3a4f7e2 to
223fb7f
Compare
|
| ]; | ||
|
|
||
| describe(label, () => { | ||
| it('accepts when every part includes the matching checksum', () => { |
There was a problem hiding this comment.
Multiple it() test names in this file do not start with "should" (e.g. line 108 "accepts when every part...", line 120 "returns BadDigest when...", line 357 "accepts CompleteMPU when...", line 413 "rejects CompleteMPU with..."). Per project convention, it() names should start with "should".
— Claude Code
| // exact placeholder digests: | ||
| const expected = require('crypto').createHash('sha256') | ||
| .update(Buffer.concat([d1, d2, d3].map(x => Buffer.from(x, 'base64')))) | ||
| .digest('base64'); |
There was a problem hiding this comment.
require() calls inside it() blocks. crypto is already imported at line 2 and algorithms at line 18. These should be removed in favor of the top-level imports.
Same pattern repeats at lines 568-569 (crypto2/algorithms re-imported) and line 659 (require('crypto') inline).
— Claude Code
| module.exports = completeMultipartUpload; | ||
| module.exports.validatePerPartChecksums = validatePerPartChecksums; | ||
| module.exports.computeFinalChecksum = computeFinalChecksum; | ||
| module.exports.validateExpectedFinalChecksum = validateExpectedFinalChecksum; No newline at end of file |
There was a problem hiding this comment.
Missing trailing newline at end of file.
— Claude Code
| }); | ||
| }); | ||
| }); | ||
| }); No newline at end of file |
There was a problem hiding this comment.
Missing trailing newline at end of file.
— Claude Code
| "@hapi/joi": "^17.1.1", | ||
| "@smithy/node-http-handler": "^3.0.0", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#8.4.1", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#improvement/ARSN-580-handle-checksums-in-complete-mpu", |
There was a problem hiding this comment.
arsenal is pinned to a branch (improvement/ARSN-580-handle-checksums-in-complete-mpu) instead of a tag. Git-based dependencies must pin to a tag for reproducible builds. Replace with the actual release tag once the Arsenal changes are tagged.
— Claude Code
|
Codecov Report❌ Patch coverage is
Additional details and impacted files
... and 1 file with indirect coverage changes @@ Coverage Diff @@
## development/9.4 #6166 +/- ##
===================================================
+ Coverage 85.26% 85.39% +0.12%
===================================================
Files 207 208 +1
Lines 13794 13990 +196
===================================================
+ Hits 11762 11947 +185
- Misses 2032 2043 +11
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
223fb7f to
8095101
Compare
| "@hapi/joi": "^17.1.1", | ||
| "@smithy/node-http-handler": "^3.0.0", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#8.4.1", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#improvement/ARSN-580-handle-checksums-in-complete-mpu", |
There was a problem hiding this comment.
Arsenal is pinned to a branch (improvement/ARSN-580-handle-checksums-in-complete-mpu) instead of a tag. Git-based deps must pin to a tag for reproducible builds; branch refs are mutable and can change without warning.
— Claude Code
| module.exports = completeMultipartUpload; | ||
| module.exports.validatePerPartChecksums = validatePerPartChecksums; | ||
| module.exports.computeFinalChecksum = computeFinalChecksum; | ||
| module.exports.validateExpectedFinalChecksum = validateExpectedFinalChecksum; No newline at end of file |
There was a problem hiding this comment.
Missing newline at end of file. The diff shows No newline at end of file — this will cause lint warnings and untidy diffs on future edits.
— Claude Code
| }); | ||
|
|
||
| it('should return FULL_OBJECT checksum without -N suffix for CRC64NVME', async () => { | ||
| const crypto2 = require('crypto'); |
There was a problem hiding this comment.
require('crypto') and require('.../validateChecksums') are called inside it() blocks (lines 543, 568-570, 659) instead of at the top of the file. Both modules are already imported at the top-level (crypto on line 2, algorithms on line 18). Use the existing top-level bindings instead.
— Claude Code
| ]; | ||
|
|
||
| describe(label, () => { | ||
| it('accepts when every part includes the matching checksum', () => { |
There was a problem hiding this comment.
Test names using it() should start with should per project convention. Multiple tests in this file use imperative forms like accepts, returns, rejects, does not, header comparison, validateMethodChecksumNoChunking, etc. For example: it('accepts when every part includes...') → it('should accept when every part includes...').
— Claude Code
|
8095101 to
9e8bcea
Compare
| "@hapi/joi": "^17.1.1", | ||
| "@smithy/node-http-handler": "^3.0.0", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#8.4.1", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#improvement/ARSN-580-handle-checksums-in-complete-mpu", |
There was a problem hiding this comment.
Arsenal is pinned to a branch (improvement/ARSN-580-handle-checksums-in-complete-mpu) instead of a tag. Git-based dependencies must pin to a tag for reproducible builds.
— Claude Code
| ]; | ||
|
|
||
| describe(label, () => { | ||
| it('accepts when every part includes the matching checksum', () => { |
There was a problem hiding this comment.
Many it() test names in this file don't start with should (e.g. "accepts when every part includes...", "returns BadDigest when...", "header comparison is case-insensitive"). Per project convention, test names should start with should.
— Claude Code
| `expected -N suffix, got ${got.value}`); | ||
| // computeCompositeMPUChecksum's deterministic output for these | ||
| // exact placeholder digests: | ||
| const expected = require('crypto').createHash('sha256') |
There was a problem hiding this comment.
crypto and algorithms are already imported at the top of the file (lines 2 and 19). Move these require() calls out of the test body — the same redundant requires appear at lines 568-569 and 659.
— Claude Code
|
9e8bcea to
054e5ef
Compare
| "@hapi/joi": "^17.1.1", | ||
| "@smithy/node-http-handler": "^3.0.0", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#8.4.1", | ||
| "arsenal": "git+https://github.com/scality/Arsenal#improvement/ARSN-580-handle-checksums-in-complete-mpu", |
There was a problem hiding this comment.
Arsenal is pinned to a branch (improvement/ARSN-580-handle-checksums-in-complete-mpu) instead of a tag. Git-based internal deps must pin to a tag for reproducible builds.
— Claude Code
| ], | ||
| (err, destinationBucket, xml) => { | ||
| const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, destinationBucket); | ||
| monitoring.promMetrics('GET', bucketName, 400, 'listMultipartUploadParts'); |
There was a problem hiding this comment.
monitoring.promMetrics('GET', bucketName, 400, 'listMultipartUploadParts') is called unconditionally in the final waterfall callback. On success, waterfall step 5 already emits a 200 metric, so this line double-counts every successful request as both 200 and 400. Guard with if (err).
— Claude Code
| const stored = [makeStoredPart(1, { algorithm, value: d1 }), makeStoredPart(2, { algorithm, value: d2 })]; | ||
|
|
||
| describe(label, () => { | ||
| it('accepts when every part includes the matching checksum', () => { |
There was a problem hiding this comment.
Test names in it() blocks should start with should per project conventions (e.g. 'should accept when every part includes the matching checksum'). Multiple tests in this file use 'accepts', 'returns', etc.
— Claude Code
| }); | ||
|
|
||
| it('should return FULL_OBJECT checksum without -N suffix for CRC64NVME', async () => { | ||
| const crypto2 = require('crypto'); |
There was a problem hiding this comment.
crypto and algorithms are already imported at the top of the file (lines 1-2 and 14-15). Move these require() calls to the file-level imports instead of re-requiring inside it() blocks.
— Claude Code
|