Skip to content

Commit 454ce6c

Browse files
committed
Add successful column to server_transfers table, get server transfers working properly :)
1 parent 6ba6c34 commit 454ce6c

File tree

8 files changed

+202
-17
lines changed

8 files changed

+202
-17
lines changed

app/Http/Controllers/Admin/Servers/ServerTransferController.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class ServerTransferController extends Controller
5555
* ServerTransferController constructor.
5656
*
5757
* @param \Prologue\Alerts\AlertsMessageBag $alert
58-
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository,
58+
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository
5959
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
6060
* @param \Pterodactyl\Repositories\Eloquent\LocationRepository $locationRepository
6161
* @param \Pterodactyl\Repositories\Eloquent\NodeRepository $nodeRepository
@@ -104,9 +104,11 @@ public function transfer(Request $request, Server $server)
104104
// Check if the node is viable for the transfer.
105105
$node = $this->nodeRepository->getNodeWithResourceUsage($node_id);
106106
if ($node->isViable($server->memory, $server->disk)) {
107-
//$this->assignAllocationsToServer($server, $node_id, $allocation_id, $additional_allocations);
107+
// Suspend the server and request an archive to be created.
108+
$this->suspensionService->toggle($server, 'suspend');
108109

109-
/*$transfer = new ServerTransfer;
110+
// Create a new ServerTransfer entry.
111+
$transfer = new ServerTransfer;
110112

111113
$transfer->server_id = $server->id;
112114
$transfer->old_node = $server->node_id;
@@ -116,10 +118,12 @@ public function transfer(Request $request, Server $server)
116118
$transfer->old_additional_allocations = json_encode($server->allocations->where('id', '!=', $server->allocation_id)->pluck('id'));
117119
$transfer->new_additional_allocations = json_encode($additional_allocations);
118120

119-
$transfer->save();*/
121+
$transfer->save();
120122

121-
// Suspend the server and request an archive to be created.
122-
// $this->suspensionService->toggle($server, 'suspend');
123+
// Add the allocations to the server so they cannot be automatically assigned while the transfer is in progress.
124+
$this->assignAllocationsToServer($server, $node_id, $allocation_id, $additional_allocations);
125+
126+
// Request an archive from the server's current daemon.
123127
$this->transferService->requestArchive($server);
124128

125129
$this->alert->success(trans('admin/server.alerts.transfer_started'))->flash();
@@ -130,6 +134,14 @@ public function transfer(Request $request, Server $server)
130134
return redirect()->route('admin.servers.view.manage', $server->id);
131135
}
132136

