2020-10-20 04:07:07 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Pterodactyl\Tests\Integration\Services\Servers;
|
|
|
|
|
2022-10-14 16:59:20 +00:00
|
|
|
use Mockery\MockInterface;
|
2021-03-21 18:49:42 +00:00
|
|
|
use GuzzleHttp\Psr7\Request;
|
|
|
|
use GuzzleHttp\Psr7\Response;
|
2020-10-20 04:07:07 +00:00
|
|
|
use Pterodactyl\Models\Server;
|
|
|
|
use Pterodactyl\Models\Allocation;
|
2021-03-21 18:49:42 +00:00
|
|
|
use GuzzleHttp\Exception\RequestException;
|
2020-10-20 04:07:07 +00:00
|
|
|
use Pterodactyl\Exceptions\DisplayException;
|
|
|
|
use Pterodactyl\Tests\Integration\IntegrationTestCase;
|
|
|
|
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
|
|
|
use Pterodactyl\Services\Servers\BuildModificationService;
|
2021-03-21 18:49:42 +00:00
|
|
|
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
class BuildModificationServiceTest extends IntegrationTestCase
|
|
|
|
{
|
2022-10-14 16:59:20 +00:00
|
|
|
private MockInterface $daemonServerRepository;
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Setup tests.
|
|
|
|
*/
|
|
|
|
public function setUp(): void
|
|
|
|
{
|
|
|
|
parent::setUp();
|
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository = $this->mock(DaemonServerRepository::class);
|
2020-10-20 04:07:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that allocations can be added and removed from a server. Only the allocations on the
|
|
|
|
* current node and belonging to this server should be modified.
|
|
|
|
*/
|
|
|
|
public function testAllocationsCanBeModifiedForTheServer()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
|
|
|
$server2 = $this->createServerModel();
|
|
|
|
|
|
|
|
/** @var \Pterodactyl\Models\Allocation[] $allocations */
|
2021-01-23 20:09:16 +00:00
|
|
|
$allocations = Allocation::factory()->times(4)->create(['node_id' => $server->node_id, 'notes' => 'Random notes']);
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$initialAllocationId = $server->allocation_id;
|
|
|
|
$allocations[0]->update(['server_id' => $server->id, 'notes' => 'Test notes']);
|
|
|
|
|
|
|
|
// Some additional test allocations for the other server, not the server we are attempting
|
|
|
|
// to modify.
|
|
|
|
$allocations[2]->update(['server_id' => $server2->id]);
|
|
|
|
$allocations[3]->update(['server_id' => $server2->id]);
|
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$response = $this->getService()->handle($server, [
|
|
|
|
// Attempt to add one new allocation, and an allocation assigned to another server. The
|
|
|
|
// other server allocation should be ignored, and only the allocation for this server should
|
|
|
|
// be used.
|
|
|
|
'add_allocations' => [$allocations[2]->id, $allocations[1]->id],
|
|
|
|
// Remove the default server allocation, ensuring that the new allocation passed through
|
|
|
|
// in the data becomes the default allocation.
|
|
|
|
'remove_allocations' => [$server->allocation_id, $allocations[0]->id, $allocations[3]->id],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->assertInstanceOf(Server::class, $response);
|
|
|
|
|
|
|
|
// Only one allocation should exist for this server now.
|
|
|
|
$this->assertCount(1, $response->allocations);
|
|
|
|
$this->assertSame($allocations[1]->id, $response->allocation_id);
|
2020-10-20 04:08:40 +00:00
|
|
|
$this->assertNull($response->allocation->notes);
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
// These two allocations should not have been touched.
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocations[2]->id, 'server_id' => $server2->id]);
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocations[3]->id, 'server_id' => $server2->id]);
|
|
|
|
|
|
|
|
// Both of these allocations should have been removed from the server, and have had their
|
|
|
|
// notes properly reset.
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $initialAllocationId, 'server_id' => null, 'notes' => null]);
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocations[0]->id, 'server_id' => null, 'notes' => null]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that an exception is thrown if removing the default allocation without also assigning
|
|
|
|
* new allocations to the server.
|
|
|
|
*/
|
|
|
|
public function testExceptionIsThrownIfRemovingTheDefaultAllocation()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
|
|
|
/** @var \Pterodactyl\Models\Allocation[] $allocations */
|
2021-01-23 20:09:16 +00:00
|
|
|
$allocations = Allocation::factory()->times(4)->create(['node_id' => $server->node_id]);
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$allocations[0]->update(['server_id' => $server->id]);
|
|
|
|
|
|
|
|
$this->expectException(DisplayException::class);
|
|
|
|
$this->expectExceptionMessage('You are attempting to delete the default allocation for this server but there is no fallback allocation to use.');
|
|
|
|
|
|
|
|
$this->getService()->handle($server, [
|
|
|
|
'add_allocations' => [],
|
|
|
|
'remove_allocations' => [$server->allocation_id, $allocations[0]->id],
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that the build data for the server is properly passed along to the Wings instance so that
|
|
|
|
* the server data is updated in realtime. This test also ensures that only certain fields get updated
|
|
|
|
* for the server, and not just any arbitrary field.
|
|
|
|
*/
|
|
|
|
public function testServerBuildDataIsProperlyUpdatedOnWings()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
|
|
|
|
2022-11-29 17:53:59 +00:00
|
|
|
$this->daemonServerRepository->expects('setServer')->with(\Mockery::on(function (Server $s) use ($server) {
|
2020-10-20 04:07:07 +00:00
|
|
|
return $s->id === $server->id;
|
|
|
|
}))->andReturnSelf();
|
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository->expects('sync')->withNoArgs()->andReturnUndefined();
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$response = $this->getService()->handle($server, [
|
|
|
|
'oom_disabled' => false,
|
|
|
|
'memory' => 256,
|
|
|
|
'swap' => 128,
|
|
|
|
'io' => 600,
|
|
|
|
'cpu' => 150,
|
|
|
|
'threads' => '1,2',
|
|
|
|
'disk' => 1024,
|
|
|
|
'backup_limit' => null,
|
|
|
|
'database_limit' => 10,
|
|
|
|
'allocation_limit' => 20,
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->assertFalse($response->oom_disabled);
|
|
|
|
$this->assertSame(256, $response->memory);
|
|
|
|
$this->assertSame(128, $response->swap);
|
|
|
|
$this->assertSame(600, $response->io);
|
|
|
|
$this->assertSame(150, $response->cpu);
|
|
|
|
$this->assertSame('1,2', $response->threads);
|
|
|
|
$this->assertSame(1024, $response->disk);
|
|
|
|
$this->assertSame(0, $response->backup_limit);
|
|
|
|
$this->assertSame(10, $response->database_limit);
|
|
|
|
$this->assertSame(20, $response->allocation_limit);
|
|
|
|
}
|
|
|
|
|
2021-03-21 18:49:42 +00:00
|
|
|
/**
|
|
|
|
* Test that an exception when connecting to the Wings instance is properly ignored
|
|
|
|
* when making updates. This allows for a server to be modified even when the Wings
|
|
|
|
* node is offline.
|
|
|
|
*/
|
|
|
|
public function testConnectionExceptionIsIgnoredWhenUpdatingServerSettings()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository->expects('setServer->sync')->andThrows(
|
2021-03-21 18:49:42 +00:00
|
|
|
new DaemonConnectionException(
|
|
|
|
new RequestException('Bad request', new Request('GET', '/test'), new Response())
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
$response = $this->getService()->handle($server, ['memory' => 256, 'disk' => 10240]);
|
|
|
|
|
|
|
|
$this->assertInstanceOf(Server::class, $response);
|
|
|
|
$this->assertSame(256, $response->memory);
|
|
|
|
$this->assertSame(10240, $response->disk);
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('servers', ['id' => $response->id, 'memory' => 256, 'disk' => 10240]);
|
|
|
|
}
|
|
|
|
|
2020-10-20 04:07:07 +00:00
|
|
|
/**
|
|
|
|
* Test that no exception is thrown if we are only removing an allocation.
|
|
|
|
*/
|
|
|
|
public function testNoExceptionIsThrownIfOnlyRemovingAllocation()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
2021-01-23 20:09:16 +00:00
|
|
|
/** @var \Pterodactyl\Models\Allocation $allocation */
|
|
|
|
$allocation = Allocation::factory()->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
|
2020-10-20 04:07:07 +00:00
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$this->getService()->handle($server, [
|
|
|
|
'remove_allocations' => [$allocation->id],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that allocations in both the add and remove arrays are only added, and not removed.
|
2022-10-14 16:59:20 +00:00
|
|
|
* This scenario wouldn't really happen in the UI, but it is possible to perform via the API,
|
2020-10-20 04:07:07 +00:00
|
|
|
* so we want to make sure that the logic being used doesn't break if the allocation exists
|
|
|
|
* in both arrays.
|
|
|
|
*
|
|
|
|
* We'll default to adding the allocation in this case.
|
|
|
|
*/
|
|
|
|
public function testAllocationInBothAddAndRemoveIsAdded()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
2021-01-23 20:09:16 +00:00
|
|
|
/** @var \Pterodactyl\Models\Allocation $allocation */
|
|
|
|
$allocation = Allocation::factory()->create(['node_id' => $server->node_id]);
|
2020-10-20 04:07:07 +00:00
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$this->getService()->handle($server, [
|
|
|
|
'add_allocations' => [$allocation->id],
|
|
|
|
'remove_allocations' => [$allocation->id],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => $server->id]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that using the same allocation ID multiple times in the array does not cause an error.
|
|
|
|
*/
|
|
|
|
public function testUsingSameAllocationIdMultipleTimesDoesNotError()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
2021-01-23 20:09:16 +00:00
|
|
|
/** @var \Pterodactyl\Models\Allocation $allocation */
|
|
|
|
$allocation = Allocation::factory()->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
|
|
|
|
/** @var \Pterodactyl\Models\Allocation $allocation2 */
|
|
|
|
$allocation2 = Allocation::factory()->create(['node_id' => $server->node_id]);
|
2020-10-20 04:07:07 +00:00
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$this->getService()->handle($server, [
|
|
|
|
'add_allocations' => [$allocation2->id, $allocation2->id],
|
|
|
|
'remove_allocations' => [$allocation->id, $allocation->id],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocation2->id, 'server_id' => $server->id]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that any changes we made to the server or allocations are rolled back if there is an
|
2022-10-14 16:59:20 +00:00
|
|
|
* exception while performing any action. This is different from the connection exception
|
2021-03-21 18:49:42 +00:00
|
|
|
* test which should properly ignore connection issues. We want any other type of exception
|
|
|
|
* to properly be thrown back to the caller.
|
2020-10-20 04:07:07 +00:00
|
|
|
*/
|
|
|
|
public function testThatUpdatesAreRolledBackIfExceptionIsEncountered()
|
|
|
|
{
|
|
|
|
$server = $this->createServerModel();
|
2021-01-23 20:09:16 +00:00
|
|
|
/** @var \Pterodactyl\Models\Allocation $allocation */
|
|
|
|
$allocation = Allocation::factory()->create(['node_id' => $server->node_id]);
|
2020-10-20 04:07:07 +00:00
|
|
|
|
2021-09-11 19:02:15 +00:00
|
|
|
$this->daemonServerRepository->expects('setServer->sync')->andThrows(new DisplayException('Test'));
|
2020-10-20 04:07:07 +00:00
|
|
|
|
|
|
|
$this->expectException(DisplayException::class);
|
|
|
|
|
|
|
|
$this->getService()->handle($server, ['add_allocations' => [$allocation->id]]);
|
|
|
|
|
|
|
|
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
|
|
|
|
}
|
|
|
|
|
2022-10-14 16:59:20 +00:00
|
|
|
private function getService(): BuildModificationService
|
2020-10-20 04:07:07 +00:00
|
|
|
{
|
|
|
|
return $this->app->make(BuildModificationService::class);
|
|
|
|
}
|
|
|
|
}
|