Feat: more conformations and fixes

This commit is contained in:
ayntk-ai
2024-09-05 17:54:32 +02:00
parent 08df814408
commit fc3c69f687
12 changed files with 174 additions and 48 deletions

View File

@@ -17,7 +17,6 @@ class StopService
if (!$server->isFunctional()) {
return 'Server is not functional';
}
ray('Stopping service: ' . $service->name);
$containersToStop = $service->getContainersToStop();
$service->stopContainers($containersToStop, $server);

View File

@@ -92,6 +92,9 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
throw $e;
} finally {
$this->resource->forceDelete();
if ($this->dockerCleanup) {
CleanupDocker::run($server, true);
}
Artisan::queue('cleanup:stucked-resources');
}
}

View File

@@ -4,11 +4,25 @@ namespace App\Livewire;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class NavbarDeleteTeam extends Component
{
public function delete()
public $team;
public function mount()
{
$this->team = currentTeam()->name;
}
public function delete($password)
{
if (!Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
}
$currentTeam = currentTeam();
$currentTeam->delete();

View File

@@ -9,6 +9,8 @@ use Illuminate\Support\Collection;
use Livewire\Component;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
use Illuminate\Process\InvokedProcess;
use Illuminate\Support\Facades\Process;
class Previews extends Component
{
@@ -177,17 +179,20 @@ class Previews extends Component
public function stop(int $pull_request_id)
{
try {
$server = $this->application->destination->server;
$timeout = 300;
if ($this->application->destination->server->isSwarm()) {
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
} else {
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
foreach ($containers as $container) {
$name = str_replace('/', '', $container['Names']);
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
}
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
$this->stopContainers($containers, $server, $timeout);
}
GetContainersStatus::dispatchSync($this->application->destination->server)->onQueue('high');
$this->dispatch('reloadWindow');
GetContainersStatus::run($server);
$this->application->refresh();
$this->dispatch('containerStatusUpdated');
$this->dispatch('success', 'Preview Deployment stopped.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -196,16 +201,21 @@ class Previews extends Component
public function delete(int $pull_request_id)
{
try {
$server = $this->application->destination->server;
$timeout = 300;
if ($this->application->destination->server->isSwarm()) {
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
} else {
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
foreach ($containers as $container) {
$name = str_replace('/', '', $container['Names']);
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
}
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
$this->stopContainers($containers, $server, $timeout);
}
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
ApplicationPreview::where('application_id', $this->application->id)
->where('pull_request_id', $pull_request_id)
->first()
->delete();
$this->application->refresh();
$this->dispatch('update_links');
$this->dispatch('success', 'Preview deleted.');
@@ -213,4 +223,49 @@ class Previews extends Component
return handleError($e, $this);
}
}
private function stopContainers(array $containers, $server, int $timeout)
{
$processes = [];
foreach ($containers as $container) {
$containerName = str_replace('/', '', $container['Names']);
$processes[$containerName] = $this->stopContainer($containerName, $timeout);
}
$startTime = time();
while (count($processes) > 0) {
$finishedProcesses = array_filter($processes, function ($process) {
return !$process->running();
});
foreach (array_keys($finishedProcesses) as $containerName) {
unset($processes[$containerName]);
$this->removeContainer($containerName, $server);
}
if (time() - $startTime >= $timeout) {
$this->forceStopRemainingContainers(array_keys($processes), $server);
break;
}
usleep(100000);
}
}
private function stopContainer(string $containerName, int $timeout): InvokedProcess
{
return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
}
private function removeContainer(string $containerName, $server)
{
instant_remote_process(["docker rm -f $containerName"], $server, throwError: false);
}
private function forceStopRemainingContainers(array $containerNames, $server)
{
foreach ($containerNames as $containerName) {
instant_remote_process(["docker kill $containerName"], $server, throwError: false);
$this->removeContainer($containerName, $server);
}
}
}

View File

@@ -15,6 +15,8 @@ use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Livewire\Component;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
class FileStorage extends Component
{
@@ -77,8 +79,13 @@ class FileStorage extends Component
}
}
public function delete()
public function delete($password)
{
if (!Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
}
try {
$message = 'File deleted.';
if ($this->fileStorage->is_directory) {
@@ -121,8 +128,15 @@ class FileStorage extends Component
$this->submit();
}
public function render()
public function render()
{
return view('livewire.project.service.file-storage');
return view('livewire.project.service.file-storage', [
'directoryDeletionCheckboxes' => [
['id' => 'permanently_delete', 'label' => 'The selected directory and all its contents will be permantely deleted form the server.'],
],
'fileDeletionCheckboxes' => [
['id' => 'permanently_delete', 'label' => 'The selected file will be permanently deleted form the server.'],
]
]);
}
}

View File

@@ -4,6 +4,8 @@ namespace App\Livewire\Project\Service;
use App\Models\ServiceApplication;
use Livewire\Component;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
class ServiceApplicationView extends Component
{
@@ -11,6 +13,10 @@ class ServiceApplicationView extends Component
public $parameters;
public $docker_cleanup = true;
public $delete_volumes = true;
protected $rules = [
'application.human_name' => 'nullable',
'application.description' => 'nullable',
@@ -23,12 +29,7 @@ class ServiceApplicationView extends Component
'application.is_stripprefix_enabled' => 'nullable|boolean',
];
public function render()
{
return view('livewire.project.service.service-application-view');
}
public function updatedApplicationFqdn()
public function updatedApplicationFqdn()
{
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
@@ -56,8 +57,13 @@ class ServiceApplicationView extends Component
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
}
public function delete()
public function delete($password)
{
if (!Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
}
try {
$this->application->delete();
$this->dispatch('success', 'Application deleted.');
@@ -91,4 +97,17 @@ class ServiceApplicationView extends Component
$this->dispatch('generateDockerCompose');
}
}
public function render()
{
return view('livewire.project.service.service-application-view', [
'checkboxes' => [
['id' => 'delete_volumes', 'label' => 'All associated volumes with this resource will be permanently deleted'],
['id' => 'docker_cleanup', 'label' => 'Docker cleanup will be run on the server which removes builder cache and unused images'],
// ['id' => 'delete_associated_backups_locally', 'label' => 'All backups associated with this Ressource will be permanently deleted from local storage.'],
// ['id' => 'delete_associated_backups_s3', 'label' => 'All backups associated with this Ressource will be permanently deleted from the selected S3 Storage.'],
// ['id' => 'delete_associated_backups_sftp', 'label' => 'All backups associated with this Ressource will be permanently deleted from the selected SFTP Storage.']
]
]);
}
}

View File

@@ -10,6 +10,8 @@ use App\Models\Server;
use App\Models\StandaloneDocker;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
class Destination extends Component
{
@@ -115,8 +117,13 @@ class Destination extends Component
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
}
public function removeServer(int $network_id, int $server_id)
public function removeServer(int $network_id, int $server_id, $password)
{
if (!Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
}
if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) {
$this->dispatch('error', 'You cannot remove this destination server.', 'You are trying to remove the main server.');

View File

@@ -45,7 +45,6 @@ class Show extends Component
return;
}
// Test deletion in more detail
$this->storage->delete();
$this->dispatch('refreshStorages');
}

View File

@@ -1,5 +1,13 @@
<div>
<x-modal-confirmation buttonFullWidth isErrorButton buttonTitle="Delete Team">
This team be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Team Deletion?"
buttonTitle="Delete Team"
isErrorButton
submitAction="delete"
:actions="['The current Team will be permanently deleted.']"
confirmationText="{{ $team }}"
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
shortConfirmationLabel="Team Name"
step3ButtonText="Permanently Delete Team"
/>
</div>

View File

@@ -160,8 +160,6 @@
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Stop Preview Deployment"
:dispatchEvent="true"
dispatchEventType="stopEvent"
>
<x-slot:customButton>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error"
@@ -189,9 +187,6 @@
confirmationLabel="Please confirm the execution of the actions by entering the Preview Deployment name below"
shortConfirmationLabel="Preview Deployment Name"
:confirmWithPassword="false"
:dispatchEvent="true"
dispatchEventType="deleteEvent"
dispatchEventMessage="Preview Deployment Deleted"
/>
</div>
</div>

View File

@@ -7,12 +7,20 @@
<h2>{{ Str::headline($application->name) }}</h2>
@endif
<x-forms.button type="submit">Save</x-forms.button>
<x-modal-confirmation isErrorButton>
<x-slot:button-title>
Delete
</x-slot:button-title>
This will delete this service application. It is not reversible.<br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Service Application Deletion?"
buttonTitle="Delete"
isErrorButton
submitAction="delete"
{{-- :checkboxes="$checkboxes" --}}
:actions="[
'The selected service application container will be stopped and permanently deleted.'
]"
confirmationText="{{ Str::headline($application->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
shortConfirmationLabel="Service Application Name"
step3ButtonText="Permanently Delete Service Application"
/>
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">

View File

@@ -66,11 +66,16 @@
wire:click="stop('{{ data_get($destination, 'server.id') }}')">Stop</x-forms.button>
@endif
<x-modal-confirmation
action="removeServer({{ data_get($destination, 'id') }},{{ data_get($destination, 'server.id') }})"
isErrorButton buttonTitle="Remove Server">
This will stop the running application in this server and remove it as a deployment
destination.<br><br>Please think again.
</x-modal-confirmation>
title="Confirm server removal?"
isErrorButton
buttonTitle="Remove Server"
submitAction="removeServer({{ data_get($destination, 'id') }},{{ data_get($destination, 'server.id') }})"
:actions="['This will stop the all running applications on this server and remove it as a deployment destination.']"
confirmationText="{{ data_get($destination, 'server.name') }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name"
step3ButtonText="Permanently Remove Server"
/>
</div>
</div>
@endforeach