mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
fix(backups): keep non-deployment helper containers during cleanup
Closes #12431
This commit is contained in:
1 parent
31653c27d1
commit
33a6682791
2 files changed
+66
-41
No files matched your search
@@ -37,56 +37,42 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
|
||||
return 'docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image|test("(^|/)coollabsio/coolify-helper(:|@)")))\'';
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes helper containers of finished deployments. Backups, imports, and file browsers also run in
|
||||
* helper containers, so a container is removed only when its name is the UUID of a deployment that
|
||||
* is not queued or in progress.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
// Get all active deployments on this server
|
||||
$activeDeployments = ApplicationDeploymentQueue::where('server_id', $this->server->id)
|
||||
->whereIn('status', [
|
||||
$containers = instant_remote_process_with_timeout([self::helperContainersCommand()], $this->server, false);
|
||||
$helperContainers = collect(json_decode((string) $containers, true) ?: [])
|
||||
->filter(fn (mixed $container): bool => is_array($container) && filled(data_get($container, 'ID')) && filled(data_get($container, 'Names')));
|
||||
|
||||
if ($helperContainers->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$finishedDeploymentUuids = ApplicationDeploymentQueue::query()
|
||||
->whereIn('deployment_uuid', $helperContainers->pluck('Names')->all())
|
||||
->whereNotIn('status', [
|
||||
ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||
ApplicationDeploymentStatus::QUEUED->value,
|
||||
])
|
||||
->pluck('deployment_uuid')
|
||||
->toArray();
|
||||
->all();
|
||||
|
||||
\Log::info('CleanupHelperContainersJob - Active deployments', [
|
||||
'server' => $this->server->name,
|
||||
'active_deployment_uuids' => $activeDeployments,
|
||||
]);
|
||||
|
||||
$containers = instant_remote_process_with_timeout([self::helperContainersCommand()], $this->server, false);
|
||||
$helperContainers = collect(json_decode($containers));
|
||||
|
||||
if ($helperContainers->count() > 0) {
|
||||
foreach ($helperContainers as $container) {
|
||||
$containerId = data_get($container, 'ID');
|
||||
$containerName = data_get($container, 'Names');
|
||||
|
||||
// Check if this container belongs to an active deployment
|
||||
$isActiveDeployment = false;
|
||||
foreach ($activeDeployments as $deploymentUuid) {
|
||||
if (str_contains($containerName, $deploymentUuid)) {
|
||||
$isActiveDeployment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isActiveDeployment) {
|
||||
\Log::info('CleanupHelperContainersJob - Skipping active deployment container', [
|
||||
'container' => $containerName,
|
||||
'id' => $containerId,
|
||||
]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
\Log::info('CleanupHelperContainersJob - Removing orphaned helper container', [
|
||||
'container' => $containerName,
|
||||
'id' => $containerId,
|
||||
]);
|
||||
|
||||
instant_remote_process_with_timeout(['docker container rm -f '.$containerId], $this->server, false);
|
||||
foreach ($helperContainers as $container) {
|
||||
if (! in_array($container['Names'], $finishedDeploymentUuids, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
\Log::info('CleanupHelperContainersJob - Removing orphaned deployment helper container', [
|
||||
'container' => $container['Names'],
|
||||
'id' => $container['ID'],
|
||||
]);
|
||||
|
||||
instant_remote_process_with_timeout(['docker container rm -f '.escapeshellarg($container['ID'])], $this->server, false);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('CleanupHelperContainersJob failed with error: '.$e->getMessage());
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Jobs\CleanupHelperContainersJob;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Process\PendingProcess;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
@@ -24,3 +30,36 @@ it('queues one helper container cleanup per server', function () {
|
||||
|
||||
Queue::assertPushed(CleanupHelperContainersJob::class, 2);
|
||||
});
|
||||
|
||||
it('removes only helper containers of finished deployments', function () {
|
||||
Storage::fake('ssh-keys');
|
||||
Storage::fake('ssh-mux');
|
||||
config(['constants.ssh.mux_enabled' => false]);
|
||||
$privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]);
|
||||
$server = Server::factory()->create(['team_id' => $this->team->id, 'private_key_id' => $privateKey->id]);
|
||||
foreach (['finished-uuid' => ApplicationDeploymentStatus::FINISHED, 'failed-uuid' => ApplicationDeploymentStatus::FAILED, 'running-uuid' => ApplicationDeploymentStatus::IN_PROGRESS] as $uuid => $status) {
|
||||
ApplicationDeploymentQueue::create(['application_id' => '1', 'deployment_uuid' => $uuid, 'server_id' => $server->id, 'status' => $status->value]);
|
||||
}
|
||||
$containers = collect([
|
||||
'finished-uuid' => 'aaa111',
|
||||
'failed-uuid' => 'bbb222',
|
||||
'running-uuid' => 'ccc333',
|
||||
'volume-upload-abc' => 'ddd444',
|
||||
'volume-backup-abc' => 'eee555',
|
||||
'backup-of-abc' => 'fff666',
|
||||
])->map(fn (string $id, string $name): array => ['ID' => $id, 'Names' => $name, 'Image' => 'ghcr.io/coollabsio/coolify-helper:1.0.13'])->values();
|
||||
|
||||
$removed = [];
|
||||
Process::fake(function (PendingProcess $process) use ($containers, &$removed) {
|
||||
$script = remoteProcessScript($process);
|
||||
if (preg_match("/docker container rm -f '?([a-z0-9]+)/", $script, $match) === 1) {
|
||||
$removed[] = $match[1];
|
||||
}
|
||||
|
||||
return Process::result(str_contains($script, 'docker container ps') ? $containers->toJson() : '');
|
||||
});
|
||||
|
||||
(new CleanupHelperContainersJob($server))->handle();
|
||||
|
||||
expect($removed)->toBe(['aaa111', 'bbb222']);
|
||||
});
|
||||
Reference in new issue
Block a user