diff --git a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php index a944b61775..52b8657145 100644 --- a/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/StoreServerRequest.php @@ -5,6 +5,7 @@ use App\Http\Requests\Api\Application\ApplicationApiRequest; use App\Models\Objects\DeploymentObject; use App\Models\Server; +use App\Rules\DockerLabel; use App\Services\Acl\Api\AdminAcl; use Illuminate\Validation\Rule; use Illuminate\Validation\Validator; @@ -17,6 +18,8 @@ class StoreServerRequest extends ApplicationApiRequest /** * Rules to be applied to this request. + * + * @return array|string> */ public function rules(): array { @@ -29,6 +32,8 @@ public function rules(): array 'user' => $rules['owner_id'], 'egg' => $rules['egg_id'], 'docker_image' => 'sometimes|string', + 'docker_labels' => ['sometimes', 'array', new DockerLabel()], + 'docker_labels.*' => 'required|string', 'startup' => 'sometimes|string', 'environment' => 'present|array', 'skip_scripts' => 'sometimes|boolean', @@ -122,6 +127,7 @@ public function validated($key = null, $default = null): array 'allocation_limit' => array_get($data, 'feature_limits.allocations'), 'backup_limit' => array_get($data, 'feature_limits.backups'), 'oom_killer' => array_get($data, 'oom_killer'), + 'docker_labels' => array_get($data, 'docker_labels'), ]; } diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php index ad419e8ac8..63aaaa120c 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerDetailsRequest.php @@ -18,22 +18,30 @@ public function rules(): array 'name' => $rules['name'], 'user' => $rules['owner_id'], 'description' => array_merge(['nullable'], $rules['description']), + 'docker_labels' => 'sometimes|array', + 'docker_labels.*' => 'string', ]; } /** * Convert the posted data into the correct format that is expected by the application. * - * @return array + * @return array */ public function validated($key = null, $default = null): array { - return [ + $attributes = [ 'external_id' => $this->input('external_id'), 'name' => $this->input('name'), 'owner_id' => $this->input('user'), 'description' => $this->input('description'), ]; + + if ($this->has('docker_labels')) { + $attributes['docker_labels'] = $this->input('docker_labels'); + } + + return $attributes; } /** diff --git a/app/Models/Server.php b/app/Models/Server.php index b501ef5b64..7d433ef6db 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -184,6 +184,8 @@ class Server extends Model implements HasAvatar, Validatable 'skip_scripts' => ['sometimes', 'boolean'], 'image' => ['required', 'string', 'max:255'], 'icon' => ['sometimes', 'nullable', 'string'], + 'docker_labels' => ['array'], + 'docker_labels.*' => ['required', 'string'], 'database_limit' => ['present', 'nullable', 'integer', 'min:0'], 'allocation_limit' => ['sometimes', 'nullable', 'integer', 'min:0'], 'backup_limit' => ['present', 'nullable', 'integer', 'min:0'], diff --git a/app/Rules/DockerLabel.php b/app/Rules/DockerLabel.php new file mode 100644 index 0000000000..c31f11e997 --- /dev/null +++ b/app/Rules/DockerLabel.php @@ -0,0 +1,27 @@ + * } $data * * @throws Throwable @@ -36,12 +37,18 @@ public function handle(Server $server, array $data): Server return $this->connection->transaction(function () use ($data, $server) { $owner = $server->owner_id; - $server->forceFill([ + $attributes = [ 'external_id' => Arr::get($data, 'external_id'), 'owner_id' => Arr::get($data, 'owner_id'), 'name' => Arr::get($data, 'name'), 'description' => Arr::get($data, 'description') ?? '', - ])->saveOrFail(); + ]; + + if ($labels = Arr::get($data, 'docker_labels')) { + $attributes['docker_labels'] = $labels; + } + + $server->forceFill($attributes)->saveOrFail(); // If the owner_id value is changed we need to revoke any tokens that exist for the server // on the daemon instance so that the old owner no longer has any permission to access the diff --git a/app/Transformers/Api/Application/ServerTransformer.php b/app/Transformers/Api/Application/ServerTransformer.php index de89d35645..518ad682d9 100644 --- a/app/Transformers/Api/Application/ServerTransformer.php +++ b/app/Transformers/Api/Application/ServerTransformer.php @@ -85,6 +85,7 @@ public function transform($server): array 'container' => [ 'startup_command' => $server->startup, 'image' => $server->image, + 'docker_labels' => $server->docker_labels ?? [], // This field is deprecated, please use "status". 'installed' => $server->isInstalled() ? 1 : 0, 'environment' => $this->environmentService->handle($server), diff --git a/tests/Integration/Services/Servers/ServerCreationServiceTest.php b/tests/Integration/Services/Servers/ServerCreationServiceTest.php index 70582dceed..085133f686 100644 --- a/tests/Integration/Services/Servers/ServerCreationServiceTest.php +++ b/tests/Integration/Services/Servers/ServerCreationServiceTest.php @@ -229,6 +229,57 @@ public function test_server_without_allocation_is_created_with_deployment_object $this->assertNull($response->allocation_id); } + /** + * Test that docker labels are persisted when provided during server creation. + */ + public function test_server_creation_accepts_docker_labels(): void + { + /** @var User $user */ + $user = User::factory()->create(); + + /** @var Node $node */ + $node = Node::factory()->create(); + + /** @var Allocation $allocation */ + $allocation = Allocation::factory()->create([ + 'node_id' => $node->id, + ]); + + $egg = $this->cloneEggAndVariables($this->bungeecord); + + $labels = [ + 'com.example/app' => 'panel', + 'tier' => 'production', + ]; + + $data = [ + 'name' => $this->faker->name(), + 'description' => $this->faker->sentence(), + 'owner_id' => $user->id, + 'allocation_id' => $allocation->id, + 'node_id' => $allocation->node_id, + 'memory' => 256, + 'swap' => 128, + 'disk' => 100, + 'io' => 500, + 'cpu' => 0, + 'startup' => 'java -jar server.jar', + 'image' => 'java:8', + 'egg_id' => $egg->id, + 'environment' => [ + 'BUNGEE_VERSION' => '123', + 'SERVER_JARFILE' => 'server.jar', + ], + 'docker_labels' => $labels, + ]; + + $this->daemonServerRepository->expects('setServer->create')->with(false)->andReturnUndefined(); + + $server = $this->getService()->handle($data); + + $this->assertSame($labels, $server->docker_labels); + } + /** * Test that a server is deleted from the Panel if daemon returns an error during the creation * process.