This commit is contained in:
Andras Bacsai 2023-11-28 15:49:24 +01:00
parent 706e4b13ee
commit c505a6ce9c
12 changed files with 198 additions and 55 deletions

View File

@ -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 @@ public function handle(Server $server, bool $async = true): string|Activity
$server->save();
return 'OK';
}
} catch(\Throwable $e) {
} catch (\Throwable $e) {
ray($e);
throw $e;
}
}
}

View File

@ -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 @@ public function saveServer()
'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()

View File

@ -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()

View File

@ -126,6 +126,7 @@ public function setServer(Server $server)
{
$this->server_id = $server->id;
$this->standaloneDockers = $server->standaloneDockers;
ray($server);
$this->swarmDockers = $server->swarmDockers;
$this->current_step = 'destinations';
}

View File

@ -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 @@ public function serverRefresh($install = true)
}
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 @@ public function validateServer($install = true)
$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 {

View File

@ -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 @@ public function submit()
'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);

View File

@ -35,25 +35,10 @@ protected static function booted()
}
$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 @@ public function settings()
{
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 @@ public function validateOS(): bool | Stringable
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 @@ public function validateConnection()
$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 @@ public function validateDockerEngine($throwError = false)
}
$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 @@ public function validateDockerEngineVersion()
$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)
{

View File

@ -4,13 +4,57 @@
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;
}
}

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('swarm_dockers', function (Blueprint $table) {
$table->string('network');
$table->unique(['server_id', 'network']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('swarm_dockers', function (Blueprint $table) {
$table->dropColumn('network');
});
}
};

View File

@ -131,16 +131,16 @@
</form>
</div>
@if (!$serverReachable)
This server is not reachable with the following public key.
<br /> <br />
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.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="box" wire:target="validateServer"
wire:click="validateServer">Check again
</x-forms.button>
@endif
This server is not reachable with the following public key.
<br /> <br />
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.
<x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="box" wire:target="validateServer" wire:click="validateServer">Check
again
</x-forms.button>
@endif
</x-slot:actions>
<x-slot:explanation>
<p>Private Keys are used to connect to a remote server through a secure shell, called SSH.</p>
@ -200,14 +200,17 @@
label="Description" id="remoteServerDescription" />
</div>
<div class="flex gap-2">
<x-forms.input required placeholder="127.0.0.1" label="IP Address"
id="remoteServerHost" />
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost" />
<x-forms.input required placeholder="Port number of your server. Default is 22."
label="Port" id="remoteServerPort" />
<x-forms.input required readonly
placeholder="Username to connect to your server. Default is root." label="Username"
id="remoteServerUser" />
</div>
<div class="w-64">
<x-forms.checkbox type="checkbox" id="isPartOfSwarm"
label="Is it part of a Swarm cluster?" />
</div>
<x-forms.button type="submit">Check Connection</x-forms.button>
</form>
</x-slot:actions>
@ -226,7 +229,7 @@
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center box" wire:click="installDocker">
Let's do it!</x-forms.button>
Let's do it!</x-forms.button>
@if ($dockerInstallationStarted)
<x-forms.button class="justify-center box" wire:click="dockerInstalledOrSkipped">
Validate Server & Continue</x-forms.button>
@ -234,7 +237,10 @@
</x-slot:actions>
<x-slot:explanation>
<p>This will install the latest Docker Engine on your server, configure a few things to be able
to run optimal.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install Docker Engine, check <a target="_blank" class="underline text-warning" href="https://coolify.io/docs/servers#install-docker-engine-manually">this documentation</a>.</p>
to run optimal.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install Docker
Engine, check <a target="_blank" class="underline text-warning"
href="https://coolify.io/docs/servers#install-docker-engine-manually">this
documentation</a>.</p>
</x-slot:explanation>
</x-boarding-step>

View File

@ -38,8 +38,7 @@
<x-forms.input id="server.description" label="Description" />
<x-forms.input placeholder="https://example.com" id="wildcard_domain" label="Wildcard Domain"
helper="Wildcard domain for your applications. If you set this, you will get a random generated domain for your new applications.<br><span class='font-bold text-white'>Example:</span><br>In case you set:<span class='text-helper'>https://example.com</span> your applications will get:<br> <span class='text-helper'>https://randomId.example.com</span>" />
{{-- <x-forms.checkbox disabled type="checkbox" id="server.settings.is_part_of_swarm"
label="Is it part of a Swarm cluster?" /> --}}
</div>
<div class="flex flex-col w-full gap-2 lg:flex-row">
<x-forms.input id="server.ip" label="IP Address/Domain"
@ -49,13 +48,15 @@
<x-forms.input type="number" id="server.port" label="Port" required />
</div>
</div>
@if (!$server->isLocalhost())
<div class="w-64">
<div class="w-64">
@if (!$server->isLocalhost())
<x-forms.checkbox instantSave
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="server.settings.is_cloudflare_tunnel" label="Cloudflare Tunnel" />
</div>
@endif
@endif
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_part_of_swarm"
label="Is it part of a Swarm cluster?" />
</div>
</div>
@if ($server->isFunctional())

View File

@ -3,7 +3,7 @@
<x-limit-reached name="servers" />
@else
<h1>Create a new Server</h1>
<div class="subtitle ">Servers are the main blocks of your infrastructure.</div>
<div class="subtitle">Servers are the main blocks of your infrastructure.</div>
<form class="flex flex-col gap-2" wire:submit.prevent='submit'>
<div class="flex gap-2">
<x-forms.input id="name" label="Name" required />
@ -25,6 +25,10 @@
@endif
@endforeach
</x-forms.select>
<div class="w-64">
<x-forms.checkbox type="checkbox" id="is_part_of_swarm"
label="Is it part of a Swarm cluster?" />
</div>
<x-forms.button type="submit">
Save New Server
</x-forms.button>