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:
parent
1639941da7
commit
28fb694fcb
10 changed files with 144 additions and 20 deletions
|
|
@ -261,14 +261,7 @@ public function __construct(public int $application_deployment_queue_id)
|
|||
$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 @@ private function rolling_update()
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ private function generate_compose_file()
|
|||
$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 @@ private function stop_running_container(bool $force = false)
|
|||
$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 @@ private function stop_running_container(bool $force = false)
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ public function delete($password, $selectedActions = [])
|
|||
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 @@ public function deleteApplication($password, $selectedActions = [])
|
|||
$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 @@ public function convertToDatabase()
|
|||
$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 @@ public function delete()
|
|||
$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 @@ public function delete()
|
|||
// @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 @@ public function delete()
|
|||
// @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 @@ public function delete()
|
|||
|
||||
$this->storage->delete();
|
||||
|
||||
return redirect()->route('storage.index');
|
||||
return redirectRoute($this, 'storage.index');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
|
|
|||
58
tests/Unit/ApplicationDeploymentContainerNamingTest.php
Normal file
58
tests/Unit/ApplicationDeploymentContainerNamingTest.php
Normal file
|
|
@ -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 () {
|
||||
|
|
|
|||
43
tests/Unit/LivewireInternalRedirectTest.php
Normal file
43
tests/Unit/LivewireInternalRedirectTest.php
Normal file
|
|
@ -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);",
|
||||
],
|
||||
],
|
||||
]);
|
||||
Loading…
Reference in a new issue