diff --git a/app/Actions/Database/RestartDatabase.php b/app/Actions/Database/RestartDatabase.php index 66c7c8c961..19e78587b1 100644 --- a/app/Actions/Database/RestartDatabase.php +++ b/app/Actions/Database/RestartDatabase.php @@ -15,11 +15,17 @@ use App\Models\StandaloneSqlite; use App\Support\DatabaseOperationReservation; use App\Support\ResourceStartActivity; use Lorisleiva\Actions\Concerns\AsAction; +use Lorisleiva\Actions\Decorators\JobDecorator; class RestartDatabase { use AsAction; + public function configureJob(JobDecorator $job): void + { + $job->onQueue(deployment_queue()); + } + /** * @param string|null $reservation The token from StartDatabase::reserveOperation(). The request * that queued this action holds the reservation; this action diff --git a/app/Actions/Service/DeployServiceApplication.php b/app/Actions/Service/DeployServiceApplication.php index 78039a8252..c89c8e1e85 100644 --- a/app/Actions/Service/DeployServiceApplication.php +++ b/app/Actions/Service/DeployServiceApplication.php @@ -64,6 +64,6 @@ class DeployServiceApplication $commands->push("docker network connect --alias {$networkAlias} {$network} {$containerName} >/dev/null 2>&1 || true"); } - return remote_process($commands->toArray(), $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); + return remote_process($commands->toArray(), $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged', queue: deployment_queue()); } } diff --git a/app/Actions/Service/StartService.php b/app/Actions/Service/StartService.php index d928866f1d..1580045d09 100644 --- a/app/Actions/Service/StartService.php +++ b/app/Actions/Service/StartService.php @@ -64,7 +64,7 @@ class StartService } $commands = array_merge($commands, $this->logDrainNetworkConnectCommands($service)); - return remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); + return remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged', queue: deployment_queue()); } /** diff --git a/app/Jobs/CoolifyTask.php b/app/Jobs/CoolifyTask.php index 44739cd3f1..cac687fc85 100755 --- a/app/Jobs/CoolifyTask.php +++ b/app/Jobs/CoolifyTask.php @@ -38,6 +38,7 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue * Create a new job instance. * * @param int|null $timeout Job timeout in seconds; null keeps the default above. + * @param string $queue Queue to run on; start actions pass deployment_queue(). */ public function __construct( public Activity $activity, @@ -45,12 +46,13 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue public $call_event_on_finish, public $call_event_data, ?int $timeout = null, + string $queue = 'high', ) { if ($timeout !== null) { $this->timeout = $timeout; } - $this->onQueue('high'); + $this->onQueue($queue); } /** diff --git a/app/Traits/ExecutesDatabaseStartCommands.php b/app/Traits/ExecutesDatabaseStartCommands.php index d267a8b1b2..fca00cafa0 100644 --- a/app/Traits/ExecutesDatabaseStartCommands.php +++ b/app/Traits/ExecutesDatabaseStartCommands.php @@ -14,6 +14,6 @@ trait ExecutesDatabaseStartCommands return app(DatabaseStartCommandExecutor::class)->execute($commands, $database, $activity); } - return remote_process($commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged'); + return remote_process($commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged', queue: deployment_queue()); } } diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index b583521221..74f5215f4a 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -29,6 +29,7 @@ function remote_process( $callEventData = null, array $properties = [], ?int $timeout = null, + string $queue = 'high', ): Activity { $type = $type ?? ActivityTypes::INLINE->value; $command = $command instanceof Collection ? $command->toArray() : $command; @@ -74,6 +75,7 @@ function remote_process( call_event_on_finish: $callEventOnFinish, call_event_data: $callEventData, timeout: $timeout, + queue: $queue, )); $activity->refresh(); diff --git a/tests/Feature/ContentFilesBeforeComposeUpTest.php b/tests/Feature/ContentFilesBeforeComposeUpTest.php index 2086f9ce88..0838b84ca8 100644 --- a/tests/Feature/ContentFilesBeforeComposeUpTest.php +++ b/tests/Feature/ContentFilesBeforeComposeUpTest.php @@ -4,6 +4,7 @@ use App\Actions\Service\DeployServiceApplication; use App\Actions\Service\StartService; use App\Actions\Shared\EnsureContentFilesOnServer; use App\Jobs\ApplicationDeploymentJob; +use App\Jobs\CoolifyTask; use App\Jobs\ServerStorageSaveJob; use App\Models\Application; use App\Models\ApplicationDeploymentQueue; @@ -579,3 +580,22 @@ test('the server file sync still marks a storage without content as a directory' expect($volume->fresh()->is_directory)->toBeTruthy(); }); + +test('starting a service runs its commands on the deployment queue', function (bool $selfHosted, string $queue) { + config(['constants.coolify.self_hosted' => $selfHosted]); + $service = Service::factory()->create([ + 'environment_id' => $this->environment->id, + 'server_id' => $this->server->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + 'docker_compose_raw' => "services:\n app:\n image: nginx:alpine\n", + ]); + fakeContentFilesServer([]); + + StartService::run($service); + + Bus::assertDispatched(CoolifyTask::class, fn (CoolifyTask $job) => $job->queue === $queue); +})->with([ + 'cloud' => [false, 'deployments'], + 'self-hosted' => [true, 'high'], +]); diff --git a/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php b/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php index a9fde57d5e..664b5fbe8b 100644 --- a/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php +++ b/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php @@ -85,3 +85,19 @@ it('removes the command when the task fails permanently', function () { expect($stored->getExtraProperty('command'))->toBeNull() ->and($stored->getExtraProperty('status'))->toBe(ProcessStatus::ERROR->value); }); + +it('queues a remote process on the high queue by default', function () { + Queue::fake(); + + remote_process(['echo ok'], $this->server); + + Queue::assertPushedOn('high', CoolifyTask::class); +}); + +it('queues a remote process on the requested queue', function () { + Queue::fake(); + + remote_process(['echo ok'], $this->server, queue: 'deployments'); + + Queue::assertPushedOn('deployments', CoolifyTask::class); +}); diff --git a/tests/Feature/QueueRoutingTest.php b/tests/Feature/QueueRoutingTest.php index 1bb837be89..717b4a5b41 100644 --- a/tests/Feature/QueueRoutingTest.php +++ b/tests/Feature/QueueRoutingTest.php @@ -1,5 +1,6 @@ queue)->toBe('deployments'); })->with([ StartDatabase::class, + RestartDatabase::class, StartDatabaseProxy::class, StartService::class, ]); @@ -52,6 +54,7 @@ describe('start action job routing', function () { expect($actionClass::makeJob()->queue)->toBe('high'); })->with([ StartDatabase::class, + RestartDatabase::class, StartDatabaseProxy::class, StartService::class, ]);