Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/app/Services/Servers/BuildModificationService.php
10276 views
1
<?php
2
3
namespace Pterodactyl\Services\Servers;
4
5
use Illuminate\Support\Arr;
6
use Pterodactyl\Models\Server;
7
use Pterodactyl\Models\Allocation;
8
use Illuminate\Support\Facades\Log;
9
use Illuminate\Database\ConnectionInterface;
10
use Pterodactyl\Exceptions\DisplayException;
11
use Illuminate\Database\Eloquent\ModelNotFoundException;
12
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
13
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
14
15
class BuildModificationService
16
{
17
/**
18
* BuildModificationService constructor.
19
*/
20
public function __construct(
21
private ConnectionInterface $connection,
22
private DaemonServerRepository $daemonServerRepository,
23
private ServerConfigurationStructureService $structureService,
24
) {
25
}
26
27
/**
28
* Change the build details for a specified server.
29
*
30
* @throws \Throwable
31
* @throws DisplayException
32
*/
33
public function handle(Server $server, array $data): Server
34
{
35
/** @var Server $server */
36
$server = $this->connection->transaction(function () use ($server, $data) {
37
$this->processAllocations($server, $data);
38
39
if (isset($data['allocation_id']) && $data['allocation_id'] != $server->allocation_id) {
40
try {
41
Allocation::query()->where('id', $data['allocation_id'])->where('server_id', $server->id)->firstOrFail();
42
} catch (ModelNotFoundException) {
43
throw new DisplayException('The requested default allocation is not currently assigned to this server.');
44
}
45
}
46
47
// If any of these values are passed through in the data array go ahead and set
48
// them correctly on the server model.
49
$merge = Arr::only($data, ['oom_disabled', 'memory', 'swap', 'io', 'cpu', 'threads', 'disk', 'allocation_id']);
50
51
$server->forceFill(array_merge($merge, [
52
'database_limit' => Arr::get($data, 'database_limit', 0) ?? null,
53
'allocation_limit' => Arr::get($data, 'allocation_limit', 0) ?? null,
54
'backup_limit' => Arr::get($data, 'backup_limit', 0) ?? 0,
55
]))->saveOrFail();
56
57
return $server->refresh();
58
});
59
60
$updateData = $this->structureService->handle($server);
61
62
// Because Wings always fetches an updated configuration from the Panel when booting
63
// a server this type of exception can be safely "ignored" and just written to the logs.
64
// Ideally this request succeeds, so we can apply resource modifications on the fly, but
65
// if it fails we can just continue on as normal.
66
if (!empty($updateData['build'])) {
67
try {
68
$this->daemonServerRepository->setServer($server)->sync();
69
} catch (DaemonConnectionException $exception) {
70
Log::warning($exception, ['server_id' => $server->id]);
71
}
72
}
73
74
return $server;
75
}
76
77
/**
78
* Process the allocations being assigned in the data and ensure they are available for a server.
79
*
80
* @throws DisplayException
81
*/
82
private function processAllocations(Server $server, array &$data): void
83
{
84
if (empty($data['add_allocations']) && empty($data['remove_allocations'])) {
85
return;
86
}
87
88
// Handle the addition of allocations to this server. Only assign allocations that are not currently
89
// assigned to a different server, and only allocations on the same node as the server.
90
if (!empty($data['add_allocations'])) {
91
$query = Allocation::query()
92
->where('node_id', $server->node_id)
93
->whereIn('id', $data['add_allocations'])
94
->whereNull('server_id');
95
96
// Keep track of all the allocations we're just now adding so that we can use the first
97
// one to reset the default allocation to.
98
$freshlyAllocated = $query->pluck('id')->first(); // @phpstan-ignore larastan.noUnnecessaryCollectionCall
99
100
$query->update(['server_id' => $server->id, 'notes' => null]);
101
}
102
103
if (!empty($data['remove_allocations'])) {
104
foreach ($data['remove_allocations'] as $allocation) {
105
// If we are attempting to remove the default allocation for the server, see if we can reassign
106
// to the first provided value in add_allocations. If there is no new first allocation then we
107
// will throw an exception back.
108
if ($allocation === ($data['allocation_id'] ?? $server->allocation_id)) {
109
if (empty($freshlyAllocated)) {
110
throw new DisplayException('You are attempting to delete the default allocation for this server but there is no fallback allocation to use.');
111
}
112
113
// Update the default allocation to be the first allocation that we are creating.
114
$data['allocation_id'] = $freshlyAllocated;
115
}
116
}
117
118
// Remove any of the allocations we got that are currently assigned to this server on
119
// this node. Also set the notes to null, otherwise when re-allocated to a new server those
120
// notes will be carried over.
121
Allocation::query()->where('node_id', $server->node_id)
122
->where('server_id', $server->id)
123
// Only remove the allocations that we didn't also attempt to add to the server...
124
->whereIn('id', array_diff($data['remove_allocations'], $data['add_allocations'] ?? []))
125
->update([
126
'notes' => null,
127
'server_id' => null,
128
]);
129
}
130
}
131
}
132
133