diff --git a/app/Jobs/ScheduledJobManager.php b/app/Jobs/ScheduledJobManager.php index 92a0db748..7d7f285c9 100644 --- a/app/Jobs/ScheduledJobManager.php +++ b/app/Jobs/ScheduledJobManager.php @@ -112,7 +112,7 @@ public function handle(): void } try { - $this->recoverPausedVolumeBackupContainers(); + $this->recoverStoppedVolumeBackupContainers(); $this->processScheduledVolumeBackups(); } catch (\Exception $e) { Log::channel('scheduled-errors')->error('Failed to process scheduled volume backups', [ @@ -365,11 +365,11 @@ private function processScheduledVolumeBackups(): void }); } - private function recoverPausedVolumeBackupContainers(): void + private function recoverStoppedVolumeBackupContainers(): void { ScheduledVolumeBackupExecution::query() ->where(fn (Builder $query) => $query - ->where('pause_recovery_pending', true) + ->where('stop_recovery_pending', true) ->orWhere('s3_cleanup_pending', true)) ->chunkById(self::CHUNK_SIZE, function ($executions): void { foreach ($executions as $execution) { @@ -385,7 +385,7 @@ private function processScheduledVolumeBackup(ScheduledVolumeBackup $backup): vo if ($backup->executions() ->where(fn (Builder $query) => $query - ->where('pause_recovery_pending', true) + ->where('stop_recovery_pending', true) ->orWhere('s3_cleanup_pending', true)) ->exists()) { $this->skippedCount++; diff --git a/app/Jobs/VolumeBackupJob.php b/app/Jobs/VolumeBackupJob.php index fe1668001..99d2cd618 100644 --- a/app/Jobs/VolumeBackupJob.php +++ b/app/Jobs/VolumeBackupJob.php @@ -83,11 +83,11 @@ public function handle(): void .' '.escapeshellarg($image) .' tar -czf - -C /volume . > '.escapeshellarg($backupLocation); - if ($this->backup->pause_during_backup) { + if ($this->backup->stop_during_backup) { $containers = $this->containersUsingVolume($source, $server); if ($containers !== []) { - $this->execution->update(['pause_recovery_pending' => true]); - $archiveCommand = $this->archiveWithPausedContainers( + $this->execution->update(['stop_recovery_pending' => true]); + $archiveCommand = $this->archiveWithStoppedContainers( $archiveCommand, $containers, VolumeBackupRecoveryJob::stateFile($this->execution), @@ -101,8 +101,8 @@ public function handle(): void $archiveCommand, ], $server, timeout: $this->timeout, disableMultiplexing: true); $this->execution->update([ - 'pause_container_ids' => null, - 'pause_recovery_pending' => false, + 'stop_container_ids' => null, + 'stop_recovery_pending' => false, ]); $size = (int) instant_remote_process( @@ -253,7 +253,7 @@ private function containersUsingVolume(string $source, Server $server): array /** * @param array $containers */ - private function archiveWithPausedContainers(string $archiveCommand, array $containers, string $stateFile): string + private function archiveWithStoppedContainers(string $archiveCommand, array $containers, string $stateFile): string { if ($containers === []) { return $archiveCommand; @@ -263,9 +263,12 @@ private function archiveWithPausedContainers(string $archiveCommand, array $cont $script = "set -eu\n" .'state_file='.escapeshellarg($stateFile)."\n" .": > \"\$state_file\"\n" - ."cleanup() { status=\$?; trap - EXIT HUP INT TERM; while IFS= read -r container; do docker unpause \"\$container\" >/dev/null || status=1; done < \"\$state_file\"; [ \$status -eq 0 ] && rm -f \"\$state_file\"; exit \$status; }\n" - ."trap cleanup EXIT HUP INT TERM\n" - ."for container in {$containerList}; do echo \"\$container\" >> \"\$state_file\"; docker pause \"\$container\" >/dev/null; done\n" + ."cleanup() { status=\$?; trap - EXIT HUP INT TERM; while IFS= read -r container; do docker start \"\$container\" >/dev/null || status=1; done < \"\$state_file\"; [ \$status -eq 0 ] && rm -f \"\$state_file\"; exit \$status; }\n" + ."trap cleanup EXIT\n" + ."trap 'exit 129' HUP\n" + ."trap 'exit 130' INT\n" + ."trap 'exit 143' TERM\n" + ."for container in {$containerList}; do echo \"\$container\" >> \"\$state_file\"; docker stop \"\$container\" >/dev/null; done\n" .$archiveCommand; return 'sh -c '.escapeshellarg($script); @@ -273,7 +276,7 @@ private function archiveWithPausedContainers(string $archiveCommand, array $cont private function recoverIncompleteBackup(ScheduledVolumeBackupExecution $execution): string { - if (! $execution->pause_recovery_pending && ! $execution->s3_cleanup_pending) { + if (! $execution->stop_recovery_pending && ! $execution->s3_cleanup_pending) { return ''; } diff --git a/app/Jobs/VolumeBackupRecoveryJob.php b/app/Jobs/VolumeBackupRecoveryJob.php index 1749b5309..44e7d47cb 100644 --- a/app/Jobs/VolumeBackupRecoveryJob.php +++ b/app/Jobs/VolumeBackupRecoveryJob.php @@ -48,7 +48,7 @@ public static function recover(ScheduledVolumeBackupExecution $execution): void { $execution->loadMissing('scheduledVolumeBackup.volume.resource'); - if ($execution->pause_recovery_pending) { + if ($execution->stop_recovery_pending) { self::recoverContainers($execution); } @@ -75,22 +75,22 @@ private static function recoverContainers(ScheduledVolumeBackupExecution $execut ->filter(fn (string $container): bool => preg_match('/^[a-f0-9]{6,64}$/i', $container) === 1) ->values() ->all(); - $execution->update(['pause_container_ids' => $containers]); + $execution->update(['stop_container_ids' => $containers]); $remainingFile = $stateFile.'.remaining'; $script = 'status=0; : > '.escapeshellarg($remainingFile).'; ' .'if [ -f '.escapeshellarg($stateFile).' ]; then while IFS= read -r container; do ' - .'[ -z "$container" ] && continue; paused=$(docker inspect --format \'{{.State.Paused}}\' "$container" 2>/dev/null) ' + .'[ -z "$container" ] && continue; running=$(docker inspect --format \'{{.State.Running}}\' "$container" 2>/dev/null) ' .'|| { echo "$container" >> '.escapeshellarg($remainingFile).'; status=1; continue; }; ' - .'if [ "$paused" = true ] && ! docker unpause "$container" >/dev/null; then echo "$container" >> ' + .'if [ "$running" != true ] && ! docker start "$container" >/dev/null; then echo "$container" >> ' .escapeshellarg($remainingFile).'; status=1; fi; done < '.escapeshellarg($stateFile).'; fi; ' .'if [ -s '.escapeshellarg($remainingFile).' ]; then mv '.escapeshellarg($remainingFile).' '.escapeshellarg($stateFile) .'; else rm -f '.escapeshellarg($stateFile).' '.escapeshellarg($remainingFile).'; fi; exit $status'; instant_remote_process(['sh -c '.escapeshellarg($script)], $server, disableMultiplexing: true); $execution->update([ - 'pause_container_ids' => null, - 'pause_recovery_pending' => false, + 'stop_container_ids' => null, + 'stop_recovery_pending' => false, ]); } @@ -112,6 +112,6 @@ public static function cleanupS3Upload(ScheduledVolumeBackupExecution $execution public static function stateFile(ScheduledVolumeBackupExecution $execution): string { - return '/tmp/coolify-volume-backup-'.$execution->uuid.'.paused'; + return '/tmp/coolify-volume-backup-'.$execution->uuid.'.stopped'; } } diff --git a/app/Livewire/Project/Shared/Storages/VolumeBackups.php b/app/Livewire/Project/Shared/Storages/VolumeBackups.php index 63441ba34..6041f139f 100644 --- a/app/Livewire/Project/Shared/Storages/VolumeBackups.php +++ b/app/Livewire/Project/Shared/Storages/VolumeBackups.php @@ -32,7 +32,7 @@ class VolumeBackups extends Component public bool $disableLocalBackup = false; - public bool $pauseDuringBackup = false; + public bool $stopDuringBackup = false; public ?int $s3StorageId = null; @@ -63,7 +63,7 @@ protected function rules(): array 'enabled' => ['required', 'boolean'], 'saveToS3' => ['required', 'boolean'], 'disableLocalBackup' => ['required', 'boolean'], - 'pauseDuringBackup' => ['required', 'boolean'], + 'stopDuringBackup' => ['required', 'boolean'], 's3StorageId' => ['nullable', 'integer'], 'retentionAmountLocally' => ['required', 'integer', 'min:0', 'max:10000'], 'retentionDaysLocally' => ['required', 'integer', 'min:0'], @@ -90,7 +90,7 @@ public function mount(): void $this->enabled = $this->backup->enabled; $this->saveToS3 = $this->backup->save_s3; $this->disableLocalBackup = $this->backup->disable_local_backup; - $this->pauseDuringBackup = $this->backup->pause_during_backup; + $this->stopDuringBackup = $this->backup->stop_during_backup; $this->s3StorageId = $this->backup->s3_storage_id ?? $this->availableS3Storages->first()?->id; $this->retentionAmountLocally = $this->backup->retention_amount_locally; $this->retentionDaysLocally = $this->backup->retention_days_locally; @@ -188,7 +188,7 @@ public function delete(?string $password = null, array $selectedActions = []) if ($this->backup->executions() ->where(fn ($query) => $query ->where('status', 'running') - ->orWhere('pause_recovery_pending', true) + ->orWhere('stop_recovery_pending', true) ->orWhere('s3_cleanup_pending', true)) ->exists()) { $this->dispatch('error', 'Wait for the running volume backup and container recovery to finish before deleting this schedule.'); @@ -246,7 +246,7 @@ public function cleanupFailed(): void $deletedCount = $this->backup?->executions() ->where('status', 'failed') - ->where('pause_recovery_pending', false) + ->where('stop_recovery_pending', false) ->where('s3_cleanup_pending', false) ->where(fn ($query) => $query ->whereNull('filename') @@ -292,7 +292,7 @@ public function deleteBackup(int $executionId, string $password, array $selected return false; } - if ($execution->status === 'running' || $execution->pause_recovery_pending || $execution->s3_cleanup_pending) { + if ($execution->status === 'running' || $execution->stop_recovery_pending || $execution->s3_cleanup_pending) { $this->dispatch('error', 'Wait for the backup and recovery operations to finish before deleting it.'); return false; @@ -369,7 +369,7 @@ private function persistBackup(bool $enabled): ScheduledVolumeBackup 'enabled' => $enabled, 'save_s3' => $this->saveToS3, 'disable_local_backup' => $this->disableLocalBackup, - 'pause_during_backup' => $this->pauseDuringBackup, + 'stop_during_backup' => $this->stopDuringBackup, 's3_storage_id' => $this->saveToS3 ? $this->s3StorageId : null, 'retention_amount_locally' => $this->retentionAmountLocally, 'retention_days_locally' => $this->retentionDaysLocally, diff --git a/app/Models/ScheduledVolumeBackup.php b/app/Models/ScheduledVolumeBackup.php index 23e2369b1..055230332 100644 --- a/app/Models/ScheduledVolumeBackup.php +++ b/app/Models/ScheduledVolumeBackup.php @@ -17,7 +17,7 @@ class ScheduledVolumeBackup extends BaseModel 'enabled', 'save_s3', 'disable_local_backup', - 'pause_during_backup', + 'stop_during_backup', 'retention_amount_locally', 'retention_days_locally', 'retention_max_storage_locally', @@ -33,7 +33,7 @@ protected function casts(): array 'enabled' => 'boolean', 'save_s3' => 'boolean', 'disable_local_backup' => 'boolean', - 'pause_during_backup' => 'boolean', + 'stop_during_backup' => 'boolean', 'retention_amount_locally' => 'integer', 'retention_days_locally' => 'integer', 'retention_max_storage_locally' => 'float', diff --git a/app/Models/ScheduledVolumeBackupExecution.php b/app/Models/ScheduledVolumeBackupExecution.php index b15294461..1bcf5e6bc 100644 --- a/app/Models/ScheduledVolumeBackupExecution.php +++ b/app/Models/ScheduledVolumeBackupExecution.php @@ -13,8 +13,8 @@ class ScheduledVolumeBackupExecution extends BaseModel 'message', 'size', 'filename', - 'pause_container_ids', - 'pause_recovery_pending', + 'stop_container_ids', + 'stop_recovery_pending', 's3_cleanup_pending', 'finished_at', 'local_storage_deleted', @@ -27,8 +27,8 @@ protected function casts(): array return [ 'size' => 'integer', 'finished_at' => 'datetime', - 'pause_container_ids' => 'array', - 'pause_recovery_pending' => 'boolean', + 'stop_container_ids' => 'array', + 'stop_recovery_pending' => 'boolean', 's3_cleanup_pending' => 'boolean', 'local_storage_deleted' => 'boolean', 's3_storage_deleted' => 'boolean', diff --git a/database/migrations/2026_07_15_102537_create_scheduled_volume_backups_table.php b/database/migrations/2026_07_15_102537_create_scheduled_volume_backups_table.php index 6ebf1a80e..f5d49c990 100644 --- a/database/migrations/2026_07_15_102537_create_scheduled_volume_backups_table.php +++ b/database/migrations/2026_07_15_102537_create_scheduled_volume_backups_table.php @@ -21,7 +21,7 @@ public function up(): void $table->boolean('enabled')->default(true); $table->boolean('save_s3')->default(false); $table->boolean('disable_local_backup')->default(false); - $table->boolean('pause_during_backup')->default(false); + $table->boolean('stop_during_backup')->default(false); $table->unsignedInteger('retention_amount_locally')->default(7); $table->unsignedInteger('retention_amount_s3')->default(7); $table->unsignedInteger('timeout')->default(3600); diff --git a/database/migrations/2026_07_15_102538_create_scheduled_volume_backup_executions_table.php b/database/migrations/2026_07_15_102538_create_scheduled_volume_backup_executions_table.php index 20f5fc273..a8a044373 100644 --- a/database/migrations/2026_07_15_102538_create_scheduled_volume_backup_executions_table.php +++ b/database/migrations/2026_07_15_102538_create_scheduled_volume_backup_executions_table.php @@ -19,8 +19,8 @@ public function up(): void $table->longText('message')->nullable(); $table->unsignedBigInteger('size')->default(0); $table->text('filename')->nullable(); - $table->json('pause_container_ids')->nullable(); - $table->boolean('pause_recovery_pending')->default(false); + $table->json('stop_container_ids')->nullable(); + $table->boolean('stop_recovery_pending')->default(false); $table->boolean('s3_cleanup_pending')->default(false); $table->timestamp('finished_at')->nullable(); $table->boolean('local_storage_deleted')->default(false); diff --git a/resources/views/livewire/project/shared/storages/show.blade.php b/resources/views/livewire/project/shared/storages/show.blade.php index b8e9e8293..6b112ede3 100644 --- a/resources/views/livewire/project/shared/storages/show.blade.php +++ b/resources/views/livewire/project/shared/storages/show.blade.php @@ -65,6 +65,15 @@ @endcan @endif + @if ($resource instanceof \App\Models\Application) + @can('update', $resource) + + + + @endcan + @endif @else @can('update', $resource) @if ($isFirst) @@ -150,15 +159,4 @@ @endcan @endif - @if ($isReadOnly && $resource instanceof \App\Models\Application) - @can('update', $resource) -
- - - -
- @endcan - @endif diff --git a/resources/views/livewire/project/shared/storages/volume-backups.blade.php b/resources/views/livewire/project/shared/storages/volume-backups.blade.php index 8549a14c5..58aed0a0f 100644 --- a/resources/views/livewire/project/shared/storages/volume-backups.blade.php +++ b/resources/views/livewire/project/shared/storages/volume-backups.blade.php @@ -33,12 +33,16 @@ {{ $storage->name }}

- +
+ Backups made while the application is writing to this volume may be inconsistent or corrupted. You can + gracefully stop containers during the archive step for a safer file-level backup, but this briefly + interrupts the application. +
- + @if ($availableS3Storages->isNotEmpty()) @elseif ($saveToS3) @@ -80,11 +84,7 @@

Settings

-
- Backups made while the application is writing to this volume may be inconsistent or corrupted. You can - pause containers during the archive step for a more consistent backup, but this briefly interrupts the - application. -
+
diff --git a/tests/Feature/VolumeBackupTest.php b/tests/Feature/VolumeBackupTest.php index 94bde7b32..e12b4f278 100644 --- a/tests/Feature/VolumeBackupTest.php +++ b/tests/Feature/VolumeBackupTest.php @@ -96,18 +96,25 @@ ->assertSee($volume->name); }); -it('shows the configure backup modal trigger instead of inline backup settings on a volume', function () { +it('shows the configure backup modal trigger inside the volume card instead of inline backup settings', function () { InstanceSettings::unguarded(fn () => InstanceSettings::create(['id' => 0])); $team = Team::factory()->create(); signInForVolumeBackups($this, $team); [$application, $volume] = createVolumeBackupApplication($team); - Livewire::test(Show::class, [ + $component = Livewire::test(Show::class, [ 'storage' => $volume, 'resource' => $application, ]) + ->set('isReadOnly', true) ->assertSee('Configure Backup') ->assertDontSee('Backups made while the application is writing'); + + $html = $component->html(); + + expect(strpos($html, 'Configure Backup')) + ->toBeGreaterThan(strpos($html, 'toBeLessThan(strpos($html, '')); }); it('only shows the backup enabled badge for an enabled volume backup', function () { @@ -151,7 +158,7 @@ 'enabled', 'save_s3', 'disable_local_backup', - 'pause_during_backup', + 'stop_during_backup', 'retention_amount_locally', 'retention_days_locally', 'retention_max_storage_locally', @@ -167,8 +174,8 @@ 'message', 'size', 'filename', - 'pause_container_ids', - 'pause_recovery_pending', + 'stop_container_ids', + 'stop_recovery_pending', 's3_cleanup_pending', 'finished_at', 'local_storage_deleted', @@ -215,7 +222,7 @@ 'Backup Now', 'Delete Backups and Schedule', 'Persistent volume:', - 'Pause containers while creating the archive', + 'Stop containers while creating the archive', 'S3 Enabled', 'Disable Local Backup', 'S3 Storage', @@ -397,11 +404,12 @@ function signInForVolumeBackups($testCase, Team $team): User ->assertSee('Scheduled Backup') ->assertSee('Persistent volume:') ->assertSee('inconsistent or corrupted') + ->assertSee('gracefully stop containers') ->set('frequency', 'daily') ->set('retentionAmountLocally', 5) ->set('retentionDaysLocally', 14) ->set('retentionMaxStorageLocally', 1.5) - ->set('pauseDuringBackup', true) + ->set('stopDuringBackup', true) ->call('save') ->assertDispatched('success'); @@ -413,7 +421,7 @@ function signInForVolumeBackups($testCase, Team $team): User ->and($backup->retention_amount_locally)->toBe(5) ->and($backup->retention_days_locally)->toBe(14) ->and($backup->retention_max_storage_locally)->toBe(1.5) - ->and($backup->pause_during_backup)->toBeTrue() + ->and($backup->stop_during_backup)->toBeTrue() ->and($backup->save_s3)->toBeFalse(); }); @@ -487,6 +495,45 @@ function signInForVolumeBackups($testCase, Team $team): User ->and($backup->fresh()->disable_local_backup)->toBeFalse(); }); +it('saves each editable volume backup checkbox immediately', function () { + $team = Team::factory()->create(); + signInForVolumeBackups($this, $team); + [$application, $volume] = createVolumeBackupApplication($team); + $s3Storage = S3Storage::create([ + 'name' => 'Volume backups', + 'region' => 'us-east-1', + 'key' => 'key', + 'secret' => 'secret', + 'bucket' => 'bucket', + 'endpoint' => 'https://s3.example.com', + 'team_id' => $team->id, + 'is_usable' => true, + ]); + $backup = ScheduledVolumeBackup::create([ + 'local_persistent_volume_id' => $volume->id, + 'team_id' => $team->id, + 's3_storage_id' => $s3Storage->id, + 'frequency' => 'daily', + 'save_s3' => true, + ]); + + $component = Livewire::test(VolumeBackups::class, ['storage' => $volume, 'resource' => $application]); + $html = $component->html(); + + foreach (['stopDuringBackup', 'saveToS3', 'disableLocalBackup'] as $property) { + preg_match('/]*wire:model=(?:"'.$property.'"|'.$property.'))[^>]*>/', $html, $matches); + + expect($matches[0] ?? null)->not->toBeNull() + ->and($matches[0])->toContain("wire:click='instantSave'"); + } + + $component->set('stopDuringBackup', true)->call('instantSave')->assertDispatched('success'); + expect($backup->refresh()->stop_during_backup)->toBeTrue(); + + $component->set('stopDuringBackup', false)->call('instantSave')->assertDispatched('success'); + expect($backup->refresh()->stop_during_backup)->toBeFalse(); +}); + it('allows volume S3 backups to be disabled when no usable storage remains', function () { $team = Team::factory()->create(); signInForVolumeBackups($this, $team); @@ -829,7 +876,7 @@ function signInForVolumeBackups($testCase, Team $team): User ->and($oldExecution->fresh()->local_storage_deleted)->toBeFalse(); }); -it('pauses and resumes containers that use the volume when requested', function () { +it('stops and restarts containers that use the volume when requested', function () { config(['broadcasting.default' => 'null']); InstanceSettings::unguarded(fn () => InstanceSettings::create(['id' => 0])); $team = Team::factory()->create(); @@ -838,7 +885,7 @@ function signInForVolumeBackups($testCase, Team $team): User 'local_persistent_volume_id' => $volume->id, 'team_id' => $team->id, 'frequency' => 'daily', - 'pause_during_backup' => true, + 'stop_during_backup' => true, ]); Process::fake([ @@ -849,9 +896,11 @@ function signInForVolumeBackups($testCase, Team $team): User (new VolumeBackupJob($backup))->handle(); - Process::assertRan(fn ($process) => str_contains($process->command, 'trap cleanup EXIT HUP INT TERM') - && str_contains($process->command, 'docker pause') - && str_contains($process->command, 'docker unpause') + Process::assertRan(fn ($process) => str_contains($process->command, 'trap cleanup EXIT') + && str_contains($process->command, 'exit 143') + && str_contains($process->command, 'TERM') + && str_contains($process->command, 'docker stop') + && str_contains($process->command, 'docker start') && str_contains($process->command, 'state_file=')); Process::assertRan(fn ($process) => str_contains($process->command, '{{println .Source}}{{println .Name}}') && str_contains($process->command, "grep -Fqx -- 'app-data'") @@ -859,7 +908,7 @@ function signInForVolumeBackups($testCase, Team $team): User && str_contains($process->command, 'continue')); }); -it('finds containers using a bind mounted host path before pausing', function () { +it('finds containers using a bind mounted host path before stopping', function () { config(['broadcasting.default' => 'null']); InstanceSettings::unguarded(fn () => InstanceSettings::create(['id' => 0])); $team = Team::factory()->create(); @@ -869,7 +918,7 @@ function signInForVolumeBackups($testCase, Team $team): User 'local_persistent_volume_id' => $volume->id, 'team_id' => $team->id, 'frequency' => 'daily', - 'pause_during_backup' => true, + 'stop_during_backup' => true, ]); Process::fake([ @@ -884,7 +933,7 @@ function signInForVolumeBackups($testCase, Team $team): User && str_contains($process->command, "grep -Fqx -- '/srv/app-data'")); }); -it('retries container recovery when a timed out backup left a container paused', function () { +it('retries container recovery when a timed out backup left a container stopped', function () { Queue::fake(); $team = Team::factory()->create(); [$application, $volume] = createVolumeBackupApplication($team); @@ -896,23 +945,23 @@ function signInForVolumeBackups($testCase, Team $team): User $execution = ScheduledVolumeBackupExecution::create([ 'scheduled_volume_backup_id' => $backup->id, 'status' => 'running', - 'pause_recovery_pending' => true, + 'stop_recovery_pending' => true, ]); Process::fake([ '*cat *coolify-volume-backup-*' => 'abc123', - '*docker unpause*' => Process::result(errorOutput: 'daemon unavailable', exitCode: 1), + '*docker start*' => Process::result(errorOutput: 'daemon unavailable', exitCode: 1), ]); (new VolumeBackupJob($backup))->failed(new RuntimeException('Worker timed out')); expect($execution->fresh()->status)->toBe('failed') ->and($execution->fresh()->message)->toContain('Container recovery failed') - ->and($execution->fresh()->pause_container_ids)->toBe(['abc123']) - ->and($execution->fresh()->pause_recovery_pending)->toBeTrue(); + ->and($execution->fresh()->stop_container_ids)->toBe(['abc123']) + ->and($execution->fresh()->stop_recovery_pending)->toBeTrue(); Queue::assertPushed(VolumeBackupRecoveryJob::class); }); -it('resumes containers and removes a partial archive when archiving fails', function () { +it('restarts containers and removes a partial archive when archiving fails', function () { config(['broadcasting.default' => 'null']); InstanceSettings::unguarded(fn () => InstanceSettings::create(['id' => 0])); $team = Team::factory()->create(); @@ -921,11 +970,11 @@ function signInForVolumeBackups($testCase, Team $team): User 'local_persistent_volume_id' => $volume->id, 'team_id' => $team->id, 'frequency' => 'daily', - 'pause_during_backup' => true, + 'stop_during_backup' => true, ]); Process::fake([ '*docker ps -q*' => 'abc123', - '*trap cleanup EXIT HUP INT TERM*' => Process::result(errorOutput: 'archive failed', exitCode: 1), + '*trap cleanup EXIT*' => Process::result(errorOutput: 'archive failed', exitCode: 1), '*' => '', ]); @@ -933,7 +982,7 @@ function signInForVolumeBackups($testCase, Team $team): User $execution = ScheduledVolumeBackupExecution::query()->sole(); expect($execution->status)->toBe('failed') - ->and($execution->pause_container_ids)->toBeNull(); + ->and($execution->stop_container_ids)->toBeNull(); Process::assertRan(fn ($process) => str_contains($process->command, 'rm -f') && str_contains($process->command, '.tar.gz')); }); @@ -1079,7 +1128,7 @@ function signInForVolumeBackups($testCase, Team $team): User $execution = ScheduledVolumeBackupExecution::create([ 'scheduled_volume_backup_id' => $backup->id, 'status' => 'failed', - 'pause_recovery_pending' => true, + 'stop_recovery_pending' => true, ]); (new ScheduledJobManager)->handle();