diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 60cd35f3b..efa906388 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -95,7 +95,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private ?string $buildTarget = null; private Collection $saved_outputs; private ?string $full_healthcheck_url = null; - private bool $custom_healthcheck_found = false; private string $serverUser = 'root'; private string $serverUserHomeDir = '/root'; @@ -903,10 +902,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted if ($this->server->isSwarm()) { // Implement healthcheck for swarm } else { - if ($this->application->isHealthcheckDisabled() && $this->custom_healthcheck_found === false) { + if ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found === false) { $this->newVersionIsHealthy = true; return; } + if ($this->application->custom_healthcheck_found) { + $this->application_deployment_queue->addLogEntry("Custom healthcheck found, skipping default healthcheck."); + } // ray('New container name: ', $this->container_name); if ($this->container_name) { $counter = 1; @@ -1272,16 +1274,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted return escapeDollarSign($value); }); $labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray(); + // Check for custom HEALTHCHECK - $this->custom_healthcheck_found = false; if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) { $this->execute_remote_command([ executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true ]); $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n")); - if (str($dockerfile)->contains('HEALTHCHECK')) { - $this->custom_healthcheck_found = true; - } + $this->application->parseHealthcheckFromDockerfile($dockerfile); } $docker_compose = [ 'version' => '3.8', @@ -1327,18 +1327,17 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted if (!is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } - if (!$this->custom_healthcheck_found) { - $docker_compose['services'][$this->container_name]['healthcheck'] = [ - 'test' => [ - 'CMD-SHELL', - $this->generate_healthcheck_commands() - ], - 'interval' => $this->application->health_check_interval . 's', - 'timeout' => $this->application->health_check_timeout . 's', - 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period . 's' - ]; - } + $docker_compose['services'][$this->container_name]['healthcheck'] = [ + 'test' => [ + 'CMD-SHELL', + $this->generate_healthcheck_commands() + ], + 'interval' => $this->application->health_check_interval . 's', + 'timeout' => $this->application->health_check_timeout . 's', + 'retries' => $this->application->health_check_retries, + 'start_period' => $this->application->health_check_start_period . 's' + ]; + if (!is_null($this->application->limits_cpuset)) { data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset); } diff --git a/app/Livewire/Project/New/SimpleDockerfile.php b/app/Livewire/Project/New/SimpleDockerfile.php index 55b48041a..172403a1a 100644 --- a/app/Livewire/Project/New/SimpleDockerfile.php +++ b/app/Livewire/Project/New/SimpleDockerfile.php @@ -70,6 +70,8 @@ CMD ["nginx", "-g", "daemon off;"] 'fqdn' => $fqdn ]); + $application->parseHealthcheckFromDockerfile(dockerfile: collect(str($this->dockerfile)->trim()->explode("\n")), isInit: true); + return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, 'environment_name' => $environment->name, diff --git a/app/Livewire/Project/Shared/HealthChecks.php b/app/Livewire/Project/Shared/HealthChecks.php index bd16ee577..56f5a2759 100644 --- a/app/Livewire/Project/Shared/HealthChecks.php +++ b/app/Livewire/Project/Shared/HealthChecks.php @@ -21,14 +21,13 @@ class HealthChecks extends Component 'resource.health_check_timeout' => 'integer|min:1', 'resource.health_check_retries' => 'integer|min:1', 'resource.health_check_start_period' => 'integer', + 'resource.custom_healthcheck_found' => 'boolean', ]; public function instantSave() { $this->resource->save(); $this->dispatch('success', 'Health check updated.'); - - } public function submit() { diff --git a/app/Models/Application.php b/app/Models/Application.php index f28d389f4..98b2c23b1 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -963,4 +963,51 @@ class Application extends BaseModel { getFilesystemVolumesFromServer($this, $isInit); } + + public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false) { + if (str($dockerfile)->contains('HEALTHCHECK') && ($this->isHealthcheckDisabled() || $isInit)) { + $healthcheckCommand = null; + $lines = $dockerfile->toArray(); + foreach ($lines as $line) { + $trimmedLine = trim($line); + if (str_starts_with($trimmedLine, 'HEALTHCHECK')) { + $healthcheckCommand .= trim($trimmedLine, '\\ '); + continue; + } + if (isset($healthcheckCommand) && str_contains($trimmedLine, '\\')) { + $healthcheckCommand .= ' ' . trim($trimmedLine, '\\ '); + } + if (isset($healthcheckCommand) && !str_contains($trimmedLine, '\\') && !empty($healthcheckCommand)) { + $healthcheckCommand .= ' ' . $trimmedLine; + break; + } + } + if (str($healthcheckCommand)->isNotEmpty()) { + $interval = str($healthcheckCommand)->match('/--interval=(\d+)/'); + $timeout = str($healthcheckCommand)->match('/--timeout=(\d+)/'); + $start_period = str($healthcheckCommand)->match('/--start-period=(\d+)/'); + $start_interval = str($healthcheckCommand)->match('/--start-interval=(\d+)/'); + $retries = str($healthcheckCommand)->match('/--retries=(\d+)/'); + if ($interval->isNotEmpty()) { + $this->health_check_interval = $interval->toInteger(); + } + if ($timeout->isNotEmpty()) { + $this->health_check_timeout = $timeout->toInteger(); + } + if ($start_period->isNotEmpty()) { + $this->health_check_start_period = $start_period->toInteger(); + } + // if ($start_interval) { + // $this->health_check_start_interval = $start_interval->value(); + // } + if ($retries->isNotEmpty()) { + $this->health_check_retries = $retries->toInteger(); + } + if ($interval || $timeout || $start_period || $start_interval || $retries) { + $this->custom_healthcheck_found = true; + $this->save(); + } + } + } + } } diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php index 1705deda1..05fe544d0 100644 --- a/app/Notifications/Application/DeploymentFailed.php +++ b/app/Notifications/Application/DeploymentFailed.php @@ -69,10 +69,10 @@ class DeploymentFailed extends Notification implements ShouldQueue public function toDiscord(): string { if ($this->preview) { - $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; + $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: '; $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; } else { - $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; + $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): '; $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; } return $message; @@ -80,9 +80,9 @@ class DeploymentFailed extends Notification implements ShouldQueue public function toTelegram(): array { if ($this->preview) { - $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: '; + $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: '; } else { - $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): '; + $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): '; } $buttons[] = [ "text" => "Deployment logs", diff --git a/database/migrations/2024_04_29_111956_add_custom_hc_indicator_apps.php b/database/migrations/2024_04_29_111956_add_custom_hc_indicator_apps.php new file mode 100644 index 000000000..b5a97f0eb --- /dev/null +++ b/database/migrations/2024_04_29_111956_add_custom_hc_indicator_apps.php @@ -0,0 +1,28 @@ +boolean('custom_healthcheck_found')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('custom_healthcheck_found'); + }); + } +}; diff --git a/resources/views/livewire/project/shared/health-checks.blade.php b/resources/views/livewire/project/shared/health-checks.blade.php index 196a20779..6b6e02a4e 100644 --- a/resources/views/livewire/project/shared/health-checks.blade.php +++ b/resources/views/livewire/project/shared/health-checks.blade.php @@ -5,6 +5,9 @@
Define how your resource's health should be checked.
+ @if ($resource->custom_healthcheck_found) +
A custom health check has been found and will be used until you enable this.
+ @endif
@@ -17,7 +20,8 @@
- +