This commit is contained in:
Andras Bacsai
2023-05-15 13:45:37 +02:00
parent 845a8e5076
commit ec3ae7f6de
13 changed files with 280 additions and 173 deletions

View File

@@ -2,34 +2,32 @@
namespace App\Actions\Proxy;
use App\Enums\ActivityTypes;
use App\Enums\ProxyTypes;
use App\Models\Server;
use Spatie\Activitylog\Models\Activity;
use Symfony\Component\Yaml\Yaml;
use Illuminate\Support\Str;
class CheckProxySettingsInSync
{
public function __invoke(Server $server)
{
// @TODO What is the mechanism to make sure setting in sync?
$folder_name = match ($server->extra_attributes->proxy) {
ProxyTypes::TRAEFIK_V2->value => 'proxy',
};
$container_name = 'coolify-proxy';
$proxy_path = config('coolify.proxy_config_path');
$output = instantRemoteProcess([
// Folder exists, in ~/projects/<folder-name>
'if [ -d "projects/' . $folder_name . '" ]; then echo "true"; else echo "false"; fi',
// Container of name <container-name> is running
<<<EOT
[[ "$(docker inspect -f '{{.State.Running}}' $container_name 2>/dev/null)" == "true" ]] && echo "true" || echo "false"
EOT,
], $server);
return collect(
explode(PHP_EOL, $output)
)->every(fn ($output) => $output === 'true');
"cat $proxy_path/docker-compose.yml",
], $server, false);
if (is_null($output)) {
$final_output = Str::of(getProxyConfiguration($server))->trim();
} else {
$final_output = Str::of($output)->trim();
}
$docker_compose_yml_base64 = base64_encode($final_output);
$server->extra_attributes->last_saved_proxy_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
$server->save();
if (is_null($output)) {
instantRemoteProcess([
"mkdir -p $proxy_path",
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
], $server);
}
return $final_output;
}
}

View File

