From c505a6ce9cfd5e729cf64e11dfcdac9dc5dc497e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 28 Nov 2023 15:49:24 +0100 Subject: [PATCH] wip --- app/Actions/Proxy/StartProxy.php | 7 +- app/Http/Livewire/Boarding/Index.php | 5 +- app/Http/Livewire/Dashboard.php | 2 + app/Http/Livewire/Project/New/Select.php | 1 + app/Http/Livewire/Server/Form.php | 23 ++++-- app/Http/Livewire/Server/New/ByIp.php | 6 +- app/Models/Server.php | 82 ++++++++++++++----- app/Models/SwarmDocker.php | 44 ++++++++++ ..._28_143533_add_fields_to_swarm_dockers.php | 30 +++++++ .../views/livewire/boarding/index.blade.php | 34 ++++---- .../views/livewire/server/form.blade.php | 13 +-- .../views/livewire/server/new/by-ip.blade.php | 6 +- 12 files changed, 198 insertions(+), 55 deletions(-) create mode 100644 database/migrations/2023_11_28_143533_add_fields_to_swarm_dockers.php diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index 6a6738177..b90508c5a 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -13,6 +13,9 @@ class StartProxy public function handle(Server $server, bool $async = true): string|Activity { try { + if ($server->isSwarm()) { + throw new \Exception("Server is part of swarm, not implemented yet."); + } $proxyType = $server->proxyType(); $commands = collect([]); $proxy_path = get_proxy_path(); @@ -46,11 +49,9 @@ class StartProxy $server->save(); return 'OK'; } - } catch(\Throwable $e) { + } catch (\Throwable $e) { ray($e); throw $e; } - - } } diff --git a/app/Http/Livewire/Boarding/Index.php b/app/Http/Livewire/Boarding/Index.php index 7f53708ad..645b0da6a 100644 --- a/app/Http/Livewire/Boarding/Index.php +++ b/app/Http/Livewire/Boarding/Index.php @@ -31,6 +31,7 @@ class Index extends Component public ?string $remoteServerHost = null; public ?int $remoteServerPort = 22; public ?string $remoteServerUser = 'root'; + public bool $isPartOfSwarm = false; public ?Server $createdServer = null; public Collection $projects; @@ -182,7 +183,9 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== 'private_key_id' => $this->createdPrivateKey->id, 'team_id' => currentTeam()->id, ]); - $this->createdServer->save(); + $this->createdServer->settings->is_part_of_swarm = $this->isPartOfSwarm; + $this->createdServer->settings->save(); + $this->createdServer->addInitialNetwork(); $this->validateServer(); } public function validateServer() diff --git a/app/Http/Livewire/Dashboard.php b/app/Http/Livewire/Dashboard.php index b7219864d..2052ded86 100644 --- a/app/Http/Livewire/Dashboard.php +++ b/app/Http/Livewire/Dashboard.php @@ -14,6 +14,8 @@ class Dashboard extends Component public function mount() { $this->servers = Server::ownedByCurrentTeam()->get(); + ray($this->servers[1]); + ray($this->servers[1]->standaloneDockers); $this->projects = Project::ownedByCurrentTeam()->get(); } // public function getIptables() diff --git a/app/Http/Livewire/Project/New/Select.php b/app/Http/Livewire/Project/New/Select.php index 13fbb8886..a23209768 100644 --- a/app/Http/Livewire/Project/New/Select.php +++ b/app/Http/Livewire/Project/New/Select.php @@ -126,6 +126,7 @@ class Select extends Component { $this->server_id = $server->id; $this->standaloneDockers = $server->standaloneDockers; + ray($server); $this->swarmDockers = $server->swarmDockers; $this->current_step = 'destinations'; } diff --git a/app/Http/Livewire/Server/Form.php b/app/Http/Livewire/Server/Form.php index dacb8faad..27dfaa8e2 100644 --- a/app/Http/Livewire/Server/Form.php +++ b/app/Http/Livewire/Server/Form.php @@ -17,14 +17,14 @@ class Form extends Component protected $listeners = ['serverRefresh']; protected $rules = [ - 'server.name' => 'required|min:6', + 'server.name' => 'required', 'server.description' => 'nullable', 'server.ip' => 'required', 'server.user' => 'required', 'server.port' => 'required', - 'server.settings.is_cloudflare_tunnel' => 'required', + 'server.settings.is_cloudflare_tunnel' => 'required|boolean', 'server.settings.is_reachable' => 'required', - 'server.settings.is_part_of_swarm' => 'required', + 'server.settings.is_part_of_swarm' => 'required|boolean', 'wildcard_domain' => 'nullable|url', ]; protected $validationAttributes = [ @@ -49,9 +49,14 @@ class Form extends Component } public function instantSave() { - refresh_server_connection($this->server->privateKey); - $this->validateServer(); - $this->server->settings->save(); + try { + refresh_server_connection($this->server->privateKey); + $this->validateServer(false); + $this->server->settings->save(); + $this->emit('success', 'Server updated successfully.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function installDocker() { @@ -100,6 +105,12 @@ class Form extends Component $install && $this->installDocker(); return; } + if ($this->server->isSwarm()) { + $swarmInstalled = $this->server->validateDockerSwarm(); + if ($swarmInstalled) { + $install && $this->emit('success', 'Docker Swarm is initiated.'); + } + } } catch (\Throwable $e) { return handleError($e, $this); } finally { diff --git a/app/Http/Livewire/Server/New/ByIp.php b/app/Http/Livewire/Server/New/ByIp.php index 66750db28..139705d94 100644 --- a/app/Http/Livewire/Server/New/ByIp.php +++ b/app/Http/Livewire/Server/New/ByIp.php @@ -29,6 +29,7 @@ class ByIp extends Component 'ip' => 'required', 'user' => 'required|string', 'port' => 'required|integer', + 'is_part_of_swarm' => 'required|boolean', ]; protected $validationAttributes = [ 'name' => 'Name', @@ -36,6 +37,7 @@ class ByIp extends Component 'ip' => 'IP Address/Domain', 'user' => 'User', 'port' => 'Port', + 'is_part_of_swarm' => 'Is part of swarm', ]; public function mount() @@ -72,11 +74,11 @@ class ByIp extends Component 'proxy' => [ "type" => ProxyTypes::TRAEFIK_V2->value, "status" => ProxyStatus::EXITED->value, - ] - + ], ]); $server->settings->is_part_of_swarm = $this->is_part_of_swarm; $server->settings->save(); + $server->addInitialNetwork(); return redirect()->route('server.show', $server->uuid); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Models/Server.php b/app/Models/Server.php index ca5bd9559..9a0c4ce7d 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -35,25 +35,10 @@ class Server extends BaseModel } $server->forceFill($payload); }); - static::created(function ($server) { ServerSetting::create([ 'server_id' => $server->id, ]); - if ($server->id === 0) { - StandaloneDocker::create([ - 'id' => 0, - 'name' => 'coolify', - 'network' => 'coolify', - 'server_id' => $server->id, - ]); - } else { - StandaloneDocker::create([ - 'name' => 'coolify', - 'network' => 'coolify', - 'server_id' => $server->id, - ]); - } }); static::deleting(function ($server) { $server->destinations()->each(function ($destination) { @@ -101,7 +86,39 @@ class Server extends BaseModel { return $this->hasOne(ServerSetting::class); } - + public function addInitialNetwork() { + if ($this->id === 0) { + if ($this->isSwarm()) { + SwarmDocker::create([ + 'id' => 0, + 'name' => 'coolify', + 'network' => 'coolify-overlay', + 'server_id' => $this->id, + ]); + } else { + StandaloneDocker::create([ + 'id' => 0, + 'name' => 'coolify', + 'network' => 'coolify', + 'server_id' => $this->id, + ]); + } + } else { + if ($this->isSwarm()) { + SwarmDocker::create([ + 'name' => 'coolify', + 'network' => 'coolify-overlay', + 'server_id' => $this->id, + ]); + } else { + StandaloneDocker::create([ + 'name' => 'coolify', + 'network' => 'coolify', + 'server_id' => $this->id, + ]); + } + } + } public function proxyType() { $proxyType = $this->proxy->get('type'); @@ -357,12 +374,16 @@ class Server extends BaseModel return false; } } + public function isSwarm() + { + return data_get($this, 'settings.is_part_of_swarm'); + } public function validateConnection() { + $server = Server::find($this->id); if ($this->skipServer()) { return false; } - $uptime = instant_remote_process(['uptime'], $this, false); if (!$uptime) { $this->settings()->update([ @@ -373,14 +394,14 @@ class Server extends BaseModel $this->settings()->update([ 'is_reachable' => true, ]); - $this->update([ + $server->update([ 'unreachable_count' => 0, ]); } if (data_get($this, 'unreachable_notification_sent') === true) { $this->team->notify(new Revived($this)); - $this->update(['unreachable_notification_sent' => false]); + $server->update(['unreachable_notification_sent' => false]); } return true; @@ -398,7 +419,20 @@ class Server extends BaseModel } $this->settings->is_usable = true; $this->settings->save(); - $this->validateCoolifyNetwork(); + $this->validateCoolifyNetwork(isSwarm: false); + return true; + } + public function validateDockerSwarm() + { + $swarmStatus = instant_remote_process(["docker info|grep -i swarm"], $this, false); + $swarmStatus = str($swarmStatus)->trim()->after(':')->trim(); + if ($swarmStatus === 'inactive') { + throw new \Exception('Docker Swarm is not initiated. Please join the server to a swarm before continuing.'); + return false; + } + $this->settings->is_usable = true; + $this->settings->save(); + $this->validateCoolifyNetwork(isSwarm: true); return true; } public function validateDockerEngineVersion() @@ -415,9 +449,13 @@ class Server extends BaseModel $this->settings->save(); return true; } - public function validateCoolifyNetwork() + public function validateCoolifyNetwork($isSwarm = false) { - return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false); + if ($isSwarm) { + return instant_remote_process(["docker network create --driver overlay coolify-overlay >/dev/null 2>&1 || true"], $this, false); + } else { + return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false); + } } public function executeRemoteCommand(Collection $commands, ?ApplicationDeploymentQueue $loggingModel = null) { diff --git a/app/Models/SwarmDocker.php b/app/Models/SwarmDocker.php index ea56f85bc..9f0973db5 100644 --- a/app/Models/SwarmDocker.php +++ b/app/Models/SwarmDocker.php @@ -4,13 +4,57 @@ namespace App\Models; class SwarmDocker extends BaseModel { + protected $guarded = []; + public function applications() { return $this->morphMany(Application::class, 'destination'); } + public function postgresqls() + { + return $this->morphMany(StandalonePostgresql::class, 'destination'); + } + + public function redis() + { + return $this->morphMany(StandaloneRedis::class, 'destination'); + } + public function mongodbs() + { + return $this->morphMany(StandaloneMongodb::class, 'destination'); + } + public function mysqls() + { + return $this->morphMany(StandaloneMysql::class, 'destination'); + } + public function mariadbs() + { + return $this->morphMany(StandaloneMariadb::class, 'destination'); + } + public function server() { return $this->belongsTo(Server::class); } + + public function services() + { + return $this->morphMany(Service::class, 'destination'); + } + + public function databases() + { + $postgresqls = $this->postgresqls; + $redis = $this->redis; + $mongodbs = $this->mongodbs; + $mysqls = $this->mysqls; + $mariadbs = $this->mariadbs; + return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs); + } + + public function attachedTo() + { + return $this->applications?->count() > 0 || $this->databases()->count() > 0; + } } diff --git a/database/migrations/2023_11_28_143533_add_fields_to_swarm_dockers.php b/database/migrations/2023_11_28_143533_add_fields_to_swarm_dockers.php new file mode 100644 index 000000000..7ca398b90 --- /dev/null +++ b/database/migrations/2023_11_28_143533_add_fields_to_swarm_dockers.php @@ -0,0 +1,30 @@ +string('network'); + + $table->unique(['server_id', 'network']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('swarm_dockers', function (Blueprint $table) { + $table->dropColumn('network'); + }); + } +}; diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index 75f2441f0..389633b2d 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -131,16 +131,16 @@ @if (!$serverReachable) - This server is not reachable with the following public key. -

- Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user - 'root' or skip the boarding process and add a new private key manually to Coolify and to the - server. - - Check again - - @endif + This server is not reachable with the following public key. +

+ Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user + 'root' or skip the boarding process and add a new private key manually to Coolify and to the + server. + + Check + again + + @endif

Private Keys are used to connect to a remote server through a secure shell, called SSH.

@@ -200,14 +200,17 @@ label="Description" id="remoteServerDescription" />
- +
+
+ +
Check Connection @@ -226,7 +229,7 @@ - Let's do it! + Let's do it! @if ($dockerInstallationStarted) Validate Server & Continue @@ -234,7 +237,10 @@

This will install the latest Docker Engine on your server, configure a few things to be able - to run optimal.

Minimum Docker Engine version is: 22

To manually install Docker Engine, check this documentation.

+ to run optimal.

Minimum Docker Engine version is: 22

To manually install Docker + Engine, check this + documentation.

diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index 4004dd096..0a98128cf 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -38,8 +38,7 @@ - {{-- --}} +
- @if (!$server->isLocalhost()) -
+
+ @if (!$server->isLocalhost()) -
- @endif + @endif + +
@if ($server->isFunctional()) diff --git a/resources/views/livewire/server/new/by-ip.blade.php b/resources/views/livewire/server/new/by-ip.blade.php index 7dfb3a5b0..9503a36a4 100644 --- a/resources/views/livewire/server/new/by-ip.blade.php +++ b/resources/views/livewire/server/new/by-ip.blade.php @@ -3,7 +3,7 @@ @else

Create a new Server

-
Servers are the main blocks of your infrastructure.
+
Servers are the main blocks of your infrastructure.
@@ -25,6 +25,10 @@ @endif @endforeach +
+ +
Save New Server