Skip to content

Node resource limits are never enforced: servers_sum_* public properties shadow the withSum() aggregates #2478

Description

@WisdomIT

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?

  • I have searched the existing issues before opening this issue.
  • I have provided all relevant details, including the specific game and Docker images I am using if this issue is related to running a server.
  • I have checked in the Discord server and believe this is a bug with the software, and not a configuration issue with my specific system.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions