-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathFindViableNodesServiceTest.php
187 lines (159 loc) · 8.07 KB
/
FindViableNodesServiceTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<?php
namespace Pterodactyl\Tests\Integration\Services\Deployment;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Database;
use Pterodactyl\Models\Location;
use Illuminate\Support\Collection;
use Pterodactyl\Tests\Integration\IntegrationTestCase;
use Pterodactyl\Services\Deployment\FindViableNodesService;
use Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException;
class FindViableNodesServiceTest extends IntegrationTestCase
{
protected function setUp(): void
{
parent::setUp();
Database::query()->delete();
Server::query()->delete();
Node::query()->delete();
}
public function testExceptionIsThrownIfNoDiskSpaceHasBeenSet()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Disk space must be an int, got NULL');
$this->getService()->handle();
}
public function testExceptionIsThrownIfNoMemoryHasBeenSet()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Memory usage must be an int, got NULL');
$this->getService()->setDisk(10)->handle();
}
/**
* Ensure that errors are not thrown back when passing in expected values.
*
* @see https://github.com/pterodactyl/panel/issues/2529
*/
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 \Pterodactyl\Models\Location[] $locations */
$locations = Location::factory()->times(2)->create();
/** @var \Pterodactyl\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
{
return $this->app->make(FindViableNodesService::class);
}
}