Skip to content

Commit 1f5e0c0

Browse files
committed
Update build modification service and cover logic with test cases
closes pterodactyl#2553
1 parent b3598b3 commit 1f5e0c0

File tree

2 files changed

+296
-90
lines changed

2 files changed

+296
-90
lines changed

app/Services/Servers/BuildModificationService.php

Lines changed: 55 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,14 @@
44

55
use Illuminate\Support\Arr;
66
use Pterodactyl\Models\Server;
7-
use GuzzleHttp\Exception\RequestException;
7+
use Pterodactyl\Models\Allocation;
88
use Illuminate\Database\ConnectionInterface;
99
use Pterodactyl\Exceptions\DisplayException;
10+
use Illuminate\Database\Eloquent\ModelNotFoundException;
1011
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
11-
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
12-
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
13-
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
14-
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
1512

1613
class BuildModificationService
1714
{
18-
/**
19-
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
20-
*/
21-
private $allocationRepository;
22-
2315
/**
2416
* @var \Illuminate\Database\ConnectionInterface
2517
*/
@@ -30,11 +22,6 @@ class BuildModificationService
3022
*/
3123
private $daemonServerRepository;
3224

33-
/**
34-
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
35-
*/
36-
private $repository;
37-
3825
/**
3926
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
4027
*/
@@ -43,23 +30,17 @@ class BuildModificationService
4330
/**
4431
* BuildModificationService constructor.
4532
*
46-
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository
4733
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $structureService
4834
* @param \Illuminate\Database\ConnectionInterface $connection
4935
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
50-
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
5136
*/
5237
public function __construct(
53-
AllocationRepositoryInterface $allocationRepository,
5438
ServerConfigurationStructureService $structureService,
5539
ConnectionInterface $connection,
56-
DaemonServerRepository $daemonServerRepository,
57-
ServerRepositoryInterface $repository
40+
DaemonServerRepository $daemonServerRepository
5841
) {
59-
$this->allocationRepository = $allocationRepository;
6042
$this->daemonServerRepository = $daemonServerRepository;
6143
$this->connection = $connection;
62-
$this->repository = $repository;
6344
$this->structureService = $structureService;
6445
}
6546

@@ -70,9 +51,8 @@ public function __construct(
7051
* @param array $data
7152
* @return \Pterodactyl\Models\Server
7253
*
54+
* @throws \Throwable
7355
* @throws \Pterodactyl\Exceptions\DisplayException
74-
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
75-
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
7656
*/
7757
public function handle(Server $server, array $data)
7858
{
@@ -82,48 +62,35 @@ public function handle(Server $server, array $data)
8262

8363
if (isset($data['allocation_id']) && $data['allocation_id'] != $server->allocation_id) {
8464
try {
85-
$this->allocationRepository->findFirstWhere([
86-
['id', '=', $data['allocation_id']],
87-
['server_id', '=', $server->id],
88-
]);
89-
} catch (RecordNotFoundException $ex) {
90-
throw new DisplayException(trans('admin/server.exceptions.default_allocation_not_found'));
65+
Allocation::query()->where('id', $data['allocation_id'])->where('server_id', $server->id)->firstOrFail();
66+
} catch (ModelNotFoundException $ex) {
67+
throw new DisplayException('The requested default allocation is not currently assigned to this server.');
9168
}
9269
}
9370

94-
/* @var \Pterodactyl\Models\Server $server */
95-
$server = $this->repository->withFreshModel()->update($server->id, [
96-
'oom_disabled' => array_get($data, 'oom_disabled'),
97-
'memory' => array_get($data, 'memory'),
98-
'swap' => array_get($data, 'swap'),
99-
'io' => array_get($data, 'io'),
100-
'cpu' => array_get($data, 'cpu'),
101-
'threads' => array_get($data, 'threads'),
102-
'disk' => array_get($data, 'disk'),
103-
'allocation_id' => array_get($data, 'allocation_id'),
104-
'database_limit' => array_get($data, 'database_limit', 0) ?? null,
105-
'allocation_limit' => array_get($data, 'allocation_limit', 0) ?? null,
106-
'backup_limit' => array_get($data, 'backup_limit', 0) ?? null,
107-
]);
71+
// If any of these values are passed through in the data array go ahead and set
72+
// them correctly on the server model.
73+
$merge = Arr::only($data, ['oom_disabled', 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', 'allocation_id']);
74+
75+
$server->forceFill(array_merge($merge, [
76+
'database_limit' => Arr::get($data, 'database_limit', 0) ?? null,
77+
'allocation_limit' => Arr::get($data, 'allocation_limit', 0) ?? null,
78+
'backup_limit' => Arr::get($data, 'backup_limit', 0) ?? 0,
79+
]))->saveOrFail();
80+
81+
$server = $server->fresh();
10882

10983
$updateData = $this->structureService->handle($server);
11084

111-
try {
112-
$this->daemonServerRepository
113-
->setServer($server)
114-
->update(Arr::only($updateData, ['build']));
85+
$this->daemonServerRepository->setServer($server)->update($updateData['build'] ?? []);
11586

116-
$this->connection->commit();
117-
} catch (RequestException $exception) {
118-
throw new DaemonConnectionException($exception);
119-
}
87+
$this->connection->commit();
12088

12189
return $server;
12290
}
12391

12492
/**
125-
* Process the allocations being assigned in the data and ensure they
126-
* are available for a server.
93+
* Process the allocations being assigned in the data and ensure they are available for a server.
12794
*
12895
* @param \Pterodactyl\Models\Server $server
12996
* @param array $data
@@ -132,55 +99,53 @@ public function handle(Server $server, array $data)
13299
*/
133100
private function processAllocations(Server $server, array &$data)
134101
{
135-
$firstAllocationId = null;
136-
137-
if (! array_key_exists('add_allocations', $data) && ! array_key_exists('remove_allocations', $data)) {
102+
if (empty($data['add_allocations']) && empty($data['remove_allocations'])) {
138103
return;
139104
}
140105

141-
// Handle the addition of allocations to this server.
142-
if (array_key_exists('add_allocations', $data) && ! empty($data['add_allocations'])) {
143-
$unassigned = $this->allocationRepository->getUnassignedAllocationIds($server->node_id);
106+
// Handle the addition of allocations to this server. Only assign allocations that are not currently
107+
// assigned to a different server, and only allocations on the same node as the server.
108+
if (! empty($data['add_allocations'])) {
109+
$query = Allocation::query()
110+
->where('node_id', $server->node_id)
111+
->whereIn('id', $data['add_allocations'])
112+
->whereNull('server_id');
144113

145-
$updateIds = [];
146-
foreach ($data['add_allocations'] as $allocation) {
147-
if (! in_array($allocation, $unassigned)) {
148-
continue;
149-
}
150-
151-
$firstAllocationId = $firstAllocationId ?? $allocation;
152-
$updateIds[] = $allocation;
153-
}
114+
// Keep track of all the allocations we're just now adding so that we can use the first
115+
// one to reset the default allocation to.
116+
$freshlyAllocated = $query->pluck('id')->first();
154117

155-
if (! empty($updateIds)) {
156-
$this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => $server->id]);
157-
}
118+
$query->update(['server_id' => $server->id]);
158119
}
159120

160-
// Handle removal of allocations from this server.
161-
if (array_key_exists('remove_allocations', $data) && ! empty($data['remove_allocations'])) {
162-
$assigned = $server->allocations->pluck('id')->toArray();
163-
164-
$updateIds = [];
121+
if (! empty($data['remove_allocations'])) {
165122
foreach ($data['remove_allocations'] as $allocation) {
166-
if (! in_array($allocation, $assigned)) {
167-
continue;
168-
}
169-
170-
if ($allocation == $data['allocation_id']) {
171-
if (is_null($firstAllocationId)) {
172-
throw new DisplayException(trans('admin/server.exceptions.no_new_default_allocation'));
123+
// If we are attempting to remove the default allocation for the server, see if we can reassign
124+
// to the first provided value in add_allocations. If there is no new first allocation then we
125+
// will throw an exception back.
126+
if ($allocation === ($data['allocation_id'] ?? $server->allocation_id)) {
127+
if (empty($freshlyAllocated)) {
128+
throw new DisplayException(
129+
'You are attempting to delete the default allocation for this server but there is no fallback allocation to use.'
130+
);
173131
}
174132

175-
$data['allocation_id'] = $firstAllocationId;
133+
// Update the default allocation to be the first allocation that we are creating.
134+
$data['allocation_id'] = $freshlyAllocated;
176135
}
177-
178-
$updateIds[] = $allocation;
179136
}
180137

181-
if (! empty($updateIds)) {
182-
$this->allocationRepository->updateWhereIn('id', $updateIds, ['server_id' => null]);
183-
}
138+
// Remove any of the allocations we got that are currently assigned to this server on
139+
// this node. Also set the notes to null, otherwise when re-allocated to a new server those
140+
// notes will be carried over.
141+
Allocation::query()->where('node_id', $server->node_id)
142+
->where('server_id', $server->id)
143+
// Only remove the allocations that we didn't also attempt to add to the server...
144+
->whereIn('id', array_diff($data['remove_allocations'], $data['add_allocations'] ?? []))
145+
->update([
146+
'notes' => null,
147+
'server_id' => null,
148+
]);
184149
}
185150
}
186151
}

0 commit comments

Comments
 (0)