feat: able to customize docker labels on applications
This commit is contained in:
parent
6b302ab786
commit
0b3cde44c3
@ -3,12 +3,9 @@
|
|||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class General extends Component
|
class General extends Component
|
||||||
{
|
{
|
||||||
@ -23,6 +20,9 @@ class General extends Component
|
|||||||
public ?string $git_commit_sha = null;
|
public ?string $git_commit_sha = null;
|
||||||
public string $build_pack;
|
public string $build_pack;
|
||||||
|
|
||||||
|
public $customLabels;
|
||||||
|
public bool $labelsChanged = false;
|
||||||
|
|
||||||
public bool $is_static;
|
public bool $is_static;
|
||||||
public bool $is_git_submodules_enabled;
|
public bool $is_git_submodules_enabled;
|
||||||
public bool $is_git_lfs_enabled;
|
public bool $is_git_lfs_enabled;
|
||||||
@ -52,6 +52,7 @@ class General extends Component
|
|||||||
'application.docker_registry_image_name' => 'nullable',
|
'application.docker_registry_image_name' => 'nullable',
|
||||||
'application.docker_registry_image_tag' => 'nullable',
|
'application.docker_registry_image_tag' => 'nullable',
|
||||||
'application.dockerfile_location' => 'nullable',
|
'application.dockerfile_location' => 'nullable',
|
||||||
|
'application.custom_labels' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@ -73,16 +74,43 @@ class General extends Component
|
|||||||
'application.docker_registry_image_name' => 'Docker registry image name',
|
'application.docker_registry_image_name' => 'Docker registry image name',
|
||||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||||
'application.dockerfile_location' => 'Dockerfile location',
|
'application.dockerfile_location' => 'Dockerfile location',
|
||||||
|
'application.custom_labels' => 'Custom labels',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function updatedApplicationBuildPack(){
|
public function mount()
|
||||||
|
{
|
||||||
|
if (is_null(data_get($this->application, 'custom_labels'))) {
|
||||||
|
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||||
|
} else {
|
||||||
|
$this->customLabels = str($this->application->custom_labels)->replace(',', "\n");
|
||||||
|
}
|
||||||
|
if (data_get($this->application, 'settings')) {
|
||||||
|
$this->is_static = $this->application->settings->is_static;
|
||||||
|
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
||||||
|
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
||||||
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
|
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
|
}
|
||||||
|
$this->checkLabelUpdates();
|
||||||
|
}
|
||||||
|
public function updatedApplicationBuildPack()
|
||||||
|
{
|
||||||
if ($this->application->build_pack !== 'nixpacks') {
|
if ($this->application->build_pack !== 'nixpacks') {
|
||||||
$this->application->settings->is_static = $this->is_static = false;
|
$this->application->settings->is_static = $this->is_static = false;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
}
|
}
|
||||||
$this->submit();
|
$this->submit();
|
||||||
}
|
}
|
||||||
|
public function checkLabelUpdates()
|
||||||
|
{
|
||||||
|
if (md5($this->application->custom_labels) !== md5(implode(",", generateLabelsApplication($this->application)))) {
|
||||||
|
$this->labelsChanged = true;
|
||||||
|
} else {
|
||||||
|
$this->labelsChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
// @TODO: find another way - if possible
|
// @TODO: find another way - if possible
|
||||||
@ -102,37 +130,35 @@ class General extends Component
|
|||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWildcardDomain() {
|
public function getWildcardDomain()
|
||||||
|
{
|
||||||
$server = data_get($this->application, 'destination.server');
|
$server = data_get($this->application, 'destination.server');
|
||||||
if ($server) {
|
if ($server) {
|
||||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||||
ray($fqdn);
|
|
||||||
$this->application->fqdn = $fqdn;
|
$this->application->fqdn = $fqdn;
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public function mount()
|
public function resetDefaultLabels($showToaster = true)
|
||||||
{
|
{
|
||||||
if (data_get($this->application,'settings')) {
|
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||||
$this->is_static = $this->application->settings->is_static;
|
$this->submit($showToaster);
|
||||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
|
||||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
|
||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function updatedApplicationFqdn()
|
||||||
|
{
|
||||||
|
$this->resetDefaultLabels(false);
|
||||||
|
$this->emit('success', 'Labels reseted to default!');
|
||||||
|
}
|
||||||
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
if (data_get($this->application,'build_pack') === 'dockerimage') {
|
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'application.docker_registry_image_name' => 'required',
|
'application.docker_registry_image_name' => 'required',
|
||||||
'application.docker_registry_image_tag' => 'required',
|
'application.docker_registry_image_tag' => 'required',
|
||||||
@ -156,10 +182,16 @@ class General extends Component
|
|||||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
||||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
||||||
}
|
}
|
||||||
|
if (gettype($this->customLabels) === 'string') {
|
||||||
|
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
||||||
|
}
|
||||||
|
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$showToaster && $this->emit('success', 'Application settings updated!');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private ApplicationPreview|null $preview = null;
|
private ApplicationPreview|null $preview = null;
|
||||||
|
|
||||||
private string $container_name;
|
private string $container_name;
|
||||||
private string|null $currently_running_container_name = null;
|
private ?string $currently_running_container_name = null;
|
||||||
private string $basedir;
|
private string $basedir;
|
||||||
private string $workdir;
|
private string $workdir;
|
||||||
private ?string $build_pack = null;
|
private ?string $build_pack = null;
|
||||||
@ -166,7 +166,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
// Get user home directory
|
// Get user home directory
|
||||||
$this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server);
|
$this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server);
|
||||||
ray("test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'");
|
|
||||||
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
||||||
try {
|
try {
|
||||||
if ($this->application->dockerfile) {
|
if ($this->application->dockerfile) {
|
||||||
@ -650,6 +649,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables($ports);
|
$environment_variables = $this->generate_environment_variables($ports);
|
||||||
|
|
||||||
|
$labels = generateLabelsApplication($this->application, $this->preview);
|
||||||
|
if (data_get($this->application, 'custom_labels')) {
|
||||||
|
$labels = str($this->application->custom_labels)->explode(',')->toArray();
|
||||||
|
}
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
@ -658,7 +661,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'container_name' => $this->container_name,
|
'container_name' => $this->container_name,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'environment' => $environment_variables,
|
'environment' => $environment_variables,
|
||||||
'labels' => generateLabelsApplication($this->application, $this->preview, $ports),
|
'labels' => $labels,
|
||||||
'expose' => $ports,
|
'expose' => $ports,
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->destination->network,
|
$this->destination->network,
|
||||||
|
@ -538,7 +538,7 @@ class Service extends BaseModel
|
|||||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
if (!$isDatabase && $fqdns->count() > 0) {
|
if (!$isDatabase && $fqdns->count() > 0) {
|
||||||
if ($fqdns) {
|
if ($fqdns) {
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($fqdns, true));
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data_set($service, 'labels', $serviceLabels->toArray());
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
|
@ -147,12 +147,11 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
|
|||||||
}
|
}
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled, $onlyPort = null)
|
function fqdnLabelsForTraefik($uuid, Collection $domains, bool $is_force_https_enabled, $onlyPort = null)
|
||||||
{
|
{
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels->push('traefik.enable=true');
|
$labels->push('traefik.enable=true');
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
$uuid = (string)new Cuid2(7);
|
|
||||||
$url = Url::fromString($domain);
|
$url = Url::fromString($domain);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$path = $url->getPath();
|
$path = $url->getPath();
|
||||||
@ -205,20 +204,21 @@ function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled,
|
|||||||
|
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null, $ports): array
|
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
||||||
{
|
{
|
||||||
|
$ports = $application->settings->is_static ? [80] : $application->ports_exposes_array;
|
||||||
$onlyPort = null;
|
$onlyPort = null;
|
||||||
if (count($ports) === 1) {
|
if (count($ports) === 1) {
|
||||||
$onlyPort = $ports[0];
|
$onlyPort = $ports[0];
|
||||||
}
|
}
|
||||||
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
||||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
// $container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
$appId = $application->id;
|
$appId = $application->id;
|
||||||
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||||
$appId = $appId . '-pr-' . $pull_request_id;
|
$appId = $appId . '-pr-' . $pull_request_id;
|
||||||
}
|
}
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels = $labels->merge(defaultLabels($appId, $container_name, $pull_request_id));
|
$labels = $labels->merge(defaultLabels($appId, $application->uuid, $pull_request_id));
|
||||||
if ($application->fqdn) {
|
if ($application->fqdn) {
|
||||||
if ($pull_request_id !== 0) {
|
if ($pull_request_id !== 0) {
|
||||||
$domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
|
$domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
|
||||||
@ -226,7 +226,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||||
}
|
}
|
||||||
// Add Traefik labels no matter which proxy is selected
|
// Add Traefik labels no matter which proxy is selected
|
||||||
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled,$onlyPort));
|
$labels = $labels->merge(fqdnLabelsForTraefik($application->uuid, $domains, $application->settings->is_force_https_enabled, $onlyPort));
|
||||||
}
|
}
|
||||||
return $labels->all();
|
return $labels->all();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?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('applications', function (Blueprint $table) {
|
||||||
|
$table->text('custom_labels')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('custom_labels');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -93,6 +93,12 @@
|
|||||||
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
||||||
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
||||||
</div>
|
</div>
|
||||||
|
@if ($labelsChanged)
|
||||||
|
<x-forms.textarea label="Custom Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||||
|
@else
|
||||||
|
<x-forms.textarea label="Coolify Generated Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||||
|
@endif
|
||||||
|
<x-forms.button wire:click="resetDefaultLabels">Reset to Default Labels</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<h3>Advanced</h3>
|
<h3>Advanced</h3>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user