mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-20 19:14:45 +02:00
Remove locations
This commit is contained in:
parent
9fb0c451f5
commit
e4cee4d69d
@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands\Location;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use App\Services\Locations\LocationDeletionService;
|
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
|
|
||||||
class DeleteLocationCommand extends Command
|
|
||||||
{
|
|
||||||
protected $description = 'Deletes a location from the Panel.';
|
|
||||||
|
|
||||||
protected $signature = 'p:location:delete {--short= : The short code of the location to delete.}';
|
|
||||||
|
|
||||||
protected Collection $locations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DeleteLocationCommand constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
private LocationDeletionService $deletionService,
|
|
||||||
private LocationRepositoryInterface $repository
|
|
||||||
) {
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Respond to the command request.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
* @throws \App\Exceptions\Service\Location\HasActiveNodesException
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$this->locations = $this->locations ?? $this->repository->all();
|
|
||||||
$short = $this->option('short') ?? $this->anticipate(
|
|
||||||
trans('command/messages.location.ask_short'),
|
|
||||||
$this->locations->pluck('short')->toArray()
|
|
||||||
);
|
|
||||||
|
|
||||||
$location = $this->locations->where('short', $short)->first();
|
|
||||||
if (is_null($location)) {
|
|
||||||
$this->error(trans('command/messages.location.no_location_found'));
|
|
||||||
if ($this->input->isInteractive()) {
|
|
||||||
$this->handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->deletionService->handle($location->id);
|
|
||||||
$this->line(trans('command/messages.location.deleted'));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands\Location;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use App\Services\Locations\LocationCreationService;
|
|
||||||
|
|
||||||
class MakeLocationCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'p:location:make
|
|
||||||
{--short= : The shortcode name of this location (ex. us1).}
|
|
||||||
{--long= : A longer description of this location.}';
|
|
||||||
|
|
||||||
protected $description = 'Creates a new location on the system via the CLI.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*/
|
|
||||||
public function __construct(private LocationCreationService $creationService)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the command execution process.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Model\DataValidationException
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$short = $this->option('short') ?? $this->ask(trans('command/messages.location.ask_short'));
|
|
||||||
$long = $this->option('long') ?? $this->ask(trans('command/messages.location.ask_long'));
|
|
||||||
|
|
||||||
$location = $this->creationService->handle(compact('short', 'long'));
|
|
||||||
$this->line(trans('command/messages.location.created', [
|
|
||||||
'name' => $location->short,
|
|
||||||
'id' => $location->id,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
@ -44,7 +44,6 @@ class MakeNodeCommand extends Command
|
|||||||
{
|
{
|
||||||
$data['name'] = $this->option('name') ?? $this->ask('Enter a short identifier used to distinguish this node from others');
|
$data['name'] = $this->option('name') ?? $this->ask('Enter a short identifier used to distinguish this node from others');
|
||||||
$data['description'] = $this->option('description') ?? $this->ask('Enter a description to identify the node');
|
$data['description'] = $this->option('description') ?? $this->ask('Enter a description to identify the node');
|
||||||
$data['location_id'] = $this->option('locationId') ?? $this->ask('Enter a valid location id');
|
|
||||||
$data['scheme'] = $this->option('scheme') ?? $this->anticipate(
|
$data['scheme'] = $this->option('scheme') ?? $this->anticipate(
|
||||||
'Please either enter https for SSL or http for a non-ssl connection',
|
'Please either enter https for SSL or http for a non-ssl connection',
|
||||||
['https', 'http'],
|
['https', 'http'],
|
||||||
@ -64,6 +63,6 @@ class MakeNodeCommand extends Command
|
|||||||
$data['daemonBase'] = $this->option('daemonBase') ?? $this->ask('Enter the base folder', '/var/lib/panel/volumes');
|
$data['daemonBase'] = $this->option('daemonBase') ?? $this->ask('Enter the base folder', '/var/lib/panel/volumes');
|
||||||
|
|
||||||
$node = $this->creationService->handle($data);
|
$node = $this->creationService->handle($data);
|
||||||
$this->line('Successfully created a new node on the location ' . $data['location_id'] . ' with the name ' . $data['name'] . ' and has an id of ' . $node->id . '.');
|
$this->line('Successfully created a new node with the name ' . $data['name'] . ' and has an id of ' . $node->id . '.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ class NodeListCommand extends Command
|
|||||||
'id' => $node->id,
|
'id' => $node->id,
|
||||||
'uuid' => $node->uuid,
|
'uuid' => $node->uuid,
|
||||||
'name' => $node->name,
|
'name' => $node->name,
|
||||||
'location' => $node->location->short,
|
|
||||||
'host' => $node->getConnectionAddress(),
|
'host' => $node->getConnectionAddress(),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
@ -24,7 +23,7 @@ class NodeListCommand extends Command
|
|||||||
if ($this->option('format') === 'json') {
|
if ($this->option('format') === 'json') {
|
||||||
$this->output->write($nodes->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
$this->output->write($nodes->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||||
} else {
|
} else {
|
||||||
$this->table(['ID', 'UUID', 'Name', 'Location', 'Host'], $nodes->toArray());
|
$this->table(['ID', 'UUID', 'Name', 'Host'], $nodes->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->output->newLine();
|
$this->output->newLine();
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Contracts\Repository;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
interface LocationRepositoryInterface extends RepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return locations with a count of nodes and servers attached to it.
|
|
||||||
*/
|
|
||||||
public function getAllWithDetails(): Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all the available locations with the nodes as a relationship.
|
|
||||||
*/
|
|
||||||
public function getAllWithNodes(): Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all the nodes and their respective count of servers for a location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function getWithNodes(int $id): Location;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a location and the count of nodes in that location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function getWithNodeCount(int $id): Location;
|
|
||||||
}
|
|
@ -23,7 +23,7 @@ interface NodeRepositoryInterface extends RepositoryInterface
|
|||||||
/**
|
/**
|
||||||
* Return a single node with location and server information.
|
* Return a single node with location and server information.
|
||||||
*/
|
*/
|
||||||
public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node;
|
public function loadServerCount(Node $node, bool $refresh = false): Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach a paginated set of allocations to a node mode including
|
* Attach a paginated set of allocations to a node mode including
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions\Service\Location;
|
|
||||||
|
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use App\Exceptions\DisplayException;
|
|
||||||
|
|
||||||
class HasActiveNodesException extends DisplayException
|
|
||||||
{
|
|
||||||
public function getStatusCode(): int
|
|
||||||
{
|
|
||||||
return Response::HTTP_BAD_REQUEST;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use Exception;
|
use App\Models\Node;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use App\Models\DatabaseHost;
|
use App\Models\DatabaseHost;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
@ -14,7 +14,6 @@ use App\Http\Requests\Admin\DatabaseHostFormRequest;
|
|||||||
use App\Services\Databases\Hosts\HostCreationService;
|
use App\Services\Databases\Hosts\HostCreationService;
|
||||||
use App\Services\Databases\Hosts\HostDeletionService;
|
use App\Services\Databases\Hosts\HostDeletionService;
|
||||||
use App\Contracts\Repository\DatabaseRepositoryInterface;
|
use App\Contracts\Repository\DatabaseRepositoryInterface;
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
use App\Contracts\Repository\DatabaseHostRepositoryInterface;
|
use App\Contracts\Repository\DatabaseHostRepositoryInterface;
|
||||||
|
|
||||||
class DatabaseController extends Controller
|
class DatabaseController extends Controller
|
||||||
@ -29,7 +28,6 @@ class DatabaseController extends Controller
|
|||||||
private HostCreationService $creationService,
|
private HostCreationService $creationService,
|
||||||
private HostDeletionService $deletionService,
|
private HostDeletionService $deletionService,
|
||||||
private HostUpdateService $updateService,
|
private HostUpdateService $updateService,
|
||||||
private LocationRepositoryInterface $locationRepository,
|
|
||||||
private ViewFactory $view
|
private ViewFactory $view
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@ -40,7 +38,7 @@ class DatabaseController extends Controller
|
|||||||
public function index(): View
|
public function index(): View
|
||||||
{
|
{
|
||||||
return $this->view->make('admin.databases.index', [
|
return $this->view->make('admin.databases.index', [
|
||||||
'locations' => $this->locationRepository->getAllWithNodes(),
|
'nodes' => Node::all(),
|
||||||
'hosts' => $this->repository->getWithViewDetails(),
|
'hosts' => $this->repository->getWithViewDetails(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -53,7 +51,6 @@ class DatabaseController extends Controller
|
|||||||
public function view(int $host): View
|
public function view(int $host): View
|
||||||
{
|
{
|
||||||
return $this->view->make('admin.databases.view', [
|
return $this->view->make('admin.databases.view', [
|
||||||
'locations' => $this->locationRepository->getAllWithNodes(),
|
|
||||||
'host' => $this->repository->find($host),
|
'host' => $this->repository->find($host),
|
||||||
'databases' => $this->databaseRepository->getDatabasesForHost($host),
|
'databases' => $this->databaseRepository->getDatabasesForHost($host),
|
||||||
]);
|
]);
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use Illuminate\View\View;
|
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Prologue\Alerts\AlertsMessageBag;
|
|
||||||
use Illuminate\View\Factory as ViewFactory;
|
|
||||||
use App\Exceptions\DisplayException;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests\Admin\LocationFormRequest;
|
|
||||||
use App\Services\Locations\LocationUpdateService;
|
|
||||||
use App\Services\Locations\LocationCreationService;
|
|
||||||
use App\Services\Locations\LocationDeletionService;
|
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
|
|
||||||
class LocationController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* LocationController constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
protected AlertsMessageBag $alert,
|
|
||||||
protected LocationCreationService $creationService,
|
|
||||||
protected LocationDeletionService $deletionService,
|
|
||||||
protected LocationRepositoryInterface $repository,
|
|
||||||
protected LocationUpdateService $updateService,
|
|
||||||
protected ViewFactory $view
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the location overview page.
|
|
||||||
*/
|
|
||||||
public function index(): View
|
|
||||||
{
|
|
||||||
return $this->view->make('admin.locations.index', [
|
|
||||||
'locations' => $this->repository->getAllWithDetails(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the location view page.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function view(int $id): View
|
|
||||||
{
|
|
||||||
return $this->view->make('admin.locations.view', [
|
|
||||||
'location' => $this->repository->getWithNodes($id),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle request to create new location.
|
|
||||||
*
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
|
||||||
public function create(LocationFormRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$location = $this->creationService->handle($request->normalize());
|
|
||||||
$this->alert->success('Location was created successfully.')->flash();
|
|
||||||
|
|
||||||
return redirect()->route('admin.locations.view', $location->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle request to update or delete location.
|
|
||||||
*
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
|
||||||
public function update(LocationFormRequest $request, Location $location): RedirectResponse
|
|
||||||
{
|
|
||||||
if ($request->input('action') === 'delete') {
|
|
||||||
return $this->delete($location);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->updateService->handle($location->id, $request->normalize());
|
|
||||||
$this->alert->success('Location was updated successfully.')->flash();
|
|
||||||
|
|
||||||
return redirect()->route('admin.locations.view', $location->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a location from the system.
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
* @throws \App\Exceptions\DisplayException
|
|
||||||
*/
|
|
||||||
public function delete(Location $location): RedirectResponse
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->deletionService->handle($location->id);
|
|
||||||
|
|
||||||
return redirect()->route('admin.locations');
|
|
||||||
} catch (DisplayException $ex) {
|
|
||||||
$this->alert->danger($ex->getMessage())->flash();
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('admin.locations.view', $location->id);
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,14 +8,12 @@ use Illuminate\View\View;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use App\Models\Mount;
|
use App\Models\Mount;
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
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\Http\Requests\Admin\MountFormRequest;
|
use App\Http\Requests\Admin\MountFormRequest;
|
||||||
use App\Repositories\Eloquent\MountRepository;
|
use App\Repositories\Eloquent\MountRepository;
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
|
|
||||||
class MountController extends Controller
|
class MountController extends Controller
|
||||||
{
|
{
|
||||||
@ -24,7 +22,6 @@ class MountController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected AlertsMessageBag $alert,
|
protected AlertsMessageBag $alert,
|
||||||
protected LocationRepositoryInterface $locationRepository,
|
|
||||||
protected MountRepository $repository,
|
protected MountRepository $repository,
|
||||||
protected ViewFactory $view
|
protected ViewFactory $view
|
||||||
) {
|
) {
|
||||||
@ -48,12 +45,10 @@ class MountController extends Controller
|
|||||||
public function view(string $id): View
|
public function view(string $id): View
|
||||||
{
|
{
|
||||||
$eggs = Egg::all();
|
$eggs = Egg::all();
|
||||||
$locations = Location::query()->with('nodes')->get();
|
|
||||||
|
|
||||||
return $this->view->make('admin.mounts.view', [
|
return $this->view->make('admin.mounts.view', [
|
||||||
'mount' => $this->repository->getWithRelations($id),
|
'mount' => $this->repository->getWithRelations($id),
|
||||||
'eggs' => $eggs,
|
'eggs' => $eggs,
|
||||||
'locations' => $locations,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ class NodeController extends Controller
|
|||||||
public function index(Request $request): View
|
public function index(Request $request): View
|
||||||
{
|
{
|
||||||
$nodes = QueryBuilder::for(
|
$nodes = QueryBuilder::for(
|
||||||
Node::query()->with('location')->withCount('servers')
|
Node::query()->withCount('servers')
|
||||||
)
|
)
|
||||||
->allowedFilters(['uuid', 'name'])
|
->allowedFilters(['uuid', 'name'])
|
||||||
->allowedSorts(['id'])
|
->allowedSorts(['id'])
|
||||||
|
@ -13,7 +13,6 @@ use App\Repositories\Eloquent\NodeRepository;
|
|||||||
use App\Repositories\Eloquent\ServerRepository;
|
use App\Repositories\Eloquent\ServerRepository;
|
||||||
use App\Traits\Controllers\JavascriptInjection;
|
use App\Traits\Controllers\JavascriptInjection;
|
||||||
use App\Services\Helpers\SoftwareVersionService;
|
use App\Services\Helpers\SoftwareVersionService;
|
||||||
use App\Repositories\Eloquent\LocationRepository;
|
|
||||||
use App\Repositories\Eloquent\AllocationRepository;
|
use App\Repositories\Eloquent\AllocationRepository;
|
||||||
|
|
||||||
class NodeViewController extends Controller
|
class NodeViewController extends Controller
|
||||||
@ -25,7 +24,6 @@ class NodeViewController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AllocationRepository $allocationRepository,
|
private AllocationRepository $allocationRepository,
|
||||||
private LocationRepository $locationRepository,
|
|
||||||
private NodeRepository $repository,
|
private NodeRepository $repository,
|
||||||
private ServerRepository $serverRepository,
|
private ServerRepository $serverRepository,
|
||||||
private SoftwareVersionService $versionService,
|
private SoftwareVersionService $versionService,
|
||||||
@ -38,7 +36,7 @@ class NodeViewController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request, Node $node): View
|
public function index(Request $request, Node $node): View
|
||||||
{
|
{
|
||||||
$node = $this->repository->loadLocationAndServerCount($node);
|
$node = $this->repository->loadServerCount($node);
|
||||||
|
|
||||||
return $this->view->make('admin.nodes.view.index', [
|
return $this->view->make('admin.nodes.view.index', [
|
||||||
'node' => $node,
|
'node' => $node,
|
||||||
@ -54,7 +52,6 @@ class NodeViewController extends Controller
|
|||||||
{
|
{
|
||||||
return $this->view->make('admin.nodes.view.settings', [
|
return $this->view->make('admin.nodes.view.settings', [
|
||||||
'node' => $node,
|
'node' => $node,
|
||||||
'locations' => $this->locationRepository->all(),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ use App\Contracts\Repository\NodeRepositoryInterface;
|
|||||||
use App\Contracts\Repository\ServerRepositoryInterface;
|
use App\Contracts\Repository\ServerRepositoryInterface;
|
||||||
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\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
use App\Contracts\Repository\AllocationRepositoryInterface;
|
use App\Contracts\Repository\AllocationRepositoryInterface;
|
||||||
use App\Http\Requests\Admin\Node\AllocationAliasFormRequest;
|
use App\Http\Requests\Admin\Node\AllocationAliasFormRequest;
|
||||||
|
|
||||||
@ -39,7 +38,6 @@ class NodesController extends Controller
|
|||||||
protected CacheRepository $cache,
|
protected CacheRepository $cache,
|
||||||
protected NodeCreationService $creationService,
|
protected NodeCreationService $creationService,
|
||||||
protected NodeDeletionService $deletionService,
|
protected NodeDeletionService $deletionService,
|
||||||
protected LocationRepositoryInterface $locationRepository,
|
|
||||||
protected NodeRepositoryInterface $repository,
|
protected NodeRepositoryInterface $repository,
|
||||||
protected ServerRepositoryInterface $serverRepository,
|
protected ServerRepositoryInterface $serverRepository,
|
||||||
protected NodeUpdateService $updateService,
|
protected NodeUpdateService $updateService,
|
||||||
@ -53,14 +51,7 @@ class NodesController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function create(): View|RedirectResponse
|
public function create(): View|RedirectResponse
|
||||||
{
|
{
|
||||||
$locations = $this->locationRepository->all();
|
return $this->view->make('admin.nodes.new');
|
||||||
if (count($locations) < 1) {
|
|
||||||
$this->alert->warning(trans('admin/node.notices.location_required'))->flash();
|
|
||||||
|
|
||||||
return redirect()->route('admin.locations');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->view->make('admin.nodes.new', ['locations' => $locations]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +5,6 @@ namespace App\Http\Controllers\Admin\Servers;
|
|||||||
use App\Models\Egg;
|
use App\Models\Egg;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Prologue\Alerts\AlertsMessageBag;
|
use Prologue\Alerts\AlertsMessageBag;
|
||||||
use Illuminate\View\Factory as ViewFactory;
|
use Illuminate\View\Factory as ViewFactory;
|
||||||
@ -49,8 +48,8 @@ class CreateServerController extends Controller
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->view->make('admin.servers.new', [
|
return $this->view->make('admin.servers.new', [
|
||||||
'locations' => Location::all(),
|
|
||||||
'eggs' => $eggs,
|
'eggs' => $eggs,
|
||||||
|
'nodes' => Node::all(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers\Admin\Servers;
|
namespace App\Http\Controllers\Admin\Servers;
|
||||||
|
|
||||||
use App\Models\Egg;
|
use App\Models\Egg;
|
||||||
|
use App\Models\Node;
|
||||||
use App\Repositories\Eloquent\EggRepository;
|
use App\Repositories\Eloquent\EggRepository;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -15,7 +16,6 @@ use App\Repositories\Eloquent\NodeRepository;
|
|||||||
use App\Repositories\Eloquent\MountRepository;
|
use App\Repositories\Eloquent\MountRepository;
|
||||||
use App\Repositories\Eloquent\ServerRepository;
|
use App\Repositories\Eloquent\ServerRepository;
|
||||||
use App\Traits\Controllers\JavascriptInjection;
|
use App\Traits\Controllers\JavascriptInjection;
|
||||||
use App\Repositories\Eloquent\LocationRepository;
|
|
||||||
use App\Repositories\Eloquent\DatabaseHostRepository;
|
use App\Repositories\Eloquent\DatabaseHostRepository;
|
||||||
|
|
||||||
class ServerViewController extends Controller
|
class ServerViewController extends Controller
|
||||||
@ -27,7 +27,6 @@ class ServerViewController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private DatabaseHostRepository $databaseHostRepository,
|
private DatabaseHostRepository $databaseHostRepository,
|
||||||
private LocationRepository $locationRepository,
|
|
||||||
private MountRepository $mountRepository,
|
private MountRepository $mountRepository,
|
||||||
private EggRepository $eggRepository,
|
private EggRepository $eggRepository,
|
||||||
private NodeRepository $nodeRepository,
|
private NodeRepository $nodeRepository,
|
||||||
@ -134,8 +133,8 @@ class ServerViewController extends Controller
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->view->make('admin.servers.view.manage', [
|
return $this->view->make('admin.servers.view.manage', [
|
||||||
|
'nodes' => Node::all(),
|
||||||
'server' => $server,
|
'server' => $server,
|
||||||
'locations' => $this->locationRepository->all(),
|
|
||||||
'canTransfer' => $canTransfer,
|
'canTransfer' => $canTransfer,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -1,104 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api\Application\Locations;
|
|
||||||
|
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Spatie\QueryBuilder\QueryBuilder;
|
|
||||||
use App\Services\Locations\LocationUpdateService;
|
|
||||||
use App\Services\Locations\LocationCreationService;
|
|
||||||
use App\Services\Locations\LocationDeletionService;
|
|
||||||
use App\Transformers\Api\Application\LocationTransformer;
|
|
||||||
use App\Http\Controllers\Api\Application\ApplicationApiController;
|
|
||||||
use App\Http\Requests\Api\Application\Locations\GetLocationRequest;
|
|
||||||
use App\Http\Requests\Api\Application\Locations\GetLocationsRequest;
|
|
||||||
use App\Http\Requests\Api\Application\Locations\StoreLocationRequest;
|
|
||||||
use App\Http\Requests\Api\Application\Locations\DeleteLocationRequest;
|
|
||||||
use App\Http\Requests\Api\Application\Locations\UpdateLocationRequest;
|
|
||||||
|
|
||||||
class LocationController extends ApplicationApiController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* LocationController constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
private LocationCreationService $creationService,
|
|
||||||
private LocationDeletionService $deletionService,
|
|
||||||
private LocationUpdateService $updateService
|
|
||||||
) {
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all the locations currently registered on the Panel.
|
|
||||||
*/
|
|
||||||
public function index(GetLocationsRequest $request): array
|
|
||||||
{
|
|
||||||
$locations = QueryBuilder::for(Location::query())
|
|
||||||
->allowedFilters(['short', 'long'])
|
|
||||||
->allowedSorts(['id'])
|
|
||||||
->paginate($request->query('per_page') ?? 50);
|
|
||||||
|
|
||||||
return $this->fractal->collection($locations)
|
|
||||||
->transformWith($this->getTransformer(LocationTransformer::class))
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a single location.
|
|
||||||
*/
|
|
||||||
public function view(GetLocationRequest $request, Location $location): array
|
|
||||||
{
|
|
||||||
return $this->fractal->item($location)
|
|
||||||
->transformWith($this->getTransformer(LocationTransformer::class))
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a new location on the Panel and return an HTTP/201 response code with the
|
|
||||||
* new location attached.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Model\DataValidationException
|
|
||||||
*/
|
|
||||||
public function store(StoreLocationRequest $request): JsonResponse
|
|
||||||
{
|
|
||||||
$location = $this->creationService->handle($request->validated());
|
|
||||||
|
|
||||||
return $this->fractal->item($location)
|
|
||||||
->transformWith($this->getTransformer(LocationTransformer::class))
|
|
||||||
->addMeta([
|
|
||||||
'resource' => route('api.application.locations.view', [
|
|
||||||
'location' => $location->id,
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
->respond(201);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a location on the Panel and return the updated record to the user.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function update(UpdateLocationRequest $request, Location $location): array
|
|
||||||
{
|
|
||||||
$location = $this->updateService->handle($location, $request->validated());
|
|
||||||
|
|
||||||
return $this->fractal->item($location)
|
|
||||||
->transformWith($this->getTransformer(LocationTransformer::class))
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a location from the Panel.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Service\Location\HasActiveNodesException
|
|
||||||
*/
|
|
||||||
public function delete(DeleteLocationRequest $request, Location $location): Response
|
|
||||||
{
|
|
||||||
$this->deletionService->handle($location);
|
|
||||||
|
|
||||||
return response('', 204);
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ class NodeDeploymentController extends ApplicationApiController
|
|||||||
public function __invoke(GetDeployableNodesRequest $request): array
|
public function __invoke(GetDeployableNodesRequest $request): array
|
||||||
{
|
{
|
||||||
$data = $request->validated();
|
$data = $request->validated();
|
||||||
$nodes = $this->viableNodesService->setLocations($data['location_ids'] ?? [])
|
$nodes = $this->viableNodesService
|
||||||
->setMemory($data['memory'])
|
->setMemory($data['memory'])
|
||||||
->setDisk($data['disk'])
|
->setDisk($data['disk'])
|
||||||
->handle($request->query('per_page'), $request->query('page'));
|
->handle($request->query('per_page'), $request->query('page'));
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Admin;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
|
|
||||||
class LocationFormRequest extends AdminFormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Set up the validation rules to use for these requests.
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
if ($this->method() === 'PATCH') {
|
|
||||||
return Location::getRulesForUpdate($this->route()->parameter('location')->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Location::getRules();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Api\Application\Locations;
|
|
||||||
|
|
||||||
use App\Services\Acl\Api\AdminAcl;
|
|
||||||
use App\Http\Requests\Api\Application\ApplicationApiRequest;
|
|
||||||
|
|
||||||
class DeleteLocationRequest extends ApplicationApiRequest
|
|
||||||
{
|
|
||||||
protected ?string $resource = AdminAcl::RESOURCE_LOCATIONS;
|
|
||||||
|
|
||||||
protected int $permission = AdminAcl::WRITE;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Api\Application\Locations;
|
|
||||||
|
|
||||||
class GetLocationRequest extends GetLocationsRequest
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Api\Application\Locations;
|
|
||||||
|
|
||||||
use App\Services\Acl\Api\AdminAcl;
|
|
||||||
use App\Http\Requests\Api\Application\ApplicationApiRequest;
|
|
||||||
|
|
||||||
class GetLocationsRequest extends ApplicationApiRequest
|
|
||||||
{
|
|
||||||
protected ?string $resource = AdminAcl::RESOURCE_LOCATIONS;
|
|
||||||
|
|
||||||
protected int $permission = AdminAcl::READ;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Api\Application\Locations;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
use App\Services\Acl\Api\AdminAcl;
|
|
||||||
use App\Http\Requests\Api\Application\ApplicationApiRequest;
|
|
||||||
|
|
||||||
class StoreLocationRequest extends ApplicationApiRequest
|
|
||||||
{
|
|
||||||
protected ?string $resource = AdminAcl::RESOURCE_LOCATIONS;
|
|
||||||
|
|
||||||
protected int $permission = AdminAcl::WRITE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules to validate the request against.
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return collect(Location::getRules())->only([
|
|
||||||
'long',
|
|
||||||
'short',
|
|
||||||
])->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rename fields to be more clear in error messages.
|
|
||||||
*/
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'long' => 'Location Description',
|
|
||||||
'short' => 'Location Identifier',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Api\Application\Locations;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
|
|
||||||
class UpdateLocationRequest extends StoreLocationRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Rules to validate this request against.
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
$locationId = $this->route()->parameter('location')->id;
|
|
||||||
|
|
||||||
return collect(Location::getRulesForUpdate($locationId))->only([
|
|
||||||
'short',
|
|
||||||
'long',
|
|
||||||
])->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,8 +10,6 @@ class GetDeployableNodesRequest extends GetNodesRequest
|
|||||||
'page' => 'integer',
|
'page' => 'integer',
|
||||||
'memory' => 'required|integer|min:0',
|
'memory' => 'required|integer|min:0',
|
||||||
'disk' => 'required|integer|min:0',
|
'disk' => 'required|integer|min:0',
|
||||||
'location_ids' => 'array',
|
|
||||||
'location_ids.*' => 'integer',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ class StoreNodeRequest extends ApplicationApiRequest
|
|||||||
return collect($rules ?? Node::getRules())->only([
|
return collect($rules ?? Node::getRules())->only([
|
||||||
'public',
|
'public',
|
||||||
'name',
|
'name',
|
||||||
'location_id',
|
|
||||||
'fqdn',
|
'fqdn',
|
||||||
'scheme',
|
'scheme',
|
||||||
'behind_proxy',
|
'behind_proxy',
|
||||||
@ -48,7 +47,6 @@ class StoreNodeRequest extends ApplicationApiRequest
|
|||||||
return [
|
return [
|
||||||
'daemon_base' => 'Daemon Base Path',
|
'daemon_base' => 'Daemon Base Path',
|
||||||
'upload_size' => 'File Upload Size Limit',
|
'upload_size' => 'File Upload Size Limit',
|
||||||
'location_id' => 'Location',
|
|
||||||
'public' => 'Node Visibility',
|
'public' => 'Node Visibility',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -123,10 +123,6 @@ class StoreServerRequest extends ApplicationApiRequest
|
|||||||
return !$input->deploy;
|
return !$input->deploy;
|
||||||
});
|
});
|
||||||
|
|
||||||
$validator->sometimes('deploy.locations', 'present', function ($input) {
|
|
||||||
return $input->deploy;
|
|
||||||
});
|
|
||||||
|
|
||||||
$validator->sometimes('deploy.port_range', 'present', function ($input) {
|
$validator->sometimes('deploy.port_range', 'present', function ($input) {
|
||||||
return $input->deploy;
|
return $input->deploy;
|
||||||
});
|
});
|
||||||
@ -143,7 +139,6 @@ class StoreServerRequest extends ApplicationApiRequest
|
|||||||
|
|
||||||
$object = new DeploymentObject();
|
$object = new DeploymentObject();
|
||||||
$object->setDedicated($this->input('deploy.dedicated_ip', false));
|
$object->setDedicated($this->input('deploy.dedicated_ip', false));
|
||||||
$object->setLocations($this->input('deploy.locations', []));
|
|
||||||
$object->setPorts($this->input('deploy.port_range', []));
|
$object->setPorts($this->input('deploy.port_range', []));
|
||||||
|
|
||||||
return $object;
|
return $object;
|
||||||
|
@ -25,7 +25,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
* @property int $r_nodes
|
* @property int $r_nodes
|
||||||
* @property int $r_allocations
|
* @property int $r_allocations
|
||||||
* @property int $r_users
|
* @property int $r_users
|
||||||
* @property int $r_locations
|
|
||||||
* @property int $r_eggs
|
* @property int $r_eggs
|
||||||
* @property int $r_database_hosts
|
* @property int $r_database_hosts
|
||||||
* @property int $r_server_databases
|
* @property int $r_server_databases
|
||||||
@ -46,7 +45,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRAllocations($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRAllocations($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRDatabaseHosts($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRDatabaseHosts($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereREggs($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereREggs($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRLocations($value)
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRNodes($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRNodes($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRServerDatabases($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRServerDatabases($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRServers($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ApiKey whereRServers($value)
|
||||||
@ -105,8 +103,6 @@ class ApiKey extends Model
|
|||||||
'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'int',
|
'r_' . AdminAcl::RESOURCE_DATABASE_HOSTS => 'int',
|
||||||
'r_' . AdminAcl::RESOURCE_SERVER_DATABASES => 'int',
|
'r_' . AdminAcl::RESOURCE_SERVER_DATABASES => 'int',
|
||||||
'r_' . AdminAcl::RESOURCE_EGGS => 'int',
|
'r_' . AdminAcl::RESOURCE_EGGS => 'int',
|
||||||
'r_' . AdminAcl::RESOURCE_LOCATIONS => 'int',
|
|
||||||
'r_' . AdminAcl::RESOURCE_EGGS => 'int',
|
|
||||||
'r_' . AdminAcl::RESOURCE_NODES => 'int',
|
'r_' . AdminAcl::RESOURCE_NODES => 'int',
|
||||||
'r_' . AdminAcl::RESOURCE_SERVERS => 'int',
|
'r_' . AdminAcl::RESOURCE_SERVERS => 'int',
|
||||||
];
|
];
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property int $id
|
|
||||||
* @property string $short
|
|
||||||
* @property string $long
|
|
||||||
* @property \Carbon\Carbon $created_at
|
|
||||||
* @property \Carbon\Carbon $updated_at
|
|
||||||
* @property \App\Models\Node[] $nodes
|
|
||||||
* @property \App\Models\Server[] $servers
|
|
||||||
*/
|
|
||||||
class Location extends Model
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The resource name for this model when it is transformed into an
|
|
||||||
* API representation using fractal.
|
|
||||||
*/
|
|
||||||
public const RESOURCE_NAME = 'location';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The table associated with the model.
|
|
||||||
*/
|
|
||||||
protected $table = 'locations';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fields that are not mass assignable.
|
|
||||||
*/
|
|
||||||
protected $guarded = ['id', 'created_at', 'updated_at'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules ensuring that the raw data stored in the database meets expectations.
|
|
||||||
*/
|
|
||||||
public static array $validationRules = [
|
|
||||||
'short' => 'required|string|between:1,60|unique:locations,short',
|
|
||||||
'long' => 'string|nullable|between:1,191',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getRouteKeyName(): string
|
|
||||||
{
|
|
||||||
return $this->getKeyName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the nodes in a specified location.
|
|
||||||
*/
|
|
||||||
public function nodes(): HasMany
|
|
||||||
{
|
|
||||||
return $this->hasMany(Node::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the servers within a given location.
|
|
||||||
*/
|
|
||||||
public function servers(): HasManyThrough
|
|
||||||
{
|
|
||||||
return $this->hasManyThrough(Server::class, Node::class);
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,6 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
|||||||
* @property bool $public
|
* @property bool $public
|
||||||
* @property string $name
|
* @property string $name
|
||||||
* @property string|null $description
|
* @property string|null $description
|
||||||
* @property int $location_id
|
|
||||||
* @property string $fqdn
|
* @property string $fqdn
|
||||||
* @property string $scheme
|
* @property string $scheme
|
||||||
* @property bool $behind_proxy
|
* @property bool $behind_proxy
|
||||||
@ -34,7 +33,6 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
|||||||
* @property string $daemonBase
|
* @property string $daemonBase
|
||||||
* @property \Carbon\Carbon $created_at
|
* @property \Carbon\Carbon $created_at
|
||||||
* @property \Carbon\Carbon $updated_at
|
* @property \Carbon\Carbon $updated_at
|
||||||
* @property \App\Models\Location $location
|
|
||||||
* @property \App\Models\Mount[]|\Illuminate\Database\Eloquent\Collection $mounts
|
* @property \App\Models\Mount[]|\Illuminate\Database\Eloquent\Collection $mounts
|
||||||
* @property \App\Models\Server[]|\Illuminate\Database\Eloquent\Collection $servers
|
* @property \App\Models\Server[]|\Illuminate\Database\Eloquent\Collection $servers
|
||||||
* @property \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations
|
* @property \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations
|
||||||
@ -66,7 +64,6 @@ class Node extends Model
|
|||||||
* Cast values to correct type.
|
* Cast values to correct type.
|
||||||
*/
|
*/
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'location_id' => 'integer',
|
|
||||||
'memory' => 'integer',
|
'memory' => 'integer',
|
||||||
'disk' => 'integer',
|
'disk' => 'integer',
|
||||||
'daemonListen' => 'integer',
|
'daemonListen' => 'integer',
|
||||||
@ -80,7 +77,7 @@ class Node extends Model
|
|||||||
* Fields that are mass assignable.
|
* Fields that are mass assignable.
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'public', 'name', 'location_id',
|
'public', 'name',
|
||||||
'fqdn', 'scheme', 'behind_proxy',
|
'fqdn', 'scheme', 'behind_proxy',
|
||||||
'memory', 'memory_overallocate', 'disk',
|
'memory', 'memory_overallocate', 'disk',
|
||||||
'disk_overallocate', 'upload_size', 'daemonBase',
|
'disk_overallocate', 'upload_size', 'daemonBase',
|
||||||
@ -91,7 +88,6 @@ class Node extends Model
|
|||||||
public static array $validationRules = [
|
public static array $validationRules = [
|
||||||
'name' => 'required|regex:/^([\w .-]{1,100})$/',
|
'name' => 'required|regex:/^([\w .-]{1,100})$/',
|
||||||
'description' => 'string|nullable',
|
'description' => 'string|nullable',
|
||||||
'location_id' => 'required|exists:locations,id',
|
|
||||||
'public' => 'boolean',
|
'public' => 'boolean',
|
||||||
'fqdn' => 'required|string',
|
'fqdn' => 'required|string',
|
||||||
'scheme' => 'required',
|
'scheme' => 'required',
|
||||||
@ -196,14 +192,6 @@ class Node extends Model
|
|||||||
return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id');
|
return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the location associated with a node.
|
|
||||||
*/
|
|
||||||
public function location(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Location::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the servers associated with a node.
|
* Gets the servers associated with a node.
|
||||||
*/
|
*/
|
||||||
|
@ -6,8 +6,6 @@ class DeploymentObject
|
|||||||
{
|
{
|
||||||
private bool $dedicated = false;
|
private bool $dedicated = false;
|
||||||
|
|
||||||
private array $locations = [];
|
|
||||||
|
|
||||||
private array $ports = [];
|
private array $ports = [];
|
||||||
|
|
||||||
public function isDedicated(): bool
|
public function isDedicated(): bool
|
||||||
@ -22,18 +20,6 @@ class DeploymentObject
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLocations(): array
|
|
||||||
{
|
|
||||||
return $this->locations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLocations(array $locations): self
|
|
||||||
{
|
|
||||||
$this->locations = $locations;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPorts(): array
|
public function getPorts(): array
|
||||||
{
|
{
|
||||||
return $this->ports;
|
return $this->ports;
|
||||||
|
@ -289,16 +289,6 @@ class Server extends Model
|
|||||||
return $this->hasMany(Database::class);
|
return $this->hasMany(Database::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the location that a server belongs to.
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function location(): \Znck\Eloquent\Relations\BelongsToThrough
|
|
||||||
{
|
|
||||||
return $this->belongsToThrough(Location::class, Node::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the associated server transfer.
|
* Returns the associated server transfer.
|
||||||
*/
|
*/
|
||||||
|
@ -12,7 +12,6 @@ use App\Repositories\Eloquent\ServerRepository;
|
|||||||
use App\Repositories\Eloquent\SessionRepository;
|
use App\Repositories\Eloquent\SessionRepository;
|
||||||
use App\Repositories\Eloquent\SubuserRepository;
|
use App\Repositories\Eloquent\SubuserRepository;
|
||||||
use App\Repositories\Eloquent\DatabaseRepository;
|
use App\Repositories\Eloquent\DatabaseRepository;
|
||||||
use App\Repositories\Eloquent\LocationRepository;
|
|
||||||
use App\Repositories\Eloquent\ScheduleRepository;
|
use App\Repositories\Eloquent\ScheduleRepository;
|
||||||
use App\Repositories\Eloquent\SettingsRepository;
|
use App\Repositories\Eloquent\SettingsRepository;
|
||||||
use App\Repositories\Eloquent\AllocationRepository;
|
use App\Repositories\Eloquent\AllocationRepository;
|
||||||
@ -28,7 +27,6 @@ use App\Repositories\Eloquent\ServerVariableRepository;
|
|||||||
use App\Contracts\Repository\SessionRepositoryInterface;
|
use App\Contracts\Repository\SessionRepositoryInterface;
|
||||||
use App\Contracts\Repository\SubuserRepositoryInterface;
|
use App\Contracts\Repository\SubuserRepositoryInterface;
|
||||||
use App\Contracts\Repository\DatabaseRepositoryInterface;
|
use App\Contracts\Repository\DatabaseRepositoryInterface;
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
use App\Contracts\Repository\ScheduleRepositoryInterface;
|
use App\Contracts\Repository\ScheduleRepositoryInterface;
|
||||||
use App\Contracts\Repository\SettingsRepositoryInterface;
|
use App\Contracts\Repository\SettingsRepositoryInterface;
|
||||||
use App\Contracts\Repository\AllocationRepositoryInterface;
|
use App\Contracts\Repository\AllocationRepositoryInterface;
|
||||||
@ -50,7 +48,6 @@ class RepositoryServiceProvider extends ServiceProvider
|
|||||||
$this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class);
|
$this->app->bind(DatabaseHostRepositoryInterface::class, DatabaseHostRepository::class);
|
||||||
$this->app->bind(EggRepositoryInterface::class, EggRepository::class);
|
$this->app->bind(EggRepositoryInterface::class, EggRepository::class);
|
||||||
$this->app->bind(EggVariableRepositoryInterface::class, EggVariableRepository::class);
|
$this->app->bind(EggVariableRepositoryInterface::class, EggVariableRepository::class);
|
||||||
$this->app->bind(LocationRepositoryInterface::class, LocationRepository::class);
|
|
||||||
$this->app->bind(NodeRepositoryInterface::class, NodeRepository::class);
|
$this->app->bind(NodeRepositoryInterface::class, NodeRepository::class);
|
||||||
$this->app->bind(ScheduleRepositoryInterface::class, ScheduleRepository::class);
|
$this->app->bind(ScheduleRepositoryInterface::class, ScheduleRepository::class);
|
||||||
$this->app->bind(ServerRepositoryInterface::class, ServerRepository::class);
|
$this->app->bind(ServerRepositoryInterface::class, ServerRepository::class);
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repositories\Eloquent;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
||||||
use App\Exceptions\Repository\RecordNotFoundException;
|
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
|
|
||||||
class LocationRepository extends EloquentRepository implements LocationRepositoryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the model backing this repository.
|
|
||||||
*/
|
|
||||||
public function model(): string
|
|
||||||
{
|
|
||||||
return Location::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return locations with a count of nodes and servers attached to it.
|
|
||||||
*/
|
|
||||||
public function getAllWithDetails(): Collection
|
|
||||||
{
|
|
||||||
return $this->getBuilder()->withCount('nodes', 'servers')->get($this->getColumns());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all the available locations with the nodes as a relationship.
|
|
||||||
*/
|
|
||||||
public function getAllWithNodes(): Collection
|
|
||||||
{
|
|
||||||
return $this->getBuilder()->with('nodes')->get($this->getColumns());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all the nodes and their respective count of servers for a location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function getWithNodes(int $id): Location
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return $this->getBuilder()->with('nodes.servers')->findOrFail($id, $this->getColumns());
|
|
||||||
} catch (ModelNotFoundException) {
|
|
||||||
throw new RecordNotFoundException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a location and the count of nodes in that location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function getWithNodeCount(int $id): Location
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return $this->getBuilder()->withCount('nodes')->findOrFail($id, $this->getColumns());
|
|
||||||
} catch (ModelNotFoundException) {
|
|
||||||
throw new RecordNotFoundException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -73,14 +73,10 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a single node with location and server information.
|
* Return a single node with server information.
|
||||||
*/
|
*/
|
||||||
public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node
|
public function loadServerCount(Node $node, bool $refresh = false): Node
|
||||||
{
|
{
|
||||||
if (!$node->relationLoaded('location') || $refresh) {
|
|
||||||
$node->load('location');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is quite ugly and can probably be improved down the road.
|
// This is quite ugly and can probably be improved down the road.
|
||||||
// And by probably, I mean it should.
|
// And by probably, I mean it should.
|
||||||
if (is_null($node->servers_count) || $refresh) {
|
if (is_null($node->servers_count) || $refresh) {
|
||||||
|
@ -28,7 +28,6 @@ class AdminAcl
|
|||||||
public const RESOURCE_NODES = 'nodes';
|
public const RESOURCE_NODES = 'nodes';
|
||||||
public const RESOURCE_ALLOCATIONS = 'allocations';
|
public const RESOURCE_ALLOCATIONS = 'allocations';
|
||||||
public const RESOURCE_USERS = 'users';
|
public const RESOURCE_USERS = 'users';
|
||||||
public const RESOURCE_LOCATIONS = 'locations';
|
|
||||||
public const RESOURCE_EGGS = 'eggs';
|
public const RESOURCE_EGGS = 'eggs';
|
||||||
public const RESOURCE_DATABASE_HOSTS = 'database_hosts';
|
public const RESOURCE_DATABASE_HOSTS = 'database_hosts';
|
||||||
public const RESOURCE_SERVER_DATABASES = 'server_databases';
|
public const RESOURCE_SERVER_DATABASES = 'server_databases';
|
||||||
|
@ -10,22 +10,9 @@ use App\Exceptions\Service\Deployment\NoViableNodeException;
|
|||||||
|
|
||||||
class FindViableNodesService
|
class FindViableNodesService
|
||||||
{
|
{
|
||||||
protected array $locations = [];
|
|
||||||
protected ?int $disk = null;
|
protected ?int $disk = null;
|
||||||
protected ?int $memory = null;
|
protected ?int $memory = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the locations that should be searched through to locate available nodes.
|
|
||||||
*/
|
|
||||||
public function setLocations(array $locations): self
|
|
||||||
{
|
|
||||||
Assert::allIntegerish($locations, 'An array of location IDs should be provided when calling setLocations.');
|
|
||||||
|
|
||||||
$this->locations = $locations;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the amount of disk that will be used by the server being created. Nodes will be
|
* Set the amount of disk that will be used by the server being created. Nodes will be
|
||||||
* filtered out if they do not have enough available free disk space for this server
|
* filtered out if they do not have enough available free disk space for this server
|
||||||
@ -77,10 +64,6 @@ class FindViableNodesService
|
|||||||
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
||||||
->where('nodes.public', 1);
|
->where('nodes.public', 1);
|
||||||
|
|
||||||
if (!empty($this->locations)) {
|
|
||||||
$query = $query->whereIn('nodes.location_id', $this->locations);
|
|
||||||
}
|
|
||||||
|
|
||||||
$results = $query->groupBy('nodes.id')
|
$results = $query->groupBy('nodes.id')
|
||||||
->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [$this->memory])
|
->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [$this->memory])
|
||||||
->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [$this->disk]);
|
->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [$this->disk]);
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services\Locations;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
|
|
||||||
class LocationCreationService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* LocationCreationService constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(protected LocationRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Model\DataValidationException
|
|
||||||
*/
|
|
||||||
public function handle(array $data): Location
|
|
||||||
{
|
|
||||||
return $this->repository->create($data);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services\Locations;
|
|
||||||
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
use App\Models\Location;
|
|
||||||
use App\Contracts\Repository\NodeRepositoryInterface;
|
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
use App\Exceptions\Service\Location\HasActiveNodesException;
|
|
||||||
|
|
||||||
class LocationDeletionService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* LocationDeletionService constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
protected LocationRepositoryInterface $repository,
|
|
||||||
protected NodeRepositoryInterface $nodeRepository
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an existing location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Service\Location\HasActiveNodesException
|
|
||||||
*/
|
|
||||||
public function handle(Location|int $location): ?int
|
|
||||||
{
|
|
||||||
$location = ($location instanceof Location) ? $location->id : $location;
|
|
||||||
|
|
||||||
Assert::integerish($location, 'First argument passed to handle must be numeric or an instance of ' . Location::class . ', received %s.');
|
|
||||||
|
|
||||||
$count = $this->nodeRepository->findCountWhere([['location_id', '=', $location]]);
|
|
||||||
if ($count > 0) {
|
|
||||||
throw new HasActiveNodesException(trans('exceptions.locations.has_nodes'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->repository->delete($location);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services\Locations;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
use App\Contracts\Repository\LocationRepositoryInterface;
|
|
||||||
|
|
||||||
class LocationUpdateService
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* LocationUpdateService constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(protected LocationRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update an existing location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Model\DataValidationException
|
|
||||||
* @throws \App\Exceptions\Repository\RecordNotFoundException
|
|
||||||
*/
|
|
||||||
public function handle(Location|int $location, array $data): Location
|
|
||||||
{
|
|
||||||
$location = ($location instanceof Location) ? $location->id : $location;
|
|
||||||
|
|
||||||
return $this->repository->update($location, $data);
|
|
||||||
}
|
|
||||||
}
|
|
@ -66,7 +66,6 @@ class EnvironmentService
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'STARTUP' => 'startup',
|
'STARTUP' => 'startup',
|
||||||
'P_SERVER_LOCATION' => 'location.short',
|
|
||||||
'P_SERVER_UUID' => 'uuid',
|
'P_SERVER_UUID' => 'uuid',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ class ServerCreationService
|
|||||||
private function configureDeployment(array $data, DeploymentObject $deployment): Allocation
|
private function configureDeployment(array $data, DeploymentObject $deployment): Allocation
|
||||||
{
|
{
|
||||||
/** @var \Illuminate\Support\Collection $nodes */
|
/** @var \Illuminate\Support\Collection $nodes */
|
||||||
$nodes = $this->findViableNodesService->setLocations($deployment->getLocations())
|
$nodes = $this->findViableNodesService
|
||||||
->setDisk(Arr::get($data, 'disk'))
|
->setDisk(Arr::get($data, 'disk'))
|
||||||
->setMemory(Arr::get($data, 'memory'))
|
->setMemory(Arr::get($data, 'memory'))
|
||||||
->handle();
|
->handle();
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Transformers\Api\Application;
|
|
||||||
|
|
||||||
use App\Models\Location;
|
|
||||||
use League\Fractal\Resource\Collection;
|
|
||||||
use League\Fractal\Resource\NullResource;
|
|
||||||
use App\Services\Acl\Api\AdminAcl;
|
|
||||||
|
|
||||||
class LocationTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* List of resources that can be included.
|
|
||||||
*/
|
|
||||||
protected array $availableIncludes = ['nodes', 'servers'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the resource name for the JSONAPI output.
|
|
||||||
*/
|
|
||||||
public function getResourceName(): string
|
|
||||||
{
|
|
||||||
return Location::RESOURCE_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a generic transformed location array.
|
|
||||||
*/
|
|
||||||
public function transform(Location $location): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => $location->id,
|
|
||||||
'short' => $location->short,
|
|
||||||
'long' => $location->long,
|
|
||||||
$location->getUpdatedAtColumn() => $this->formatTimestamp($location->updated_at),
|
|
||||||
$location->getCreatedAtColumn() => $this->formatTimestamp($location->created_at),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the nodes associated with this location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
|
|
||||||
*/
|
|
||||||
public function includeServers(Location $location): Collection|NullResource
|
|
||||||
{
|
|
||||||
if (!$this->authorize(AdminAcl::RESOURCE_SERVERS)) {
|
|
||||||
return $this->null();
|
|
||||||
}
|
|
||||||
|
|
||||||
$location->loadMissing('servers');
|
|
||||||
|
|
||||||
return $this->collection($location->getRelation('servers'), $this->makeTransformer(ServerTransformer::class), 'server');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the nodes associated with this location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
|
|
||||||
*/
|
|
||||||
public function includeNodes(Location $location): Collection|NullResource
|
|
||||||
{
|
|
||||||
if (!$this->authorize(AdminAcl::RESOURCE_NODES)) {
|
|
||||||
return $this->null();
|
|
||||||
}
|
|
||||||
|
|
||||||
$location->loadMissing('nodes');
|
|
||||||
|
|
||||||
return $this->collection($location->getRelation('nodes'), $this->makeTransformer(NodeTransformer::class), 'node');
|
|
||||||
}
|
|
||||||
}
|
|
@ -70,26 +70,6 @@ class NodeTransformer extends BaseTransformer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the nodes associated with this location.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
|
|
||||||
*/
|
|
||||||
public function includeLocation(Node $node): Item|NullResource
|
|
||||||
{
|
|
||||||
if (!$this->authorize(AdminAcl::RESOURCE_LOCATIONS)) {
|
|
||||||
return $this->null();
|
|
||||||
}
|
|
||||||
|
|
||||||
$node->loadMissing('location');
|
|
||||||
|
|
||||||
return $this->item(
|
|
||||||
$node->getRelation('location'),
|
|
||||||
$this->makeTransformer(LocationTransformer::class),
|
|
||||||
'location'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the nodes associated with this location.
|
* Return the nodes associated with this location.
|
||||||
*
|
*
|
||||||
|
@ -169,22 +169,6 @@ class ServerTransformer extends BaseTransformer
|
|||||||
return $this->collection($server->getRelation('variables'), $this->makeTransformer(ServerVariableTransformer::class), 'server_variable');
|
return $this->collection($server->getRelation('variables'), $this->makeTransformer(ServerVariableTransformer::class), 'server_variable');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a generic array with location information for this server.
|
|
||||||
*
|
|
||||||
* @throws \App\Exceptions\Transformer\InvalidTransformerLevelException
|
|
||||||
*/
|
|
||||||
public function includeLocation(Server $server): Item|NullResource
|
|
||||||
{
|
|
||||||
if (!$this->authorize(AdminAcl::RESOURCE_LOCATIONS)) {
|
|
||||||
return $this->null();
|
|
||||||
}
|
|
||||||
|
|
||||||
$server->loadMissing('location');
|
|
||||||
|
|
||||||
return $this->item($server->getRelation('location'), $this->makeTransformer(LocationTransformer::class), 'location');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a generic array with node information for this server.
|
* Return a generic array with node information for this server.
|
||||||
*
|
*
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Factories;
|
|
||||||
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
||||||
|
|
||||||
class LocationFactory extends Factory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name of the factory's corresponding model.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $model = Location::class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the model's default state.
|
|
||||||
*/
|
|
||||||
public function definition(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'short' => Str::random(8),
|
|
||||||
'long' => Str::random(32),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('nodes', function (Blueprint $table) {
|
||||||
|
$table->dropForeign('nodes_location_id_foreign');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::drop('locations');
|
||||||
|
|
||||||
|
Schema::table('api_keys', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('r_locations');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::create('locations', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->string('short');
|
||||||
|
$table->text('long')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('api_keys', function (Blueprint $table) {
|
||||||
|
$table->unsignedTinyInteger('r_locations')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -8,7 +8,6 @@ return [
|
|||||||
'notices' => [
|
'notices' => [
|
||||||
'allocations_added' => 'Allocations have successfully been added to this node.',
|
'allocations_added' => 'Allocations have successfully been added to this node.',
|
||||||
'node_deleted' => 'Node has been successfully removed from the panel.',
|
'node_deleted' => 'Node has been successfully removed from the panel.',
|
||||||
'location_required' => 'You must have at least one location configured before you can add a node to this panel.',
|
|
||||||
'node_created' => 'Successfully created new node. You can automatically configure the daemon on this machine by visiting the \'Configuration\' tab. <strong>Before you can add any servers you must first allocate at least one IP address and port.</strong>',
|
'node_created' => 'Successfully created new node. You can automatically configure the daemon on this machine by visiting the \'Configuration\' tab. <strong>Before you can add any servers you must first allocate at least one IP address and port.</strong>',
|
||||||
'node_updated' => 'Node information has been updated. If any daemon settings were changed you will need to reboot it for those changes to take effect.',
|
'node_updated' => 'Node information has been updated. If any daemon settings were changed you will need to reboot it for those changes to take effect.',
|
||||||
'unallocated_deleted' => 'Deleted all un-allocated ports for <code>:ip</code>.',
|
'unallocated_deleted' => 'Deleted all un-allocated ports for <code>:ip</code>.',
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'location' => [
|
|
||||||
'no_location_found' => 'Could not locate a record matching the provided short code.',
|
|
||||||
'ask_short' => 'Location Short Code',
|
|
||||||
'ask_long' => 'Location Description',
|
|
||||||
'created' => 'Successfully created a new location (:name) with an ID of :id.',
|
|
||||||
'deleted' => 'Successfully deleted the requested location.',
|
|
||||||
],
|
|
||||||
'user' => [
|
'user' => [
|
||||||
'search_users' => 'Enter a Username, User ID, or Email Address',
|
'search_users' => 'Enter a Username, User ID, or Email Address',
|
||||||
'select_search_user' => 'ID of user to delete (Enter \'0\' to re-search)',
|
'select_search_user' => 'ID of user to delete (Enter \'0\' to re-search)',
|
||||||
|
@ -99,12 +99,8 @@
|
|||||||
<label for="pNodeId" class="form-label">Linked Node</label>
|
<label for="pNodeId" class="form-label">Linked Node</label>
|
||||||
<select name="node_id" id="pNodeId" class="form-control">
|
<select name="node_id" id="pNodeId" class="form-control">
|
||||||
<option value="">None</option>
|
<option value="">None</option>
|
||||||
@foreach($locations as $location)
|
@foreach($nodes as $node)
|
||||||
<optgroup label="{{ $location->short }}">
|
<option value="{{ $node->id }}">{{ $node->name }}</option>
|
||||||
@foreach($location->nodes as $node)
|
|
||||||
<option value="{{ $node->id }}">{{ $node->name }}</option>
|
|
||||||
@endforeach
|
|
||||||
</optgroup>
|
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
<p class="text-muted small">This setting does nothing other than default to this database host when adding a database to a server on the selected node.</p>
|
<p class="text-muted small">This setting does nothing other than default to this database host when adding a database to a server on the selected node.</p>
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
@extends('layouts.admin')
|
|
||||||
|
|
||||||
@section('title')
|
|
||||||
Locations
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('content-header')
|
|
||||||
<h1>Locations<small>All locations that nodes can be assigned to for easier categorization.</small></h1>
|
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li><a href="{{ route('admin.index') }}">Admin</a></li>
|
|
||||||
<li class="active">Locations</li>
|
|
||||||
</ol>
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('content')
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<div class="box box-primary">
|
|
||||||
<div class="box-header with-border">
|
|
||||||
<h3 class="box-title">Location List</h3>
|
|
||||||
<div class="box-tools">
|
|
||||||
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#newLocationModal">Create New</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box-body table-responsive no-padding">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Short Code</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th class="text-center">Nodes</th>
|
|
||||||
<th class="text-center">Servers</th>
|
|
||||||
</tr>
|
|
||||||
@foreach ($locations as $location)
|
|
||||||
<tr>
|
|
||||||
<td><code>{{ $location->id }}</code></td>
|
|
||||||
<td><a href="{{ route('admin.locations.view', $location->id) }}">{{ $location->short }}</a></td>
|
|
||||||
<td>{{ $location->long }}</td>
|
|
||||||
<td class="text-center">{{ $location->nodes_count }}</td>
|
|
||||||
<td class="text-center">{{ $location->servers_count }}</td>
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal fade" id="newLocationModal" tabindex="-1" role="dialog">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<form action="{{ route('admin.locations') }}" method="POST">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
||||||
<h4 class="modal-title">Create Location</h4>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<label for="pShortModal" class="form-label">Short Code</label>
|
|
||||||
<input type="text" name="short" id="pShortModal" class="form-control" />
|
|
||||||
<p class="text-muted small">A short identifier used to distinguish this location from others. Must be between 1 and 60 characters, for example, <code>us.nyc.lvl3</code>.</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-12">
|
|
||||||
<label for="pLongModal" class="form-label">Description</label>
|
|
||||||
<textarea name="long" id="pLongModal" class="form-control" rows="4"></textarea>
|
|
||||||
<p class="text-muted small">A longer description of this location. Must be less than 191 characters.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
{!! csrf_field() !!}
|
|
||||||
<button type="button" class="btn btn-default btn-sm pull-left" data-dismiss="modal">Cancel</button>
|
|
||||||
<button type="submit" class="btn btn-success btn-sm">Create</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endsection
|
|
@ -1,69 +0,0 @@
|
|||||||
@extends('layouts.admin')
|
|
||||||
|
|
||||||
@section('title')
|
|
||||||
Locations → View → {{ $location->short }}
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('content-header')
|
|
||||||
<h1>{{ $location->short }}<small>{{ str_limit($location->long, 75) }}</small></h1>
|
|
||||||
<ol class="breadcrumb">
|
|
||||||
<li><a href="{{ route('admin.index') }}">Admin</a></li>
|
|
||||||
<li><a href="{{ route('admin.locations') }}">Locations</a></li>
|
|
||||||
<li class="active">{{ $location->short }}</li>
|
|
||||||
</ol>
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('content')
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="box box-primary">
|
|
||||||
<div class="box-header with-border">
|
|
||||||
<h3 class="box-title">Location Details</h3>
|
|
||||||
</div>
|
|
||||||
<form action="{{ route('admin.locations.view', $location->id) }}" method="POST">
|
|
||||||
<div class="box-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="pShort" class="form-label">Short Code</label>
|
|
||||||
<input type="text" id="pShort" name="short" class="form-control" value="{{ $location->short }}" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="pLong" class="form-label">Description</label>
|
|
||||||
<textarea id="pLong" name="long" class="form-control" rows="4">{{ $location->long }}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box-footer">
|
|
||||||
{!! csrf_field() !!}
|
|
||||||
{!! method_field('PATCH') !!}
|
|
||||||
<button name="action" value="edit" class="btn btn-sm btn-primary pull-right">Save</button>
|
|
||||||
<button name="action" value="delete" class="btn btn-sm btn-danger pull-left muted muted-hover"><i class="fa fa-trash-o"></i></button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-header with-border">
|
|
||||||
<h3 class="box-title">Nodes</h3>
|
|
||||||
</div>
|
|
||||||
<div class="box-body table-responsive no-padding">
|
|
||||||
<table class="table table-hover">
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>FQDN</th>
|
|
||||||
<th>Servers</th>
|
|
||||||
</tr>
|
|
||||||
@foreach($location->nodes as $node)
|
|
||||||
<tr>
|
|
||||||
<td><code>{{ $node->id }}</code></td>
|
|
||||||
<td><a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></td>
|
|
||||||
<td><code>{{ $node->fqdn }}</code></td>
|
|
||||||
<td>{{ $node->servers->count() }}</td>
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endsection
|
|
@ -41,7 +41,6 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Location</th>
|
|
||||||
<th>Memory</th>
|
<th>Memory</th>
|
||||||
<th>Disk</th>
|
<th>Disk</th>
|
||||||
<th class="text-center">Servers</th>
|
<th class="text-center">Servers</th>
|
||||||
@ -52,7 +51,6 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="text-center text-muted left-icon" data-action="ping" data-secret="{{ $node->getDecryptedKey() }}" data-location="{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/api/system"><i class="fa fa-fw fa-refresh fa-spin"></i></td>
|
<td class="text-center text-muted left-icon" data-action="ping" data-secret="{{ $node->getDecryptedKey() }}" data-location="{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/api/system"><i class="fa fa-fw fa-refresh fa-spin"></i></td>
|
||||||
<td>{!! $node->maintenance_mode ? '<span class="label label-warning"><i class="fa fa-wrench"></i></span> ' : '' !!}<a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></td>
|
<td>{!! $node->maintenance_mode ? '<span class="label label-warning"><i class="fa fa-wrench"></i></span> ' : '' !!}<a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></td>
|
||||||
<td>{{ $node->location->short }}</td>
|
|
||||||
<td>{{ $node->memory }} MiB</td>
|
<td>{{ $node->memory }} MiB</td>
|
||||||
<td>{{ $node->disk }} MiB</td>
|
<td>{{ $node->disk }} MiB</td>
|
||||||
<td class="text-center">{{ $node->servers_count }}</td>
|
<td class="text-center">{{ $node->servers_count }}</td>
|
||||||
|
@ -31,14 +31,6 @@
|
|||||||
<label for="pDescription" class="form-label">Description</label>
|
<label for="pDescription" class="form-label">Description</label>
|
||||||
<textarea name="description" id="pDescription" rows="4" class="form-control">{{ old('description') }}</textarea>
|
<textarea name="description" id="pDescription" rows="4" class="form-control">{{ old('description') }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="pLocationId" class="form-label">Location</label>
|
|
||||||
<select name="location_id" id="pLocationId">
|
|
||||||
@foreach($locations as $location)
|
|
||||||
<option value="{{ $location->id }}" {{ $location->id != old('location_id') ?: 'selected' }}>{{ $location->short }}</option>
|
|
||||||
@endforeach
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Node Visibility</label>
|
<label class="form-label">Node Visibility</label>
|
||||||
<div>
|
<div>
|
||||||
@ -166,10 +158,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('footer-scripts')
|
|
||||||
@parent
|
|
||||||
<script>
|
|
||||||
$('#pLocationId').select2();
|
|
||||||
</script>
|
|
||||||
@endsection
|
|
||||||
|
@ -49,16 +49,6 @@
|
|||||||
<textarea name="description" id="description" rows="4" class="form-control">{{ $node->description }}</textarea>
|
<textarea name="description" id="description" rows="4" class="form-control">{{ $node->description }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-xs-12">
|
|
||||||
<label for="name" class="control-label">Location</label>
|
|
||||||
<div>
|
|
||||||
<select name="location_id" class="form-control">
|
|
||||||
@foreach($locations as $location)
|
|
||||||
<option value="{{ $location->id }}" {{ (old('location_id', $node->location_id) === $location->id) ? 'selected' : '' }}>{{ $location->long }} ({{ $location->short }})</option>
|
|
||||||
@endforeach
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group col-xs-12">
|
<div class="form-group col-xs-12">
|
||||||
<label for="public" class="control-label">Allow Automatic Allocation <sup><a data-toggle="tooltip" data-placement="top" title="Allow automatic allocation to this Node?">?</a></sup></label>
|
<label for="public" class="control-label">Allow Automatic Allocation <sup><a data-toggle="tooltip" data-placement="top" title="Allow automatic allocation to this Node?">?</a></sup></label>
|
||||||
<div>
|
<div>
|
||||||
|
@ -68,16 +68,8 @@
|
|||||||
<div class="form-group col-sm-4">
|
<div class="form-group col-sm-4">
|
||||||
<label for="pNodeId">Node</label>
|
<label for="pNodeId">Node</label>
|
||||||
<select name="node_id" id="pNodeId" class="form-control">
|
<select name="node_id" id="pNodeId" class="form-control">
|
||||||
@foreach($locations as $location)
|
@foreach($nodes as $node)
|
||||||
<optgroup label="{{ $location->long }} ({{ $location->short }})">
|
<option value="{{ $node->id }}">{{ $node->name }}</option>
|
||||||
@foreach($location->nodes as $node)
|
|
||||||
|
|
||||||
<option value="{{ $node->id }}"
|
|
||||||
@if($location->id === old('location_id')) selected @endif
|
|
||||||
>{{ $node->name }}</option>
|
|
||||||
|
|
||||||
@endforeach
|
|
||||||
</optgroup>
|
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
@if($canTransfer)
|
@if(!$canTransfer)
|
||||||
<button class="btn btn-success" data-toggle="modal" data-target="#transferServerModal">Transfer Server</button>
|
<button class="btn btn-success" data-toggle="modal" data-target="#transferServerModal">Transfer Server</button>
|
||||||
@else
|
@else
|
||||||
<button class="btn btn-success disabled">Transfer Server</button>
|
<button class="btn btn-success disabled">Transfer Server</button>
|
||||||
@ -150,18 +150,10 @@
|
|||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="pNodeId">Node</label>
|
<label for="pNodeId">Node</label>
|
||||||
<select name="node_id" id="pNodeId" class="form-control">
|
<select name="node_id" id="pNodeId" class="form-control">
|
||||||
@foreach($locations as $location)
|
@foreach($nodes as $node)
|
||||||
<optgroup label="{{ $location->long }} ({{ $location->short }})">
|
@if($node->id != $server->node_id)
|
||||||
@foreach($location->nodes as $node)
|
<option value="{{ $node->id }}">{{ $node->name }}</option>
|
||||||
|
@endif
|
||||||
@if($node->id != $server->node_id)
|
|
||||||
<option value="{{ $node->id }}"
|
|
||||||
@if($location->id === old('location_id')) selected @endif
|
|
||||||
>{{ $node->name }}</option>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@endforeach
|
|
||||||
</optgroup>
|
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
<p class="small text-muted no-margin">The node which this server will be transferred to.</p>
|
<p class="small text-muted no-margin">The node which this server will be transferred to.</p>
|
||||||
|
@ -80,11 +80,6 @@
|
|||||||
<i class="fa fa-database"></i> <span>Databases</span>
|
<i class="fa fa-database"></i> <span>Databases</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="{{ ! starts_with(Route::currentRouteName(), 'admin.locations') ?: 'active' }}">
|
|
||||||
<a href="{{ route('admin.locations') }}">
|
|
||||||
<i class="fa fa-globe"></i> <span>Locations</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="{{ ! starts_with(Route::currentRouteName(), 'admin.nodes') ?: 'active' }}">
|
<li class="{{ ! starts_with(Route::currentRouteName(), 'admin.nodes') ?: 'active' }}">
|
||||||
<a href="{{ route('admin.nodes') }}">
|
<a href="{{ route('admin.nodes') }}">
|
||||||
<i class="fa fa-sitemap"></i> <span>Nodes</span>
|
<i class="fa fa-sitemap"></i> <span>Nodes</span>
|
||||||
|
@ -8,7 +8,7 @@ Route::get('/', [Admin\BaseController::class, 'index'])->name('admin.index');
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Location Controller Routes
|
| API Controller Routes
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| Endpoint: /admin/api
|
| Endpoint: /admin/api
|
||||||
@ -23,22 +23,6 @@ Route::group(['prefix' => 'api'], function () {
|
|||||||
Route::delete('/revoke/{identifier}', [Admin\ApiController::class, 'delete'])->name('admin.api.delete');
|
Route::delete('/revoke/{identifier}', [Admin\ApiController::class, 'delete'])->name('admin.api.delete');
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Location Controller Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Endpoint: /admin/locations
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
Route::group(['prefix' => 'locations'], function () {
|
|
||||||
Route::get('/', [Admin\LocationController::class, 'index'])->name('admin.locations');
|
|
||||||
Route::get('/view/{location:id}', [Admin\LocationController::class, 'view'])->name('admin.locations.view');
|
|
||||||
|
|
||||||
Route::post('/', [Admin\LocationController::class, 'create']);
|
|
||||||
Route::patch('/view/{location:id}', [Admin\LocationController::class, 'update']);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Database Controller Routes
|
| Database Controller Routes
|
||||||
|
@ -48,24 +48,6 @@ Route::group(['prefix' => '/nodes'], function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Location Controller Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Endpoint: /api/application/locations
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
Route::group(['prefix' => '/locations'], function () {
|
|
||||||
Route::get('/', [Application\Locations\LocationController::class, 'index'])->name('api.applications.locations');
|
|
||||||
Route::get('/{location:id}', [Application\Locations\LocationController::class, 'view'])->name('api.application.locations.view');
|
|
||||||
|
|
||||||
Route::post('/', [Application\Locations\LocationController::class, 'store']);
|
|
||||||
Route::patch('/{location:id}', [Application\Locations\LocationController::class, 'update']);
|
|
||||||
|
|
||||||
Route::delete('/{location:id}', [Application\Locations\LocationController::class, 'delete']);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Server Controller Routes
|
| Server Controller Routes
|
||||||
|
@ -84,7 +84,6 @@ abstract class ApplicationApiIntegrationTestCase extends IntegrationTestCase
|
|||||||
'r_nodes' => AdminAcl::READ | AdminAcl::WRITE,
|
'r_nodes' => AdminAcl::READ | AdminAcl::WRITE,
|
||||||
'r_allocations' => AdminAcl::READ | AdminAcl::WRITE,
|
'r_allocations' => AdminAcl::READ | AdminAcl::WRITE,
|
||||||
'r_users' => AdminAcl::READ | AdminAcl::WRITE,
|
'r_users' => AdminAcl::READ | AdminAcl::WRITE,
|
||||||
'r_locations' => AdminAcl::READ | AdminAcl::WRITE,
|
|
||||||
'r_eggs' => AdminAcl::READ | AdminAcl::WRITE,
|
'r_eggs' => AdminAcl::READ | AdminAcl::WRITE,
|
||||||
'r_database_hosts' => AdminAcl::READ | AdminAcl::WRITE,
|
'r_database_hosts' => AdminAcl::READ | AdminAcl::WRITE,
|
||||||
'r_server_databases' => AdminAcl::READ | AdminAcl::WRITE,
|
'r_server_databases' => AdminAcl::READ | AdminAcl::WRITE,
|
||||||
|
@ -1,268 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Tests\Integration\Api\Application\Location;
|
|
||||||
|
|
||||||
use App\Models\Node;
|
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use App\Models\Location;
|
|
||||||
use App\Transformers\Api\Application\NodeTransformer;
|
|
||||||
use App\Transformers\Api\Application\ServerTransformer;
|
|
||||||
use App\Transformers\Api\Application\LocationTransformer;
|
|
||||||
use App\Tests\Integration\Api\Application\ApplicationApiIntegrationTestCase;
|
|
||||||
|
|
||||||
class LocationControllerTest extends ApplicationApiIntegrationTestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Test getting all locations through the API.
|
|
||||||
*/
|
|
||||||
public function testGetLocations()
|
|
||||||
{
|
|
||||||
$locations = Location::factory()->times(2)->create();
|
|
||||||
|
|
||||||
$response = $this->getJson('/api/application/locations?per_page=60');
|
|
||||||
$response->assertStatus(Response::HTTP_OK);
|
|
||||||
$response->assertJsonCount(2, 'data');
|
|
||||||
$response->assertJsonStructure([
|
|
||||||
'object',
|
|
||||||
'data' => [
|
|
||||||
['object', 'attributes' => ['id', 'short', 'long', 'created_at', 'updated_at']],
|
|
||||||
['object', 'attributes' => ['id', 'short', 'long', 'created_at', 'updated_at']],
|
|
||||||
],
|
|
||||||
'meta' => ['pagination' => ['total', 'count', 'per_page', 'current_page', 'total_pages']],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response
|
|
||||||
->assertJson([
|
|
||||||
'object' => 'list',
|
|
||||||
'data' => [[], []],
|
|
||||||
'meta' => [
|
|
||||||
'pagination' => [
|
|
||||||
'total' => 2,
|
|
||||||
'count' => 2,
|
|
||||||
'per_page' => 60,
|
|
||||||
'current_page' => 1,
|
|
||||||
'total_pages' => 1,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
])
|
|
||||||
->assertJsonFragment([
|
|
||||||
'object' => 'location',
|
|
||||||
'attributes' => [
|
|
||||||
'id' => $locations[0]->id,
|
|
||||||
'short' => $locations[0]->short,
|
|
||||||
'long' => $locations[0]->long,
|
|
||||||
'created_at' => $this->formatTimestamp($locations[0]->created_at),
|
|
||||||
'updated_at' => $this->formatTimestamp($locations[0]->updated_at),
|
|
||||||
],
|
|
||||||
])->assertJsonFragment([
|
|
||||||
'object' => 'location',
|
|
||||||
'attributes' => [
|
|
||||||
'id' => $locations[1]->id,
|
|
||||||
'short' => $locations[1]->short,
|
|
||||||
'long' => $locations[1]->long,
|
|
||||||
'created_at' => $this->formatTimestamp($locations[1]->created_at),
|
|
||||||
'updated_at' => $this->formatTimestamp($locations[1]->updated_at),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test getting a single location on the API.
|
|
||||||
*/
|
|
||||||
public function testGetSingleLocation()
|
|
||||||
{
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->getJson('/api/application/locations/' . $location->id);
|
|
||||||
$response->assertStatus(Response::HTTP_OK);
|
|
||||||
$response->assertJsonCount(2);
|
|
||||||
$response->assertJsonStructure(['object', 'attributes' => ['id', 'short', 'long', 'created_at', 'updated_at']]);
|
|
||||||
$response->assertJson([
|
|
||||||
'object' => 'location',
|
|
||||||
'attributes' => [
|
|
||||||
'id' => $location->id,
|
|
||||||
'short' => $location->short,
|
|
||||||
'long' => $location->long,
|
|
||||||
'created_at' => $this->formatTimestamp($location->created_at),
|
|
||||||
'updated_at' => $this->formatTimestamp($location->updated_at),
|
|
||||||
],
|
|
||||||
], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that a location can be created.
|
|
||||||
*/
|
|
||||||
public function testCreateLocation()
|
|
||||||
{
|
|
||||||
$response = $this->postJson('/api/application/locations', [
|
|
||||||
'short' => 'inhouse',
|
|
||||||
'long' => 'This is my inhouse location',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response->assertStatus(Response::HTTP_CREATED);
|
|
||||||
$response->assertJsonCount(3);
|
|
||||||
$response->assertJsonStructure([
|
|
||||||
'object',
|
|
||||||
'attributes' => ['id', 'short', 'long', 'created_at', 'updated_at'],
|
|
||||||
'meta' => ['resource'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertDatabaseHas('locations', ['short' => 'inhouse', 'long' => 'This is my inhouse location']);
|
|
||||||
|
|
||||||
$location = Location::where('short', 'inhouse')->first();
|
|
||||||
$response->assertJson([
|
|
||||||
'object' => 'location',
|
|
||||||
'attributes' => $this->getTransformer(LocationTransformer::class)->transform($location),
|
|
||||||
'meta' => [
|
|
||||||
'resource' => route('api.application.locations.view', $location->id),
|
|
||||||
],
|
|
||||||
], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that a location can be updated.
|
|
||||||
*/
|
|
||||||
public function testUpdateLocation()
|
|
||||||
{
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->patchJson('/api/application/locations/' . $location->id, [
|
|
||||||
'short' => 'new inhouse',
|
|
||||||
'long' => 'This is my new inhouse location',
|
|
||||||
]);
|
|
||||||
$response->assertStatus(Response::HTTP_OK);
|
|
||||||
$response->assertJsonCount(2);
|
|
||||||
$response->assertJsonStructure([
|
|
||||||
'object',
|
|
||||||
'attributes' => ['id', 'short', 'long', 'created_at', 'updated_at'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertDatabaseHas('locations', ['short' => 'new inhouse', 'long' => 'This is my new inhouse location']);
|
|
||||||
$location = $location->fresh();
|
|
||||||
|
|
||||||
$response->assertJson([
|
|
||||||
'object' => 'location',
|
|
||||||
'attributes' => $this->getTransformer(LocationTransformer::class)->transform($location),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that a location can be deleted from the database.
|
|
||||||
*/
|
|
||||||
public function testDeleteLocation()
|
|
||||||
{
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
$this->assertDatabaseHas('locations', ['id' => $location->id]);
|
|
||||||
|
|
||||||
$response = $this->delete('/api/application/locations/' . $location->id);
|
|
||||||
$response->assertStatus(Response::HTTP_NO_CONTENT);
|
|
||||||
|
|
||||||
$this->assertDatabaseMissing('locations', ['id' => $location->id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that all the defined relationships for a location can be loaded successfully.
|
|
||||||
*/
|
|
||||||
public function testRelationshipsCanBeLoaded()
|
|
||||||
{
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
$server = $this->createServerModel(['user_id' => $this->getApiUser()->id, 'location_id' => $location->id]);
|
|
||||||
|
|
||||||
$response = $this->getJson('/api/application/locations/' . $location->id . '?include=servers,nodes');
|
|
||||||
$response->assertStatus(Response::HTTP_OK);
|
|
||||||
$response->assertJsonCount(2)->assertJsonCount(2, 'attributes.relationships');
|
|
||||||
$response->assertJsonStructure([
|
|
||||||
'attributes' => [
|
|
||||||
'relationships' => [
|
|
||||||
'nodes' => ['object', 'data' => [['attributes' => ['id']]]],
|
|
||||||
'servers' => ['object', 'data' => [['attributes' => ['id']]]],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Just assert that we see the expected relationship IDs in the response.
|
|
||||||
$response->assertJson([
|
|
||||||
'attributes' => [
|
|
||||||
'relationships' => [
|
|
||||||
'nodes' => [
|
|
||||||
'object' => 'list',
|
|
||||||
'data' => [
|
|
||||||
[
|
|
||||||
'object' => 'node',
|
|
||||||
'attributes' => $this->getTransformer(NodeTransformer::class)->transform($server->getRelation('node')),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'servers' => [
|
|
||||||
'object' => 'list',
|
|
||||||
'data' => [
|
|
||||||
[
|
|
||||||
'object' => 'server',
|
|
||||||
'attributes' => $this->getTransformer(ServerTransformer::class)->transform($server),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that a relationship that an API key does not have permission to access
|
|
||||||
* cannot be loaded onto the model.
|
|
||||||
*/
|
|
||||||
public function testKeyWithoutPermissionCannotLoadRelationship()
|
|
||||||
{
|
|
||||||
$this->createNewDefaultApiKey($this->getApiUser(), ['r_nodes' => 0]);
|
|
||||||
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
Node::factory()->create(['location_id' => $location->id]);
|
|
||||||
|
|
||||||
$response = $this->getJson('/api/application/locations/' . $location->id . '?include=nodes');
|
|
||||||
$response->assertStatus(Response::HTTP_OK);
|
|
||||||
$response->assertJsonCount(2)->assertJsonCount(1, 'attributes.relationships');
|
|
||||||
$response->assertJsonStructure([
|
|
||||||
'attributes' => [
|
|
||||||
'relationships' => [
|
|
||||||
'nodes' => ['object', 'attributes'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Just assert that we see the expected relationship IDs in the response.
|
|
||||||
$response->assertJson([
|
|
||||||
'attributes' => [
|
|
||||||
'relationships' => [
|
|
||||||
'nodes' => [
|
|
||||||
'object' => 'null_resource',
|
|
||||||
'attributes' => null,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that a missing location returns a 404 error.
|
|
||||||
*
|
|
||||||
* GET /api/application/locations/:id
|
|
||||||
*/
|
|
||||||
public function testGetMissingLocation()
|
|
||||||
{
|
|
||||||
$response = $this->getJson('/api/application/locations/nil');
|
|
||||||
$this->assertNotFoundJson($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that an authentication error occurs if a key does not have permission
|
|
||||||
* to access a resource.
|
|
||||||
*/
|
|
||||||
public function testErrorReturnedIfNoPermission()
|
|
||||||
{
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
$this->createNewDefaultApiKey($this->getApiUser(), ['r_locations' => 0]);
|
|
||||||
|
|
||||||
$response = $this->getJson('/api/application/locations/' . $location->id);
|
|
||||||
$this->assertAccessDeniedJson($response);
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ use App\Models\Model;
|
|||||||
use App\Models\Backup;
|
use App\Models\Backup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Database;
|
use App\Models\Database;
|
||||||
use App\Models\Location;
|
|
||||||
use App\Models\Schedule;
|
use App\Models\Schedule;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
@ -31,7 +30,6 @@ abstract class ClientApiIntegrationTestCase extends IntegrationTestCase
|
|||||||
Backup::query()->forceDelete();
|
Backup::query()->forceDelete();
|
||||||
Server::query()->forceDelete();
|
Server::query()->forceDelete();
|
||||||
Node::query()->forceDelete();
|
Node::query()->forceDelete();
|
||||||
Location::query()->forceDelete();
|
|
||||||
User::query()->forceDelete();
|
User::query()->forceDelete();
|
||||||
|
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
|
@ -61,7 +61,7 @@ class DeployServerDatabaseServiceTest extends IntegrationTestCase
|
|||||||
{
|
{
|
||||||
$server = $this->createServerModel();
|
$server = $this->createServerModel();
|
||||||
|
|
||||||
$node = Node::factory()->create(['location_id' => $server->location->id]);
|
$node = Node::factory()->create();
|
||||||
DatabaseHost::factory()->create(['node_id' => $node->id]);
|
DatabaseHost::factory()->create(['node_id' => $node->id]);
|
||||||
|
|
||||||
config()->set('panel.client_features.databases.allow_random', false);
|
config()->set('panel.client_features.databases.allow_random', false);
|
||||||
@ -96,7 +96,7 @@ class DeployServerDatabaseServiceTest extends IntegrationTestCase
|
|||||||
{
|
{
|
||||||
$server = $this->createServerModel();
|
$server = $this->createServerModel();
|
||||||
|
|
||||||
$node = Node::factory()->create(['location_id' => $server->location->id]);
|
$node = Node::factory()->create();
|
||||||
DatabaseHost::factory()->create(['node_id' => $node->id]);
|
DatabaseHost::factory()->create(['node_id' => $node->id]);
|
||||||
$host = DatabaseHost::factory()->create(['node_id' => $server->node_id]);
|
$host = DatabaseHost::factory()->create(['node_id' => $server->node_id]);
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ class DeployServerDatabaseServiceTest extends IntegrationTestCase
|
|||||||
{
|
{
|
||||||
$server = $this->createServerModel();
|
$server = $this->createServerModel();
|
||||||
|
|
||||||
$node = Node::factory()->create(['location_id' => $server->location->id]);
|
$node = Node::factory()->create();
|
||||||
$host = DatabaseHost::factory()->create(['node_id' => $node->id]);
|
$host = DatabaseHost::factory()->create(['node_id' => $node->id]);
|
||||||
|
|
||||||
$this->managementService->expects('create')->with($server, [
|
$this->managementService->expects('create')->with($server, [
|
||||||
|
@ -5,11 +5,8 @@ namespace App\Tests\Integration\Services\Deployment;
|
|||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Database;
|
use App\Models\Database;
|
||||||
use App\Models\Location;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use App\Tests\Integration\IntegrationTestCase;
|
use App\Tests\Integration\IntegrationTestCase;
|
||||||
use App\Services\Deployment\FindViableNodesService;
|
use App\Services\Deployment\FindViableNodesService;
|
||||||
use App\Exceptions\Service\Deployment\NoViableNodeException;
|
|
||||||
|
|
||||||
class FindViableNodesServiceTest extends IntegrationTestCase
|
class FindViableNodesServiceTest extends IntegrationTestCase
|
||||||
{
|
{
|
||||||
@ -38,146 +35,6 @@ class FindViableNodesServiceTest extends IntegrationTestCase
|
|||||||
$this->getService()->setDisk(10)->handle();
|
$this->getService()->setDisk(10)->handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that errors are not thrown back when passing in expected values.
|
|
||||||
*/
|
|
||||||
public function testNoExceptionIsThrownIfStringifiedIntegersArePassedForLocations()
|
|
||||||
{
|
|
||||||
$this->getService()->setLocations([1, 2, 3]);
|
|
||||||
$this->getService()->setLocations(['1', '2', '3']);
|
|
||||||
$this->getService()->setLocations(['1', 2, 3]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->getService()->setLocations(['a']);
|
|
||||||
$this->fail('This expectation should not be called.');
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
$this->assertInstanceOf(\InvalidArgumentException::class, $exception);
|
|
||||||
$this->assertSame('An array of location IDs should be provided when calling setLocations.', $exception->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->getService()->setLocations(['1.2', '1', 2]);
|
|
||||||
$this->fail('This expectation should not be called.');
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
$this->assertInstanceOf(\InvalidArgumentException::class, $exception);
|
|
||||||
$this->assertSame('An array of location IDs should be provided when calling setLocations.', $exception->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testExpectedNodeIsReturnedForLocation()
|
|
||||||
{
|
|
||||||
/** @var \App\Models\Location[] $locations */
|
|
||||||
$locations = Location::factory()->times(2)->create();
|
|
||||||
|
|
||||||
/** @var \App\Models\Node[] $nodes */
|
|
||||||
$nodes = [
|
|
||||||
// This node should never be returned once we've completed the initial test which
|
|
||||||
// runs without a location filter.
|
|
||||||
Node::factory()->create([
|
|
||||||
'location_id' => $locations[0]->id,
|
|
||||||
'memory' => 2048,
|
|
||||||
'disk' => 1024 * 100,
|
|
||||||
]),
|
|
||||||
Node::factory()->create([
|
|
||||||
'location_id' => $locations[1]->id,
|
|
||||||
'memory' => 1024,
|
|
||||||
'disk' => 10240,
|
|
||||||
'disk_overallocate' => 10,
|
|
||||||
]),
|
|
||||||
Node::factory()->create([
|
|
||||||
'location_id' => $locations[1]->id,
|
|
||||||
'memory' => 1024 * 4,
|
|
||||||
'memory_overallocate' => 50,
|
|
||||||
'disk' => 102400,
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Expect that all the nodes are returned as we're under all of their limits
|
|
||||||
// and there is no location filter being provided.
|
|
||||||
$response = $this->getService()->setDisk(512)->setMemory(512)->handle();
|
|
||||||
$this->assertInstanceOf(Collection::class, $response);
|
|
||||||
$this->assertCount(3, $response);
|
|
||||||
$this->assertInstanceOf(Node::class, $response[0]);
|
|
||||||
|
|
||||||
// Expect that only the last node is returned because it is the only one with enough
|
|
||||||
// memory available to this instance.
|
|
||||||
$response = $this->getService()->setDisk(512)->setMemory(2049)->handle();
|
|
||||||
$this->assertInstanceOf(Collection::class, $response);
|
|
||||||
$this->assertCount(1, $response);
|
|
||||||
$this->assertSame($nodes[2]->id, $response[0]->id);
|
|
||||||
|
|
||||||
// Helper, I am lazy.
|
|
||||||
$base = function () use ($locations) {
|
|
||||||
return $this->getService()->setLocations([$locations[1]->id])->setDisk(512);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Expect that we can create this server on either node since the disk and memory
|
|
||||||
// limits are below the allowed amount.
|
|
||||||
$response = $base()->setMemory(512)->handle();
|
|
||||||
$this->assertCount(2, $response);
|
|
||||||
$this->assertSame(2, $response->where('location_id', $locations[1]->id)->count());
|
|
||||||
|
|
||||||
// Expect that we can only create this server on the second node since the memory
|
|
||||||
// allocated is over the amount of memory available to the first node.
|
|
||||||
$response = $base()->setMemory(2048)->handle();
|
|
||||||
$this->assertCount(1, $response);
|
|
||||||
$this->assertSame($nodes[2]->id, $response[0]->id);
|
|
||||||
|
|
||||||
// Expect that we can only create this server on the second node since the disk
|
|
||||||
// allocated is over the limit assigned to the first node (even with the overallocate).
|
|
||||||
$response = $base()->setDisk(20480)->setMemory(256)->handle();
|
|
||||||
$this->assertCount(1, $response);
|
|
||||||
$this->assertSame($nodes[2]->id, $response[0]->id);
|
|
||||||
|
|
||||||
// Expect that we could create the server on either node since the disk allocated is
|
|
||||||
// right at the limit for Node 1 when the overallocate value is included in the calc.
|
|
||||||
$response = $base()->setDisk(11264)->setMemory(256)->handle();
|
|
||||||
$this->assertCount(2, $response);
|
|
||||||
|
|
||||||
// Create two servers on the first node so that the disk space used is equal to the
|
|
||||||
// base amount available to the node (without overallocation included).
|
|
||||||
$servers = Collection::make([
|
|
||||||
$this->createServerModel(['node_id' => $nodes[1]->id, 'disk' => 5120]),
|
|
||||||
$this->createServerModel(['node_id' => $nodes[1]->id, 'disk' => 5120]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Expect that we cannot create a server with a 1GB disk on the first node since there
|
|
||||||
// is not enough space (even with the overallocate) available to the node.
|
|
||||||
$response = $base()->setDisk(1024)->setMemory(256)->handle();
|
|
||||||
$this->assertCount(1, $response);
|
|
||||||
$this->assertSame($nodes[2]->id, $response[0]->id);
|
|
||||||
|
|
||||||
// Cleanup servers since we need to test some other stuff with memory here.
|
|
||||||
$servers->each->delete();
|
|
||||||
|
|
||||||
// Expect that no viable node can be found when the memory limit for the given instance
|
|
||||||
// is greater than either node can support, even with the overallocation limits taken
|
|
||||||
// into account.
|
|
||||||
$this->expectException(NoViableNodeException::class);
|
|
||||||
$base()->setMemory(10000)->handle();
|
|
||||||
|
|
||||||
// Create four servers so that the memory used for the second node is equal to the total
|
|
||||||
// limit for that node (pre-overallocate calculation).
|
|
||||||
Collection::make([
|
|
||||||
$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),
|
|
||||||
$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),
|
|
||||||
$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),
|
|
||||||
$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Expect that either node can support this server when we account for the overallocate
|
|
||||||
// value of the second node.
|
|
||||||
$response = $base()->setMemory(500)->handle();
|
|
||||||
$this->assertCount(2, $response);
|
|
||||||
$this->assertSame(2, $response->where('location_id', $locations[1]->id)->count());
|
|
||||||
|
|
||||||
// Expect that only the first node can support this server when we go over the remaining
|
|
||||||
// memory for the second nodes overallocate calculation.
|
|
||||||
$response = $base()->setMemory(640)->handle();
|
|
||||||
$this->assertCount(1, $response);
|
|
||||||
$this->assertSame($nodes[1]->id, $response[0]->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getService(): FindViableNodesService
|
private function getService(): FindViableNodesService
|
||||||
{
|
{
|
||||||
return $this->app->make(FindViableNodesService::class);
|
return $this->app->make(FindViableNodesService::class);
|
||||||
|
@ -9,7 +9,6 @@ use App\Models\Node;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use GuzzleHttp\Psr7\Response;
|
use GuzzleHttp\Psr7\Response;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Location;
|
|
||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
use Illuminate\Foundation\Testing\WithFaker;
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
use GuzzleHttp\Exception\BadResponseException;
|
use GuzzleHttp\Exception\BadResponseException;
|
||||||
@ -57,20 +56,15 @@ class ServerCreationServiceTest extends IntegrationTestCase
|
|||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
/** @var \App\Models\Location $location */
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
|
|
||||||
/** @var \App\Models\Node $node */
|
/** @var \App\Models\Node $node */
|
||||||
$node = Node::factory()->create([
|
$node = Node::factory()->create();
|
||||||
'location_id' => $location->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** @var \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations */
|
/** @var \App\Models\Allocation[]|\Illuminate\Database\Eloquent\Collection $allocations */
|
||||||
$allocations = Allocation::factory()->times(5)->create([
|
$allocations = Allocation::factory()->times(5)->create([
|
||||||
'node_id' => $node->id,
|
'node_id' => $node->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$deployment = (new DeploymentObject())->setDedicated(true)->setLocations([$node->location_id])->setPorts([
|
$deployment = (new DeploymentObject())->setDedicated(true)->setPorts([
|
||||||
$allocations[0]->port,
|
$allocations[0]->port,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -159,13 +153,8 @@ class ServerCreationServiceTest extends IntegrationTestCase
|
|||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
/** @var \App\Models\Location $location */
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
|
|
||||||
/** @var \App\Models\Node $node */
|
/** @var \App\Models\Node $node */
|
||||||
$node = Node::factory()->create([
|
$node = Node::factory()->create();
|
||||||
'location_id' => $location->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** @var \App\Models\Allocation $allocation */
|
/** @var \App\Models\Allocation $allocation */
|
||||||
$allocation = Allocation::factory()->create([
|
$allocation = Allocation::factory()->create([
|
||||||
|
@ -8,7 +8,6 @@ use App\Models\Node;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Subuser;
|
use App\Models\Subuser;
|
||||||
use App\Models\Location;
|
|
||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
|
|
||||||
trait CreatesTestModels
|
trait CreatesTestModels
|
||||||
@ -33,14 +32,8 @@ trait CreatesTestModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($attributes['node_id'])) {
|
if (!isset($attributes['node_id'])) {
|
||||||
if (!isset($attributes['location_id'])) {
|
|
||||||
/** @var \App\Models\Location $location */
|
|
||||||
$location = Location::factory()->create();
|
|
||||||
$attributes['location_id'] = $location->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var \App\Models\Node $node */
|
/** @var \App\Models\Node $node */
|
||||||
$node = Node::factory()->create(['location_id' => $attributes['location_id']]);
|
$node = Node::factory()->create();
|
||||||
$attributes['node_id'] = $node->id;
|
$attributes['node_id'] = $node->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +49,7 @@ trait CreatesTestModels
|
|||||||
$attributes['egg_id'] = $egg->id;
|
$attributes['egg_id'] = $egg->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($attributes['user_id'], $attributes['location_id']);
|
unset($attributes['user_id']);
|
||||||
|
|
||||||
/** @var \App\Models\Server $server */
|
/** @var \App\Models\Server $server */
|
||||||
$server = Server::factory()->create($attributes);
|
$server = Server::factory()->create($attributes);
|
||||||
@ -64,7 +57,7 @@ trait CreatesTestModels
|
|||||||
Allocation::query()->where('id', $server->allocation_id)->update(['server_id' => $server->id]);
|
Allocation::query()->where('id', $server->allocation_id)->update(['server_id' => $server->id]);
|
||||||
|
|
||||||
return $server->fresh([
|
return $server->fresh([
|
||||||
'location', 'user', 'node', 'allocation', 'egg',
|
'user', 'node', 'allocation', 'egg',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user