fix(backups): send Content-MD5 for S3-only volume backups on Object Lock (#12215)

This commit is contained in:
Andras Bacsai
2026-10-19 21:05:38 +02:00
committed by GitHub
parent 536db50e58
commit 3b407c8413
2 changed files with 9 additions and 4 deletions
+4 -1
View File
@@ -417,9 +417,12 @@ class VolumeBackupJob implements ShouldBeEncrypted, ShouldQueue
// `mc pipe --quiet` panics in recent mc releases, so keep its progress bar out of the error output instead.
// Each progress frame starts with "\r", so printing the last field per line keeps only the final frame and real errors.
$mcPipeLog = '/tmp/mc-pipe.log';
// Object Lock buckets need Content-MD5 on each upload. `mc cp` adds it when the bucket lock is enabled, `mc pipe` only with `--checksum=MD5`.
$streamScript = 'set -o pipefail; mc alias set'.$resolveOptions.' temporary '
.escapeshellarg($s3->endpoint).' '.escapeshellarg($s3->key).' '.escapeshellarg($s3->secret)
.' >/dev/null && ('.$archiveScript.' | mc pipe'.$resolveOptions.' '.escapeshellarg($destination).' >/dev/null 2>'.$mcPipeLog
.' >/dev/null && lock=$(mc retention info --default --json'.$resolveOptions.' '.escapeshellarg('temporary/'.$s3->bucket).' 2>/dev/null || true)'
.' && case "$lock" in *\'"enabled":"Enabled"\'*) checksum=--checksum=MD5 ;; *) checksum= ;; esac'
.' && ('.$archiveScript.' | mc pipe'.$resolveOptions.' $checksum '.escapeshellarg($destination).' >/dev/null 2>'.$mcPipeLog
.' || { status=$?; awk -F '.escapeshellarg('\r').' '.escapeshellarg('{ print $NF }').' '.$mcPipeLog.' >&2; exit $status; })'
.' && mc stat --json'.$resolveOptions.' '.escapeshellarg($destination)
.' | sed -n '.escapeshellarg('s/.*"size":\([0-9][0-9]*\).*/\1/p');
+5 -3
View File
@@ -1987,7 +1987,7 @@ it('keeps the upload destination on the volume backup execution', function () {
->and($execution->s3->is($s3Storage))->toBeTrue();
});
it('streams S3-only volume backups without creating or copying a local archive', function () {
it('streams S3-only volume backups with Content-MD5 on Object Lock buckets and without a local archive', function () {
config(['broadcasting.default' => 'null']);
defined('CURLOPT_RESOLVE') || define('CURLOPT_RESOLVE', 10203);
Carbon::setTestNow('2026-08-15 12:00:00');
@@ -2029,12 +2029,14 @@ it('streams S3-only volume backups without creating or copying a local archive',
Process::assertRan(fn ($process) => str_contains($process->command, 'tar -I')
&& str_contains($process->command, 'mc pipe')
&& ! str_contains($process->command, '--quiet')
&& str_contains($process->command, '>/dev/null && (compressor=')
&& str_contains($process->command, 'lock=$(mc retention info --default --json --resolve')
&& str_contains($process->command, '*) checksum=--checksum=MD5 ;; *) checksum= ;; esac && (compressor=')
&& str_contains($process->command, ' $checksum ')
&& str_contains($process->command, '>/dev/null 2>/tmp/mc-pipe.log || {')
&& str_contains($process->command, '/tmp/mc-pipe.log >&2; exit $status; }) && mc stat')
&& str_contains($process->command, 'temporary/bucket/')
&& str_contains($process->command, $expectedFilename)
&& substr_count($process->command, '--resolve') === 3
&& substr_count($process->command, '--resolve') === 4
&& substr_count($process->command, '>/dev/null') >= 2
&& ! str_contains($process->command, ' > ')
&& ! str_contains($process->command, 'mc cp'));