feat: implement prerequisite validation and installation for server setup
This commit is contained in:
parent
ce4f8d02a2
commit
01957f2752
9 changed files with 218 additions and 32 deletions
|
|
@ -59,8 +59,6 @@ public function handle(Server $server)
|
||||||
$command = collect([]);
|
$command = collect([]);
|
||||||
if (isDev() && $server->id === 0) {
|
if (isDev() && $server->id === 0) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
|
||||||
'sleep 1',
|
|
||||||
"echo 'Installing Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
'sleep 4',
|
'sleep 4',
|
||||||
|
|
@ -70,35 +68,6 @@ public function handle(Server $server)
|
||||||
|
|
||||||
return remote_process($command, $server);
|
return remote_process($command, $server);
|
||||||
} else {
|
} else {
|
||||||
if ($supported_os_type->contains('debian')) {
|
|
||||||
$command = $command->merge([
|
|
||||||
"echo 'Installing Prerequisites...'",
|
|
||||||
'apt-get update -y',
|
|
||||||
'command -v curl >/dev/null || apt install -y curl',
|
|
||||||
'command -v wget >/dev/null || apt install -y wget',
|
|
||||||
'command -v git >/dev/null || apt install -y git',
|
|
||||||
'command -v jq >/dev/null || apt install -y jq',
|
|
||||||
]);
|
|
||||||
} elseif ($supported_os_type->contains('rhel')) {
|
|
||||||
$command = $command->merge([
|
|
||||||
"echo 'Installing Prerequisites...'",
|
|
||||||
'command -v curl >/dev/null || dnf install -y curl',
|
|
||||||
'command -v wget >/dev/null || dnf install -y wget',
|
|
||||||
'command -v git >/dev/null || dnf install -y git',
|
|
||||||
'command -v jq >/dev/null || dnf install -y jq',
|
|
||||||
]);
|
|
||||||
} elseif ($supported_os_type->contains('sles')) {
|
|
||||||
$command = $command->merge([
|
|
||||||
"echo 'Installing Prerequisites...'",
|
|
||||||
'zypper update -y',
|
|
||||||
'command -v curl >/dev/null || zypper install -y curl',
|
|
||||||
'command -v wget >/dev/null || zypper install -y wget',
|
|
||||||
'command -v git >/dev/null || zypper install -y git',
|
|
||||||
'command -v jq >/dev/null || zypper install -y jq',
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
throw new \Exception('Unsupported OS');
|
|
||||||
}
|
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
57
app/Actions/Server/InstallPrerequisites.php
Normal file
57
app/Actions/Server/InstallPrerequisites.php
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class InstallPrerequisites
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public string $jobQueue = 'high';
|
||||||
|
|
||||||
|
public function handle(Server $server)
|
||||||
|
{
|
||||||
|
$supported_os_type = $server->validateOS();
|
||||||
|
if (! $supported_os_type) {
|
||||||
|
throw new \Exception('Server OS type is not supported for automated installation. Please install prerequisites manually.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$command = collect([]);
|
||||||
|
|
||||||
|
if ($supported_os_type->contains('debian')) {
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Installing Prerequisites...'",
|
||||||
|
'apt-get update -y',
|
||||||
|
'command -v curl >/dev/null || apt install -y curl',
|
||||||
|
'command -v wget >/dev/null || apt install -y wget',
|
||||||
|
'command -v git >/dev/null || apt install -y git',
|
||||||
|
'command -v jq >/dev/null || apt install -y jq',
|
||||||
|
]);
|
||||||
|
} elseif ($supported_os_type->contains('rhel')) {
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Installing Prerequisites...'",
|
||||||
|
'command -v curl >/dev/null || dnf install -y curl',
|
||||||
|
'command -v wget >/dev/null || dnf install -y wget',
|
||||||
|
'command -v git >/dev/null || dnf install -y git',
|
||||||
|
'command -v jq >/dev/null || dnf install -y jq',
|
||||||
|
]);
|
||||||
|
} elseif ($supported_os_type->contains('sles')) {
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Installing Prerequisites...'",
|
||||||
|
'zypper update -y',
|
||||||
|
'command -v curl >/dev/null || zypper install -y curl',
|
||||||
|
'command -v wget >/dev/null || zypper install -y wget',
|
||||||
|
'command -v git >/dev/null || zypper install -y git',
|
||||||
|
'command -v jq >/dev/null || zypper install -y jq',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
throw new \Exception('Unsupported OS type for prerequisites installation');
|
||||||
|
}
|
||||||
|
|
||||||
|
$command->push("echo 'Prerequisites installed successfully.'");
|
||||||
|
|
||||||
|
return remote_process($command, $server);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/Actions/Server/ValidatePrerequisites.php
Normal file
27
app/Actions/Server/ValidatePrerequisites.php
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class ValidatePrerequisites
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public string $jobQueue = 'high';
|
||||||
|
|
||||||
|
public function handle(Server $server): bool
|
||||||
|
{
|
||||||
|
$requiredCommands = ['git', 'curl', 'jq'];
|
||||||
|
|
||||||
|
foreach ($requiredCommands as $cmd) {
|
||||||
|
$found = instant_remote_process(["command -v {$cmd}"], $server, false);
|
||||||
|
if (! $found) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,15 @@ public function handle(Server $server)
|
||||||
throw new \Exception($this->error);
|
throw new \Exception($this->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$prerequisitesInstalled = $server->validatePrerequisites();
|
||||||
|
if (! $prerequisitesInstalled) {
|
||||||
|
$this->error = 'Prerequisites (git, curl, jq) are not installed. Please install them before continuing or use the validation with installation endpoint.';
|
||||||
|
$server->update([
|
||||||
|
'validation_logs' => $this->error,
|
||||||
|
]);
|
||||||
|
throw new \Exception($this->error);
|
||||||
|
}
|
||||||
|
|
||||||
$this->docker_installed = $server->validateDockerEngine();
|
$this->docker_installed = $server->validateDockerEngine();
|
||||||
$this->docker_compose_installed = $server->validateDockerCompose();
|
$this->docker_compose_installed = $server->validateDockerCompose();
|
||||||
if (! $this->docker_installed || ! $this->docker_compose_installed) {
|
if (! $this->docker_installed || ! $this->docker_compose_installed) {
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,37 @@ public function handle(): void
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check and install prerequisites
|
||||||
|
$prerequisitesInstalled = $this->server->validatePrerequisites();
|
||||||
|
if (! $prerequisitesInstalled) {
|
||||||
|
if ($this->numberOfTries >= $this->maxTries) {
|
||||||
|
$errorMessage = 'Prerequisites (git, curl, jq) could not be installed after '.$this->maxTries.' attempts. Please install them manually before continuing.';
|
||||||
|
$this->server->update([
|
||||||
|
'validation_logs' => $errorMessage,
|
||||||
|
'is_validating' => false,
|
||||||
|
]);
|
||||||
|
Log::error('ValidateAndInstallServer: Prerequisites installation failed after max tries', [
|
||||||
|
'server_id' => $this->server->id,
|
||||||
|
'attempts' => $this->numberOfTries,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info('ValidateAndInstallServer: Installing prerequisites', [
|
||||||
|
'server_id' => $this->server->id,
|
||||||
|
'attempt' => $this->numberOfTries + 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Install prerequisites
|
||||||
|
$this->server->installPrerequisites();
|
||||||
|
|
||||||
|
// Retry validation after installation
|
||||||
|
self::dispatch($this->server, $this->numberOfTries + 1)->delay(now()->addSeconds(30));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if Docker is installed
|
// Check if Docker is installed
|
||||||
$dockerInstalled = $this->server->validateDockerEngine();
|
$dockerInstalled = $this->server->validateDockerEngine();
|
||||||
$dockerComposeInstalled = $this->server->validateDockerCompose();
|
$dockerComposeInstalled = $this->server->validateDockerCompose();
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,21 @@ public function validateServer()
|
||||||
return handleError(error: $e, livewire: $this);
|
return handleError(error: $e, livewire: $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check prerequisites
|
||||||
|
$prerequisitesInstalled = $this->createdServer->validatePrerequisites();
|
||||||
|
if (! $prerequisitesInstalled) {
|
||||||
|
$this->createdServer->installPrerequisites();
|
||||||
|
// Recheck after installation
|
||||||
|
$prerequisitesInstalled = $this->createdServer->validatePrerequisites();
|
||||||
|
if (! $prerequisitesInstalled) {
|
||||||
|
throw new \Exception('Prerequisites (git, curl, jq) could not be installed. Please install them manually.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError(error: $e, livewire: $this);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
|
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
|
||||||
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ class ValidateAndInstall extends Component
|
||||||
|
|
||||||
public $supported_os_type = null;
|
public $supported_os_type = null;
|
||||||
|
|
||||||
|
public $prerequisites_installed = null;
|
||||||
|
|
||||||
public $docker_installed = null;
|
public $docker_installed = null;
|
||||||
|
|
||||||
public $docker_compose_installed = null;
|
public $docker_compose_installed = null;
|
||||||
|
|
@ -33,12 +35,15 @@ class ValidateAndInstall extends Component
|
||||||
|
|
||||||
public $error = null;
|
public $error = null;
|
||||||
|
|
||||||
|
public string $installationStep = 'Prerequisites';
|
||||||
|
|
||||||
public bool $ask = false;
|
public bool $ask = false;
|
||||||
|
|
||||||
protected $listeners = [
|
protected $listeners = [
|
||||||
'init',
|
'init',
|
||||||
'validateConnection',
|
'validateConnection',
|
||||||
'validateOS',
|
'validateOS',
|
||||||
|
'validatePrerequisites',
|
||||||
'validateDockerEngine',
|
'validateDockerEngine',
|
||||||
'validateDockerVersion',
|
'validateDockerVersion',
|
||||||
'refresh' => '$refresh',
|
'refresh' => '$refresh',
|
||||||
|
|
@ -48,6 +53,7 @@ public function init(int $data = 0)
|
||||||
{
|
{
|
||||||
$this->uptime = null;
|
$this->uptime = null;
|
||||||
$this->supported_os_type = null;
|
$this->supported_os_type = null;
|
||||||
|
$this->prerequisites_installed = null;
|
||||||
$this->docker_installed = null;
|
$this->docker_installed = null;
|
||||||
$this->docker_version = null;
|
$this->docker_version = null;
|
||||||
$this->docker_compose_installed = null;
|
$this->docker_compose_installed = null;
|
||||||
|
|
@ -69,6 +75,7 @@ public function retry()
|
||||||
$this->authorize('update', $this->server);
|
$this->authorize('update', $this->server);
|
||||||
$this->uptime = null;
|
$this->uptime = null;
|
||||||
$this->supported_os_type = null;
|
$this->supported_os_type = null;
|
||||||
|
$this->prerequisites_installed = null;
|
||||||
$this->docker_installed = null;
|
$this->docker_installed = null;
|
||||||
$this->docker_compose_installed = null;
|
$this->docker_compose_installed = null;
|
||||||
$this->docker_version = null;
|
$this->docker_version = null;
|
||||||
|
|
@ -103,6 +110,40 @@ public function validateOS()
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$this->dispatch('validatePrerequisites');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validatePrerequisites()
|
||||||
|
{
|
||||||
|
$this->prerequisites_installed = $this->server->validatePrerequisites();
|
||||||
|
if (! $this->prerequisites_installed) {
|
||||||
|
if ($this->install) {
|
||||||
|
if ($this->number_of_tries == $this->max_tries) {
|
||||||
|
$this->error = 'Prerequisites (git, curl, jq) could not be installed. Please install them manually before continuing.';
|
||||||
|
$this->server->update([
|
||||||
|
'validation_logs' => $this->error,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if ($this->number_of_tries <= $this->max_tries) {
|
||||||
|
$this->installationStep = 'Prerequisites';
|
||||||
|
$activity = $this->server->installPrerequisites();
|
||||||
|
$this->number_of_tries++;
|
||||||
|
$this->dispatch('activityMonitor', $activity->id, 'init', $this->number_of_tries);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->error = 'Prerequisites (git, curl, jq) are not installed. Please install them before continuing.';
|
||||||
|
$this->server->update([
|
||||||
|
'validation_logs' => $this->error,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
$this->dispatch('validateDockerEngine');
|
$this->dispatch('validateDockerEngine');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,6 +162,7 @@ public function validateDockerEngine()
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if ($this->number_of_tries <= $this->max_tries) {
|
if ($this->number_of_tries <= $this->max_tries) {
|
||||||
|
$this->installationStep = 'Docker';
|
||||||
$activity = $this->server->installDocker();
|
$activity = $this->server->installDocker();
|
||||||
$this->number_of_tries++;
|
$this->number_of_tries++;
|
||||||
$this->dispatch('activityMonitor', $activity->id, 'init', $this->number_of_tries);
|
$this->dispatch('activityMonitor', $activity->id, 'init', $this->number_of_tries);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Actions\Server\InstallDocker;
|
use App\Actions\Server\InstallDocker;
|
||||||
|
use App\Actions\Server\InstallPrerequisites;
|
||||||
use App\Actions\Server\StartSentinel;
|
use App\Actions\Server\StartSentinel;
|
||||||
|
use App\Actions\Server\ValidatePrerequisites;
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
use App\Events\ServerReachabilityChanged;
|
use App\Events\ServerReachabilityChanged;
|
||||||
use App\Helpers\SslHelper;
|
use App\Helpers\SslHelper;
|
||||||
|
|
@ -1184,6 +1186,16 @@ public function installDocker()
|
||||||
return InstallDocker::run($this);
|
return InstallDocker::run($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function validatePrerequisites(): bool
|
||||||
|
{
|
||||||
|
return ValidatePrerequisites::run($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function installPrerequisites()
|
||||||
|
{
|
||||||
|
return InstallPrerequisites::run($this);
|
||||||
|
}
|
||||||
|
|
||||||
public function validateDockerEngine($throwError = false)
|
public function validateDockerEngine($throwError = false)
|
||||||
{
|
{
|
||||||
$dockerBinary = instant_remote_process(['command -v docker'], $this, false, no_sudo: true);
|
$dockerBinary = instant_remote_process(['command -v docker'], $this, false, no_sudo: true);
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,30 @@
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
@if ($uptime && $supported_os_type)
|
@if ($uptime && $supported_os_type)
|
||||||
|
@if ($prerequisites_installed)
|
||||||
|
<div class="flex w-64 gap-2">Prerequisites are installed: <svg class="w-5 h-5 text-success"
|
||||||
|
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="m237.66 85.26l-128.4 128.4a8 8 0 0 1-11.32 0l-71.6-72a8 8 0 0 1 0-11.31l24-24a8 8 0 0 1 11.32 0l36.68 35.32a8 8 0 0 0 11.32 0l92.68-91.32a8 8 0 0 1 11.32 0l24 23.6a8 8 0 0 1 0 11.31"
|
||||||
|
opacity=".2" />
|
||||||
|
<path
|
||||||
|
d="m243.28 68.24l-24-23.56a16 16 0 0 0-22.58 0L104 136l-.11-.11l-36.64-35.27a16 16 0 0 0-22.57.06l-24 24a16 16 0 0 0 0 22.61l71.62 72a16 16 0 0 0 22.63 0l128.4-128.38a16 16 0 0 0-.05-22.67M103.62 208L32 136l24-24l.11.11l36.64 35.27a16 16 0 0 0 22.52 0L208.06 56L232 79.6Z" />
|
||||||
|
</g>
|
||||||
|
</svg></div>
|
||||||
|
@else
|
||||||
|
@if ($error)
|
||||||
|
<div class="flex w-64 gap-2">Prerequisites are installed: <svg class="w-5 h-5 text-error"
|
||||||
|
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="M208.49 191.51a12 12 0 0 1-17 17L128 145l-63.51 63.49a12 12 0 0 1-17-17L111 128L47.51 64.49a12 12 0 0 1 17-17L128 111l63.51-63.52a12 12 0 0 1 17 17L145 128Z" />
|
||||||
|
</svg></div>
|
||||||
|
@else
|
||||||
|
<div class="w-64"><x-loading text="Prerequisites are installed:" /></div>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
@if ($uptime && $supported_os_type && $prerequisites_installed)
|
||||||
@if ($docker_installed)
|
@if ($docker_installed)
|
||||||
<div class="flex w-64 gap-2">Docker is installed: <svg class="w-5 h-5 text-success"
|
<div class="flex w-64 gap-2">Docker is installed: <svg class="w-5 h-5 text-success"
|
||||||
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
@ -120,7 +144,7 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@endif
|
@endif
|
||||||
<livewire:activity-monitor header="Docker Installation Logs" :showWaiting="false" />
|
<livewire:activity-monitor header="{{ $installationStep }} Installation Logs" :showWaiting="false" />
|
||||||
@isset($error)
|
@isset($error)
|
||||||
<pre class="font-bold whitespace-pre-line text-error">{!! $error !!}</pre>
|
<pre class="font-bold whitespace-pre-line text-error">{!! $error !!}</pre>
|
||||||
<x-forms.button canGate="update" :canResource="$server" wire:click="retry" class="mt-4">
|
<x-forms.button canGate="update" :canResource="$server" wire:click="retry" class="mt-4">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue