Streaming Transfers (#4548)
This commit is contained in:
parent
032e4f2e31
commit
df2402b54f
6 changed files with 38 additions and 106 deletions
|
@ -2,15 +2,17 @@
|
|||
|
||||
namespace Pterodactyl\Http\Controllers\Admin\Servers;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Models\ServerTransfer;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\Servers\TransferService;
|
||||
use Pterodactyl\Services\Nodes\NodeJWTService;
|
||||
use Pterodactyl\Repositories\Eloquent\NodeRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonTransferRepository;
|
||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||
|
||||
class ServerTransferController extends Controller
|
||||
|
@ -21,9 +23,10 @@ class ServerTransferController extends Controller
|
|||
public function __construct(
|
||||
private AlertsMessageBag $alert,
|
||||
private AllocationRepositoryInterface $allocationRepository,
|
||||
private NodeRepository $nodeRepository,
|
||||
private TransferService $transferService,
|
||||
private DaemonConfigurationRepository $daemonConfigurationRepository
|
||||
private ConnectionInterface $connection,
|
||||
private DaemonTransferRepository $daemonTransferRepository,
|
||||
private NodeJWTService $nodeJWTService,
|
||||
private NodeRepository $nodeRepository
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -46,12 +49,15 @@ 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)) {
|
||||
// Check if the selected daemon is online.
|
||||
$this->daemonConfigurationRepository->setNode($node)->getSystemInformation();
|
||||
if (!$node->isViable($server->memory, $server->disk)) {
|
||||
$this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash();
|
||||
|
||||
return redirect()->route('admin.servers.view.manage', $server->id);
|
||||
}
|
||||
|
||||
$server->validateTransferState();
|
||||
|
||||
$this->connection->transaction(function () use ($server, $node_id, $allocation_id, $additional_allocations) {
|
||||
// Create a new ServerTransfer entry.
|
||||
$transfer = new ServerTransfer();
|
||||
|
||||
|
@ -68,13 +74,19 @@ class ServerTransferController extends Controller
|
|||
// 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 also checks if the daemon is online)
|
||||
$this->transferService->requestArchive($server);
|
||||
// Generate a token for the destination node that the source node can use to authenticate with.
|
||||
$token = $this->nodeJWTService
|
||||
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
||||
->setSubject($server->uuid)
|
||||
->handle($transfer->newNode, $server->uuid, 'sha256');
|
||||
|
||||
// Notify the source node of the pending outgoing transfer.
|
||||
$this->daemonTransferRepository->setServer($server)->notify($transfer->newNode, $token);
|
||||
|
||||
return $transfer;
|
||||
});
|
||||
|
||||
$this->alert->success(trans('admin/server.alerts.transfer_started'))->flash();
|
||||
} else {
|
||||
$this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash();
|
||||
}
|
||||
|
||||
return redirect()->route('admin.servers.view.manage', $server->id);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Remote\Servers;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
|
@ -11,10 +9,8 @@ use Illuminate\Support\Facades\Log;
|
|||
use Pterodactyl\Models\ServerTransfer;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\Nodes\NodeJWTService;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonTransferRepository;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
|
||||
class ServerTransferController extends Controller
|
||||
|
@ -25,52 +21,10 @@ class ServerTransferController extends Controller
|
|||
public function __construct(
|
||||
private ConnectionInterface $connection,
|
||||
private ServerRepository $repository,
|
||||
private DaemonServerRepository $daemonServerRepository,
|
||||
private DaemonTransferRepository $daemonTransferRepository,
|
||||
private NodeJWTService $jwtService
|
||||
private DaemonServerRepository $daemonServerRepository
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The daemon notifies us about the archive status.
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function archive(Request $request, string $uuid): JsonResponse
|
||||
{
|
||||
$server = $this->repository->getByUuid($uuid);
|
||||
|
||||
// Unsuspend the server and don't continue the transfer.
|
||||
if (!$request->input('successful')) {
|
||||
return $this->processFailedTransfer($server->transfer);
|
||||
}
|
||||
|
||||
$this->connection->transaction(function () use ($server) {
|
||||
// This token is used by the new node the server is being transferred to. It allows
|
||||
// that node to communicate with the old node during the process to initiate the
|
||||
// actual file transfer.
|
||||
$token = $this->jwtService
|
||||
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
|
||||
->setSubject($server->uuid)
|
||||
->handle($server->node, $server->uuid, 'sha256');
|
||||
|
||||
// Update the archived field on the transfer to make clients connect to the websocket
|
||||
// on the new node to be able to receive transfer logs.
|
||||
$server->transfer->forceFill(['archived' => true])->saveOrFail();
|
||||
|
||||
// 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($server->transfer->newNode)
|
||||
->notify($server, $token);
|
||||
});
|
||||
|
||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* The daemon notifies us about a transfer failure.
|
||||
*
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Pterodactyl\Repositories\Wings;
|
||||
|
||||
use Pterodactyl\Models\Node;
|
||||
use Lcobucci\JWT\Token\Plain;
|
||||
use Pterodactyl\Models\Server;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
|
||||
|
@ -12,16 +12,16 @@ class DaemonTransferRepository extends DaemonRepository
|
|||
/**
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
*/
|
||||
public function notify(Server $server, Plain $token): void
|
||||
public function notify(Node $targetNode, Plain $token): void
|
||||
{
|
||||
try {
|
||||
$this->getHttpClient()->post('/api/transfer', [
|
||||
$this->getHttpClient()->post(sprintf('/api/servers/%s/transfer', $this->server->uuid), [
|
||||
'json' => [
|
||||
'server_id' => $server->uuid,
|
||||
'url' => $server->node->getConnectionAddress() . sprintf('/api/servers/%s/archive', $server->uuid),
|
||||
'server_id' => $this->server->uuid,
|
||||
'url' => $targetNode->getConnectionAddress() . '/api/transfers',
|
||||
'token' => 'Bearer ' . $token->toString(),
|
||||
'server' => [
|
||||
'uuid' => $server->uuid,
|
||||
'uuid' => $this->server->uuid,
|
||||
'start_on_completion' => false,
|
||||
],
|
||||
],
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Services\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
|
||||
class TransferService
|
||||
{
|
||||
/**
|
||||
* TransferService constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
private DaemonServerRepository $daemonServerRepository
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests an archive from the daemon.
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
*/
|
||||
public function requestArchive(Server $server): void
|
||||
{
|
||||
$this->daemonServerRepository->setServer($server)->requestArchive();
|
||||
}
|
||||
}
|
|
@ -7,19 +7,19 @@ const TransferListener = () => {
|
|||
const getServer = ServerContext.useStoreActions((actions) => actions.server.getServer);
|
||||
const setServerFromState = ServerContext.useStoreActions((actions) => actions.server.setServerFromState);
|
||||
|
||||
// Listen for the transfer status event so we can update the state of the server.
|
||||
// Listen for the transfer status event, so we can update the state of the server.
|
||||
useWebsocketEvent(SocketEvent.TRANSFER_STATUS, (status: string) => {
|
||||
if (status === 'starting') {
|
||||
if (status === 'pending' || status === 'processing') {
|
||||
setServerFromState((s) => ({ ...s, isTransferring: true }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (status === 'failure') {
|
||||
if (status === 'failed') {
|
||||
setServerFromState((s) => ({ ...s, isTransferring: false }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (status !== 'success') {
|
||||
if (status !== 'completed') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,13 +76,6 @@ export default () => {
|
|||
case 'failure':
|
||||
terminal.writeln(TERMINAL_PRELUDE + 'Transfer has failed.\u001b[0m');
|
||||
return;
|
||||
|
||||
// Sent by the source node whenever the server was archived successfully.
|
||||
case 'archive':
|
||||
terminal.writeln(
|
||||
TERMINAL_PRELUDE +
|
||||
'Server has been archived successfully, attempting connection to target node..\u001b[0m'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue