Merge pull request #1423 from coollabsio/next

v4.0.0-beta.120
This commit is contained in:
Andras Bacsai 2023-11-10 12:06:55 +01:00 committed by GitHub
commit df021760a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 158 additions and 52 deletions

View File

@ -21,26 +21,33 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
$type = $database->getMorphClass();
$network = data_get($database, 'destination.network');
$server = data_get($database, 'destination.server');
$containerName = data_get($database, 'uuid');
$proxyContainerName = "{$database->uuid}-proxy";
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
$databaseType = $database->databaseType();
$network = data_get($database, 'service.destination.network');
$server = data_get($database, 'service.destination.server');
ray($databaseType, $network);
$proxyContainerName = "{$database->service->uuid}-proxy";
switch ($databaseType) {
case 'standalone-mariadb':
$type = 'App\Models\StandaloneMariadb';
$containerName = "mariadb-{$database->service->uuid}";
break;
case 'standalone-mongodb':
$type = 'App\Models\StandaloneMongodb';
$containerName = "mongodb-{$database->service->uuid}";
break;
case 'standalone-mysql':
$type = 'App\Models\StandaloneMysql';
$containerName = "mysql-{$database->service->uuid}";
break;
case 'standalone-postgresql':
$type = 'App\Models\StandalonePostgresql';
$containerName = "postgresql-{$database->service->uuid}";
break;
case 'standalone-redis':
$type = 'App\Models\StandaloneRedis';
$containerName = "redis-{$database->service->uuid}";
break;
}
}
@ -55,7 +62,6 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
} else if ($type === 'App\Models\StandaloneMariadb') {
$internalPort = 3306;
}
$containerName = "{$database->uuid}-proxy";
$configuration_dir = database_proxy_dir($database->uuid);
$nginxconf = <<<EOF
user nginx;
@ -69,7 +75,7 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
stream {
server {
listen $database->public_port;
proxy_pass $database->uuid:$internalPort;
proxy_pass $containerName:$internalPort;
}
}
EOF;
@ -81,13 +87,13 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
$docker_compose = [
'version' => '3.8',
'services' => [
$containerName => [
$proxyContainerName => [
'build' => [
'context' => $configuration_dir,
'dockerfile' => 'Dockerfile',
],
'image' => "nginx:stable-alpine",
'container_name' => $containerName,
'container_name' => $proxyContainerName,
'restart' => RESTART_MODE,
'ports' => [
"$database->public_port:$database->public_port",

View File

@ -56,7 +56,7 @@ public function handle(StandaloneMariadb $database)
'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => $this->database->limits_cpus,
'cpus' => (int) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares,
]

View File

@ -63,7 +63,7 @@ public function handle(StandaloneMongodb $database)
'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => $this->database->limits_cpus,
'cpus' => (int) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares,
]

View File

@ -56,7 +56,7 @@ public function handle(StandaloneMysql $database)
'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => $this->database->limits_cpus,
'cpus' => (int) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares,
]

View File

@ -66,7 +66,7 @@ public function handle(StandalonePostgresql $database)
'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => $this->database->limits_cpus,
'cpus' => (int) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares,
]

View File

@ -65,7 +65,7 @@ public function handle(StandaloneRedis $database)
'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => $this->database->limits_cpus,
'cpus' => (int) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares,
]

View File

