Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Fix all deletion issues, add graceful shutdown to everything, new delete confirmation process #3024

Merged
merged 99 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
df796df
fix delte networks and unused images of services when deleted
peaklabs-dev Aug 7, 2024
070daee
remove networks and cleanup unused images when stoping dockercompose …
peaklabs-dev Aug 7, 2024
74bea37
Merge branch 'coollabsio:main' into fix-#2546-deletion-issues
peaklabs-dev Aug 8, 2024
4d0acee
UI options for deletion WIP
peaklabs-dev Aug 8, 2024
2f95349
Merge branch 'coollabsio:main' into fix-#2546-deletion-issues
peaklabs-dev Aug 8, 2024
0135e2b
add logic
peaklabs-dev Aug 8, 2024
70aa05b
order in importance
peaklabs-dev Aug 8, 2024
51071da
fix order
peaklabs-dev Aug 8, 2024
86a0870
fix volume deletion for services
peaklabs-dev Aug 9, 2024
e67e03f
added comments and removed temp ones
peaklabs-dev Aug 9, 2024
7722809
typo
peaklabs-dev Aug 9, 2024
53dff4c
simplify uuid variabel
peaklabs-dev Aug 9, 2024
d980c7a
only run network removal on stop service if it is not a deletion oper…
peaklabs-dev Aug 9, 2024
97c2bed
add delete_connected_networks function to services.php
peaklabs-dev Aug 9, 2024
5595853
WIP database network, image removal
peaklabs-dev Aug 9, 2024
d177e49
updated warning message and formating
peaklabs-dev Aug 9, 2024
72bcf03
graceful service container stop
peaklabs-dev Aug 9, 2024
1cfddfd
fix stop large amount of containers
peaklabs-dev Aug 9, 2024
a4bb87d
simplify DeleteService.php
peaklabs-dev Aug 9, 2024
4503519
added public functions
peaklabs-dev Aug 9, 2024
41be1f7
use public function
peaklabs-dev Aug 9, 2024
2ca6ffb
fix public function service.php
peaklabs-dev Aug 9, 2024
c566152
improve graceful_shutdown_container
peaklabs-dev Aug 9, 2024
16a5c60
graceful db stop and db deletion fixes
peaklabs-dev Aug 9, 2024
7d1179e
fix cleanup images for databases
peaklabs-dev Aug 9, 2024
840e225
formatting and waring text
peaklabs-dev Aug 9, 2024
b5360e5
improve CleanupDocker.php
peaklabs-dev Aug 9, 2024
5b54dc8
Revert "improve CleanupDocker.php"
peaklabs-dev Aug 9, 2024
2a58114
new confirm delete dialog
peaklabs-dev Aug 27, 2024
ac50d8b
fix styling
peaklabs-dev Aug 27, 2024
9040f5d
confirm with password
peaklabs-dev Aug 27, 2024
4726676
make things more clear
peaklabs-dev Aug 27, 2024
8d2a02d
change title of confirmation popup
peaklabs-dev Aug 27, 2024
73068aa
Refactor: Integrate tow step process in the modal component WIP
peaklabs-dev Aug 28, 2024
141752b
fix checkbox actions default display
peaklabs-dev Aug 28, 2024
1b51d46
more props, nav button fixes
peaklabs-dev Aug 28, 2024
a2651ab
reset modal on cancel, fix back button when there is no prior step
peaklabs-dev Aug 28, 2024
c926599
improve button text
peaklabs-dev Aug 28, 2024
62b7900
more props and more fixes
peaklabs-dev Aug 28, 2024
354c74e
fix
peaklabs-dev Aug 29, 2024
a22e757
fix execute action on the last button
peaklabs-dev Aug 29, 2024
182af1e
Fix application delete
peaklabs-dev Aug 29, 2024
d984bec
Ajust text
peaklabs-dev Aug 29, 2024
6820fcc
Fix name for services and DBs
peaklabs-dev Aug 29, 2024
da0398f
remove unused code
peaklabs-dev Aug 29, 2024
9136d7a
WIP more delete confirmations
peaklabs-dev Aug 30, 2024
b807601
delete user confirm
peaklabs-dev Aug 30, 2024
bff6964
scheduled task deletion
peaklabs-dev Aug 30, 2024
73dfdb8
fix rendering bug, more props
peaklabs-dev Aug 31, 2024
76cb473
fix default checkbox state
peaklabs-dev Aug 31, 2024
bcfca40
rest checkboxes on close
peaklabs-dev Aug 31, 2024
b118a62
fix password validation and password error
peaklabs-dev Aug 31, 2024
a3dd48d
destination and dashboard confirmation
peaklabs-dev Aug 31, 2024
830c047
delete environment confirmation
peaklabs-dev Aug 31, 2024
b656cab
delete project confirmation
peaklabs-dev Aug 31, 2024
2adac01
confirm danger
peaklabs-dev Aug 31, 2024
3b3bc6c
fix default checkbox state false or true
peaklabs-dev Aug 31, 2024
38845d7
confirm backup and delete all backups of the job
peaklabs-dev Aug 31, 2024
d2a0621
delete backup folder if empty
peaklabs-dev Aug 31, 2024
f8226cf
confirm backup deletion
peaklabs-dev Aug 31, 2024
f857bbc
fix submit action
peaklabs-dev Aug 31, 2024
776d416
Fix 3 risk levels
peaklabs-dev Aug 31, 2024
a97ccd2
confirm init script
peaklabs-dev Aug 31, 2024
843e3fb
fix checkboxes in danger
peaklabs-dev Aug 31, 2024
dfd218e
fixes here and there
peaklabs-dev Sep 2, 2024
1b0c5f8
css fix for long text
peaklabs-dev Sep 2, 2024
5944ee5
fix close modal on submit
peaklabs-dev Sep 2, 2024
a4d1ae1
Feat: ability to hide labels
peaklabs-dev Sep 2, 2024
ff1e08c
add toast dispatching
peaklabs-dev Sep 2, 2024
70043c2
100000000x Speed improvement first toast then submit in the background
peaklabs-dev Sep 2, 2024
c24fec9
fix step and password error
peaklabs-dev Sep 2, 2024
c318895
Feat: DB start, stop confirm
peaklabs-dev Sep 2, 2024
3d1c730
Feat: del init script
peaklabs-dev Sep 2, 2024
792f6bc
made wording more clear
peaklabs-dev Sep 2, 2024
20558d4
remove ray
peaklabs-dev Sep 2, 2024
3d21f1a
fix checkbox hide label
peaklabs-dev Sep 3, 2024
f29bc52
Feat: general confirm
peaklabs-dev Sep 3, 2024
d5b7e9e
Feat: preview deployments and typos
peaklabs-dev Sep 3, 2024
d94e39c
Feat: service confirmation
peaklabs-dev Sep 3, 2024
9a2d5be
Feat: confirm file storage
peaklabs-dev Sep 3, 2024
b314b08
Feat: stop service confirm
peaklabs-dev Sep 4, 2024
bec974d
Fix application image cleanup
peaklabs-dev Sep 4, 2024
c16e914
Fix: DB image cleanup
peaklabs-dev Sep 4, 2024
a29353c
Feat: confirm ressource operation
peaklabs-dev Sep 4, 2024
7fe3b78
Feat: Environment variabel deletion
peaklabs-dev Sep 4, 2024
bbbd5cb
Feat: confirm scheduled tasks
peaklabs-dev Sep 4, 2024
93a4a3e
Feat: confirm API token
peaklabs-dev Sep 4, 2024
9515bc6
Feat: confirm private key
peaklabs-dev Sep 4, 2024
371fe53
Feat: confirm server deletion
peaklabs-dev Sep 4, 2024
505127d
Feat: confirm server settings
peaklabs-dev Sep 4, 2024
3e04a79
Feat/Fix: Proxy stop and restart confirmation
peaklabs-dev Sep 4, 2024
9105c1a
Feat: GH app deletion confirmation
peaklabs-dev Sep 4, 2024
44f3f60
Feat: Redeploy all confirmation
peaklabs-dev Sep 4, 2024
f4263ee
Feat: User deletion confirmation
peaklabs-dev Sep 4, 2024
2b5df8d
Feat: Team deletion confirmation
peaklabs-dev Sep 4, 2024
a7b78dc
Feat: Backup job confirmation
peaklabs-dev Sep 4, 2024
08df814
Feat: delete volume confirmation
peaklabs-dev Sep 4, 2024
fc3c69f
Feat: more conformations and fixes
peaklabs-dev Sep 5, 2024
5ec45d5
Merge branch 'next' into fix-#2546-deletion-issues
andrasbacsai Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 22 additions & 32 deletions app/Actions/Application/StopApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,40 @@
namespace App\Actions\Application;

