Fix data integrity exception, closes #922
This commit is contained in:
parent
e1d6980c0b
commit
cfb7415e2a
4 changed files with 116 additions and 5 deletions
|
@ -8,6 +8,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
|
||||||
* `[rc.2]` — Fixes bad API behavior on `/user` routes.
|
* `[rc.2]` — Fixes bad API behavior on `/user` routes.
|
||||||
* `[rc.2]` — Fixes Admin CP user editing resetting a password on users unintentionally.
|
* `[rc.2]` — Fixes Admin CP user editing resetting a password on users unintentionally.
|
||||||
* `[rc.2]` — Fixes bug with server creation API endpoint that would fail to validate `allocation.default` correctly.
|
* `[rc.2]` — Fixes bug with server creation API endpoint that would fail to validate `allocation.default` correctly.
|
||||||
|
* `[rc.2]` — Fix data integrity exception occuring due to invalid data being passed to server creation service on the API.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* Added ability to search the following API endpoints: list users, list servers, and list locations.
|
* Added ability to search the following API endpoints: list users, list servers, and list locations.
|
||||||
|
|
|
@ -49,6 +49,10 @@ class StoreServerRequest extends ApplicationApiRequest
|
||||||
'limits.io' => $rules['io'],
|
'limits.io' => $rules['io'],
|
||||||
'limits.cpu' => $rules['cpu'],
|
'limits.cpu' => $rules['cpu'],
|
||||||
|
|
||||||
|
// Placeholders for rules added in withValidator() function.
|
||||||
|
'allocation.default' => '',
|
||||||
|
'allocation.additional.*' => '',
|
||||||
|
|
||||||
// Automatic deployment rules
|
// Automatic deployment rules
|
||||||
'deploy' => 'sometimes|required|array',
|
'deploy' => 'sometimes|required|array',
|
||||||
'deploy.locations' => 'array',
|
'deploy.locations' => 'array',
|
||||||
|
|
|
@ -112,7 +112,9 @@ class ServerCreationService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a server on the Panel and trigger a request to the Daemon to begin the server
|
* Create a server on the Panel and trigger a request to the Daemon to begin the server
|
||||||
* creation process.
|
* creation process. This function will attempt to set as many additional values
|
||||||
|
* as possible given the input data. For example, if an allocation_id is passed with
|
||||||
|
* no node_id the node_is will be picked from the allocation.
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param \Pterodactyl\Models\Objects\DeploymentObject|null $deployment
|
* @param \Pterodactyl\Models\Objects\DeploymentObject|null $deployment
|
||||||
|
@ -138,6 +140,12 @@ class ServerCreationService
|
||||||
$data['node_id'] = $allocation->node_id;
|
$data['node_id'] = $allocation->node_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-configure the node based on the selected allocation
|
||||||
|
// if no node was defined.
|
||||||
|
if (is_null(array_get($data, 'node_id'))) {
|
||||||
|
$data['node_id'] = $this->getNodeFromAllocation($data['allocation_id']);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_null(array_get($data, 'nest_id'))) {
|
if (is_null(array_get($data, 'nest_id'))) {
|
||||||
$egg = $this->eggRepository->setColumns(['id', 'nest_id'])->find(array_get($data, 'egg_id'));
|
$egg = $this->eggRepository->setColumns(['id', 'nest_id'])->find(array_get($data, 'egg_id'));
|
||||||
$data['nest_id'] = $egg->nest_id;
|
$data['nest_id'] = $egg->nest_id;
|
||||||
|
@ -263,4 +271,19 @@ class ServerCreationService
|
||||||
$this->serverVariableRepository->insert($records);
|
$this->serverVariableRepository->insert($records);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the node that an allocation belongs to.
|
||||||
|
*
|
||||||
|
* @param int $allocation
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||||
|
*/
|
||||||
|
private function getNodeFromAllocation(int $allocation): int
|
||||||
|
{
|
||||||
|
$allocation = $this->allocationRepository->setColumns(['id', 'node_id'])->find($allocation);
|
||||||
|
|
||||||
|
return $allocation->node_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,14 @@ namespace Tests\Unit\Services\Servers;
|
||||||
|
|
||||||
use Mockery as m;
|
use Mockery as m;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
use Pterodactyl\Models\Node;
|
use Pterodactyl\Models\Egg;
|
||||||
use Pterodactyl\Models\User;
|
use Pterodactyl\Models\User;
|
||||||
use Tests\Traits\MocksUuids;
|
use Tests\Traits\MocksUuids;
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use Pterodactyl\Models\Allocation;
|
||||||
use Tests\Traits\MocksRequestException;
|
use Tests\Traits\MocksRequestException;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
|
use Pterodactyl\Models\Objects\DeploymentObject;
|
||||||
use Pterodactyl\Services\Servers\ServerCreationService;
|
use Pterodactyl\Services\Servers\ServerCreationService;
|
||||||
use Pterodactyl\Services\Servers\VariableValidatorService;
|
use Pterodactyl\Services\Servers\VariableValidatorService;
|
||||||
use Pterodactyl\Services\Deployment\FindViableNodesService;
|
use Pterodactyl\Services\Deployment\FindViableNodesService;
|
||||||
|
@ -35,7 +37,7 @@ class ServerCreationServiceTest extends TestCase
|
||||||
private $allocationRepository;
|
private $allocationRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Services\Deployment\AllocationSelectionService
|
* @var \Pterodactyl\Services\Deployment\AllocationSelectionService|\Mockery\Mock
|
||||||
*/
|
*/
|
||||||
private $allocationSelectionService;
|
private $allocationSelectionService;
|
||||||
|
|
||||||
|
@ -55,12 +57,12 @@ class ServerCreationServiceTest extends TestCase
|
||||||
private $daemonServerRepository;
|
private $daemonServerRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
|
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface|\Mockery\Mock
|
||||||
*/
|
*/
|
||||||
private $eggRepository;
|
private $eggRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Pterodactyl\Services\Deployment\FindViableNodesService
|
* @var \Pterodactyl\Services\Deployment\FindViableNodesService|\Mockery\Mock
|
||||||
*/
|
*/
|
||||||
private $findViableNodesService;
|
private $findViableNodesService;
|
||||||
|
|
||||||
|
@ -117,6 +119,7 @@ class ServerCreationServiceTest extends TestCase
|
||||||
$this->repository->shouldReceive('create')->with(m::subset([
|
$this->repository->shouldReceive('create')->with(m::subset([
|
||||||
'uuid' => $this->getKnownUuid(),
|
'uuid' => $this->getKnownUuid(),
|
||||||
'node_id' => $model->node_id,
|
'node_id' => $model->node_id,
|
||||||
|
'allocation_id' => $model->allocation_id,
|
||||||
'owner_id' => $model->owner_id,
|
'owner_id' => $model->owner_id,
|
||||||
'nest_id' => $model->nest_id,
|
'nest_id' => $model->nest_id,
|
||||||
'egg_id' => $model->egg_id,
|
'egg_id' => $model->egg_id,
|
||||||
|
@ -147,6 +150,86 @@ class ServerCreationServiceTest extends TestCase
|
||||||
$this->assertSame($model, $response);
|
$this->assertSame($model, $response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that optional parameters get auto-filled correctly on the model.
|
||||||
|
*/
|
||||||
|
public function testDataIsAutoFilled()
|
||||||
|
{
|
||||||
|
$model = factory(Server::class)->make(['uuid' => $this->getKnownUuid()]);
|
||||||
|
$allocationModel = factory(Allocation::class)->make(['node_id' => $model->node_id]);
|
||||||
|
$eggModel = factory(Egg::class)->make(['nest_id' => $model->nest_id]);
|
||||||
|
|
||||||
|
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs();
|
||||||
|
$this->allocationRepository->shouldReceive('setColumns->find')->once()->with($model->allocation_id)->andReturn($allocationModel);
|
||||||
|
$this->eggRepository->shouldReceive('setColumns->find')->once()->with($model->egg_id)->andReturn($eggModel);
|
||||||
|
|
||||||
|
$this->validatorService->shouldReceive('setUserLevel->handle')->once()->andReturn(collect([]));
|
||||||
|
$this->repository->shouldReceive('create')->once()->with(m::subset([
|
||||||
|
'uuid' => $this->getKnownUuid(),
|
||||||
|
'node_id' => $model->node_id,
|
||||||
|
'allocation_id' => $model->allocation_id,
|
||||||
|
'nest_id' => $model->nest_id,
|
||||||
|
'egg_id' => $model->egg_id,
|
||||||
|
]))->andReturn($model);
|
||||||
|
|
||||||
|
$this->allocationRepository->shouldReceive('assignAllocationsToServer')->once()->with($model->id, [$model->allocation_id]);
|
||||||
|
$this->configurationStructureService->shouldReceive('handle')->once()->with($model)->andReturn([]);
|
||||||
|
|
||||||
|
$this->daemonServerRepository->shouldReceive('setServer->create')->once();
|
||||||
|
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||||
|
|
||||||
|
$this->getService()->handle(
|
||||||
|
collect($model->toArray())->except(['node_id', 'nest_id'])->toArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an auto-deployment object is used correctly if passed.
|
||||||
|
*/
|
||||||
|
public function testAutoDeploymentObject()
|
||||||
|
{
|
||||||
|
$model = factory(Server::class)->make(['uuid' => $this->getKnownUuid()]);
|
||||||
|
$deploymentObject = new DeploymentObject();
|
||||||
|
$deploymentObject->setPorts(['25565']);
|
||||||
|
$deploymentObject->setDedicated(false);
|
||||||
|
$deploymentObject->setLocations([1]);
|
||||||
|
|
||||||
|
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs();
|
||||||
|
|
||||||
|
$this->findViableNodesService->shouldReceive('setLocations')->once()->with($deploymentObject->getLocations())->andReturnSelf();
|
||||||
|
$this->findViableNodesService->shouldReceive('setDisk')->once()->with($model->disk)->andReturnSelf();
|
||||||
|
$this->findViableNodesService->shouldReceive('setMemory')->once()->with($model->memory)->andReturnSelf();
|
||||||
|
$this->findViableNodesService->shouldReceive('handle')->once()->withNoArgs()->andReturn([1, 2]);
|
||||||
|
|
||||||
|
$allocationModel = factory(Allocation::class)->make([
|
||||||
|
'id' => $model->allocation_id,
|
||||||
|
'node_id' => $model->node_id,
|
||||||
|
]);
|
||||||
|
$this->allocationSelectionService->shouldReceive('setDedicated')->once()->with($deploymentObject->isDedicated())->andReturnSelf();
|
||||||
|
$this->allocationSelectionService->shouldReceive('setNodes')->once()->with([1, 2])->andReturnSelf();
|
||||||
|
$this->allocationSelectionService->shouldReceive('setPorts')->once()->with($deploymentObject->getPorts())->andReturnSelf();
|
||||||
|
$this->allocationSelectionService->shouldReceive('handle')->once()->withNoArgs()->andReturn($allocationModel);
|
||||||
|
|
||||||
|
$this->validatorService->shouldReceive('setUserLevel->handle')->once()->andReturn(collect([]));
|
||||||
|
$this->repository->shouldReceive('create')->once()->with(m::subset([
|
||||||
|
'uuid' => $this->getKnownUuid(),
|
||||||
|
'node_id' => $model->node_id,
|
||||||
|
'allocation_id' => $model->allocation_id,
|
||||||
|
'nest_id' => $model->nest_id,
|
||||||
|
'egg_id' => $model->egg_id,
|
||||||
|
]))->andReturn($model);
|
||||||
|
|
||||||
|
$this->allocationRepository->shouldReceive('assignAllocationsToServer')->once()->with($model->id, [$model->allocation_id]);
|
||||||
|
$this->configurationStructureService->shouldReceive('handle')->once()->with($model)->andReturn([]);
|
||||||
|
|
||||||
|
$this->daemonServerRepository->shouldReceive('setServer->create')->once();
|
||||||
|
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||||
|
|
||||||
|
$this->getService()->handle(
|
||||||
|
collect($model->toArray())->except(['allocation_id', 'node_id'])->toArray(), $deploymentObject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test handling of node timeout or other daemon error.
|
* Test handling of node timeout or other daemon error.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue