mirror of
https://github.com/pelican-dev/panel.git
synced 2025-09-08 23:08:37 +02:00
add backend for supporting multiple egg startup commands
This commit is contained in:
parent
8f277aaca0
commit
a4bc001728
@ -20,8 +20,7 @@ use Illuminate\Support\Str;
|
|||||||
* @property string $name
|
* @property string $name
|
||||||
* @property string|null $description
|
* @property string|null $description
|
||||||
* @property string[]|null $features
|
* @property string[]|null $features
|
||||||
* @property string $docker_image -- deprecated, use $docker_images
|
* @property array<string, string> $docker_images
|
||||||
* @property array<array-key, string> $docker_images
|
|
||||||
* @property string|null $update_url
|
* @property string|null $update_url
|
||||||
* @property bool $force_outgoing_ip
|
* @property bool $force_outgoing_ip
|
||||||
* @property string[]|null $file_denylist
|
* @property string[]|null $file_denylist
|
||||||
@ -30,7 +29,7 @@ use Illuminate\Support\Str;
|
|||||||
* @property string|null $config_logs
|
* @property string|null $config_logs
|
||||||
* @property string|null $config_stop
|
* @property string|null $config_stop
|
||||||
* @property int|null $config_from
|
* @property int|null $config_from
|
||||||
* @property string|null $startup
|
* @property array<string, string> $startup_commands
|
||||||
* @property bool $script_is_privileged
|
* @property bool $script_is_privileged
|
||||||
* @property string|null $script_install
|
* @property string|null $script_install
|
||||||
* @property string $script_entry
|
* @property string $script_entry
|
||||||
@ -69,7 +68,7 @@ class Egg extends Model implements Validatable
|
|||||||
/**
|
/**
|
||||||
* Defines the current egg export version.
|
* Defines the current egg export version.
|
||||||
*/
|
*/
|
||||||
public const EXPORT_VERSION = 'PLCN_v2';
|
public const EXPORT_VERSION = 'PLCN_v3';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fields that are not mass assignable.
|
* Fields that are not mass assignable.
|
||||||
@ -88,7 +87,7 @@ class Egg extends Model implements Validatable
|
|||||||
'config_logs',
|
'config_logs',
|
||||||
'config_stop',
|
'config_stop',
|
||||||
'config_from',
|
'config_from',
|
||||||
'startup',
|
'startup_commands',
|
||||||
'update_url',
|
'update_url',
|
||||||
'script_is_privileged',
|
'script_is_privileged',
|
||||||
'script_install',
|
'script_install',
|
||||||
@ -109,7 +108,8 @@ class Egg extends Model implements Validatable
|
|||||||
'file_denylist.*' => ['string'],
|
'file_denylist.*' => ['string'],
|
||||||
'docker_images' => ['required', 'array', 'min:1'],
|
'docker_images' => ['required', 'array', 'min:1'],
|
||||||
'docker_images.*' => ['required', 'string'],
|
'docker_images.*' => ['required', 'string'],
|
||||||
'startup' => ['required', 'nullable', 'string'],
|
'startup_commands' => ['required', 'array', 'min:1'],
|
||||||
|
'startup_commands.*' => ['required', 'string'],
|
||||||
'config_from' => ['sometimes', 'bail', 'nullable', 'numeric', 'exists:eggs,id'],
|
'config_from' => ['sometimes', 'bail', 'nullable', 'numeric', 'exists:eggs,id'],
|
||||||
'config_stop' => ['required_without:config_from', 'nullable', 'string', 'max:255'],
|
'config_stop' => ['required_without:config_from', 'nullable', 'string', 'max:255'],
|
||||||
'config_startup' => ['required_without:config_from', 'nullable', 'json'],
|
'config_startup' => ['required_without:config_from', 'nullable', 'json'],
|
||||||
@ -141,6 +141,7 @@ class Egg extends Model implements Validatable
|
|||||||
'features' => 'array',
|
'features' => 'array',
|
||||||
'docker_images' => 'array',
|
'docker_images' => 'array',
|
||||||
'file_denylist' => 'array',
|
'file_denylist' => 'array',
|
||||||
|
'startup_commands' => 'array',
|
||||||
'tags' => 'array',
|
'tags' => 'array',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ namespace App\Services\Eggs;
|
|||||||
use App\Models\Egg;
|
use App\Models\Egg;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\ServerVariable;
|
use App\Models\ServerVariable;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
class EggChangerService
|
class EggChangerService
|
||||||
{
|
{
|
||||||
@ -21,8 +22,8 @@ class EggChangerService
|
|||||||
// Change egg id, default image and startup command
|
// Change egg id, default image and startup command
|
||||||
$server->forceFill([
|
$server->forceFill([
|
||||||
'egg_id' => $newEgg->id,
|
'egg_id' => $newEgg->id,
|
||||||
'image' => array_values($newEgg->docker_images)[0],
|
'image' => Arr::first($newEgg->docker_images),
|
||||||
'startup' => $newEgg->startup,
|
'startup' => Arr::first($newEgg->startup_commands),
|
||||||
])->saveOrFail();
|
])->saveOrFail();
|
||||||
|
|
||||||
$oldVariables = [];
|
$oldVariables = [];
|
||||||
|
@ -33,7 +33,7 @@ class EggExporterService
|
|||||||
'features' => $egg->features,
|
'features' => $egg->features,
|
||||||
'docker_images' => $egg->docker_images,
|
'docker_images' => $egg->docker_images,
|
||||||
'file_denylist' => Collection::make($egg->inherit_file_denylist)->filter(fn ($v) => !empty($v))->values(),
|
'file_denylist' => Collection::make($egg->inherit_file_denylist)->filter(fn ($v) => !empty($v))->values(),
|
||||||
'startup' => $egg->startup,
|
'startup_commands' => $egg->startup_commands,
|
||||||
'config' => [
|
'config' => [
|
||||||
'files' => $egg->inherit_config_files,
|
'files' => $egg->inherit_config_files,
|
||||||
'startup' => $egg->inherit_config_startup,
|
'startup' => $egg->inherit_config_startup,
|
||||||
|
@ -133,8 +133,9 @@ class EggImporterService
|
|||||||
$version = $parsed['meta']['version'] ?? '';
|
$version = $parsed['meta']['version'] ?? '';
|
||||||
|
|
||||||
$parsed = match ($version) {
|
$parsed = match ($version) {
|
||||||
'PTDL_v1' => $this->convertToV2($parsed),
|
'PTDL_v1' => $this->convertToV3($this->convertLegacy($parsed)),
|
||||||
'PTDL_v2', 'PLCN_v1', 'PLCN_v2' => $parsed,
|
'PTDL_v2', 'PLCN_v1', 'PLCN_v2' => $this->convertToV3($parsed),
|
||||||
|
'PLCN_v3' => $parsed,
|
||||||
default => throw new InvalidFileUploadException('The file format is not recognized.'),
|
default => throw new InvalidFileUploadException('The file format is not recognized.'),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,7 +181,7 @@ class EggImporterService
|
|||||||
if ($forbidden->count()) {
|
if ($forbidden->count()) {
|
||||||
$parsed['variables'] = $allowed->merge($updatedVariables)->all();
|
$parsed['variables'] = $allowed->merge($updatedVariables)->all();
|
||||||
|
|
||||||
if (!empty($parsed['startup'])) {
|
if (count($parsed['startup_commands']) > 0) {
|
||||||
$pattern = '/\b(' . collect($forbidden)->map(fn ($variable) => preg_quote($variable['env_variable']))->join('|') . ')\b/';
|
$pattern = '/\b(' . collect($forbidden)->map(fn ($variable) => preg_quote($variable['env_variable']))->join('|') . ')\b/';
|
||||||
$parsed['startup'] = preg_replace($pattern, 'SERVER_$1', $parsed['startup']) ?? $parsed['startup'];
|
$parsed['startup'] = preg_replace($pattern, 'SERVER_$1', $parsed['startup']) ?? $parsed['startup'];
|
||||||
}
|
}
|
||||||
@ -206,7 +207,7 @@ class EggImporterService
|
|||||||
'config_startup' => json_encode(json_decode(Arr::get($parsed, 'config.startup')), JSON_PRETTY_PRINT),
|
'config_startup' => json_encode(json_decode(Arr::get($parsed, 'config.startup')), JSON_PRETTY_PRINT),
|
||||||
'config_logs' => json_encode(json_decode(Arr::get($parsed, 'config.logs')), JSON_PRETTY_PRINT),
|
'config_logs' => json_encode(json_decode(Arr::get($parsed, 'config.logs')), JSON_PRETTY_PRINT),
|
||||||
'config_stop' => Arr::get($parsed, 'config.stop'),
|
'config_stop' => Arr::get($parsed, 'config.stop'),
|
||||||
'startup' => Arr::get($parsed, 'startup'),
|
'startup_commands' => Arr::get($parsed, 'startup_commands'),
|
||||||
'script_install' => Arr::get($parsed, 'scripts.installation.script'),
|
'script_install' => Arr::get($parsed, 'scripts.installation.script'),
|
||||||
'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'),
|
'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'),
|
||||||
'script_container' => Arr::get($parsed, 'scripts.installation.container'),
|
'script_container' => Arr::get($parsed, 'scripts.installation.container'),
|
||||||
@ -217,7 +218,7 @@ class EggImporterService
|
|||||||
* @param array<string, mixed> $parsed
|
* @param array<string, mixed> $parsed
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
protected function convertToV2(array $parsed): array
|
protected function convertLegacy(array $parsed): array
|
||||||
{
|
{
|
||||||
if (!isset($parsed['images'])) {
|
if (!isset($parsed['images'])) {
|
||||||
$images = [Arr::get($parsed, 'image') ?? 'nil'];
|
$images = [Arr::get($parsed, 'image') ?? 'nil'];
|
||||||
@ -234,4 +235,21 @@ class EggImporterService
|
|||||||
|
|
||||||
return $parsed;
|
return $parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $parsed
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
protected function convertToV3(array $parsed): array
|
||||||
|
{
|
||||||
|
$startup = $parsed['startup'];
|
||||||
|
|
||||||
|
unset($parsed['startup']);
|
||||||
|
|
||||||
|
$parsed['startup_commands'] = [
|
||||||
|
$startup => $startup,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $parsed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,8 @@ class ServerCreationService
|
|||||||
$egg = Egg::query()->findOrFail($data['egg_id']);
|
$egg = Egg::query()->findOrFail($data['egg_id']);
|
||||||
|
|
||||||
// Fill missing fields from egg
|
// Fill missing fields from egg
|
||||||
$data['image'] ??= collect($egg->docker_images)->first();
|
$data['image'] ??= Arr::first($egg->docker_images);
|
||||||
$data['startup'] ??= $egg->startup;
|
$data['startup'] ??= Arr::first($egg->startup_commands);
|
||||||
|
|
||||||
// If a deployment object has been passed we need to get the allocation and node that the server should use.
|
// If a deployment object has been passed we need to get the allocation and node that the server should use.
|
||||||
if ($deployment) {
|
if ($deployment) {
|
||||||
|
@ -84,8 +84,8 @@ class StartupModificationService
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Fill missing fields from egg
|
// Fill missing fields from egg
|
||||||
$data['docker_image'] = $data['docker_image'] ?? collect($egg->docker_images)->first();
|
$data['docker_image'] ??= Arr::first($egg->docker_images);
|
||||||
$data['startup'] = $data['startup'] ?? $egg->startup;
|
$data['startup'] ??= Arr::first($egg->startup_commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
$server->fill([
|
$server->fill([
|
||||||
|
@ -48,10 +48,7 @@ class EggTransformer extends BaseTransformer
|
|||||||
'description' => $model->description,
|
'description' => $model->description,
|
||||||
'features' => $model->features,
|
'features' => $model->features,
|
||||||
'tags' => $model->tags,
|
'tags' => $model->tags,
|
||||||
// "docker_image" is deprecated, but left here to avoid breaking too many things at once
|
'docker_image' => Arr::first($model->docker_images, default: ''), // docker_images, use startup_commands
|
||||||
// in external software. We'll remove it down the road once things have gotten the chance
|
|
||||||
// to upgrade to using "docker_images".
|
|
||||||
'docker_image' => count($model->docker_images) > 0 ? Arr::first($model->docker_images) : '',
|
|
||||||
'docker_images' => $model->docker_images,
|
'docker_images' => $model->docker_images,
|
||||||
'config' => [
|
'config' => [
|
||||||
'files' => $files,
|
'files' => $files,
|
||||||
@ -61,7 +58,8 @@ class EggTransformer extends BaseTransformer
|
|||||||
'file_denylist' => $model->inherit_file_denylist,
|
'file_denylist' => $model->inherit_file_denylist,
|
||||||
'extends' => $model->config_from,
|
'extends' => $model->config_from,
|
||||||
],
|
],
|
||||||
'startup' => $model->startup,
|
'startup' => Arr::first($model->startup_commands, default: ''), // deprecated, use startup_commands
|
||||||
|
'startup_commands' => $model->startup_commands,
|
||||||
'script' => [
|
'script' => [
|
||||||
'privileged' => $model->script_is_privileged,
|
'privileged' => $model->script_is_privileged,
|
||||||
'install' => $model->copy_script_install,
|
'install' => $model->copy_script_install,
|
||||||
|
@ -30,7 +30,7 @@ class EggFactory extends Factory
|
|||||||
'config_files' => '{}',
|
'config_files' => '{}',
|
||||||
'name' => $this->faker->name(),
|
'name' => $this->faker->name(),
|
||||||
'description' => implode(' ', $this->faker->sentences()),
|
'description' => implode(' ', $this->faker->sentences()),
|
||||||
'startup' => 'java -jar test.jar',
|
'startup_commands' => ['java -jar test.jar'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('eggs', function (Blueprint $table) {
|
||||||
|
$table->json('startup_commands')->after('startup')->nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::table('eggs')->select(['id', 'startup'])->cursor()->each(function ($egg) {
|
||||||
|
$startup = $egg->startup;
|
||||||
|
|
||||||
|
DB::table('eggs')->where('id', $egg->id)->update(['startup_commands' => [$startup => $startup]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('eggs', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('startup');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('eggs', function (Blueprint $table) {
|
||||||
|
$table->text('startup')->after('startup_commands');
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::table('eggs')->select(['id', 'startup_commands'])->cursor()->each(function ($egg) {
|
||||||
|
DB::table('eggs')->where('id', $egg->id)->update([
|
||||||
|
'startup' => Arr::first(json_decode($egg->startup_commands, true, 512, JSON_THROW_ON_ERROR)),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('eggs', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('startup_commands');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user