mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-24 15:30:03 -05:00
fix: preserve custom names and use Livewire redirects
Resolve custom container names consistently, clean up legacy container names during deployments, and route Livewire resource redirects through the navigation helper.
This commit is contained in:
@@ -261,14 +261,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->configuration_dir = application_configuration_dir()."/{$this->application->uuid}";
|
||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||
|
||||
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||
if ($this->application->settings->custom_internal_name && ! $this->application->settings->is_consistent_container_name_enabled) {
|
||||
if ($this->pull_request_id === 0) {
|
||||
$this->container_name = $this->application->settings->custom_internal_name;
|
||||
} else {
|
||||
$this->container_name = addPreviewDeploymentSuffix($this->application->settings->custom_internal_name, $this->pull_request_id);
|
||||
}
|
||||
}
|
||||
$this->container_name = $this->resolveContainerName();
|
||||
|
||||
$this->saved_outputs = collect();
|
||||
|
||||
@@ -1986,6 +1979,19 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveContainerName(): string
|
||||
{
|
||||
if (str($this->application->settings->custom_internal_name)->isEmpty()) {
|
||||
return generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||
}
|
||||
|
||||
if ($this->pull_request_id === 0) {
|
||||
return $this->application->settings->custom_internal_name;
|
||||
}
|
||||
|
||||
return addPreviewDeploymentSuffix($this->application->settings->custom_internal_name, $this->pull_request_id);
|
||||
}
|
||||
|
||||
private function health_check()
|
||||
{
|
||||
try {
|
||||
@@ -3428,6 +3434,9 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$custom_compose = convertDockerRunToCompose($this->application->custom_docker_run_options);
|
||||
if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||
if ($this->container_name !== $this->application->uuid) {
|
||||
unset($docker_compose['services'][$this->container_name]);
|
||||
}
|
||||
if (count($custom_compose) > 0) {
|
||||
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||
@@ -4027,7 +4036,10 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$this->application_deployment_queue->addLogEntry('Removing old containers.');
|
||||
if ($this->newVersionIsHealthy || $force) {
|
||||
if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) {
|
||||
$this->graceful_shutdown_container($this->container_name);
|
||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
|
||||
$this->containerNamesToRemove($containers)->each(function (string $containerName) {
|
||||
$this->graceful_shutdown_container($containerName);
|
||||
});
|
||||
} else {
|
||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
|
||||
if ($this->pull_request_id === 0) {
|
||||
@@ -4066,6 +4078,16 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
}
|
||||
|
||||
private function containerNamesToRemove(Collection $containers): Collection
|
||||
{
|
||||
return $containers
|
||||
->pluck('Names')
|
||||
->push($this->container_name)
|
||||
->filter()
|
||||
->unique()
|
||||
->values();
|
||||
}
|
||||
|
||||
private function start_by_compose_file()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -212,14 +212,14 @@ class BackupEdit extends Component
|
||||
if ($this->backup->database->getMorphClass() === ServiceDatabase::class) {
|
||||
$serviceDatabase = $this->backup->database;
|
||||
|
||||
return redirect()->route('project.service.database.backups', [
|
||||
return redirectRoute($this, 'project.service.database.backups', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'environment_uuid' => $this->parameters['environment_uuid'],
|
||||
'service_uuid' => $serviceDatabase->service->uuid,
|
||||
'stack_service_uuid' => $serviceDatabase->uuid,
|
||||
]);
|
||||
} else {
|
||||
return redirect()->route('project.database.backup.index', [
|
||||
return redirectRoute($this, 'project.database.backup.index', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'environment_uuid' => $this->parameters['environment_uuid'],
|
||||
'database_uuid' => $this->parameters['database_uuid'],
|
||||
|
||||
@@ -428,7 +428,7 @@ class Index extends Component
|
||||
$this->serviceApplication->delete();
|
||||
$this->dispatch('success', 'Application deleted.');
|
||||
|
||||
return redirect()->route('project.service.configuration', $this->parameters);
|
||||
return redirectRoute($this, 'project.service.configuration', $this->parameters);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -462,7 +462,7 @@ class Index extends Component
|
||||
$serviceApplication->delete();
|
||||
});
|
||||
|
||||
return redirect()->route('project.service.configuration', $redirectParams);
|
||||
return redirectRoute($this, 'project.service.configuration', $redirectParams);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -169,9 +169,9 @@ class Show extends Component
|
||||
$this->task->delete();
|
||||
|
||||
if ($this->type === 'application') {
|
||||
return redirect()->route('project.application.scheduled-tasks.show', $this->parameters);
|
||||
return redirectRoute($this, 'project.application.scheduled-tasks.show', $this->parameters);
|
||||
} else {
|
||||
return redirect()->route('project.service.scheduled-tasks.show', $this->parameters);
|
||||
return redirectRoute($this, 'project.service.scheduled-tasks.show', $this->parameters);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
|
||||
@@ -484,7 +484,7 @@ class Change extends Component
|
||||
// @can and canGate checks against a deleted model (null team_id TypeError).
|
||||
$this->github_app = null;
|
||||
|
||||
return redirect()->route('source.all');
|
||||
return redirectRoute($this, 'source.all');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ class Change extends Component
|
||||
// @can and canGate checks against a deleted model (null team_id TypeError).
|
||||
$this->gitlab_app = null;
|
||||
|
||||
return redirect()->route('source.all');
|
||||
return redirectRoute($this, 'source.all');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class Show extends Component
|
||||
|
||||
$this->storage->delete();
|
||||
|
||||
return redirect()->route('storage.index');
|
||||
return redirectRoute($this, 'storage.index');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
use App\Jobs\ApplicationDeploymentJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationSetting;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
function containerNamingJob(Application $application, int $pullRequestId = 0): array
|
||||
{
|
||||
$job = (new ReflectionClass(ApplicationDeploymentJob::class))->newInstanceWithoutConstructor();
|
||||
$reflection = new ReflectionClass(ApplicationDeploymentJob::class);
|
||||
$reflection->getProperty('application')->setValue($job, $application);
|
||||
$reflection->getProperty('pull_request_id')->setValue($job, $pullRequestId);
|
||||
|
||||
return [$job, $reflection];
|
||||
}
|
||||
|
||||
function applicationWithContainerNaming(string $customName = 'shadowuw'): Application
|
||||
{
|
||||
$application = new Application;
|
||||
$application->forceFill(['uuid' => 'application-uuid']);
|
||||
$application->setRelation('settings', new ApplicationSetting([
|
||||
'custom_internal_name' => $customName,
|
||||
'is_consistent_container_name_enabled' => true,
|
||||
]));
|
||||
|
||||
return $application;
|
||||
}
|
||||
|
||||
it('uses the custom container name when consistent naming is enabled', function () {
|
||||
$application = applicationWithContainerNaming();
|
||||
|
||||
[$job, $reflection] = containerNamingJob($application);
|
||||
|
||||
expect($reflection->getMethod('resolveContainerName')->invoke($job))->toBe('shadowuw');
|
||||
});
|
||||
|
||||
it('adds the pull request suffix to a custom container name', function () {
|
||||
$application = applicationWithContainerNaming();
|
||||
|
||||
[$job, $reflection] = containerNamingJob($application, 42);
|
||||
|
||||
expect($reflection->getMethod('resolveContainerName')->invoke($job))->toBe('shadowuw-pr-42');
|
||||
});
|
||||
|
||||
it('includes old generated containers when cleaning up a consistent deployment', function () {
|
||||
$application = applicationWithContainerNaming();
|
||||
[$job, $reflection] = containerNamingJob($application);
|
||||
$reflection->getProperty('container_name')->setValue($job, 'shadowuw');
|
||||
|
||||
$containers = new Collection([
|
||||
['Names' => 'application-uuid-192238854305'],
|
||||
['Names' => 'shadowuw'],
|
||||
]);
|
||||
|
||||
expect($reflection->getMethod('containerNamesToRemove')->invoke($job, $containers)->all())
|
||||
->toBe(['application-uuid-192238854305', 'shadowuw']);
|
||||
});
|
||||
@@ -64,7 +64,7 @@ function generateComposeServiceWithCustomDockerOptions(string $customDockerOptio
|
||||
'server' => $server,
|
||||
'mainServer' => $server,
|
||||
'pull_request_id' => 0,
|
||||
'container_name' => $application->uuid,
|
||||
'container_name' => 'custom-internal-name',
|
||||
'production_image_name' => 'example/app:latest',
|
||||
'deployment_uuid' => 'deployment-uuid',
|
||||
'workdir' => '/artifacts/custom-docker-options-app',
|
||||
@@ -82,7 +82,8 @@ function generateComposeServiceWithCustomDockerOptions(string $customDockerOptio
|
||||
|
||||
it('applies an entrypoint when consistent naming and a custom internal name are configured', function () {
|
||||
expect(generateComposeServiceWithCustomDockerOptions('--entrypoint "/bin/echo hello world"'))
|
||||
->toHaveKey('entrypoint', '/bin/echo hello world');
|
||||
->toHaveKey('entrypoint', '/bin/echo hello world')
|
||||
->toHaveKey('container_name', 'custom-internal-name');
|
||||
});
|
||||
|
||||
it('preserves custom network aliases when a static IP is configured', function () {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
it('uses Livewire navigation after deleting or converting page resources', function (string $path, array $redirects) {
|
||||
$contents = file_get_contents(dirname(__DIR__, 2).'/'.$path);
|
||||
|
||||
foreach ($redirects as $redirect) {
|
||||
expect($contents)->toContain($redirect);
|
||||
}
|
||||
})->with([
|
||||
'S3 storage' => [
|
||||
'app/Livewire/Storage/Show.php',
|
||||
["redirectRoute(\$this, 'storage.index')"],
|
||||
],
|
||||
'database backup schedule' => [
|
||||
'app/Livewire/Project/Database/BackupEdit.php',
|
||||
[
|
||||
"redirectRoute(\$this, 'project.service.database.backups'",
|
||||
"redirectRoute(\$this, 'project.database.backup.index'",
|
||||
],
|
||||
],
|
||||
'scheduled task' => [
|
||||
'app/Livewire/Project/Shared/ScheduledTask/Show.php',
|
||||
[
|
||||
"redirectRoute(\$this, 'project.application.scheduled-tasks.show'",
|
||||
"redirectRoute(\$this, 'project.service.scheduled-tasks.show'",
|
||||
],
|
||||
],
|
||||
'GitHub source' => [
|
||||
'app/Livewire/Source/Github/Change.php',
|
||||
["redirectRoute(\$this, 'source.all')"],
|
||||
],
|
||||
'GitLab source' => [
|
||||
'app/Livewire/Source/Gitlab/Change.php',
|
||||
["redirectRoute(\$this, 'source.all')"],
|
||||
],
|
||||
'service resources' => [
|
||||
'app/Livewire/Project/Service/Index.php',
|
||||
[
|
||||
"return redirectRoute(\$this, 'project.service.configuration', \$this->parameters);",
|
||||
"return redirectRoute(\$this, 'project.service.configuration', \$redirectParams);",
|
||||
],
|
||||
],
|
||||
]);
|
||||
Reference in New Issue
Block a user