mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-20 12:23:48 +00:00
Move V5 source, migrations, UI, scripts, and tests into documentation, then remove V5 routes, models, jobs, configuration, dependencies, and application hooks.
640 lines
24 KiB
Plaintext
640 lines
24 KiB
Plaintext
<?php
|
|
|
|
use App\Actions\V5\Flux\ApplyFluxResourceStatusUpdate;
|
|
use App\Events\V5CanvasResourceUpdated;
|
|
use App\Events\V5ClusterUpdated;
|
|
use App\Models\Team;
|
|
use App\Models\V5\Application as V5Application;
|
|
use App\Models\V5\Cluster;
|
|
use App\Models\V5\ContainerStatus;
|
|
use App\Models\V5\Server as V5Server;
|
|
use Illuminate\Support\Facades\Config;
|
|
use Illuminate\Support\Facades\Event;
|
|
|
|
beforeEach(function () {
|
|
resetV5DashboardTestState();
|
|
});
|
|
|
|
it('applies flux application status updates to the database and broadcasts to the team canvas', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
[$project, $environment] = createV5ProjectWithEnvironment($team, 'Production Project', 'Production');
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$application = V5Application::query()->create([
|
|
'team_id' => $team->id,
|
|
'project_id' => $project->id,
|
|
'environment_id' => $environment->id,
|
|
'server_id' => $server->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'nginx-test',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'status' => 'running',
|
|
'status_message' => 'Container started.',
|
|
'runtime_container_id' => 'nginx-container-id',
|
|
'canvas_x' => 0,
|
|
'canvas_y' => 0,
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class]);
|
|
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'application',
|
|
'host_id' => '100.64.0.5',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'container_id' => 'nginx-container-id',
|
|
'status' => 'exited',
|
|
'status_message' => 'Status received from coold through flux.',
|
|
]);
|
|
|
|
expect($resource)->toBeInstanceOf(V5Application::class)
|
|
->and($application->refresh()->status)->toBe('exited')
|
|
->and($application->status_message)->toBe('Status received from coold through flux.')
|
|
->and($application->runtime_container_id)->toBe('nginx-container-id');
|
|
|
|
Event::assertDispatched(V5CanvasResourceUpdated::class, fn (V5CanvasResourceUpdated $event) => $event->teamId === $team->id
|
|
&& $event->applicationId === $application->id);
|
|
});
|
|
|
|
it('ignores stale flux status updates for a superseded container', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
[$project, $environment] = createV5ProjectWithEnvironment($team, 'Production Project', 'Production');
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$application = V5Application::query()->create([
|
|
'team_id' => $team->id,
|
|
'project_id' => $project->id,
|
|
'environment_id' => $environment->id,
|
|
'server_id' => $server->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'nginx-test',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'status' => 'running',
|
|
'status_message' => 'Container started.',
|
|
'runtime_container_id' => 'new-container-id',
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class]);
|
|
|
|
ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'application',
|
|
'host_id' => '100.64.0.5',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'container_id' => 'old-container-id',
|
|
'status' => 'exited',
|
|
'status_message' => 'Stale event for the old container.',
|
|
]);
|
|
|
|
expect($application->refresh()->status)->toBe('running')
|
|
->and($application->runtime_container_id)->toBe('new-container-id');
|
|
|
|
Event::assertNotDispatched(V5CanvasResourceUpdated::class);
|
|
});
|
|
|
|
it('scopes flux application status updates to the reporting server', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$userA, $teamA] = createV5UserWithTeam();
|
|
[$userB, $teamB] = createV5UserWithTeam('other@example.com');
|
|
[$projectA, $environmentA] = createV5ProjectWithEnvironment($teamA, 'Project A', 'Production');
|
|
[$projectB, $environmentB] = createV5ProjectWithEnvironment($teamB, 'Project B', 'Production');
|
|
$serverA = V5Server::query()->create([
|
|
'team_id' => $teamA->id,
|
|
'created_by_user_id' => $userA->id,
|
|
'name' => 'edge-a',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$serverB = V5Server::query()->create([
|
|
'team_id' => $teamB->id,
|
|
'created_by_user_id' => $userB->id,
|
|
'name' => 'edge-b',
|
|
'host' => '203.0.113.11',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.6',
|
|
]);
|
|
$applicationA = V5Application::query()->create([
|
|
'team_id' => $teamA->id,
|
|
'project_id' => $projectA->id,
|
|
'environment_id' => $environmentA->id,
|
|
'server_id' => $serverA->id,
|
|
'created_by_user_id' => $userA->id,
|
|
'name' => 'api',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-api-a',
|
|
'status' => 'running',
|
|
]);
|
|
$applicationB = V5Application::query()->create([
|
|
'uuid' => $applicationA->uuid.'-b',
|
|
'team_id' => $teamB->id,
|
|
'project_id' => $projectB->id,
|
|
'environment_id' => $environmentB->id,
|
|
'server_id' => $serverB->id,
|
|
'created_by_user_id' => $userB->id,
|
|
'name' => 'api',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-api-b',
|
|
'status' => 'running',
|
|
]);
|
|
|
|
// An update reported by team A's host must never resolve to team B's app,
|
|
// even when the payload carries team B's application uuid.
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'application',
|
|
'host_id' => '100.64.0.5',
|
|
'application_uuid' => $applicationB->uuid,
|
|
'status' => 'exited',
|
|
]);
|
|
|
|
expect($resource)->toBeNull()
|
|
->and($applicationA->refresh()->status)->toBe('running')
|
|
->and($applicationB->refresh()->status)->toBe('running');
|
|
});
|
|
|
|
it('ignores flux application status updates whose host does not resolve to a server', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
[$project, $environment] = createV5ProjectWithEnvironment($team, 'Production Project', 'Production');
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$application = V5Application::query()->create([
|
|
'team_id' => $team->id,
|
|
'project_id' => $project->id,
|
|
'environment_id' => $environment->id,
|
|
'server_id' => $server->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'nginx-test',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'status' => 'running',
|
|
]);
|
|
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'application',
|
|
'host_id' => '100.99.99.99',
|
|
'application_uuid' => $application->uuid,
|
|
'status' => 'exited',
|
|
]);
|
|
|
|
expect($resource)->toBeNull()
|
|
->and($application->refresh()->status)->toBe('running');
|
|
});
|
|
|
|
it('maps generic flux container status updates to v5 applications', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
[$project, $environment] = createV5ProjectWithEnvironment($team, 'Production Project', 'Production');
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$application = V5Application::query()->create([
|
|
'team_id' => $team->id,
|
|
'project_id' => $project->id,
|
|
'environment_id' => $environment->id,
|
|
'server_id' => $server->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'nginx-test',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'status' => 'running',
|
|
'status_message' => 'Container started.',
|
|
'runtime_container_id' => 'nginx-container-id',
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class]);
|
|
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'container',
|
|
'host_id' => '100.64.0.5',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'container_id' => 'nginx-container-id',
|
|
'status' => 'exited',
|
|
'status_message' => 'Container state received from coold.',
|
|
]);
|
|
|
|
expect($resource)->toBeInstanceOf(V5Application::class)
|
|
->and($application->refresh()->status)->toBe('exited')
|
|
->and($application->status_message)->toBe('Container state received from coold.');
|
|
|
|
Event::assertDispatched(V5CanvasResourceUpdated::class, fn (V5CanvasResourceUpdated $event) => $event->teamId === $team->id
|
|
&& $event->applicationId === $application->id);
|
|
});
|
|
|
|
it('applies flux ingress server status updates to the database and broadcasts cluster plus canvas updates', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
$cluster = Cluster::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'Production Cluster',
|
|
]);
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'cluster_id' => $cluster->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => ['ingress'],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class, V5ClusterUpdated::class]);
|
|
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'server',
|
|
'host_id' => '100.64.0.5',
|
|
'status' => 'unreachable',
|
|
'message' => 'coold heartbeat timed out.',
|
|
]);
|
|
|
|
expect($resource)->toBeInstanceOf(V5Server::class)
|
|
->and($server->refresh()->status)->toBe('unreachable')
|
|
->and($server->last_status_check)->toBe('flux')
|
|
->and($server->last_status_output)->toBe('coold heartbeat timed out.')
|
|
->and($server->last_status_checked_at)->not->toBeNull();
|
|
|
|
Event::assertDispatched(V5ClusterUpdated::class, fn (V5ClusterUpdated $event) => $event->teamId === $team->id
|
|
&& $event->clusterId === $cluster->id);
|
|
Event::assertDispatched(V5CanvasResourceUpdated::class, fn (V5CanvasResourceUpdated $event) => $event->teamId === $team->id
|
|
&& $event->caddyIngressServerId === $server->id);
|
|
});
|
|
|
|
it('ignores flux server status updates when a host id matches multiple v5 servers', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
$cluster = Cluster::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'Production Cluster',
|
|
]);
|
|
$serverOne = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'cluster_id' => $cluster->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$serverTwo = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'cluster_id' => $cluster->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-02',
|
|
'host' => '203.0.113.11',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class, V5ClusterUpdated::class]);
|
|
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'server',
|
|
'host_id' => '100.64.0.5',
|
|
'status' => 'unreachable',
|
|
'message' => 'ambiguous host id.',
|
|
]);
|
|
|
|
expect($resource)->toBeNull()
|
|
->and($serverOne->refresh()->status)->toBe('installed')
|
|
->and($serverTwo->refresh()->status)->toBe('installed');
|
|
|
|
Event::assertNotDispatched(V5ClusterUpdated::class);
|
|
Event::assertNotDispatched(V5CanvasResourceUpdated::class);
|
|
});
|
|
|
|
it('broadcasts v5 canvas application updates when a non-ingress server goes unreachable', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
[$project, $environment] = createV5ProjectWithEnvironment($team, 'Production Project', 'Production');
|
|
$cluster = Cluster::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'Production Cluster',
|
|
]);
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'cluster_id' => $cluster->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'worker-01',
|
|
'host' => '203.0.113.11',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.6',
|
|
]);
|
|
$application = V5Application::query()->create([
|
|
'team_id' => $team->id,
|
|
'project_id' => $project->id,
|
|
'environment_id' => $environment->id,
|
|
'server_id' => $server->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'nginx-test',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'status' => 'running',
|
|
'status_message' => 'Container started.',
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class, V5ClusterUpdated::class]);
|
|
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'server',
|
|
'host_id' => '100.64.0.6',
|
|
'status' => 'unreachable',
|
|
'message' => 'coold heartbeat timed out.',
|
|
]);
|
|
|
|
expect($resource)->toBeInstanceOf(V5Server::class)
|
|
->and($server->refresh()->status)->toBe('unreachable');
|
|
|
|
Event::assertDispatched(V5CanvasResourceUpdated::class, fn (V5CanvasResourceUpdated $event) => $event->teamId === $team->id
|
|
&& $event->serverId === $server->id);
|
|
|
|
$payload = (new V5CanvasResourceUpdated($team->id, serverId: $server->id))->broadcastWith();
|
|
expect($payload['applications'])
|
|
->toHaveCount(1)
|
|
->and($payload['applications'][0])
|
|
->toMatchArray([
|
|
'id' => $application->uuid,
|
|
'status' => 'running',
|
|
'effectiveStatus' => 'unknown',
|
|
'effectiveStatusMessage' => 'coold heartbeat timed out.',
|
|
'serverStatus' => 'unreachable',
|
|
'isServerReachable' => false,
|
|
]);
|
|
});
|
|
|
|
it('applies flux caddy ingress container status updates without changing server install status', function () {
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'ingress_status' => 'running',
|
|
'capabilities' => ['ingress'],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class]);
|
|
|
|
$resource = ApplyFluxResourceStatusUpdate::run([
|
|
'resource_type' => 'caddy_ingress',
|
|
'host_id' => '100.64.0.5',
|
|
'container_name' => 'coolify-v5-caddy',
|
|
'status' => 'exited',
|
|
'message' => 'Caddy container exited.',
|
|
]);
|
|
|
|
expect($resource)->toBeInstanceOf(V5Server::class)
|
|
->and($server->refresh()->status)->toBe('installed')
|
|
->and($server->ingress_type)->toBe('caddy')
|
|
->and($server->ingress_status)->toBe('exited')
|
|
->and($server->last_status_check)->toBe('flux')
|
|
->and($server->last_status_output)->toBe('Caddy container exited.')
|
|
->and($server->last_status_checked_at)->not->toBeNull();
|
|
|
|
Event::assertDispatched(V5CanvasResourceUpdated::class, fn (V5CanvasResourceUpdated $event) => $event->teamId === $team->id
|
|
&& $event->caddyIngressServerId === $server->id);
|
|
});
|
|
|
|
it('accepts flux status updates for non coolify managed containers', function () {
|
|
Config::set('flux.laravel_api_token', 'test-flux-token');
|
|
createSharedUserAndTeamTables();
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
|
|
$this
|
|
->postJson('/api/v1/internal/flux/resource-status', [
|
|
'resource_type' => 'container',
|
|
'host_id' => '100.64.0.5',
|
|
'container_id' => 'external-container-id',
|
|
'container_name' => 'external-container',
|
|
'status' => 'running',
|
|
], [
|
|
'Authorization' => 'Bearer test-flux-token',
|
|
])
|
|
->assertSuccessful()
|
|
->assertJsonPath('message', 'Resource status updated.');
|
|
|
|
expect(ContainerStatus::query()
|
|
->where('container_id', 'external-container-id')
|
|
->where('container_name', 'external-container')
|
|
->where('status', 'running')
|
|
->exists())->toBeTrue();
|
|
});
|
|
|
|
it('rejects flux resource status http updates without the shared token', function () {
|
|
Config::set('flux.laravel_api_token', 'test-flux-token');
|
|
|
|
$this
|
|
->postJson('/api/v1/internal/flux/resource-status', [
|
|
'resource_type' => 'application',
|
|
'status' => 'running',
|
|
])
|
|
->assertUnauthorized();
|
|
});
|
|
|
|
it('accepts flux resource status http updates identified by resource uuids', function () {
|
|
createSharedUserAndTeamTables();
|
|
Config::set('flux.laravel_api_token', 'test-flux-token');
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
[$project, $environment] = createV5ProjectWithEnvironment($team, 'Production Project', 'Production');
|
|
$server = V5Server::query()->create([
|
|
'uuid' => 'server-public-uuid',
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$application = V5Application::query()->create([
|
|
'uuid' => 'application-public-uuid',
|
|
'team_id' => $team->id,
|
|
'project_id' => $project->id,
|
|
'environment_id' => $environment->id,
|
|
'server_id' => $server->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'nginx-test',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'status' => 'starting',
|
|
'status_message' => 'Container starting.',
|
|
]);
|
|
|
|
$this
|
|
->withToken('test-flux-token')
|
|
->postJson('/api/v1/internal/flux/resource-status', [
|
|
'resource_type' => 'application',
|
|
'server_uuid' => $server->uuid,
|
|
'application_uuid' => $application->uuid,
|
|
'container_id' => 'new-container-id',
|
|
'status' => 'running',
|
|
'status_message' => 'Status received from coold through flux.',
|
|
])
|
|
->assertSuccessful()
|
|
->assertJsonPath('message', 'Resource status updated.');
|
|
|
|
expect($application->refresh()->status)->toBe('running')
|
|
->and($application->runtime_container_id)->toBe('new-container-id');
|
|
});
|
|
|
|
it('rejects numeric laravel ids in flux resource status http payloads', function () {
|
|
Config::set('flux.laravel_api_token', 'test-flux-token');
|
|
|
|
$this
|
|
->withToken('test-flux-token')
|
|
->postJson('/api/v1/internal/flux/resource-status', [
|
|
'resource_type' => 'application',
|
|
'server_id' => 1,
|
|
'application_id' => 1,
|
|
'status' => 'running',
|
|
])
|
|
->assertInvalid(['server_id', 'application_id']);
|
|
});
|
|
|
|
it('accepts flux resource status http updates and stores them in the database', function () {
|
|
createSharedUserAndTeamTables();
|
|
Config::set('flux.laravel_api_token', 'test-flux-token');
|
|
|
|
[$user, $team] = createV5UserWithTeam();
|
|
[$project, $environment] = createV5ProjectWithEnvironment($team, 'Production Project', 'Production');
|
|
$server = V5Server::query()->create([
|
|
'team_id' => $team->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'edge-01',
|
|
'host' => '203.0.113.10',
|
|
'ssh_user' => 'root',
|
|
'ssh_port' => 22,
|
|
'status' => 'installed',
|
|
'capabilities' => [],
|
|
'wireguard_management_ip' => '100.64.0.5',
|
|
]);
|
|
$application = V5Application::query()->create([
|
|
'team_id' => $team->id,
|
|
'project_id' => $project->id,
|
|
'environment_id' => $environment->id,
|
|
'server_id' => $server->id,
|
|
'created_by_user_id' => $user->id,
|
|
'name' => 'nginx-test',
|
|
'image' => 'docker.io/library/nginx:alpine',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'status' => 'starting',
|
|
'status_message' => 'Container starting.',
|
|
'runtime_container_id' => 'old-container-id',
|
|
]);
|
|
|
|
Event::fake([V5CanvasResourceUpdated::class]);
|
|
|
|
$this
|
|
->withToken('test-flux-token')
|
|
->postJson('/api/v1/internal/flux/resource-status', [
|
|
'resource_type' => 'application',
|
|
'host_id' => '100.64.0.5',
|
|
'container_name' => 'coolify-v5-nginx-1',
|
|
'container_id' => 'old-container-id',
|
|
'status' => 'running',
|
|
'status_message' => 'Status received from coold through flux.',
|
|
])
|
|
->assertSuccessful()
|
|
->assertJsonPath('message', 'Resource status updated.');
|
|
|
|
expect($application->refresh()->status)->toBe('running')
|
|
->and($application->status_message)->toBe('Status received from coold through flux.')
|
|
->and($application->runtime_container_id)->toBe('old-container-id');
|
|
|
|
Event::assertDispatched(V5CanvasResourceUpdated::class, fn (V5CanvasResourceUpdated $event) => $event->teamId === $team->id
|
|
&& $event->applicationId === $application->id);
|
|
});
|
|
|
|
it('configures flux resource status updates for local http instead of redis', function () {
|
|
$configSource = file_get_contents(config_path('flux.php'));
|
|
|
|
expect($configSource)
|
|
->toContain('COOLIFY_FLUX_LARAVEL_API_TOKEN')
|
|
->not->toContain('APP_KEY')
|
|
->not->toContain('COOLIFY_FLUX_RESOURCE_STATUS_CHANNEL')
|
|
->not->toContain('resource_status_channel');
|
|
});
|