Add successful column to server_transfers table, get server transfers working properly :)

This commit is contained in:
Matthew Penner 2020-04-04 16:16:18 -06:00
parent 6ba6c34252
commit 454ce6ce45
8 changed files with 202 additions and 17 deletions

View file

@ -55,7 +55,7 @@ class ServerTransferController extends Controller
* ServerTransferController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository,
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
* @param \Pterodactyl\Repositories\Eloquent\LocationRepository $locationRepository
* @param \Pterodactyl\Repositories\Eloquent\NodeRepository $nodeRepository
@ -104,9 +104,11 @@ class ServerTransferController extends Controller
// Check if the node is viable for the transfer.
$node = $this->nodeRepository->getNodeWithResourceUsage($node_id);
if ($node->isViable($server->memory, $server->disk)) {
//$this->assignAllocationsToServer($server, $node_id, $allocation_id, $additional_allocations);
// Suspend the server and request an archive to be created.
$this->suspensionService->toggle($server, 'suspend');
/*$transfer = new ServerTransfer;
// Create a new ServerTransfer entry.
$transfer = new ServerTransfer;
$transfer->server_id = $server->id;
$transfer->old_node = $server->node_id;
@ -116,10 +118,12 @@ class ServerTransferController extends Controller
$transfer->old_additional_allocations = json_encode($server->allocations->where('id', '!=', $server->allocation_id)->pluck('id'));
$transfer->new_additional_allocations = json_encode($additional_allocations);
$transfer->save();*/
$transfer->save();
// Suspend the server and request an archive to be created.
// $this->suspensionService->toggle($server, 'suspend');
// Add the allocations to the server so they cannot be automatically assigned while the transfer is in progress.
$this->assignAllocationsToServer($server, $node_id, $allocation_id, $additional_allocations);
// Request an archive from the server's current daemon.
$this->transferService->requestArchive($server);
$this->alert->success(trans('admin/server.alerts.transfer_started'))->flash();
@ -130,6 +134,14 @@ class ServerTransferController extends Controller
return redirect()->route('admin.servers.view.manage', $server->id);
}
/**
* Assigns the specified allocations to the specified server.
*
* @param Server $server
* @param int $node_id
* @param int $allocation_id
* @param array $additional_allocations
*/
private function assignAllocationsToServer(Server $server, int $node_id, int $allocation_id, array $additional_allocations)
{
$allocations = $additional_allocations;

View file

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Controllers\Api\Remote\Servers;
use Cake\Chronos\Chronos;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
@ -10,19 +11,32 @@ use Illuminate\Support\Facades\Log;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Models\Server;
use Pterodactyl\Repositories\Eloquent\ServerRepository;
use Pterodactyl\Repositories\Eloquent\NodeRepository;
use Pterodactyl\Repositories\Wings\DaemonTransferRepository;
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
use Pterodactyl\Services\Servers\SuspensionService;
class ServerTransferController extends Controller
{
/**
* @var \Illuminate\Database\ConnectionInterface
*/
private $connection;
/**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
*/
private $repository;
/**
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
*/
private $allocationRepository;
/**
* @var \Pterodactyl\Repositories\Eloquent\NodeRepository
*/
@ -33,21 +47,43 @@ class ServerTransferController extends Controller
*/
private $daemonTransferRepository;
/**
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
*/
private $configurationStructureService;
/**
* @var \Pterodactyl\Services\Servers\SuspensionService
*/
private $suspensionService;
/**
* ServerTransferController constructor.
*
* @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $allocationRepository
* @param \Pterodactyl\Repositories\Eloquent\NodeRepository $nodeRepository
* @param DaemonTransferRepository $daemonTransferRepository
* @param \Pterodactyl\Repositories\Wings\DaemonTransferRepository $daemonTransferRepository
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService
* @param \Pterodactyl\Services\Servers\SuspensionService $suspensionService
*/
public function __construct(
ConnectionInterface $connection,
ServerRepository $repository,
AllocationRepositoryInterface $allocationRepository,
NodeRepository $nodeRepository,
DaemonTransferRepository $daemonTransferRepository
DaemonTransferRepository $daemonTransferRepository,
ServerConfigurationStructureService $configurationStructureService,
SuspensionService $suspensionService
) {
$this->connection = $connection;
$this->repository = $repository;
$this->allocationRepository = $allocationRepository;
$this->nodeRepository = $nodeRepository;
$this->daemonTransferRepository = $daemonTransferRepository;
$this->configurationStructureService = $configurationStructureService;
$this->suspensionService = $suspensionService;
}
/**
@ -59,6 +95,7 @@ class ServerTransferController extends Controller
*
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable
*/
public function archive(Request $request, string $uuid)
{
@ -66,10 +103,20 @@ class ServerTransferController extends Controller
// Unsuspend the server and don't continue the transfer.
if (!$request->input('successful')) {
// $this->suspensionService->toggle($server, 'unsuspend');
$this->suspensionService->toggle($server, 'unsuspend');
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
}
$server->node_id = $server->transfer->new_node;
$data = $this->configurationStructureService->handle($server);
$data['suspended'] = false;
$data['service']['skip_scripts'] = true;
$allocations = $server->getAllocationMappings();
$data['allocations']['default']['ip'] = array_key_first($allocations);
$data['allocations']['default']['port'] = $allocations[$data['allocations']['default']['ip']][0];
$now = Chronos::now();
$signer = new Sha256;
@ -85,10 +132,92 @@ class ServerTransferController extends Controller
// On the daemon transfer repository, make sure to set the node after the server
// because setServer() tells the repository to use the server's node and not the one
// we want to specify.
$this->daemonTransferRepository
->setServer($server)
->setNode($this->nodeRepository->find($server->transfer->new_node))
->notify($server, $server->node, $token->__toString());
try {
$this->daemonTransferRepository
->setServer($server)
->setNode($this->nodeRepository->find($server->transfer->new_node))
->notify($server, $data, $server->node, $token->__toString());
} catch (DaemonConnectionException $exception) {
throw $exception;
}
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
}
/**
* The daemon notifies us about a transfer failure.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable
*/
public function failure(string $uuid)
{
$server = $this->repository->getByUuid($uuid);
$transfer = $server->transfer;
$allocationIds = json_decode($transfer->new_additional_allocations);
array_push($allocationIds, $transfer->new_allocation);
// Begin a transaction.
$this->connection->beginTransaction();
// Remove the new allocations.
$this->allocationRepository->updateWhereIn('id', $allocationIds, ['server_id' => null]);
// Commit the transaction.
$this->connection->commit();
// Unsuspend the server.
$this->suspensionService->toggle($server, 'unsuspend');
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
}
/**
* The daemon notifies us about a transfer success.
*
* @param string $uuid
* @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable
*/
public function success(string $uuid)
{
$server = $this->repository->getByUuid($uuid);
$transfer = $server->transfer;
// TODO: Notify old daemon about transfer and get it to remove server files.
$allocationIds = json_decode($transfer->old_additional_allocations);
array_push($allocationIds, $transfer->old_allocation);
// Begin a transaction.
$this->connection->beginTransaction();
// Remove the old allocations.
$this->allocationRepository->updateWhereIn('id', $allocationIds, ['server_id' => null]);
// Update the server's allocation_id and node_id.
$server->allocation_id = $transfer->new_allocation;
$server->node_id = $transfer->new_node;
$server->save();
// Mark the transfer as successful.
$transfer->successful = true;
$transfer->save();
// Commit the transaction.
$this->connection->commit();
// Unsuspend the server
$server->load('node');
Log::debug(json_encode($server));
Log::debug(json_encode($server->node_id));
Log::debug(json_encode($server->node));
$this->suspensionService->toggle($server, $this->suspensionService::ACTION_UNSUSPEND);
return JsonResponse::create([], Response::HTTP_NO_CONTENT);
}

View file

@ -184,7 +184,7 @@ class Server extends Validable
*/
public function getAllocationMappings(): array
{
return $this->allocations->groupBy('ip')->map(function ($item) {
return $this->allocations->where('node_id', $this->node_id)->groupBy('ip')->map(function ($item) {
return $item->pluck('port');
})->toArray();
}

View file

@ -11,6 +11,7 @@ namespace Pterodactyl\Models;
* @property int $new_allocation
* @property string $old_additional_allocations
* @property string $new_additional_allocations
* @property bool $successful
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
*
@ -51,6 +52,7 @@ class ServerTransfer extends Validable
'new_allocation' => 'int',
'old_additional_allocations' => 'string',
'new_additional_allocations' => 'string',
'successful' => 'bool',
];
/**
@ -64,10 +66,11 @@ class ServerTransfer extends Validable
'new_allocation' => 'required|numeric',
'old_additional_allocations' => 'nullable',
'new_additional_allocations' => 'nullable',
'successful' => 'sometimes|boolean',
];
/**
* Gets the server associated with a subuser.
* Gets the server associated with a server transfer.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View file

@ -11,18 +11,20 @@ class DaemonTransferRepository extends DaemonRepository
{
/**
* @param Server $server
* @param array $data
* @param Node $node
* @param string $token
*
* @throws DaemonConnectionException
*/
public function notify(Server $server, Node $node, string $token): void {
public function notify(Server $server, array $data, Node $node, string $token): void {
try {
$this->getHttpClient()->post('/api/transfer', [
'json' => [
'server_id' => $server->uuid,
'url' => $node->getConnectionAddress() . sprintf('/api/servers/%s/archive', $server->uuid),
'token' => 'Bearer ' . $token,
'server' => $data,
],
]);
} catch(TransferException $exception) {

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Services\Servers;
use Illuminate\Support\Facades\Log;
use Psr\Log\LoggerInterface;
use Webmozart\Assert\Assert;
use Pterodactyl\Models\Server;
@ -73,10 +74,14 @@ class SuspensionService
return;
}
Log::debug('SuspensionService: ' . $action);
$this->connection->transaction(function () use ($action, $server) {
$this->repository->withoutFreshModel()->update($server->id, [
'suspended' => $action === self::ACTION_SUSPEND,
]);
Log::debug('Server suspended: ' . ($action === self::ACTION_SUSPEND) ? 'true' : 'false');
Log::debug('Daemon unsuspended: ' . ($action === self::ACTION_UNSUSPEND) ? 'true' : 'false');
$this->daemonServerRepository->setServer($server)->suspend($action === self::ACTION_UNSUSPEND);
});

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddSuccessfulColumnToServerTransfers extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('server_transfers', function (Blueprint $table) {
$table->tinyInteger('successful')->unsigned()->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('server_transfers', function (Blueprint $table) {
$table->dropColumn('successful');
});
}
}

View file

@ -12,6 +12,8 @@ Route::group(['prefix' => '/servers/{uuid}'], function () {
Route::get('/install', 'Servers\ServerInstallController@index');
Route::post('/install', 'Servers\ServerInstallController@store');
Route::post('/archive', 'Servers\ServerTransferController@archive');
Route::get('/transfer/failure', 'Servers\ServerTransferController@failure');
Route::get('/transfer/success', 'Servers\ServerTransferController@success');
});