Current Behavior
Node resource limits (memory / disk / cpu) are never enforced on the deployment path, because Node::isViable() always reads 0 for the already-allocated sums.
Node declares three real public typed properties whose names collide with the aggregate columns produced by withSum():
https://github.com/pelican-dev/panel/blob/main/app/Models/Node.php#L193-L197
public int $servers_sum_memory = 0;
public int $servers_sum_disk = 0;
public int $servers_sum_cpu = 0;
Because these are declared properties, PHP resolves $node->servers_sum_memory directly against the property and never falls through to Eloquent's __get(). The value fetched by withSum() lands in the attribute bag but is never read.
FindViableNodesService populates the aggregates and then calls isViable(), which reads the shadowed properties:
// app/Services/Deployment/FindViableNodesService.php
->withSum('servers', 'memory')
->withSum('servers', 'disk')
->withSum('servers', 'cpu')
...
->filter(fn (Node $node) => $node->isViable($memory, $disk, $cpu));
// app/Models/Node.php isViable()
if ($this->servers_sum_memory + $memory > $memoryLimit) { // $this->servers_sum_memory is always 0
So the check degenerates to 0 + requested > limit, i.e. it only rejects a single server larger than the whole node. Total allocation across servers is unbounded.
This is distinct from #321 — that one was about manually creating servers in the admin area, and was closed with "node limits only apply to the API". This report is about the API/deployment path where limits are supposed to apply.
Expected Behavior
isViable() should compare already allocated + requested against the node limit, so that the sum of all servers on a node cannot exceed its configured memory / disk / cpu (plus overallocate).
Steps to Reproduce
Node with memory = 48000, memory_overallocate = 0, and one existing server with memory = 16384 (31616 MiB should remain).
$node = Node::withSum('servers', 'memory')->find(1);
$node->getAttribute('servers_sum_memory'); // 16384 <- aggregate is correct
$node->servers_sum_memory; // 0 <- shadowed by the declared property
$node->isViable(32000, 0, 0); // true <- 16384 + 32000 = 48384 > 48000
Proof that the property is what isViable() reads — same object, same DB state, only the property changed:
$node->isViable(32000, 0, 0); // true
$node->servers_sum_memory = 16384; // write the real value into the property
$node->isViable(32000, 0, 0); // false <- now correct
Creating servers through the deployment path (e.g. the User Creatable Servers plugin, which calls ServerCreationService with a DeploymentObject) therefore lets users allocate well past the node's configured limits.
grep -rn "servers_sum" app/ shows the properties are only ever declared and read — nothing ever assigns them.
Suggested Fix
Removing the three property declarations is enough for __get() to resolve the aggregates again. Since there is currently no @property docblock for them, adding one keeps static analysis happy:
/**
* @property-read int|null $servers_sum_memory
* @property-read int|null $servers_sum_disk
* @property-read int|null $servers_sum_cpu
*/
Note the aggregates are null when withSum() was not applied, so isViable() would need (int) casts / null-coalescing to keep its current behaviour when called on a plain model.
Panel Version
1.0.0-beta35 (also present on main as of this writing)
Wings Version
1.0.0-beta26
Games and/or Eggs Affected
No response
Docker Image
ghcr.io/pelican-dev/panel:v1.0.0-beta35
Error Logs
No response
Is there an existing issue for this?
Current Behavior
Node resource limits (memory / disk / cpu) are never enforced on the deployment path, because
Node::isViable()always reads0for the already-allocated sums.Nodedeclares three real public typed properties whose names collide with the aggregate columns produced bywithSum():https://github.com/pelican-dev/panel/blob/main/app/Models/Node.php#L193-L197
Because these are declared properties, PHP resolves
$node->servers_sum_memorydirectly against the property and never falls through to Eloquent's__get(). The value fetched bywithSum()lands in the attribute bag but is never read.FindViableNodesServicepopulates the aggregates and then callsisViable(), which reads the shadowed properties:So the check degenerates to
0 + requested > limit, i.e. it only rejects a single server larger than the whole node. Total allocation across servers is unbounded.This is distinct from #321 — that one was about manually creating servers in the admin area, and was closed with "node limits only apply to the API". This report is about the API/deployment path where limits are supposed to apply.
Expected Behavior
isViable()should comparealready allocated + requestedagainst the node limit, so that the sum of all servers on a node cannot exceed its configured memory / disk / cpu (plus overallocate).Steps to Reproduce
Node with
memory = 48000,memory_overallocate = 0, and one existing server withmemory = 16384(31616 MiB should remain).Proof that the property is what
isViable()reads — same object, same DB state, only the property changed:Creating servers through the deployment path (e.g. the User Creatable Servers plugin, which calls
ServerCreationServicewith aDeploymentObject) therefore lets users allocate well past the node's configured limits.grep -rn "servers_sum" app/shows the properties are only ever declared and read — nothing ever assigns them.Suggested Fix
Removing the three property declarations is enough for
__get()to resolve the aggregates again. Since there is currently no@propertydocblock for them, adding one keeps static analysis happy:Note the aggregates are
nullwhenwithSum()was not applied, soisViable()would need(int)casts / null-coalescing to keep its current behaviour when called on a plain model.Panel Version
1.0.0-beta35 (also present on
mainas of this writing)Wings Version
1.0.0-beta26
Games and/or Eggs Affected
No response
Docker Image
ghcr.io/pelican-dev/panel:v1.0.0-beta35Error Logs
No response
Is there an existing issue for this?