feat(applications): add configurable stop grace period

Add centralized stop grace period resolution for application settings and use it across manual stops, preview stops, and deployments. Validate the Livewire advanced setting against shared min/max constants and cover persistence, fillable creation, and fallback behavior with tests.
This commit is contained in:
Andras Bacsai committed 2026-05-11 23:43:53 +02:00
1 parent d1220895d9
commit 63c2d31ca0
11 files changed
+173 -46

No files matched your search

+1 -3
View File
@@ -36,9 +36,7 @@ class StopApplication
: getCurrentApplicationContainerStatus($server, $application->id, 0);
$containersToStop = $containers->pluck('Names')->toArray();
$timeout = ($application->settings->stop_grace_period > 0)
? $application->settings->stop_grace_period
: DEFAULT_STOP_GRACE_PERIOD_SECONDS;
$timeout = $application->settings->stopGracePeriodSeconds();
foreach ($containersToStop as $containerName) {
instant_remote_process(command: [
@@ -20,9 +20,7 @@ class StopApplicationOneServer
}
try {
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
$timeout = ($application->settings->stop_grace_period > 0)
? $application->settings->stop_grace_period
: DEFAULT_STOP_GRACE_PERIOD_SECONDS;
$timeout = $application->settings->stopGracePeriodSeconds();
if ($containers->count() > 0) {
foreach ($containers as $container) {
+1 -7
View File
@@ -3790,13 +3790,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
private function graceful_shutdown_container(string $containerName, bool $skipRemove = false)
{
try {
if (isDev()) {
$timeout = 1;
} else {
$timeout = ($this->application->settings->stop_grace_period > 0)
? $this->application->settings->stop_grace_period
: DEFAULT_STOP_GRACE_PERIOD_SECONDS;
}
$timeout = $this->application->settings->deploymentStopGracePeriodSeconds();
if ($skipRemove) {
$this->execute_remote_command(
+13 -14
View File
@@ -4,6 +4,8 @@ namespace App\Livewire\Project\Application;
use App\Models\Application;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Validate;
use Livewire\Component;
@@ -264,24 +266,21 @@ class Advanced extends Component
try {
$this->authorize('update', $this->application);
// Convert empty string to null, otherwise cast to integer
$value = ($this->stopGracePeriod === '' || $this->stopGracePeriod === null)
$validated = Validator::make(
['stopGracePeriod' => $this->stopGracePeriod === '' ? null : $this->stopGracePeriod],
['stopGracePeriod' => ['nullable', 'integer', 'min:'.MIN_STOP_GRACE_PERIOD_SECONDS, 'max:'.MAX_STOP_GRACE_PERIOD_SECONDS]],
[],
['stopGracePeriod' => 'stop grace period']
)->validate();
$this->application->settings->stop_grace_period = $validated['stopGracePeriod'] === null
? null
: (int) $this->stopGracePeriod;
// Validate the integer value
if ($value !== null && ($value < 1 || $value > 3600)) {
$this->dispatch('error', 'Stop grace period must be between 1 and 3600 seconds.');
return;
}
// Save to model
$this->application->settings->stop_grace_period = $value;
: (int) $validated['stopGracePeriod'];
$this->application->settings->save();
// User feedback
$this->dispatch('success', 'Stop grace period updated.');
} catch (ValidationException $e) {
throw $e;
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -338,9 +338,7 @@ class Previews extends Component
private function stopContainers(array $containers, $server)
{
$containersToStop = collect($containers)->pluck('Names')->toArray();
$timeout = ($this->application->settings->stop_grace_period > 0)
? $this->application->settings->stop_grace_period
: DEFAULT_STOP_GRACE_PERIOD_SECONDS;
$timeout = $this->application->settings->stopGracePeriodSeconds();
foreach ($containersToStop as $containerName) {
instant_remote_process(command: [
+22
View File
@@ -65,8 +65,30 @@ class ApplicationSetting extends Model
'inject_build_args_to_dockerfile',
'include_source_commit_in_build',
'docker_images_to_keep',
'stop_grace_period',
];
public function stopGracePeriodSeconds(): int
{
if (
$this->stop_grace_period >= MIN_STOP_GRACE_PERIOD_SECONDS &&
$this->stop_grace_period <= MAX_STOP_GRACE_PERIOD_SECONDS
) {
return $this->stop_grace_period;
}
return DEFAULT_STOP_GRACE_PERIOD_SECONDS;
}
public function deploymentStopGracePeriodSeconds(): int
{
if (isDev() && $this->stop_grace_period === null) {
return MIN_STOP_GRACE_PERIOD_SECONDS;
}
return $this->stopGracePeriodSeconds();
}
public function isStatic(): Attribute
{
return Attribute::make(