@ -192,6 +192,8 @@ public function handle(): void
$this->deploy_dockerimage_buildpack();
} else if ($this->application->build_pack === 'dockerfile') {
$this->deploy_dockerfile_buildpack();
} else if ($this->application->build_pack === 'static') {
$this->deploy_static_buildpack();
} else {
if ($this->pull_request_id !== 0) {
$this->deploy_pull_request();
@ -227,6 +229,14 @@ public function handle(): void
[
"docker rm -f {$this->deployment_uuid} >/dev/null 2>&1",
"hidden" => true,
"ignore_errors" => true,
]
);
$this->execute_remote_command(
[
"docker image prune -f >/dev/null 2>&1",
"hidden" => true,
"ignore_errors" => true,
]
);
}
@ -421,6 +431,23 @@ private function deploy_nixpacks_buildpack()
$this->build_image();
$this->rolling_update();
}
private function deploy_static_buildpack()
{
$this->execute_remote_command(
[
"echo 'Starting deployment of {$this->customRepository}:{$this->application->git_branch}.'"
],
);
$this->prepare_builder_image();
$this->check_git_if_build_needed();
$this->set_base_dir();
$this->generate_image_names();
$this->clone_repository();
$this->cleanup_git();
$this->build_image();
$this->generate_compose_file();
$this->rolling_update();
}
private function rolling_update()
{
@ -787,7 +814,7 @@ private function generate_compose_file()
'memswap_limit' => $this->application->limits_memory_swap,
'mem_swappiness' => $this->application->limits_memory_swappiness,
'mem_reservation' => $this->application->limits_memory_reservation,
'cpus' => $this->application->limits_cpus,
'cpus' => (int) $this->application->limits_cpus,
'cpuset' => $this->application->limits_cpuset,
'cpu_shares' => $this->application->limits_cpu_shares,
]
@ -910,22 +937,26 @@ private function generate_healthcheck_commands()
private function build_image()
{
$this->execute_remote_command([
"echo -n 'Building docker image for your application. To check the current progress, click on Show Debug Logs.'",
]);
if ($this->application->settings->is_static) {
if ($this->application->build_pack === 'static') {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
"echo -n 'Static deployment. Copying static assets to the image.'",
]);
} else {
$this->execute_remote_command([
"echo -n 'Building docker image for your application. To check the current progress, click on Show Debug Logs.'",
]);
}
$dockerfile = base64_encode("FROM {$this->application->static_image}
if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
if ($this->application->build_pack === 'static') {
$dockerfile = base64_encode("FROM {$this->application->static_image}
WORKDIR /usr/share/nginx/html/
LABEL coolify.deploymentId={$this->deployment_uuid}
COPY --from=$this->build_image_name /app/{$this->application->publish_directory} .
COPY . .
RUN rm -f /usr/share/nginx/html/nginx.conf
RUN rm -f /usr/share/nginx/html/Dockerfile
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$nginx_config = base64_encode("server {
$nginx_config = base64_encode("server {
listen 80;
listen [::]:80;
server_name localhost;
@ -941,15 +972,43 @@ private function build_image()
root /usr/share/nginx/html;
}
}");
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]);
$dockerfile = base64_encode("FROM {$this->application->static_image}
WORKDIR /usr/share/nginx/html/
LABEL coolify.deploymentId={$this->deployment_uuid}
COPY --from=$this->build_image_name /app/{$this->application->publish_directory} .
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$nginx_config = base64_encode("server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files \$uri \$uri.html \$uri/index.html \$uri/ /index.html =404;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}");
}
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile-prod")
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile")
],
[
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
],
[
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/Dockerfile-prod {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
]
);
} else {

View File

@ -3,6 +3,7 @@
namespace App\Jobs;
use App\Models\Server;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -10,12 +11,13 @@
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 1000;
public $timeout = 300;
public ?string $dockerRootFilesystem = null;
public ?int $usageBefore = null;
@ -33,14 +35,15 @@ public function __construct(public Server $server)
}
public function handle(): void
{
$queuedCount = 0;
$this->server->applications()->each(function ($application) use ($queuedCount) {
$count = data_get($application->deployments(), 'count', 0);
$queuedCount += $count;
$isInprogress = false;
$this->server->applications()->each(function ($application) use (&$isInprogress) {
if ($application->isDeploymentInprogress()) {
$isInprogress = true;
return;
}
});
if ($queuedCount > 0) {
ray('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping')->color('orange');
return;
if ($isInprogress) {
throw new Exception('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...');
}
try {
if (!$this->server->isFunctional()) {
@ -49,23 +52,25 @@ public function handle(): void
$this->dockerRootFilesystem = "/";
$this->usageBefore = $this->getFilesystemUsage();
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) {
ray('Cleaning up ' . $this->server->name)->color('orange');
ray('Cleaning up ' . $this->server->name);
instant_remote_process(['docker image prune -af'], $this->server);
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $this->server);
instant_remote_process(['docker builder prune -af'], $this->server);
$usageAfter = $this->getFilesystemUsage();
if ($usageAfter < $this->usageBefore) {
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name)->color('orange');
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
Log::info('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
} else {
ray('DockerCleanupJob failed to save disk space on ' . $this->server->name)->color('orange');
Log::info('DockerCleanupJob failed to save disk space on ' . $this->server->name);
}
} else {
ray('No need to clean up ' . $this->server->name)->color('orange');
ray('No need to clean up ' . $this->server->name);
Log::info('No need to clean up ' . $this->server->name);
}
} catch (\Throwable $e) {
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
ray($e->getMessage())->color('orange');
ray($e->getMessage());
throw $e;
}
}

View File

@ -213,6 +213,14 @@ public function source()
return $this->morphTo();
}
public function isDeploymentInprogress() {
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'in_progress')->count();
if ($deployments > 0) {
return true;
}
return false;
}
public function deployments(int $skip = 0, int $take = 10)
{
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->orderBy('created_at', 'desc');

View File

@ -20,7 +20,7 @@ public function __construct(
public bool $readonly = false,
public string|null $helper = null,
public bool $allowToPeak = true,
public string $defaultClass = "input input-sm bg-coolgray-200 rounded text-white w-full disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
public string $defaultClass = "input input-sm bg-coolgray-100 rounded text-white w-full disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
) {
}

View File

@ -19,7 +19,7 @@ public function __construct(
public string|null $label = null,
public string|null $helper = null,
public bool $required = false,
public string $defaultClass = "select select-sm w-full rounded text-white text-sm bg-coolgray-200 font-normal disabled:bg-coolgray-200/50 disabled:border-none"
public string $defaultClass = "select select-sm w-full rounded text-white text-sm bg-coolgray-100 font-normal disabled:bg-coolgray-200/50 disabled:border-none"
) {
//
}

View File

@ -25,7 +25,7 @@ public function __construct(
public bool $readonly = false,
public string|null $helper = null,
public bool $realtimeValidation = false,
public string $defaultClass = "textarea leading-normal bg-coolgray-200 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
public string $defaultClass = "textarea leading-normal bg-coolgray-100 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
) {
//
}

View File

@ -7,7 +7,7 @@
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.119',
'release' => '4.0.0-beta.120',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.119';
return '4.0.0-beta.120';

View File

@ -53,7 +53,7 @@ .icon:hover {
@apply text-white;
}
.box {
@apply flex p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem];
@apply flex p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-100 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem];
}
.box-without-bg {
@apply flex p-2 transition-colors min-h-16 hover:text-white hover:no-underline min-w-[24rem];

View File

@ -1,7 +1,7 @@
<template>
<Transition name="fade">
<div>
<div class="flex items-center p-1 px-2 overflow-hidden transition-all transform rounded cursor-pointer bg-coolgray-200"
<div class="flex items-center p-1 px-2 overflow-hidden transition-all transform rounded cursor-pointer bg-coolgray-100"
@click="showCommandPalette = true">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 icon" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">

View File

@ -1,6 +1,7 @@
@auth
<nav class="fixed h-full overflow-hidden overflow-y-auto pt-14 scrollbar">
<a href="/" class="fixed top-0 z-50 mx-3 mt-3 cursor-pointer bg-coolgray-100"><img class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
<a href="/" class="fixed top-0 z-50 mx-3 mt-3 bg-transparent cursor-pointer"><img
class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
<li title="Dashboard">
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
@ -11,6 +12,18 @@
</svg>
</a>
</li>
<li title="Help us!">
<a class="hover:bg-transparent"href="https://coolify.io/sponsorships" target="_blank">
<svg class="icon hover:text-pink-500" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.5 12.572L12 20l-7.5-7.428A5 5 0 1 1 12 6.006a5 5 0 1 1 7.5 6.572" />
<path
d="M12 6L8.707 9.293a1 1 0 0 0 0 1.414l.543.543c.69.69 1.81.69 2.5 0l1-1a3.182 3.182 0 0 1 4.5 0l2.25 2.25m-7 3l2 2M15 13l2 2" />
</g>
</svg>
</a>
</li>
<li title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs">
<div class="justify-center" wire:click="help" onclick="help.showModal()">
<svg class="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">

View File

@ -1,6 +1,7 @@
@auth
<nav class="fixed h-full overflow-hidden overflow-y-auto pt-28 scrollbar">
<a href="/" class="fixed top-0 z-50 mx-3 mt-3 cursor-pointer bg-coolgray-100"><img class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
<a href="/" class="fixed top-0 z-50 mx-3 mt-3 bg-transparent cursor-pointer"><img
class="transition rounded w-11 h-11" src="{{ asset('coolify-transparent.png') }}"></a>
<ul class="flex flex-col h-full gap-4 menu flex-nowrap">
<li title="Dashboard">
<a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif>
@ -87,6 +88,18 @@ class="{{ request()->is('command-center') ? 'text-warning icon' : 'icon' }}" vie
@if (isInstanceAdmin() && !isCloud())
<livewire:upgrade />
@endif
<li title="Help us!">
<a class="hover:bg-transparent"href="https://coolify.io/sponsorships" target="_blank">
<svg class="icon hover:text-pink-500" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.5 12.572L12 20l-7.5-7.428A5 5 0 1 1 12 6.006a5 5 0 1 1 7.5 6.572" />
<path
d="M12 6L8.707 9.293a1 1 0 0 0 0 1.414l.543.543c.69.69 1.81.69 2.5 0l1-1a3.182 3.182 0 0 1 4.5 0l2.25 2.25m-7 3l2 2M15 13l2 2" />
</g>
</svg>
</a>
</li>
<li title="Profile">
<a class="hover:bg-transparent" @if (!request()->is('profile')) href="/profile" @endif>
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" stroke-width="1.5"

View File

@ -25,7 +25,6 @@
@endif
</head>
@section('body')
<body>
@livewireScripts
<dialog id="help" class="modal">

View File

@ -27,10 +27,11 @@
<div class="flex gap-2">
<x-forms.select wire:model="application.build_pack" label="Build Pack" required>
<option value="nixpacks">Nixpacks</option>
<option value="static">Static</option>
<option value="dockerfile">Dockerfile</option>
<option value="dockerimage">Docker Image</option>
</x-forms.select>
@if ($application->settings->is_static)
@if ($application->settings->is_static || $application->build_pack === 'static')
<x-forms.select id="application.static_image" label="Static Image" required>
<option value="nginx:alpine">nginx:alpine</option>
<option disabled value="apache:alpine">apache:alpine</option>
@ -97,7 +98,7 @@
@endif
<h3>Network</h3>
<div class="flex flex-col gap-2 xl:flex-row">
@if ($application->settings->is_static)
@if ($application->settings->is_static || $application->build_pack === 'static')
<x-forms.input id="application.ports_exposes" label="Ports Exposes" readonly />
@else
<x-forms.input placeholder="3000,3001" id="application.ports_exposes" label="Ports Exposes" required

View File

@ -2,7 +2,9 @@
@if (
$resource->getMorphClass() == 'App\Models\Application' ||
$resource->getMorphClass() == 'App\Models\StandalonePostgresql' ||
$resource->getMorphClass() == 'App\Models\StandaloneRedis')
$resource->getMorphClass() == 'App\Models\StandaloneRedis' ||
$resource->getMorphClass() == 'App\Models\StandaloneMariadb' ||
$resource->getMorphClass() == 'App\Models\StandaloneMongodb')
<div class="flex items-center gap-2">
<h2>Storages</h2>
<x-helper

View File

@ -39,12 +39,12 @@ module.exports = {
themes: [
{
coollabs: {
primary: "#323232",
primary: "#202020",
"primary-focus": "#242424",
secondary: "#6B16ED",
accent: "#4338ca",
neutral: "#1B1D1D",
"base-100": "#181818",
"base-100": "#101010",
info: "#2563EB",
success: "#16A34A",
warning: "#FCD34D",

View File

@ -4,7 +4,7 @@
"version": "3.12.36"
},
"v4": {
"version": "4.0.0-beta.119"
"version": "4.0.0-beta.120"
}
}
}