refactor(ResourceLimits): make all params optional and use defaults in DB; adjust UI placeholders for clarity

This commit is contained in:
Dominic 2026-01-11 06:06:20 +01:00 committed by Dominic Schmid
parent 5d21b9fadd
commit 4e643c3226
2 changed files with 82 additions and 51 deletions

View file

@ -9,28 +9,30 @@ class ResourceLimits extends Component
{
use AuthorizesRequests;
public $resource;
// Default values for resource limits
private const DEFAULT_CPU_LIMIT = 0.0;
private const DEFAULT_CPU_SET = '0';
private const DEFAULT_CPU_SHARES = 1024;
private const DEFAULT_MEMORY_SWAPPINESS = 60;
private const DEFAULT_MEMORY_LIMIT = '0';
private const DEFAULT_MEMORY_SWAP = '0';
private const DEFAULT_MEMORY_RESERVATION = '0';
// Explicit properties
public ?string $limitsCpus = null;
public mixed $resource;
public ?float $limitsCpus = null;
public ?string $limitsCpuset = null;
public ?int $limitsCpuShares = null;
public string $limitsMemory;
public string $limitsMemorySwap;
public int $limitsMemorySwappiness;
public string $limitsMemoryReservation;
public ?string $limitsMemory = null;
public ?string $limitsMemorySwap = null;
public ?int $limitsMemorySwappiness = null;
public ?string $limitsMemoryReservation = null;
protected $rules = [
'limitsMemory' => 'required|string',
'limitsMemorySwap' => 'required|string',
'limitsMemorySwappiness' => 'required|integer|min:0|max:100',
'limitsMemoryReservation' => 'required|string',
'limitsMemory' => 'nullable|string',
'limitsMemorySwap' => 'nullable|string',
'limitsMemorySwappiness' => 'nullable|integer|min:0|max:100',
'limitsMemoryReservation' => 'nullable|string',
'limitsCpus' => 'nullable|numeric|min:0|max:1024',
'limitsCpuset' => 'nullable|string',
'limitsCpuShares' => 'nullable|integer|min:0|max:8192',
@ -51,7 +53,7 @@ class ResourceLimits extends Component
*
* @param bool $toModel If true, sync FROM properties TO model. If false, sync FROM model TO properties.
*/
private function syncData(bool $toModel = false): void
private function syncData(bool $toModel): void
{
if ($toModel) {
// Sync TO model (before save)
@ -62,58 +64,87 @@ class ResourceLimits extends Component
$this->resource->limits_memory_swap = $this->limitsMemorySwap;
$this->resource->limits_memory_swappiness = $this->limitsMemorySwappiness;
$this->resource->limits_memory_reservation = $this->limitsMemoryReservation;
} else {
// Sync FROM model (on load/refresh)
$this->limitsCpus = $this->resource->limits_cpus;
$this->limitsCpuset = $this->resource->limits_cpuset;
$this->limitsCpuShares = $this->resource->limits_cpu_shares;
$this->limitsMemory = $this->resource->limits_memory;
$this->limitsMemorySwap = $this->resource->limits_memory_swap;
$this->limitsMemorySwappiness = $this->resource->limits_memory_swappiness;
$this->limitsMemoryReservation = $this->resource->limits_memory_reservation;
return;
}
// Sync FROM model (on load/refresh)
$this->limitsCpus = $this->resource->limits_cpus;
$this->limitsCpuset = $this->resource->limits_cpuset;
$this->limitsCpuShares = $this->resource->limits_cpu_shares;
$this->limitsMemory = $this->resource->limits_memory;
$this->limitsMemorySwap = $this->resource->limits_memory_swap;
$this->limitsMemorySwappiness = $this->resource->limits_memory_swappiness;
$this->limitsMemoryReservation = $this->resource->limits_memory_reservation;
// Convert default values to null so UI shows placeholders instead of defaults
if ($this->limitsCpus === self::DEFAULT_CPU_LIMIT) {
$this->limitsCpus = null;
}
if ($this->limitsCpuset === self::DEFAULT_CPU_SET) {
$this->limitsCpuset = null;
}
if ($this->limitsCpuShares === self::DEFAULT_CPU_SHARES) {
$this->limitsCpuShares = null;
}
if ($this->limitsMemorySwappiness === self::DEFAULT_MEMORY_SWAPPINESS) {
$this->limitsMemorySwappiness = null;
}
if ($this->limitsMemory === self::DEFAULT_MEMORY_LIMIT) {
$this->limitsMemory = null;
}
if ($this->limitsMemorySwap === self::DEFAULT_MEMORY_SWAP) {
$this->limitsMemorySwap = null;
}
if ($this->limitsMemoryReservation === self::DEFAULT_MEMORY_RESERVATION) {
$this->limitsMemoryReservation = null;
}
}
public function mount()
public function mount(): void
{
$this->syncData(false);
$this->syncData(toModel: false);
}
public function submit()
public function submit(): void
{
try {
$this->authorize('update', $this->resource);
// Apply default values to properties
if (!$this->limitsMemory) {
$this->limitsMemory = '0';
// Apply defaults for empty fields
if (empty($this->limitsMemory)) {
$this->limitsMemory = self::DEFAULT_MEMORY_LIMIT;
}
if (!$this->limitsMemorySwap) {
$this->limitsMemorySwap = '0';
if (empty($this->limitsMemorySwap)) {
$this->limitsMemorySwap = self::DEFAULT_MEMORY_SWAP;
}
if (is_null($this->limitsMemorySwappiness)) {
$this->limitsMemorySwappiness = 60;
if (empty($this->limitsMemoryReservation)) {
$this->limitsMemoryReservation = self::DEFAULT_MEMORY_RESERVATION;
}
if (!$this->limitsMemoryReservation) {
$this->limitsMemoryReservation = '0';
if ($this->limitsCpus === null) {
$this->limitsCpus = self::DEFAULT_CPU_LIMIT;
}
if (!$this->limitsCpus) {
$this->limitsCpus = '0';
if (empty($this->limitsCpuset)) {
$this->limitsCpuset = self::DEFAULT_CPU_SET;
}
if ($this->limitsCpuset === '') {
$this->limitsCpuset = null;
if ($this->limitsCpuShares === null) {
$this->limitsCpuShares = self::DEFAULT_CPU_SHARES;
}
if (is_null($this->limitsCpuShares)) {
$this->limitsCpuShares = 1024;
if ($this->limitsMemorySwappiness === null) {
$this->limitsMemorySwappiness = self::DEFAULT_MEMORY_SWAPPINESS;
}
$this->validate();
$this->syncData(true);
$this->syncData(toModel: true);
$this->resource->save();
// Reload from model to convert defaults back to null for placeholder display
$this->syncData(toModel: false);
$this->dispatch('success', 'Resource limits updated.');
} catch (\Throwable $e) {
return handleError($e, $this);
handleError($e, $this);
}
}
}

View file

@ -11,7 +11,7 @@
<div class="flex flex-col gap-4">
<div class="flex flex-col md:flex-row gap-4">
<x-forms.input canGate="update" :canResource="$resource" type="number" min="0" max="1024" step="0.1"
placeholder="1.5"
placeholder="0"
helper="Limit how much CPU the container can use. 0 means unlimited (use all available CPUs). Use decimal numbers like 1.5 for one and a half CPUs, or 0.5 for half a CPU.<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/engine/reference/run/#cpu-quota-constraint'>cpu-quota</a>."
label="CPU Limit" id="limitsCpus">
<x-slot:suffix>
@ -34,7 +34,7 @@
</x-forms.input>
</div>
<div class="flex flex-col md:flex-row gap-4">
<x-forms.input canGate="update" :canResource="$resource" placeholder="0-2"
<x-forms.input canGate="update" :canResource="$resource" placeholder="0"
helper="Pin container to specific CPU threads. 0 means use all threads. Example: 0-1,4 results in using threads 0,1,4.<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/engine/reference/run/#cpuset-constraint'>cpuset</a>."
label="CPU sets to use" id="limitsCpuset">
<x-slot:suffix>
@ -74,7 +74,7 @@
<x-forms.input-with-select canGate="update" :canResource="$resource"
type="number"
min="0"
placeholder="512"
placeholder="0"
helper="Hard limit on container memory usage. The container will be killed if it exceeds this limit.<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_limit'>mem_limit</a>."
label="Memory Limit" id="limitsMemory"
:options="['b' => 'B', 'k' => 'KiB', 'm' => 'MiB', 'g' => 'GiB']"
@ -82,7 +82,7 @@
<x-forms.input-with-select canGate="update" :canResource="$resource"
type="number"
min="0"
placeholder="256"
placeholder="0"
helper="Guaranteed memory reservation for the container. Docker attempts to ensure this amount is always available.<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#mem_reservation'>mem_reservation</a>."
label="Memory Reservation" id="limitsMemoryReservation"
:options="['b' => 'B', 'k' => 'KiB', 'm' => 'MiB', 'g' => 'GiB']"
@ -92,7 +92,7 @@
<x-forms.input-with-select canGate="update" :canResource="$resource"
type="number"
min="0"
placeholder="512"
placeholder="0"
helper="Total limit for memory plus swap space. Combined limit for both RAM and swap usage.<br>More info <a class='underline dark:text-white' target='_blank' href='https://docs.docker.com/compose/compose-file/05-services/#memswap_limit'>memswap_limit</a>."
label="Maximum Swap Limit" id="limitsMemorySwap"
:options="['b' => 'B', 'k' => 'KiB', 'm' => 'MiB', 'g' => 'GiB']"