Files
coolify/tests/Feature/Jobs/PushServerUpdateJobTest.php
Andras Bacsai af983a29d8 refactor(docker): identify containers by resource UUID instead of numeric id
Container ownership labels now use UUIDs: coolify.applicationUuid,
coolify.serviceUuid, coolify.service.subUuid and coolify.databaseUuid.
Numeric ids differ between Coolify instances, so after a server transfer
the target could not find existing containers: redeploys left the old
container running and service statuses were wrong.

Containers are matched by UUID label first, then by the compose project
(Swarm: stack namespace), which is the resource UUID since July 2024, and
last by the older numeric id label. Existing containers keep working
without a redeploy and get the UUID labels on the next deploy or restart.
2026-09-29 09:57:56 +02:00

122 lines
4.1 KiB
PHP

<?php
use App\Jobs\PushServerUpdateJob;
use App\Models\Server;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\Team;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('containers with empty service subId are skipped', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$service = Service::factory()->create([
'server_id' => $server->id,
]);
$serviceApp = ServiceApplication::create([
'service_id' => $service->id,
'uuid' => (string) str()->uuid(),
'name' => 'app-'.str()->random(8),
]);
$data = [
'containers' => [
[
'name' => 'test-container',
'state' => 'running',
'health_status' => 'healthy',
'labels' => [
'coolify.managed' => true,
'coolify.serviceId' => (string) $service->id,
'coolify.service.subType' => 'application',
'coolify.service.subId' => '',
],
],
],
];
$job = new PushServerUpdateJob($server, $data);
// Run handle - should not throw a PDOException about empty bigint
$job->handle();
// The empty subId container should have been skipped
expect($job->foundServiceApplicationIds)->not->toContain('');
expect($job->serviceContainerStatuses)->toBeEmpty();
});
test('service containers are matched by their UUID labels', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$service = Service::factory()->create([
'server_id' => $server->id,
]);
$serviceApp = ServiceApplication::create([
'service_id' => $service->id,
'uuid' => (string) str()->uuid(),
'name' => 'app-'.str()->random(8),
]);
$data = [
'containers' => [
[
'name' => 'test-container',
'state' => 'running',
'health_status' => 'healthy',
'labels' => [
'coolify.managed' => true,
'coolify.serviceUuid' => $service->uuid,
'coolify.service.subType' => 'application',
'coolify.service.subUuid' => $serviceApp->uuid,
'com.docker.compose.service' => 'myapp',
],
],
],
];
$job = new PushServerUpdateJob($server, $data);
$job->handle();
expect($job->foundServiceApplicationIds)->toContain((string) $serviceApp->id);
});
test('legacy service containers from another instance are matched by compose project, not by their numeric ids', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$service = Service::factory()->create([
'server_id' => $server->id,
]);
$serviceApp = ServiceApplication::create([
'service_id' => $service->id,
'uuid' => (string) str()->uuid(),
'name' => 'web',
]);
// Ids from the source instance: they point to nothing, or to other resources, on this instance.
$data = [
'containers' => [
[
'name' => 'web-'.$service->uuid,
'state' => 'running',
'health_status' => 'healthy',
'labels' => [
'coolify.managed' => true,
'coolify.serviceId' => (string) ($service->id + 1000),
'coolify.service.subType' => 'application',
'coolify.service.subId' => (string) ($serviceApp->id + 1000),
'com.docker.compose.project' => $service->uuid,
'com.docker.compose.service' => 'web',
],
],
],
];
$job = new PushServerUpdateJob($server, $data);
$job->handle();
expect($job->foundServiceApplicationIds)->toContain((string) $serviceApp->id)
->and($job->serviceContainerStatuses->keys()->all())->toBe(["{$service->id}:application:{$serviceApp->id}"]);
});