mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-28 09:34:36 +08:00
- Refresh the navbar proxy status on the 30 s poll - Skip Sentinel and server check auto-start while a proxy start or restart runs - Use the StartProxy commands in RestartProxyJob, so Caddy restarts keep the maintenance blocks and remove the Traefik logrotate sidecar Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
232 lines
7.5 KiB
PHP
232 lines
7.5 KiB
PHP
<?php
|
|
|
|
use App\Actions\Proxy\CheckProxy;
|
|
use App\Actions\Proxy\StartProxy;
|
|
use App\Events\ProxyStatusChanged;
|
|
use App\Events\ServiceChecked;
|
|
use App\Jobs\PushServerUpdateJob;
|
|
use App\Jobs\ServerStorageCheckJob;
|
|
use App\Listeners\ProxyStatusChangedNotification;
|
|
use App\Models\Server;
|
|
use App\Models\Team;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Event;
|
|
use Illuminate\Support\Facades\Process;
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
Queue::fake();
|
|
Cache::flush();
|
|
});
|
|
|
|
it('dispatches storage check when disk percentage changes above threshold', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
|
|
// Default notification threshold is 80%.
|
|
$data = [
|
|
'containers' => [],
|
|
'filesystem_usage_root' => ['used_percentage' => 85],
|
|
];
|
|
|
|
$job = new PushServerUpdateJob($server, $data);
|
|
$job->handle();
|
|
|
|
Queue::assertPushed(ServerStorageCheckJob::class, function ($job) use ($server) {
|
|
return $job->server->id === $server->id && $job->percentage === 85;
|
|
});
|
|
});
|
|
|
|
it('does not dispatch storage check when disk usage is below threshold', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
|
|
// 45% is well below the default 80% notification threshold — nothing to do.
|
|
$data = [
|
|
'containers' => [],
|
|
'filesystem_usage_root' => ['used_percentage' => 45],
|
|
];
|
|
|
|
$job = new PushServerUpdateJob($server, $data);
|
|
$job->handle();
|
|
|
|
Queue::assertNotPushed(ServerStorageCheckJob::class);
|
|
});
|
|
|
|
it('clears stale storage cache when disk usage drops below threshold', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
$storageCacheKey = 'storage-check:'.$server->id;
|
|
|
|
Cache::put($storageCacheKey, 85, 600);
|
|
|
|
$belowThresholdData = [
|
|
'containers' => [],
|
|
'filesystem_usage_root' => ['used_percentage' => 45],
|
|
];
|
|
|
|
$job = new PushServerUpdateJob($server, $belowThresholdData);
|
|
$job->handle();
|
|
|
|
Queue::assertNotPushed(ServerStorageCheckJob::class);
|
|
expect(Cache::missing($storageCacheKey))->toBeTrue();
|
|
|
|
Queue::fake();
|
|
|
|
$aboveThresholdData = [
|
|
'containers' => [],
|
|
'filesystem_usage_root' => ['used_percentage' => 85],
|
|
];
|
|
|
|
$job = new PushServerUpdateJob($server, $aboveThresholdData);
|
|
$job->handle();
|
|
|
|
Queue::assertPushed(ServerStorageCheckJob::class, function ($job) use ($server) {
|
|
return $job->server->id === $server->id && $job->percentage === 85;
|
|
});
|
|
});
|
|
|
|
it('does not dispatch storage check when disk percentage is unchanged', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
|
|
// Simulate a previous push that cached the percentage (above threshold).
|
|
Cache::put('storage-check:'.$server->id, 85, 600);
|
|
|
|
$data = [
|
|
'containers' => [],
|
|
'filesystem_usage_root' => ['used_percentage' => 85],
|
|
];
|
|
|
|
$job = new PushServerUpdateJob($server, $data);
|
|
$job->handle();
|
|
|
|
Queue::assertNotPushed(ServerStorageCheckJob::class);
|
|
});
|
|
|
|
it('dispatches storage check when disk percentage changes from cached value', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
|
|
// Simulate a previous push that cached 85% (above threshold).
|
|
Cache::put('storage-check:'.$server->id, 85, 600);
|
|
|
|
$data = [
|
|
'containers' => [],
|
|
'filesystem_usage_root' => ['used_percentage' => 90],
|
|
];
|
|
|
|
$job = new PushServerUpdateJob($server, $data);
|
|
$job->handle();
|
|
|
|
Queue::assertPushed(ServerStorageCheckJob::class, function ($job) use ($server) {
|
|
return $job->server->id === $server->id && $job->percentage === 90;
|
|
});
|
|
});
|
|
|
|
it('does not dispatch proxy network work when the proxy is running', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
|
|
|
|
$data = [
|
|
'containers' => [
|
|
[
|
|
'name' => 'coolify-proxy',
|
|
'state' => 'running',
|
|
'health_status' => 'healthy',
|
|
'labels' => ['coolify.managed' => true],
|
|
],
|
|
],
|
|
'filesystem_usage_root' => ['used_percentage' => 10],
|
|
];
|
|
|
|
Event::fake([ServiceChecked::class]);
|
|
|
|
(new PushServerUpdateJob($server, $data))->handle();
|
|
|
|
Queue::assertNothingPushed();
|
|
});
|
|
|
|
it('marks a stale proxy status as running when sentinel reports a running proxy', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
|
|
$server->proxy->set('type', 'TRAEFIK');
|
|
$server->proxy->set('status', 'exited');
|
|
$server->save();
|
|
|
|
$data = [
|
|
'containers' => [
|
|
[
|
|
'name' => 'coolify-proxy',
|
|
'state' => 'running',
|
|
'health_status' => 'healthy',
|
|
'labels' => ['coolify.managed' => true],
|
|
],
|
|
],
|
|
'filesystem_usage_root' => ['used_percentage' => 10],
|
|
];
|
|
|
|
Event::fake([ServiceChecked::class]);
|
|
|
|
(new PushServerUpdateJob($server, $data))->handle();
|
|
|
|
expect($server->fresh()->proxy->get('status'))->toBe('running');
|
|
});
|
|
|
|
it('does not start a second proxy while a proxy restart runs', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
|
|
$server->proxy->set('type', 'TRAEFIK');
|
|
$server->proxy->set('status', 'restarting');
|
|
$server->save();
|
|
$server->markProxyTransitionStarted();
|
|
|
|
CheckProxy::shouldNotRun();
|
|
StartProxy::shouldNotRun();
|
|
Event::fake([ServiceChecked::class]);
|
|
|
|
(new PushServerUpdateJob($server, ['containers' => [], 'filesystem_usage_root' => ['used_percentage' => 10]]))->handle();
|
|
|
|
expect($server->fresh()->proxy->get('status'))->toBe('restarting');
|
|
});
|
|
|
|
it('starts a missing proxy when no proxy restart runs', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
|
|
$server->proxy->set('type', 'TRAEFIK');
|
|
$server->save();
|
|
|
|
CheckProxy::shouldRun()->once()->andReturn(true);
|
|
StartProxy::shouldRun()->once();
|
|
Event::fake([ServiceChecked::class]);
|
|
|
|
(new PushServerUpdateJob($server, ['containers' => [], 'filesystem_usage_root' => ['used_percentage' => 10]]))->handle();
|
|
});
|
|
|
|
it('ends the proxy restart when the proxy status listener runs', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
$server->markProxyTransitionStarted();
|
|
Process::fake();
|
|
|
|
(new ProxyStatusChangedNotification)->handle(new ProxyStatusChanged($server->id));
|
|
|
|
expect($server->isProxyTransitionInProgress())->toBeFalse();
|
|
});
|
|
|
|
it('uses default queue for PushServerUpdateJob', function () {
|
|
$team = Team::factory()->create();
|
|
$server = Server::factory()->create(['team_id' => $team->id]);
|
|
|
|
$job = new PushServerUpdateJob($server, ['containers' => []]);
|
|
|
|
expect($job->queue)->toBeNull();
|
|
});
|