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.