Remove node repository
This commit is contained in:
parent
f988cc8cfa
commit
50fa260a38
@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Contracts\Repository;
|
|
||||||
|
|
||||||
use App\Models\Node;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
interface NodeRepositoryInterface extends RepositoryInterface
|
|
||||||
{
|
|
||||||
public const THRESHOLD_PERCENTAGE_LOW = 75;
|
|
||||||
public const THRESHOLD_PERCENTAGE_MEDIUM = 90;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
public function getUsageStats(Node $node): array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
public function getUsageStatsRaw(Node $node): array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a single node with location and server information.
|
|
||||||
*/
|
|
||||||
public function loadServerCount(Node $node, bool $refresh = false): Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach a paginated set of allocations to a node mode including
|
|
||||||
* any servers that are also attached to those allocations.
|
|
||||||
*/
|
|
||||||
public function loadNodeAllocations(Node $node, bool $refresh = false): Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a collection of nodes for all locations to use in server creation UI.
|
|
||||||
*/
|
|
||||||
public function getNodesForServerCreation(): Collection;
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ use Illuminate\Support\Collection;
|
|||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
use App\Traits\Controllers\JavascriptInjection;
|
use App\Traits\Controllers\JavascriptInjection;
|
||||||
use App\Services\Helpers\SoftwareVersionService;
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
|
|
||||||
@ -17,11 +16,13 @@ class NodeViewController extends Controller
|
|||||||
{
|
{
|
||||||
use JavascriptInjection;
|
use JavascriptInjection;
|
||||||
|
|
||||||
|
public const THRESHOLD_PERCENTAGE_LOW = 75;
|
||||||
|
public const THRESHOLD_PERCENTAGE_MEDIUM = 90;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NodeViewController constructor.
|
* NodeViewController constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private NodeRepository $repository,
|
|
||||||
private SoftwareVersionService $versionService,
|
private SoftwareVersionService $versionService,
|
||||||
private ViewFactory $view
|
private ViewFactory $view
|
||||||
) {
|
) {
|
||||||
@ -32,11 +33,37 @@ class NodeViewController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request, Node $node): View
|
public function index(Request $request, Node $node): View
|
||||||
{
|
{
|
||||||
$node = $this->repository->loadServerCount($node);
|
$node->load('location')->loadCount('servers');
|
||||||
|
|
||||||
|
$stats = Node::query()
|
||||||
|
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
||||||
|
->join('servers', 'servers.node_id', '=', 'nodes.id')
|
||||||
|
->where('node_id', '=', $node->id)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$usageStats = Collection::make(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])
|
||||||
|
->mapWithKeys(function ($value, $key) use ($node) {
|
||||||
|
$maxUsage = $node->{$key};
|
||||||
|
if ($node->{$key . '_overallocate'} > 0) {
|
||||||
|
$maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
$percent = ($value / $maxUsage) * 100;
|
||||||
|
|
||||||
|
return [
|
||||||
|
$key => [
|
||||||
|
'value' => number_format($value),
|
||||||
|
'max' => number_format($maxUsage),
|
||||||
|
'percent' => $percent,
|
||||||
|
'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
|
||||||
return $this->view->make('admin.nodes.view.index', [
|
return $this->view->make('admin.nodes.view.index', [
|
||||||
'node' => $node,
|
'node' => $node,
|
||||||
'stats' => $this->repository->getUsageStats($node),
|
'stats' => $stats,
|
||||||
'version' => $this->versionService,
|
'version' => $this->versionService,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -64,7 +91,15 @@ class NodeViewController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function allocations(Request $request, Node $node): View
|
public function allocations(Request $request, Node $node): View
|
||||||
{
|
{
|
||||||
$node = $this->repository->loadNodeAllocations($node);
|
$node->setRelation(
|
||||||
|
'allocations',
|
||||||
|
$node->allocations()
|
||||||
|
->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL')
|
||||||
|
->orderByRaw('INET_ATON(ip) ASC')
|
||||||
|
->orderBy('port')
|
||||||
|
->with('server:id,name')
|
||||||
|
->paginate(50)
|
||||||
|
);
|
||||||
|
|
||||||
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
|
$this->plainInject(['node' => Collection::wrap($node)->only(['id'])]);
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ use App\Services\Nodes\NodeDeletionService;
|
|||||||
use App\Services\Allocations\AssignmentService;
|
use App\Services\Allocations\AssignmentService;
|
||||||
use App\Services\Helpers\SoftwareVersionService;
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
use App\Http\Requests\Admin\Node\NodeFormRequest;
|
use App\Http\Requests\Admin\Node\NodeFormRequest;
|
||||||
use App\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use App\Http\Requests\Admin\Node\AllocationFormRequest;
|
use App\Http\Requests\Admin\Node\AllocationFormRequest;
|
||||||
use App\Services\Allocations\AllocationDeletionService;
|
use App\Services\Allocations\AllocationDeletionService;
|
||||||
use App\Http\Requests\Admin\Node\AllocationAliasFormRequest;
|
use App\Http\Requests\Admin\Node\AllocationAliasFormRequest;
|
||||||
@ -35,7 +34,6 @@ class NodesController extends Controller
|
|||||||
protected CacheRepository $cache,
|
protected CacheRepository $cache,
|
||||||
protected NodeCreationService $creationService,
|
protected NodeCreationService $creationService,
|
||||||
protected NodeDeletionService $deletionService,
|
protected NodeDeletionService $deletionService,
|
||||||
protected NodeRepositoryInterface $repository,
|
|
||||||
protected NodeUpdateService $updateService,
|
protected NodeUpdateService $updateService,
|
||||||
protected SoftwareVersionService $versionService,
|
protected SoftwareVersionService $versionService,
|
||||||
protected ViewFactory $view
|
protected ViewFactory $view
|
||||||
|
@ -9,7 +9,6 @@ use Illuminate\Http\RedirectResponse;
|
|||||||
use Prologue\Alerts\AlertsMessageBag;
|
use Prologue\Alerts\AlertsMessageBag;
|
||||||
use Illuminate\View\Factory as ViewFactory;
|
use Illuminate\View\Factory as ViewFactory;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
use App\Http\Requests\Admin\ServerFormRequest;
|
use App\Http\Requests\Admin\ServerFormRequest;
|
||||||
use App\Services\Servers\ServerCreationService;
|
use App\Services\Servers\ServerCreationService;
|
||||||
|
|
||||||
@ -20,7 +19,6 @@ class CreateServerController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AlertsMessageBag $alert,
|
private AlertsMessageBag $alert,
|
||||||
private NodeRepository $nodeRepository,
|
|
||||||
private ServerCreationService $creationService,
|
private ServerCreationService $creationService,
|
||||||
private ViewFactory $view
|
private ViewFactory $view
|
||||||
) {
|
) {
|
||||||
@ -43,7 +41,7 @@ class CreateServerController extends Controller
|
|||||||
$eggs = Egg::with('variables')->get();
|
$eggs = Egg::with('variables')->get();
|
||||||
|
|
||||||
\JavaScript::put([
|
\JavaScript::put([
|
||||||
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
|
'nodeData' => Node::getForServerCreation(),
|
||||||
'eggs' => $eggs->keyBy('id'),
|
'eggs' => $eggs->keyBy('id'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ use App\Models\ServerTransfer;
|
|||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Services\Nodes\NodeJWTService;
|
use App\Services\Nodes\NodeJWTService;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
|
|
||||||
class ServerTransferController extends Controller
|
class ServerTransferController extends Controller
|
||||||
{
|
{
|
||||||
@ -28,7 +27,6 @@ class ServerTransferController extends Controller
|
|||||||
private AlertsMessageBag $alert,
|
private AlertsMessageBag $alert,
|
||||||
private ConnectionInterface $connection,
|
private ConnectionInterface $connection,
|
||||||
private NodeJWTService $nodeJWTService,
|
private NodeJWTService $nodeJWTService,
|
||||||
private NodeRepository $nodeRepository
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +67,13 @@ class ServerTransferController extends Controller
|
|||||||
$additional_allocations = array_map('intval', $validatedData['allocation_additional'] ?? []);
|
$additional_allocations = array_map('intval', $validatedData['allocation_additional'] ?? []);
|
||||||
|
|
||||||
// Check if the node is viable for the transfer.
|
// Check if the node is viable for the transfer.
|
||||||
$node = $this->nodeRepository->getNodeWithResourceUsage($node_id);
|
$node = Node::query()
|
||||||
|
->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemonListen', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate'])
|
||||||
|
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
||||||
|
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
||||||
|
->where('nodes.id', $node_id)
|
||||||
|
->first();
|
||||||
|
|
||||||
if (!$node->isViable($server->memory, $server->disk)) {
|
if (!$node->isViable($server->memory, $server->disk)) {
|
||||||
$this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash();
|
$this->alert->danger(trans('admin/server.alerts.transfer_not_viable'))->flash();
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ use App\Exceptions\DisplayException;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Services\Servers\EnvironmentService;
|
use App\Services\Servers\EnvironmentService;
|
||||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
use App\Repositories\Eloquent\MountRepository;
|
use App\Repositories\Eloquent\MountRepository;
|
||||||
use App\Traits\Controllers\JavascriptInjection;
|
use App\Traits\Controllers\JavascriptInjection;
|
||||||
use App\Repositories\Eloquent\DatabaseHostRepository;
|
use App\Repositories\Eloquent\DatabaseHostRepository;
|
||||||
@ -26,7 +25,6 @@ class ServerViewController extends Controller
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private DatabaseHostRepository $databaseHostRepository,
|
private DatabaseHostRepository $databaseHostRepository,
|
||||||
private MountRepository $mountRepository,
|
private MountRepository $mountRepository,
|
||||||
private NodeRepository $nodeRepository,
|
|
||||||
private EnvironmentService $environmentService,
|
private EnvironmentService $environmentService,
|
||||||
private ViewFactory $view
|
private ViewFactory $view
|
||||||
) {
|
) {
|
||||||
@ -118,14 +116,14 @@ class ServerViewController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the panel doesn't have at least 2 nodes configured.
|
// Check if the panel doesn't have at least 2 nodes configured.
|
||||||
$nodes = $this->nodeRepository->all();
|
$nodeCount = Node::query()->count();
|
||||||
$canTransfer = false;
|
$canTransfer = false;
|
||||||
if (count($nodes) >= 2) {
|
if ($nodeCount >= 2) {
|
||||||
$canTransfer = true;
|
$canTransfer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
\JavaScript::put([
|
\JavaScript::put([
|
||||||
'nodeData' => $this->nodeRepository->getNodesForServerCreation(),
|
'nodeData' => Node::getForServerCreation(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->view->make('admin.servers.view.manage', [
|
return $this->view->make('admin.servers.view.manage', [
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware\Api\Daemon;
|
namespace App\Http\Middleware\Api\Daemon;
|
||||||
|
|
||||||
|
use App\Models\Node;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
use App\Exceptions\Repository\RecordNotFoundException;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ class DaemonAuthenticate
|
|||||||
/**
|
/**
|
||||||
* DaemonAuthenticate constructor.
|
* DaemonAuthenticate constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(private Encrypter $encrypter, private NodeRepository $repository)
|
public function __construct(private Encrypter $encrypter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,24 +41,18 @@ class DaemonAuthenticate
|
|||||||
}
|
}
|
||||||
|
|
||||||
$parts = explode('.', $bearer);
|
$parts = explode('.', $bearer);
|
||||||
// Ensure that all of the correct parts are provided in the header.
|
// Ensure that all the correct parts are provided in the header.
|
||||||
if (count($parts) !== 2 || empty($parts[0]) || empty($parts[1])) {
|
if (count($parts) !== 2 || empty($parts[0]) || empty($parts[1])) {
|
||||||
throw new BadRequestHttpException('The Authorization header provided was not in a valid format.');
|
throw new BadRequestHttpException('The Authorization header provided was not in a valid format.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
/** @var Node $node */
|
||||||
/** @var \App\Models\Node $node */
|
$node = Node::query()->where('daemon_token_id', $parts[0])->firstOrFail();
|
||||||
$node = $this->repository->findFirstWhere([
|
|
||||||
'daemon_token_id' => $parts[0],
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (hash_equals((string) $this->encrypter->decrypt($node->daemon_token), $parts[1])) {
|
if (hash_equals((string) $this->encrypter->decrypt($node->daemon_token), $parts[1])) {
|
||||||
$request->attributes->set('node', $node);
|
$request->attributes->set('node', $node);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
|
||||||
} catch (RecordNotFoundException $exception) {
|
|
||||||
// Do nothing, we don't want to expose a node not existing at all.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AccessDeniedHttpException('You are not authorized to access this resource.');
|
throw new AccessDeniedHttpException('You are not authorized to access this resource.');
|
||||||
|
@ -221,4 +221,26 @@ class Node extends Model
|
|||||||
|
|
||||||
return ($this->sum_memory + $memory) <= $memoryLimit && ($this->sum_disk + $disk) <= $diskLimit;
|
return ($this->sum_memory + $memory) <= $memoryLimit && ($this->sum_disk + $disk) <= $diskLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getForServerCreation()
|
||||||
|
{
|
||||||
|
return self::with('allocations')->get()->map(function (Node $item) {
|
||||||
|
$filtered = $item->getRelation('allocations')->where('server_id', null)->map(function ($map) {
|
||||||
|
return collect($map)->only(['id', 'ip', 'port']);
|
||||||
|
});
|
||||||
|
|
||||||
|
$item->ports = $filtered->map(function ($map) {
|
||||||
|
return [
|
||||||
|
'id' => $map['id'],
|
||||||
|
'text' => sprintf('%s:%s', $map['ip'], $map['port']),
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $item->id,
|
||||||
|
'text' => $item->name,
|
||||||
|
'allocations' => $item->ports,
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,8 @@
|
|||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
use App\Repositories\Eloquent\SubuserRepository;
|
use App\Repositories\Eloquent\SubuserRepository;
|
||||||
use App\Repositories\Eloquent\EggVariableRepository;
|
use App\Repositories\Eloquent\EggVariableRepository;
|
||||||
use App\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use App\Repositories\Eloquent\DatabaseHostRepository;
|
use App\Repositories\Eloquent\DatabaseHostRepository;
|
||||||
use App\Contracts\Repository\SubuserRepositoryInterface;
|
use App\Contracts\Repository\SubuserRepositoryInterface;
|
||||||
use App\Contracts\Repository\EggVariableRepositoryInterface;
|
use App\Contracts\Repository\EggVariableRepositoryInterface;
|
||||||
@ -22,7 +20,6 @@ class RepositoryServiceProvider extends ServiceProvider
|
|||||||
// Eloquent Repositories
|
// Eloquent Repositories
|
||||||
$this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class);
|
$this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class);
|
||||||
$this->app->bind(EggVariableRepositoryInterface::class, EggVariableRepository::class);
|
$this->app->bind(EggVariableRepositoryInterface::class, EggVariableRepository::class);
|
||||||
$this->app->bind(NodeRepositoryInterface::class, NodeRepository::class);
|
|
||||||
$this->app->bind(SubuserRepositoryInterface::class, SubuserRepository::class);
|
$this->app->bind(SubuserRepositoryInterface::class, SubuserRepository::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,148 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repositories\Eloquent;
|
|
||||||
|
|
||||||
use App\Models\Node;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use App\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
|
|
||||||
class NodeRepository extends EloquentRepository implements NodeRepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the model backing this repository.
|
|
||||||
*/
|
|
||||||
public function model(): string
|
|
||||||
{
|
|
||||||
return Node::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
public function getUsageStats(Node $node): array
|
|
||||||
{
|
|
||||||
$stats = $this->getBuilder()
|
|
||||||
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
|
||||||
->join('servers', 'servers.node_id', '=', 'nodes.id')
|
|
||||||
->where('node_id', '=', $node->id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return Collection::make(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])
|
|
||||||
->mapWithKeys(function ($value, $key) use ($node) {
|
|
||||||
$maxUsage = $node->{$key};
|
|
||||||
if ($node->{$key . '_overallocate'} > 0) {
|
|
||||||
$maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
$percent = ($value / $maxUsage) * 100;
|
|
||||||
|
|
||||||
return [
|
|
||||||
$key => [
|
|
||||||
'value' => number_format($value),
|
|
||||||
'max' => number_format($maxUsage),
|
|
||||||
'percent' => $percent,
|
|
||||||
'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
})
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the usage stats for a single node.
|
|
||||||
*/
|
|
||||||
public function getUsageStatsRaw(Node $node): array
|
|
||||||
{
|
|
||||||
$stats = $this->getBuilder()->select(
|
|
||||||
$this->getBuilder()->raw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
|
||||||
)->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $node->id)->first();
|
|
||||||
|
|
||||||
return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])->mapWithKeys(function ($value, $key) use ($node) {
|
|
||||||
$maxUsage = $node->{$key};
|
|
||||||
if ($node->{$key . '_overallocate'} > 0) {
|
|
||||||
$maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
$key => [
|
|
||||||
'value' => $value,
|
|
||||||
'max' => $maxUsage,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a single node with server information.
|
|
||||||
*/
|
|
||||||
public function loadServerCount(Node $node, bool $refresh = false): Node
|
|
||||||
{
|
|
||||||
// This is quite ugly and can probably be improved down the road.
|
|
||||||
// And by probably, I mean it should.
|
|
||||||
if (is_null($node->servers_count) || $refresh) {
|
|
||||||
$node->load('servers');
|
|
||||||
$node->setRelation('servers_count', count($node->getRelation('servers')));
|
|
||||||
unset($node->servers);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach a paginated set of allocations to a node mode including
|
|
||||||
* any servers that are also attached to those allocations.
|
|
||||||
*/
|
|
||||||
public function loadNodeAllocations(Node $node, bool $refresh = false): Node
|
|
||||||
{
|
|
||||||
$node->setRelation(
|
|
||||||
'allocations',
|
|
||||||
$node->allocations()
|
|
||||||
->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL')
|
|
||||||
->orderByRaw('INET_ATON(ip) ASC')
|
|
||||||
->orderBy('port')
|
|
||||||
->with('server:id,name')
|
|
||||||
->paginate(50)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a collection of nodes for all locations to use in server creation UI.
|
|
||||||
*/
|
|
||||||
public function getNodesForServerCreation(): Collection
|
|
||||||
{
|
|
||||||
return $this->getBuilder()->with('allocations')->get()->map(function (Node $item) {
|
|
||||||
$filtered = $item->getRelation('allocations')->where('server_id', null)->map(function ($map) {
|
|
||||||
return collect($map)->only(['id', 'ip', 'port']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$item->ports = $filtered->map(function ($map) {
|
|
||||||
return [
|
|
||||||
'id' => $map['id'],
|
|
||||||
'text' => sprintf('%s:%s', $map['ip'], $map['port']),
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $item->id,
|
|
||||||
'text' => $item->name,
|
|
||||||
'allocations' => $item->ports,
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a node with the given id with the Node's resource usage.
|
|
||||||
*/
|
|
||||||
public function getNodeWithResourceUsage(int $node_id): Node
|
|
||||||
{
|
|
||||||
$instance = $this->getBuilder()
|
|
||||||
->select(['nodes.id', 'nodes.fqdn', 'nodes.scheme', 'nodes.daemon_token', 'nodes.daemonListen', 'nodes.memory', 'nodes.disk', 'nodes.memory_overallocate', 'nodes.disk_overallocate'])
|
|
||||||
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk')
|
|
||||||
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
|
||||||
->where('nodes.id', $node_id);
|
|
||||||
|
|
||||||
return $instance->first();
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ use App\Helpers\Utilities;
|
|||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use App\Extensions\DynamicDatabaseConnection;
|
use App\Extensions\DynamicDatabaseConnection;
|
||||||
use App\Repositories\Eloquent\DatabaseRepository;
|
|
||||||
use App\Exceptions\Repository\DuplicateDatabaseNameException;
|
use App\Exceptions\Repository\DuplicateDatabaseNameException;
|
||||||
use App\Exceptions\Service\Database\TooManyDatabasesException;
|
use App\Exceptions\Service\Database\TooManyDatabasesException;
|
||||||
use App\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException;
|
use App\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException;
|
||||||
|
@ -6,17 +6,9 @@ use Ramsey\Uuid\Uuid;
|
|||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use App\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
|
|
||||||
class NodeCreationService
|
class NodeCreationService
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* NodeCreationService constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(protected NodeRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new node on the panel.
|
* Create a new node on the panel.
|
||||||
*
|
*
|
||||||
@ -28,6 +20,6 @@ class NodeCreationService
|
|||||||
$data['daemon_token'] = app(Encrypter::class)->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH));
|
$data['daemon_token'] = app(Encrypter::class)->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH));
|
||||||
$data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH);
|
$data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH);
|
||||||
|
|
||||||
return $this->repository->create($data, true, true);
|
return Node::query()->create($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ namespace App\Services\Nodes;
|
|||||||
|
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Illuminate\Contracts\Translation\Translator;
|
use Illuminate\Contracts\Translation\Translator;
|
||||||
use App\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use App\Exceptions\Service\HasActiveServersException;
|
use App\Exceptions\Service\HasActiveServersException;
|
||||||
|
|
||||||
class NodeDeletionService
|
class NodeDeletionService
|
||||||
@ -13,7 +12,6 @@ class NodeDeletionService
|
|||||||
* NodeDeletionService constructor.
|
* NodeDeletionService constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected NodeRepositoryInterface $repository,
|
|
||||||
protected Translator $translator
|
protected Translator $translator
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@ -33,6 +31,6 @@ class NodeDeletionService
|
|||||||
throw new HasActiveServersException($this->translator->get('exceptions.node.servers_attached'));
|
throw new HasActiveServersException($this->translator->get('exceptions.node.servers_attached'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->repository->delete($node);
|
return $node->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ use App\Models\Node;
|
|||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
use App\Repositories\Daemon\DaemonConfigurationRepository;
|
||||||
use App\Exceptions\Http\Connection\DaemonConnectionException;
|
use App\Exceptions\Http\Connection\DaemonConnectionException;
|
||||||
use App\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
use App\Exceptions\Service\Node\ConfigurationNotPersistedException;
|
||||||
@ -21,7 +20,6 @@ class NodeUpdateService
|
|||||||
private ConnectionInterface $connection,
|
private ConnectionInterface $connection,
|
||||||
private DaemonConfigurationRepository $configurationRepository,
|
private DaemonConfigurationRepository $configurationRepository,
|
||||||
private Encrypter $encrypter,
|
private Encrypter $encrypter,
|
||||||
private NodeRepository $repository
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +37,7 @@ class NodeUpdateService
|
|||||||
|
|
||||||
[$updated, $exception] = $this->connection->transaction(function () use ($data, $node) {
|
[$updated, $exception] = $this->connection->transaction(function () use ($data, $node) {
|
||||||
/** @var \App\Models\Node $updated */
|
/** @var \App\Models\Node $updated */
|
||||||
$updated = $this->repository->withFreshModel()->update($node->id, $data, true, true);
|
$updated = $node->replicate()->forceFill($data)->save();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If we're changing the FQDN for the node, use the newly provided FQDN for the connection
|
// If we're changing the FQDN for the node, use the newly provided FQDN for the connection
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Tests\Unit\Http\Middleware\Api\Daemon;
|
namespace App\Tests\Unit\Http\Middleware\Api\Daemon;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Mockery as m;
|
use Mockery as m;
|
||||||
use Mockery\MockInterface;
|
use Mockery\MockInterface;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use Illuminate\Contracts\Encryption\Encrypter;
|
use Illuminate\Contracts\Encryption\Encrypter;
|
||||||
use App\Repositories\Eloquent\NodeRepository;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
use App\Exceptions\Repository\RecordNotFoundException;
|
use App\Exceptions\Repository\RecordNotFoundException;
|
||||||
use App\Http\Middleware\Api\Daemon\DaemonAuthenticate;
|
use App\Http\Middleware\Api\Daemon\DaemonAuthenticate;
|
||||||
@ -18,8 +18,6 @@ class DaemonAuthenticateTest extends MiddlewareTestCase
|
|||||||
{
|
{
|
||||||
private MockInterface $encrypter;
|
private MockInterface $encrypter;
|
||||||
|
|
||||||
private MockInterface $repository;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup tests.
|
* Setup tests.
|
||||||
*/
|
*/
|
||||||
@ -28,11 +26,10 @@ class DaemonAuthenticateTest extends MiddlewareTestCase
|
|||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->encrypter = m::mock(Encrypter::class);
|
$this->encrypter = m::mock(Encrypter::class);
|
||||||
$this->repository = m::mock(NodeRepository::class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that if we are accessing the daemon.configuration route this middleware is not
|
* Test that if we are accessing the daemon configuration route this middleware is not
|
||||||
* applied in order to allow an unauthenticated request to use a token to grab data.
|
* applied in order to allow an unauthenticated request to use a token to grab data.
|
||||||
*/
|
*/
|
||||||
public function testResponseShouldContinueIfRouteIsExempted()
|
public function testResponseShouldContinueIfRouteIsExempted()
|
||||||
@ -83,16 +80,14 @@ class DaemonAuthenticateTest extends MiddlewareTestCase
|
|||||||
*/
|
*/
|
||||||
public function testResponseShouldFailIfTokenIsNotValid()
|
public function testResponseShouldFailIfTokenIsNotValid()
|
||||||
{
|
{
|
||||||
|
$node = Node::factory()->create();
|
||||||
|
|
||||||
$this->expectException(AccessDeniedHttpException::class);
|
$this->expectException(AccessDeniedHttpException::class);
|
||||||
|
|
||||||
/** @var \App\Models\Node $model */
|
|
||||||
$model = Node::factory()->make();
|
|
||||||
|
|
||||||
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
|
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
|
||||||
$this->request->expects('bearerToken')->withNoArgs()->andReturn($model->daemon_token_id . '.random_string_123');
|
$this->request->expects('bearerToken')->withNoArgs()->andReturn($node->daemon_token_id . '.random_string_123');
|
||||||
|
|
||||||
$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => $model->daemon_token_id])->andReturn($model);
|
$this->encrypter->expects('decrypt')->with($node->daemon_token)->andReturns(decrypt($node->daemon_token));
|
||||||
$this->encrypter->expects('decrypt')->with($model->daemon_token)->andReturns(decrypt($model->daemon_token));
|
|
||||||
|
|
||||||
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
||||||
}
|
}
|
||||||
@ -103,13 +98,11 @@ class DaemonAuthenticateTest extends MiddlewareTestCase
|
|||||||
*/
|
*/
|
||||||
public function testResponseShouldFailIfNodeIsNotFound()
|
public function testResponseShouldFailIfNodeIsNotFound()
|
||||||
{
|
{
|
||||||
$this->expectException(AccessDeniedHttpException::class);
|
$this->expectException(ModelNotFoundException::class);
|
||||||
|
|
||||||
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
|
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
|
||||||
$this->request->expects('bearerToken')->withNoArgs()->andReturn('abcd1234.random_string_123');
|
$this->request->expects('bearerToken')->withNoArgs()->andReturn('abcd1234.random_string_123');
|
||||||
|
|
||||||
$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => 'abcd1234'])->andThrow(RecordNotFoundException::class);
|
|
||||||
|
|
||||||
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,18 +111,17 @@ class DaemonAuthenticateTest extends MiddlewareTestCase
|
|||||||
*/
|
*/
|
||||||
public function testSuccessfulMiddlewareProcess()
|
public function testSuccessfulMiddlewareProcess()
|
||||||
{
|
{
|
||||||
/** @var \App\Models\Node $model */
|
$node = Node::factory()->create();
|
||||||
$model = Node::factory()->make();
|
$node->daemon_token = encrypt('the_same');
|
||||||
|
$node->save();
|
||||||
|
|
||||||
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
|
$this->request->expects('route->getName')->withNoArgs()->andReturn('random.route');
|
||||||
$this->request->expects('bearerToken')->withNoArgs()->andReturn($model->daemon_token_id . '.' . decrypt($model->daemon_token));
|
$this->request->expects('bearerToken')->withNoArgs()->andReturn($node->daemon_token_id . '.the_same');
|
||||||
|
$this->encrypter->expects('decrypt')->with($node->daemon_token)->andReturns(decrypt($node->daemon_token));
|
||||||
$this->repository->expects('findFirstWhere')->with(['daemon_token_id' => $model->daemon_token_id])->andReturn($model);
|
|
||||||
$this->encrypter->expects('decrypt')->with($model->daemon_token)->andReturns(decrypt($model->daemon_token));
|
|
||||||
|
|
||||||
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
|
||||||
$this->assertRequestHasAttribute('node');
|
$this->assertRequestHasAttribute('node');
|
||||||
$this->assertRequestAttributeEquals($model, 'node');
|
$this->assertRequestAttributeEquals($node->fresh(), 'node');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -156,6 +148,6 @@ class DaemonAuthenticateTest extends MiddlewareTestCase
|
|||||||
*/
|
*/
|
||||||
private function getMiddleware(): DaemonAuthenticate
|
private function getMiddleware(): DaemonAuthenticate
|
||||||
{
|
{
|
||||||
return new DaemonAuthenticate($this->encrypter, $this->repository);
|
return new DaemonAuthenticate($this->encrypter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user