@@ -5,103 +5,63 @@ namespace App\Actions\Proxy;
use App\Enums\ActivityTypes;
use App\Enums\ProxyTypes;
use App\Models\Server;
use Illuminate\Support\Collection;
use Spatie\Activitylog\Models\Activity;
use Symfony\Component\Yaml\Yaml;
use Illuminate\Support\Str;
class InstallProxy
{
public Collection $networks;
public function __invoke(Server $server): Activity
{
$docker_compose_yml_base64 = base64_encode(
$this->getDockerComposeContents()
);
$proxy_path = config('coolify.proxy_config_path');
$networks = collect($server->standaloneDockers)->map(function ($docker) {
return $docker['network'];
})->unique();
if ($networks->count() === 0) {
$this->networks = collect(['coolify']);
}
$create_networks_command = $this->networks->map(function ($network) {
return "docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null 2>&1 || docker network create --attachable $network > /dev/null 2>&1";
});
$configuration = instantRemoteProcess([
"cat $proxy_path/docker-compose.yml",
], $server, false);
if (is_null($configuration)) {
$configuration = Str::of(getProxyConfiguration($server))->trim();
} else {
$configuration = Str::of($configuration)->trim();
}
$docker_compose_yml_base64 = base64_encode($configuration);
$server->extra_attributes->last_applied_proxy_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
$server->save();
$env_file_base64 = base64_encode(
$this->getEnvContents()
);
$activity = remoteProcess([
"docker network ls --format '{{.Name}}' | grep '^coolify$' || docker network create coolify",
'mkdir -p projects',
'mkdir -p projects/proxy',
'mkdir -p projects/proxy/letsencrypt',
'cd projects/proxy',
"echo '$docker_compose_yml_base64' | base64 -d > docker-compose.yml",
"echo '$env_file_base64' | base64 -d > .env",
...$create_networks_command,
"echo 'Docker networks created...'",
"mkdir -p $proxy_path",
"cd $proxy_path",
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
"echo '$env_file_base64' | base64 -d > $proxy_path/.env",
"echo 'Docker compose file created...'",
"echo 'Pulling docker image...'",
'docker compose pull -q',
"echo 'Stopping proxy...'",
'docker compose down -v --remove-orphans',
"echo 'Starting proxy...'",
'docker compose up -d --remove-orphans',
'docker ps',
"echo 'Proxy installed successfully...'"
], $server, ActivityTypes::INLINE->value);
// Persist to Database
$server->extra_attributes->proxy = ProxyTypes::TRAEFIK_V2->value;
$server->save();
return $activity;
}
protected function getDockerComposeContents()
{
return Yaml::dump($this->getComposeData());
}
/**
* @return array
*/
protected function getComposeData(): array
{
$cwd = config('app.env') === 'local'
? config('proxy.project_path_on_host') . '/_testing_hosts/host_2_proxy'
: '.';
return [
"version" => "3.7",
"networks" => [
"coolify" => [
"external" => true,
],
],
"services" => [
"traefik" => [
"container_name" => "coolify-proxy",
"image" => "traefik:v2.10",
"restart" => "always",
"extra_hosts" => [
"host.docker.internal:host-gateway",
],
"networks" => [
"coolify",
],
"ports" => [
"80:80",
"443:443",
"8080:8080",
],
"volumes" => [
"/var/run/docker.sock:/var/run/docker.sock:ro",
"{$cwd}/letsencrypt:/letsencrypt",
"{$cwd}/traefik.auth:/auth/traefik.auth",
],
"command" => [
"--api.dashboard=true",
"--api.insecure=true",
"--entrypoints.http.address=:80",
"--entrypoints.https.address=:443",
"--providers.docker=true",
"--providers.docker.exposedbydefault=false",
],
"labels" => [
"traefik.enable=true",
"traefik.http.routers.traefik.entrypoints=http",
'traefik.http.routers.traefik.rule=Host(`${TRAEFIK_DASHBOARD_HOST}`)',
"traefik.http.routers.traefik.service=api@internal",
"traefik.http.services.traefik.loadbalancer.server.port=8080",
"traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https",
],
],
],
];
}
protected function getEnvContents()
{
$data = [

View File

@@ -4,6 +4,7 @@ namespace App\Console;
use App\Jobs\ContainerStatusJob;
use App\Jobs\DockerCleanupDanglingImagesJob;
use App\Jobs\ProxyCheckJob;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -16,6 +17,7 @@ class Kernel extends ConsoleKernel
{
$schedule->job(new ContainerStatusJob)->everyMinute();
$schedule->job(new DockerCleanupDanglingImagesJob)->everyMinute();
// $schedule->job(new ProxyCheckJob)->everyMinute();
}
/**

View File

@@ -4,7 +4,8 @@ namespace App\Http\Livewire\Server;
use App\Actions\Proxy\CheckProxySettingsInSync;
use App\Actions\Proxy\InstallProxy;
use App\Enums\ActivityTypes;
use App\Enums\ProxyTypes;
use Illuminate\Support\Str;
use App\Models\Server;
use Livewire\Component;
@@ -12,34 +13,60 @@ class Proxy extends Component
{
public Server $server;
protected string $selectedProxy = '';
public ProxyTypes $selectedProxy = ProxyTypes::TRAEFIK_V2;
public $is_proxy_installed;
public $is_check_proxy_complete = false;
public $is_proxy_settings_in_sync = false;
public $proxy_settings = null;
public function mount(Server $server)
{
$this->server = $server;
}
public function runInstallProxy()
public function installProxy()
{
$this->saveConfiguration($this->server);
$activity = resolve(InstallProxy::class)($this->server);
$this->emit('newMonitorActivity', $activity->id);
}
public function proxyStatus()
{
$this->server->extra_attributes->proxy_status = checkContainerStatus(server: $this->server, container_id: 'coolify-proxy');
$this->server->save();
}
public function setProxy()
{
$this->server->extra_attributes->proxy_type = $this->selectedProxy->value;
$this->server->extra_attributes->proxy_status = 'exited';
$this->server->save();
}
public function stopProxy()
{
instantRemoteProcess([
"docker rm -f coolify-proxy",
], $this->server);
$this->server->extra_attributes->proxy_status = 'exited';
$this->server->save();
}
public function saveConfiguration()
{
try {
$proxy_path = config('coolify.proxy_config_path');
$this->proxy_settings = Str::of($this->proxy_settings)->trim();
$docker_compose_yml_base64 = base64_encode($this->proxy_settings);
$this->server->extra_attributes->last_saved_proxy_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
$this->server->save();
instantRemoteProcess([
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
], $this->server);
} catch (\Exception $e) {
return generalErrorHandler($e);
}
}
public function checkProxySettingsInSync()
{
$this->is_proxy_settings_in_sync = resolve(CheckProxySettingsInSync::class)($this->server);
$this->is_check_proxy_complete = true;
}
public function render()
{
return view('livewire.server.proxy');
try {
$this->proxy_settings = resolve(CheckProxySettingsInSync::class)($this->server);
$this->is_check_proxy_complete = true;
} catch (\Exception $e) {
return generalErrorHandler($e);
}
}
}

View File

@@ -67,9 +67,7 @@ class ContainerStatusJob implements ShouldQueue
return;
}
if ($application->destination->server) {
$container = instantRemoteProcess(["docker inspect --format '{{json .State}}' {$this->container_id}"], $application->destination->server);
$container = formatDockerCmdOutputToJson($container);
$application->status = $container[0]['Status'];
$application->status = checkContainerStatus(server: $application->destination->server, container_id: $this->container_id);
$application->save();
}
}

View File

@@ -69,7 +69,6 @@ class DeployApplicationJob implements ShouldQueue
protected function stopRunningContainer()
{
$this->executeNow([
"echo -n 'Removing old instance... '",
$this->execute_in_builder("docker rm -f {$this->application->uuid} >/dev/null 2>&1"),
"echo 'Done.'",

45
app/Jobs/ProxyCheckJob.php Executable file
View File

@@ -0,0 +1,45 @@
<?php
namespace App\Jobs;
use App\Actions\Proxy\InstallProxy;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProxyCheckJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*/
public function __construct()
{
}
/**
* Execute the job.
*/
public function handle()
{
try {
$container_name = 'coolify-proxy';
$configuration_path = config('coolify.proxy_config_path');
$servers = Server::whereRelation('settings', 'is_validated', true)->get();
foreach ($servers as $server) {
$status = checkContainerStatus(server: $server, container_id: $container_name);
if ($status === 'running') {
continue;
}
resolve(InstallProxy::class)($server);
}
} catch (\Throwable $th) {
//throw $th;
}
}
}