Add endpoints to return a server's egg configuration
This commit is contained in:
parent
5df46b23d2
commit
67ff67a1bd
10 changed files with 309 additions and 106 deletions
|
@ -9,46 +9,189 @@
|
|||
|
||||
namespace Pterodactyl\Services\Eggs;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Pterodactyl\Models\Egg;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
|
||||
|
||||
class EggConfigurationService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService
|
||||
*/
|
||||
private $configurationStructureService;
|
||||
|
||||
/**
|
||||
* EggConfigurationService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService
|
||||
*/
|
||||
public function __construct(EggRepositoryInterface $repository)
|
||||
{
|
||||
public function __construct(
|
||||
EggRepositoryInterface $repository,
|
||||
ServerConfigurationStructureService $configurationStructureService
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
$this->configurationStructureService = $configurationStructureService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an Egg file to be used by the Daemon.
|
||||
*
|
||||
* @param int|\Pterodactyl\Models\Egg $egg
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function handle($egg): array
|
||||
public function handle(Server $server): array
|
||||
{
|
||||
if (! $egg instanceof Egg) {
|
||||
$egg = $this->repository->getWithCopyAttributes($egg);
|
||||
$configs = $this->replacePlaceholders(
|
||||
$server, json_decode($server->egg->inherit_config_files)
|
||||
);
|
||||
|
||||
return [
|
||||
'startup' => json_decode($server->egg->inherit_config_startup),
|
||||
'stop' => $this->convertStopToNewFormat($server->egg->inherit_config_stop),
|
||||
'configs' => $configs,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a legacy stop string into a new generation stop option for a server.
|
||||
*
|
||||
* For most eggs, this ends up just being a command sent to the server console, but
|
||||
* if the stop command is something starting with a caret (^), it will be converted
|
||||
* into the associated kill signal for the instance.
|
||||
*
|
||||
* @param string $stop
|
||||
* @return array
|
||||
*/
|
||||
protected function convertStopToNewFormat(string $stop): array
|
||||
{
|
||||
if (! Str::startsWith($stop, '^')) {
|
||||
return [
|
||||
'type' => 'command',
|
||||
'value' => $stop,
|
||||
];
|
||||
}
|
||||
|
||||
$signal = substr($stop, 1);
|
||||
if (strtoupper($signal) === 'C') {
|
||||
return [
|
||||
'type' => 'stop',
|
||||
'value' => null,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'startup' => json_decode($egg->inherit_config_startup),
|
||||
'stop' => $egg->inherit_config_stop,
|
||||
'configs' => json_decode($egg->inherit_config_files),
|
||||
'log' => json_decode($egg->inherit_config_logs),
|
||||
'query' => 'none',
|
||||
'type' => 'signal',
|
||||
'value' => strtoupper($signal),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param object $configs
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
protected function replacePlaceholders(Server $server, object $configs)
|
||||
{
|
||||
// Get the legacy configuration structure for the server so that we
|
||||
// can property map the egg placeholders to values.
|
||||
$structure = $this->configurationStructureService->handle($server);
|
||||
|
||||
foreach ($configs as $file => $data) {
|
||||
foreach ($data->find ?? [] as &$value) {
|
||||
preg_match('/^{{(?<key>.*)}}$/', $value, $matches);
|
||||
|
||||
if (! $key = $matches['key'] ?? null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Matched something in {{server.X}} format, now replace that with the actual
|
||||
// value from the server properties.
|
||||
//
|
||||
// The Daemon supports server.X, env.X, and config.X placeholders.
|
||||
if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't want to do anything with config keys since the Daemon will need to handle
|
||||
// that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker
|
||||
// interface to proxy through, but the Panel would be unaware of that.
|
||||
if (Str::startsWith($key, 'config.')) {
|
||||
$value = "{{{$key}}}";
|
||||
continue;
|
||||
}
|
||||
|
||||
// The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their
|
||||
// respective values on the Daemon side. Ensure that anything referencing those properly
|
||||
// replaces them with the matching config value.
|
||||
switch ($key) {
|
||||
case 'server.build.env.SERVER_MEMORY':
|
||||
case 'env.SERVER_MEMORY':
|
||||
$key = 'server.build.memory';
|
||||
break;
|
||||
case 'server.build.env.SERVER_IP':
|
||||
case 'env.SERVER_IP':
|
||||
$key = 'server.build.default.ip';
|
||||
break;
|
||||
case 'server.build.env.SERVER_PORT':
|
||||
case 'env.SERVER_PORT':
|
||||
$key = 'server.build.default.port';
|
||||
break;
|
||||
}
|
||||
|
||||
// Replace anything starting with "server." with the value out of the server configuration
|
||||
// array that used to be created for the old daemon.
|
||||
if (Str::startsWith($key, 'server.')) {
|
||||
$value = Arr::get(
|
||||
$structure, preg_replace('/^server\./', '', $key), ''
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finally, replace anything starting with env. with the expected environment
|
||||
// variable from the server configuration.
|
||||
$value = Arr::get(
|
||||
$structure, preg_replace('/^env\./', 'build.env.', $key), ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [];
|
||||
// Normalize the output of the configuration for the new Wings Daemon to more
|
||||
// easily ingest, as well as make things more flexible down the road.
|
||||
foreach ($configs as $file => $data) {
|
||||
$append = ['file' => $file, 'replace' => []];
|
||||
|
||||
// I like to think I understand PHP pretty well, but if you don't pass $value
|
||||
// by reference here, you'll end up with a resursive array loop if the config
|
||||
// file has two replacements that reference the same item in the configuration
|
||||
// array for the server.
|
||||
foreach ($data as $key => &$value) {
|
||||
if ($key !== 'find') {
|
||||
$append[$key] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($value as $find => $replace) {
|
||||
$append['replace'][] = ['match' => $find, 'value' => $replace];
|
||||
}
|
||||
}
|
||||
|
||||
$response[] = $append;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
|||
|
||||
class ServerConfigurationStructureService
|
||||
{
|
||||
const REQUIRED_RELATIONS = ['allocation', 'allocations', 'pack', 'option'];
|
||||
const REQUIRED_RELATIONS = ['allocation', 'allocations', 'pack', 'egg'];
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\EnvironmentService
|
||||
|
@ -43,6 +43,9 @@ class ServerConfigurationStructureService
|
|||
/**
|
||||
* Return a configuration array for a specific server when passed a server model.
|
||||
*
|
||||
* DO NOT MODIFY THIS FUNCTION. This powers legacy code handling for the new Wings
|
||||
* daemon, if you modify the structure eggs will break unexpectedly.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @return array
|
||||
*
|
||||
|
@ -50,14 +53,7 @@ class ServerConfigurationStructureService
|
|||
*/
|
||||
public function handle(Server $server): array
|
||||
{
|
||||
if (array_diff(self::REQUIRED_RELATIONS, $server->getRelations())) {
|
||||
$server = $this->repository->getDataForCreation($server);
|
||||
}
|
||||
|
||||
$pack = $server->getRelation('pack');
|
||||
if (! is_null($pack)) {
|
||||
$pack = $server->getRelation('pack')->uuid;
|
||||
}
|
||||
$server->loadMissing(self::REQUIRED_RELATIONS);
|
||||
|
||||
return [
|
||||
'uuid' => $server->uuid,
|
||||
|
@ -80,7 +76,7 @@ class ServerConfigurationStructureService
|
|||
],
|
||||
'service' => [
|
||||
'egg' => $server->egg->uuid,
|
||||
'pack' => $pack,
|
||||
'pack' => $server->pack ? $server->pack->uuid : null,
|
||||
'skip_scripts' => $server->skip_scripts,
|
||||
],
|
||||
'rebuild' => false,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue