mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
DeleteResourceJob no longer restores a service and rethrows when remote cleanup fails. The resource is always removed from Coolify, and the team gets a notification that Docker resources may remain on the server. No notification is sent when the server itself was already deleted. removeSubresourceContainer now returns false instead of throwing when the server is not functional. Deleting a service application or database then shows a warning that the container is removed on the next service start. StopService handles a missing destination server, environment, project, or team without throwing. The danger zone warning now shows for any resource whose server is unreachable, not only services.
91 lines
3.2 KiB
PHP
91 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Service;
|
|
|
|
use App\Actions\Server\CleanupDocker;
|
|
use App\Enums\ProcessStatus;
|
|
use App\Events\ServiceStatusChanged;
|
|
use App\Models\Server;
|
|
use App\Models\Service;
|
|
use Lorisleiva\Actions\Concerns\AsAction;
|
|
use Spatie\Activitylog\Models\Activity;
|
|
|
|
class StopService
|
|
{
|
|
use AsAction;
|
|
|
|
public string $jobQueue = 'high';
|
|
|
|
public function handle(Service $service, bool $deleteConnectedNetworks = false, bool $dockerCleanup = true)
|
|
{
|
|
try {
|
|
// Cancel any in-progress deployment activities so status doesn't stay stuck at "starting"
|
|
Activity::where('properties->type_uuid', $service->uuid)
|
|
->where(function ($q) {
|
|
$q->where('properties->status', ProcessStatus::IN_PROGRESS->value)
|
|
->orWhere('properties->status', ProcessStatus::QUEUED->value);
|
|
})
|
|
->each(function ($activity) {
|
|
$activity->properties = $activity->properties->put('status', ProcessStatus::CANCELLED->value);
|
|
$activity->save();
|
|
});
|
|
|
|
// The server is null once it is deleted (for example "Delete server" with all resources).
|
|
$server = $service->destination?->server;
|
|
if (! $server?->isFunctional()) {
|
|
return 'Server is not functional';
|
|
}
|
|
|
|
$containersToStop = [];
|
|
$applications = $service->applications()->get();
|
|
foreach ($applications as $application) {
|
|
$containersToStop[] = "{$application->name}-{$service->uuid}";
|
|
}
|
|
$dbs = $service->databases()->get();
|
|
foreach ($dbs as $db) {
|
|
$containersToStop[] = "{$db->name}-{$service->uuid}";
|
|
}
|
|
|
|
if (! empty($containersToStop)) {
|
|
$this->stopContainersInParallel($containersToStop, $server);
|
|
}
|
|
|
|
$applications->each(function ($application): void {
|
|
$application->update(['status' => 'exited']);
|
|
$application->resetRestartLimit();
|
|
});
|
|
$dbs->each(function ($database): void {
|
|
$database->update(['status' => 'exited']);
|
|
});
|
|
|
|
if ($deleteConnectedNetworks) {
|
|
$service->deleteConnectedNetworks();
|
|
}
|
|
if ($dockerCleanup) {
|
|
CleanupDocker::dispatch($server, false, false);
|
|
}
|
|
} catch (\Exception $e) {
|
|
return $e->getMessage();
|
|
} finally {
|
|
$teamId = $service->environment?->project?->team?->id;
|
|
if ($teamId !== null) {
|
|
ServiceStatusChanged::dispatch($teamId);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function stopContainersInParallel(array $containersToStop, Server $server): void
|
|
{
|
|
$timeout = count($containersToStop) > 5 ? 10 : 30;
|
|
$commands = [];
|
|
$containerList = implode(' ', array_map('escapeshellarg', $containersToStop));
|
|
$commands[] = dockerStopCommand($timeout, $containerList, $server);
|
|
$commands[] = "docker rm -f $containerList";
|
|
instant_remote_process(
|
|
command: $commands,
|
|
server: $server,
|
|
throwError: false
|
|
);
|
|
}
|
|
}
|