use App\Models\Application;
use App\Actions\Server\CleanupDocker;
use Lorisleiva\Actions\Concerns\AsAction;

class StopApplication
{
use AsAction;

public function handle(Application $application, bool $previewDeployments = false)
public function handle(Application $application, bool $previewDeployments = false, bool $dockerCleanup = true)
{
if ($application->destination->server->isSwarm()) {
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);

return;
}

$servers = collect([]);
$servers->push($application->destination->server);
$application->additional_servers->map(function ($server) use ($servers) {
$servers->push($server);
});
foreach ($servers as $server) {
if (! $server->isFunctional()) {
try {
$server = $application->destination->server;
if (!$server->isFunctional()) {
return 'Server is not functional';
}
if ($previewDeployments) {
$containers = getCurrentApplicationContainerStatus($server, $application->id, includePullrequests: true);
} else {
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
}
if ($containers->count() > 0) {
foreach ($containers as $container) {
$containerName = data_get($container, 'Names');
if ($containerName) {
instant_remote_process(command: ["docker stop --time=30 $containerName"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm $containerName"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f {$containerName}"], server: $server, throwError: false);
}
}
ray('Stopping application: ' . $application->name);

if ($server->isSwarm()) {
instant_remote_process(["docker stack rm {$application->uuid}"], $server);
return;
}

$containersToStop = $application->getContainersToStop($previewDeployments);
$application->stopContainers($containersToStop, $server);

if ($application->build_pack === 'dockercompose') {
// remove network
$uuid = $application->uuid;
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
instant_remote_process(["docker network rm {$uuid}"], $server, false);
$application->delete_connected_networks($application->uuid);
}

if ($dockerCleanup) {
CleanupDocker::run($server, true);
}
} catch (\Exception $e) {
ray($e->getMessage());
return $e->getMessage();
}
}
}
52 changes: 47 additions & 5 deletions app/Actions/Database/StopDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,67 @@
use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Support\Facades\Process;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Actions\Server\CleanupDocker;

class StopDatabase
{
use AsAction;

public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database, bool $isDeleteOperation = false, bool $dockerCleanup = true)
{
$server = $database->destination->server;
if (! $server->isFunctional()) {
if (!$server->isFunctional()) {
return 'Server is not functional';
}

instant_remote_process(command: ["docker stop --time=30 $database->uuid"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm $database->uuid"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f $database->uuid"], server: $server, throwError: false);
$this->stopContainer($database, $database->uuid, 300);
if (!$isDeleteOperation) {
$this->deleteConnectedNetworks($database->uuid, $server); //Probably not needed as DBs do not have a network normally
if ($dockerCleanup) {
CleanupDocker::run($server, true);
}
}

if ($database->is_public) {
StopDatabaseProxy::run($database);
}

return 'Database stopped successfully';
}

private function stopContainer($database, string $containerName, int $timeout = 300): void
{
$server = $database->destination->server;

$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");

$startTime = time();
while ($process->running()) {
if (time() - $startTime >= $timeout) {
$this->forceStopContainer($containerName, $server);
break;
}
usleep(100000);
}

$this->removeContainer($containerName, $server);
}

private function forceStopContainer(string $containerName, $server): void
{
instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
}

private function removeContainer(string $containerName, $server): void
{
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
}

private function deleteConnectedNetworks($uuid, $server)
{
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
instant_remote_process(["docker network rm {$uuid}"], $server, false);
}
}
30 changes: 26 additions & 4 deletions app/Actions/Service/DeleteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
namespace App\Actions\Service;

use App\Models\Service;
use App\Actions\Server\CleanupDocker;
use Lorisleiva\Actions\Concerns\AsAction;

class DeleteService
{
use AsAction;

public function handle(Service $service)
public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $dockerCleanup, bool $deleteConnectedNetworks)
{
try {
$server = data_get($service, 'server');
if ($server->isFunctional()) {
if ($deleteVolumes && $server->isFunctional()) {
$storagesToDelete = collect([]);

$service->environment_variables()->delete();
Expand All @@ -33,13 +34,29 @@ public function handle(Service $service)
foreach ($storagesToDelete as $storage) {
$commands[] = "docker volume rm -f $storage->name";
}
$commands[] = "docker rm -f $service->uuid";

instant_remote_process($commands, $server, false);
// Execute volume deletion first, this must be done first otherwise volumes will not be deleted.
if (!empty($commands)) {
foreach ($commands as $command) {
$result = instant_remote_process([$command], $server, false);
if ($result !== 0) {
ray("Failed to execute: $command");
}
}
}
}

if ($deleteConnectedNetworks) {
$service->delete_connected_networks($service->uuid);
}

instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
} finally {
if ($deleteConfigurations) {
$service->delete_configurations();
}
foreach ($service->applications()->get() as $application) {
$application->forceDelete();
}
Expand All @@ -50,6 +67,11 @@ public function handle(Service $service)
$task->delete();
}
$service->tags()->detach();
$service->forceDelete();

if ($dockerCleanup) {
CleanupDocker::run($server, true);
}
}
}
}
33 changes: 10 additions & 23 deletions app/Actions/Service/StopService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,33 @@
namespace App\Actions\Service;

use App\Models\Service;
use App\Actions\Server\CleanupDocker;
use Lorisleiva\Actions\Concerns\AsAction;

class StopService
{
use AsAction;

public function handle(Service $service)
public function handle(Service $service, bool $isDeleteOperation = false, bool $dockerCleanup = true)
{
try {
$server = $service->destination->server;
if (! $server->isFunctional()) {
if (!$server->isFunctional()) {
return 'Server is not functional';
}
ray('Stopping service: '.$service->name);
$applications = $service->applications()->get();
foreach ($applications as $application) {
if ($applications->count() < 6) {
instant_remote_process(command: ["docker stop --time=10 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
}
instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false);
$application->update(['status' => 'exited']);
}
$dbs = $service->databases()->get();
foreach ($dbs as $db) {
if ($dbs->count() < 6) {

instant_remote_process(command: ["docker stop --time=10 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
$containersToStop = $service->getContainersToStop();
$service->stopContainers($containersToStop, $server);

if (!$isDeleteOperation) {
$service->delete_connected_networks($service->uuid);
if ($dockerCleanup) {
CleanupDocker::run($server, true);
}
instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false);
$db->update(['status' => 'exited']);
}
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy"], $service->server);
instant_remote_process(["docker network rm {$service->uuid}"], $service->server);
} catch (\Exception $e) {
ray($e->getMessage());

return $e->getMessage();
}

}
}
41 changes: 31 additions & 10 deletions app/Jobs/ApplicationDeploymentJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Sleep;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Process;
use RuntimeException;
use Symfony\Component\Yaml\Yaml;
use Throwable;
Expand Down Expand Up @@ -2211,20 +2212,40 @@ private function build_image()
$this->application_deployment_queue->addLogEntry('Building docker image completed.');
}

/**
* @param int $timeout in seconds
*/
private function graceful_shutdown_container(string $containerName, int $timeout = 30)
private function graceful_shutdown_container(string $containerName, int $timeout = 300)
{
try {
$this->execute_remote_command(
["docker stop --time=$timeout $containerName", 'hidden' => true, 'ignore_errors' => true],
["docker rm $containerName", 'hidden' => true, 'ignore_errors' => true]
);
$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");

$startTime = time();
while ($process->running()) {
if (time() - $startTime >= $timeout) {
$this->execute_remote_command(
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
);
break;
}
usleep(100000);
}

$isRunning = $this->execute_remote_command(
["docker inspect -f '{{.State.Running}}' $containerName", 'hidden' => true, 'ignore_errors' => true]
) === 'true';

if ($isRunning) {
$this->execute_remote_command(
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
);
}
} catch (\Exception $error) {
// report error if needed
$this->application_deployment_queue->addLogEntry("Error stopping container $containerName: " . $error->getMessage(), 'stderr');
}

$this->remove_container($containerName);
}

private function remove_container(string $containerName)
{
$this->execute_remote_command(
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
);
Expand All @@ -2240,7 +2261,7 @@ private function stop_running_container(bool $force = false)
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
if ($this->pull_request_id === 0) {
$containers = $containers->filter(function ($container) {
return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id;
return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id;
});
}
$containers->each(function ($container) {
Expand Down
Loading
Loading