137+
/**
138+
* Assigns the specified allocations to the specified server.
139+
*
140+
* @param Server $server
141+
* @param int $node_id
142+
* @param int $allocation_id
143+
* @param array $additional_allocations
144+
*/
133145
private function assignAllocationsToServer(Server $server, int $node_id, int $allocation_id, array $additional_allocations)
134146
{
135147
$allocations = $additional_allocations;

app/Http/Controllers/Api/Remote/Servers/ServerTransferController.php

Lines changed: 137 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,40 @@
33
namespace Pterodactyl\Http\Controllers\Api\Remote\Servers;
44

55
use Cake\Chronos\Chronos;
6+
use Illuminate\Database\ConnectionInterface;
67
use Illuminate\Http\JsonResponse;
78
use Illuminate\Http\Request;
89
use Illuminate\Http\Response;
910
use Illuminate\Support\Facades\Log;
1011
use Lcobucci\JWT\Builder;
1112
use Lcobucci\JWT\Signer\Hmac\Sha256;
1213
use Lcobucci\JWT\Signer\Key;
14+
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
15+
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
1316
use Pterodactyl\Http\Controllers\Controller;
14-
use Pterodactyl\Models\Server;
1517
use Pterodactyl\Repositories\Eloquent\ServerRepository;
1618
use Pterodactyl\Repositories\Eloquent\NodeRepository;
1719
use Pterodactyl\Repositories\Wings\DaemonTransferRepository;
20+
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
21+
use Pterodactyl\Services\Servers\SuspensionService;
1822

1923
class ServerTransferController extends Controller
2024
{
25+
/**
26+
* @var \Illuminate\Database\ConnectionInterface
27+
*/
28+
private $connection;
29+
2130
/**
2231
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
2332
*/
2433
private $repository;
2534

35+
/**
36+
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
37+
*/
38+
private $allocationRepository;
39+
2640
/**
2741
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
2842
*/
@@ -33,21 +47,43 @@ class ServerTransferController extends Controller
3347
*/
3448
private $daemonTransferRepository;
3549

50+
/**
51+
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
52+
*/
53+
private $configurationStructureService;
54+
55+
/**
56+
* @var \Pterodactyl\Services\Servers\SuspensionService
57+
*/
58+
private $suspensionService;
59+
3660
/**
3761
* ServerTransferController constructor.
3862
*
63+
* @param \Illuminate\Database\ConnectionInterface $connection
3964
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
65+
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository
4066
* @param \Pterodactyl\Repositories\Eloquent\NodeRepository $nodeRepository
41-
* @param DaemonTransferRepository $daemonTransferRepository
67+
* @param \Pterodactyl\Repositories\Wings\DaemonTransferRepository $daemonTransferRepository
68+
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService
69+
* @param \Pterodactyl\Services\Servers\SuspensionService $suspensionService
4270
*/
4371
public function __construct(
72+
ConnectionInterface $connection,
4473
ServerRepository $repository,
74+
AllocationRepositoryInterface $allocationRepository,
4575
NodeRepository $nodeRepository,
46-
DaemonTransferRepository $daemonTransferRepository
76+
DaemonTransferRepository $daemonTransferRepository,
77+
ServerConfigurationStructureService $configurationStructureService,
78+
SuspensionService $suspensionService
4779
) {
80+
$this->connection = $connection;
4881
$this->repository = $repository;
82+
$this->allocationRepository = $allocationRepository;
4983
$this->nodeRepository = $nodeRepository;
5084
$this->daemonTransferRepository = $daemonTransferRepository;
85+
$this->configurationStructureService = $configurationStructureService;
86+
$this->suspensionService = $suspensionService;
5187
}
5288

5389
/**
@@ -59,17 +95,28 @@ public function __construct(
5995
*
6096
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
6197
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
98+
* @throws \Throwable
6299
*/
63100
public function archive(Request $request, string $uuid)
64101
{
65102
$server = $this->repository->getByUuid($uuid);
66103

67104
// Unsuspend the server and don't continue the transfer.
68105
if (!$request->input('successful')) {
69-
// $this->suspensionService->toggle($server, 'unsuspend');
106+
$this->suspensionService->toggle($server, 'unsuspend');
70107
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
71108
}
72109

110+
$server->node_id = $server->transfer->new_node;
111+
112+
$data = $this->configurationStructureService->handle($server);
113+
$data['suspended'] = false;
114+
$data['service']['skip_scripts'] = true;
115+
116+
$allocations = $server->getAllocationMappings();
117+
$data['allocations']['default']['ip'] = array_key_first($allocations);
118+
$data['allocations']['default']['port'] = $allocations[$data['allocations']['default']['ip']][0];
119+
73120
$now = Chronos::now();
74121
$signer = new Sha256;
75122

@@ -85,10 +132,92 @@ public function archive(Request $request, string $uuid)
85132
// On the daemon transfer repository, make sure to set the node after the server
86133
// because setServer() tells the repository to use the server's node and not the one
87134
// we want to specify.
88-
$this->daemonTransferRepository
89-
->setServer($server)
90-
->setNode($this->nodeRepository->find($server->transfer->new_node))
91-
->notify($server, $server->node, $token->__toString());
135+
try {
136+
$this->daemonTransferRepository
137+
->setServer($server)
138+
->setNode($this->nodeRepository->find($server->transfer->new_node))
139+
->notify($server, $data, $server->node, $token->__toString());
140+
} catch (DaemonConnectionException $exception) {
141+
throw $exception;
142+
}
143+
144+
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
145+
}
146+
147+
/**
148+
* The daemon notifies us about a transfer failure.
149+
*
150+
* @param \Illuminate\Http\Request $request
151+
* @param string $uuid
152+
* @return \Illuminate\Http\JsonResponse
153+
*
154+
* @throws \Throwable
155+
*/
156+
public function failure(string $uuid)
157+
{
158+
$server = $this->repository->getByUuid($uuid);
159+
$transfer = $server->transfer;
160+
161+
$allocationIds = json_decode($transfer->new_additional_allocations);
162+
array_push($allocationIds, $transfer->new_allocation);
163+
164+
// Begin a transaction.
165+
$this->connection->beginTransaction();
166+
167+
// Remove the new allocations.
168+
$this->allocationRepository->updateWhereIn('id', $allocationIds, ['server_id' => null]);
169+
170+
// Commit the transaction.
171+
$this->connection->commit();
172+
173+
// Unsuspend the server.
174+
$this->suspensionService->toggle($server, 'unsuspend');
175+
176+
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
177+
}
178+
179+
/**
180+
* The daemon notifies us about a transfer success.
181+
*
182+
* @param string $uuid
183+
* @return \Illuminate\Http\JsonResponse
184+
*
185+
* @throws \Throwable
186+
*/
187+
public function success(string $uuid)
188+
{
189+
$server = $this->repository->getByUuid($uuid);
190+
$transfer = $server->transfer;
191+
192+
// TODO: Notify old daemon about transfer and get it to remove server files.
193+
194+
$allocationIds = json_decode($transfer->old_additional_allocations);
195+
array_push($allocationIds, $transfer->old_allocation);
196+
197+
// Begin a transaction.
198+
$this->connection->beginTransaction();
199+
200+
// Remove the old allocations.
201+
$this->allocationRepository->updateWhereIn('id', $allocationIds, ['server_id' => null]);
202+
203+
// Update the server's allocation_id and node_id.
204+
$server->allocation_id = $transfer->new_allocation;
205+
$server->node_id = $transfer->new_node;
206+
$server->save();
207+
208+
// Mark the transfer as successful.
209+
$transfer->successful = true;
210+
$transfer->save();
211+
212+
// Commit the transaction.
213+
$this->connection->commit();
214+
215+
// Unsuspend the server
216+
$server->load('node');
217+
Log::debug(json_encode($server));
218+
Log::debug(json_encode($server->node_id));
219+
Log::debug(json_encode($server->node));
220+
$this->suspensionService->toggle($server, $this->suspensionService::ACTION_UNSUSPEND);
92221

93222
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
94223
}

app/Models/Server.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public function getTableColumns()
184184
*/
185185
public function getAllocationMappings(): array
186186
{
187-
return $this->allocations->groupBy('ip')->map(function ($item) {
187+
return $this->allocations->where('node_id', $this->node_id)->groupBy('ip')->map(function ($item) {
188188
return $item->pluck('port');
189189
})->toArray();
190190
}

app/Models/ServerTransfer.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* @property int $new_allocation
1212
* @property string $old_additional_allocations
1313
* @property string $new_additional_allocations
14+
* @property bool $successful
1415
* @property \Carbon\Carbon $created_at
1516
* @property \Carbon\Carbon $updated_at
1617
*
@@ -51,6 +52,7 @@ class ServerTransfer extends Validable
5152
'new_allocation' => 'int',
5253
'old_additional_allocations' => 'string',
5354
'new_additional_allocations' => 'string',
55+
'successful' => 'bool',
5456
];
5557

5658
/**
@@ -64,10 +66,11 @@ class ServerTransfer extends Validable
6466
'new_allocation' => 'required|numeric',
6567
'old_additional_allocations' => 'nullable',
6668
'new_additional_allocations' => 'nullable',
69+
'successful' => 'sometimes|boolean',
6770
];
6871

6972
/**
70-
* Gets the server associated with a subuser.
73+
* Gets the server associated with a server transfer.
7174
*
7275
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
7376
*/

app/Repositories/Wings/DaemonTransferRepository.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,20 @@ class DaemonTransferRepository extends DaemonRepository
1111
{
1212
/**
1313
* @param Server $server
14+
* @param array $data
1415
* @param Node $node
1516
* @param string $token
1617
*
1718
* @throws DaemonConnectionException
1819
*/
19-
public function notify(Server $server, Node $node, string $token): void {
20+
public function notify(Server $server, array $data, Node $node, string $token): void {
2021
try {
2122
$this->getHttpClient()->post('/api/transfer', [
2223
'json' => [
2324
'server_id' => $server->uuid,
2425
'url' => $node->getConnectionAddress() . sprintf('/api/servers/%s/archive', $server->uuid),
2526
'token' => 'Bearer ' . $token,
27+
'server' => $data,
2628
],
2729
]);
2830
} catch(TransferException $exception) {

app/Services/Servers/SuspensionService.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Pterodactyl\Services\Servers;
44

5+
use Illuminate\Support\Facades\Log;
56
use Psr\Log\LoggerInterface;
67
use Webmozart\Assert\Assert;
78
use Pterodactyl\Models\Server;
@@ -73,10 +74,14 @@ public function toggle(Server $server, $action = self::ACTION_SUSPEND)
7374
return;
7475
}
7576

77+
Log::debug('SuspensionService: ' . $action);
78+
7679
$this->connection->transaction(function () use ($action, $server) {
7780
$this->repository->withoutFreshModel()->update($server->id, [
7881
'suspended' => $action === self::ACTION_SUSPEND,
7982
]);
83+
Log::debug('Server suspended: ' . ($action === self::ACTION_SUSPEND) ? 'true' : 'false');
84+
Log::debug('Daemon unsuspended: ' . ($action === self::ACTION_UNSUSPEND) ? 'true' : 'false');
8085

8186
$this->daemonServerRepository->setServer($server)->suspend($action === self::ACTION_UNSUSPEND);
8287
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class AddSuccessfulColumnToServerTransfers extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::table('server_transfers', function (Blueprint $table) {
17+
$table->tinyInteger('successful')->unsigned()->default(0);
18+
});
19+
}
20+
21+
/**
22+
* Reverse the migrations.
23+
*
24+
* @return void
25+
*/
26+
public function down()
27+
{
28+
Schema::table('server_transfers', function (Blueprint $table) {
29+
$table->dropColumn('successful');
30+
});
31+
}
32+
}

routes/api-remote.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
Route::get('/install', 'Servers\ServerInstallController@index');
1313
Route::post('/install', 'Servers\ServerInstallController@store');
1414
Route::post('/archive', 'Servers\ServerTransferController@archive');
15+
Route::get('/transfer/failure', 'Servers\ServerTransferController@failure');
16+
Route::get('/transfer/success', 'Servers\ServerTransferController@success');
1517
});
1618

1719

0 commit comments

Comments
 (0)