From 40bf9e22b1fcfab68336c13a77eca01b58536005 Mon Sep 17 00:00:00 2001 From: davidhorvat Date: Thu, 5 Dec 2024 19:42:41 +0100 Subject: [PATCH 1/4] refactor: list docker images --- .../Docker/GetServerDockerImageDetails.php | 16 ++++++++++++++ app/Actions/Docker/ListServerDockerImages.php | 21 +------------------ 2 files changed, 17 insertions(+), 20 deletions(-) create mode 100644 app/Actions/Docker/GetServerDockerImageDetails.php diff --git a/app/Actions/Docker/GetServerDockerImageDetails.php b/app/Actions/Docker/GetServerDockerImageDetails.php new file mode 100644 index 000000000..cded92506 --- /dev/null +++ b/app/Actions/Docker/GetServerDockerImageDetails.php @@ -0,0 +1,16 @@ +filter() - ->map(function ($line) { - $data = json_decode($line, true); - if (!$data) return null; - - return [ - 'tag' => $data['Tag'] ?? '', - 'id' => $data['ID'] ?? '', - 'created_at' => $data['CreatedAt'] ?? '', - 'size' => $data['Size'] ?? '', - 'name' => $data['Repository'] ?? '', - ]; - }) - ->filter() - ->values() - ->toArray(); + return format_docker_command_output_to_json(instant_remote_process(["docker images --format '{{json .}}'"], $server)); } } From 8cab52855186ffbc32d60f70ae89db0c4e91a1c7 Mon Sep 17 00:00:00 2001 From: davidhorvat Date: Thu, 5 Dec 2024 20:43:31 +0100 Subject: [PATCH 2/4] chore: add get_server_dcoker_image_details --- .../Docker/GetServerDockerImageDetails.php | 5 +++-- app/Http/Controllers/Api/DockerController.php | 21 ++++++++++++++++++- routes/api.php | 4 +++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/Actions/Docker/GetServerDockerImageDetails.php b/app/Actions/Docker/GetServerDockerImageDetails.php index cded92506..dffb63dd5 100644 --- a/app/Actions/Docker/GetServerDockerImageDetails.php +++ b/app/Actions/Docker/GetServerDockerImageDetails.php @@ -2,15 +2,16 @@ namespace App\Actions\Docker; -use Illuminate\Database\Eloquent\Collection; use Lorisleiva\Actions\Concerns\AsAction; class GetServerDockerImageDetails { use AsAction; - public static function run() + public static function run($server, $imageId) { + $result = instant_remote_process(["docker inspect --format 'json' --type=image {$imageId}"], $server); + return json_decode($result, true); } } diff --git a/app/Http/Controllers/Api/DockerController.php b/app/Http/Controllers/Api/DockerController.php index b6e9dc4a9..615ecbdbb 100644 --- a/app/Http/Controllers/Api/DockerController.php +++ b/app/Http/Controllers/Api/DockerController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api; +use App\Actions\Docker\GetServerDockerImageDetails; use App\Actions\Docker\ListServerDockerImages; use App\Http\Controllers\Controller; use App\Models\Server; @@ -26,6 +27,24 @@ class DockerController extends Controller return response()->json(['error' => 'server is not reachable.'], 403); } - return response()->json(ListServerDockerImages::run($server)); + return ListServerDockerImages::run($server); + } + + public function get_server_dcoker_image_details($server_uuid, $id) + { + $query = Server::query(); + + $server = $query->where('uuid', $server_uuid)->first(); + if (!$server) { + return response()->json(['error' => 'server not found'], 404); + } + + $isReachable = (bool) $server->settings->is_reachable; + // If the server is reachable, send the reachable notification if it was sent before + if ($isReachable !== true) { + return response()->json(['error' => 'server is not reachable.'], 403); + } + + return response()->json(GetServerDockerImageDetails::run($server, $id)); } } diff --git a/routes/api.php b/routes/api.php index 0fa488689..59687d8a6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -130,10 +130,12 @@ Route::group([ Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]); Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::get('/docker/images/{server_uuid}', [DockerController::class, 'list_server_docker_images']); + Route::get('/docker/{server_uuid}/images', [DockerController::class, 'list_server_docker_images']); + Route::get('/docker/{server_uuid}/image/{id}', [DockerController::class, 'get_server_dcoker_image_details']); }); + Route::group([ 'prefix' => 'v1', ], function () { From 6a16e71310ca2e1681c18b727092a31655d74978 Mon Sep 17 00:00:00 2001 From: davidhorvat Date: Thu, 5 Dec 2024 20:50:47 +0100 Subject: [PATCH 3/4] chore: add container count to image details --- app/Actions/Docker/GetServerDockerImageDetails.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/Actions/Docker/GetServerDockerImageDetails.php b/app/Actions/Docker/GetServerDockerImageDetails.php index dffb63dd5..bacfb621c 100644 --- a/app/Actions/Docker/GetServerDockerImageDetails.php +++ b/app/Actions/Docker/GetServerDockerImageDetails.php @@ -10,8 +10,18 @@ class GetServerDockerImageDetails public static function run($server, $imageId) { - $result = instant_remote_process(["docker inspect --format 'json' --type=image {$imageId}"], $server); + $imageDetailsRaw = instant_remote_process(["docker inspect --type=image {$imageId}"], $server); + $imageDetails = json_decode($imageDetailsRaw, true); - return json_decode($result, true); + if (json_last_error() !== JSON_ERROR_NONE) { + return ['error' => 'Invalid JSON returned from Docker inspect', 'raw_output' => $imageDetailsRaw]; + } + + $containerCountRaw = instant_remote_process(["docker ps -q --filter ancestor={$imageId} | wc -l"], $server); + $containerCount = intval(trim($containerCountRaw)); // Convert to an integer + + $imageDetails[0]['ContainerCount'] = $containerCount; + + return $imageDetails; } } From 20fcd437c5ece69d0d0447d2c475b3a896f4ee2e Mon Sep 17 00:00:00 2001 From: davidhorvat Date: Thu, 5 Dec 2024 21:18:24 +0100 Subject: [PATCH 4/4] chore: add delete_all_dangling_server_docker_images --- .../DeleteAllDanglingServerDockerImages.php | 15 ++++++++++++ app/Http/Controllers/Api/DockerController.php | 23 ++++++++++++++++++- routes/api.php | 3 ++- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 app/Actions/Docker/DeleteAllDanglingServerDockerImages.php diff --git a/app/Actions/Docker/DeleteAllDanglingServerDockerImages.php b/app/Actions/Docker/DeleteAllDanglingServerDockerImages.php new file mode 100644 index 000000000..592241b95 --- /dev/null +++ b/app/Actions/Docker/DeleteAllDanglingServerDockerImages.php @@ -0,0 +1,15 @@ +json(GetServerDockerImageDetails::run($server, $id)); } + + public function delete_all_dangling_server_docker_images($server_uuid) + { + $query = Server::query(); + + $server = $query->where('uuid', $server_uuid)->first(); + if (!$server) { + return response()->json(['error' => 'server not found'], 404); + } + + $isReachable = (bool) $server->settings->is_reachable; + // If the server is reachable, send the reachable notification if it was sent before + if ($isReachable !== true) { + return response()->json(['error' => 'server is not reachable.'], 403); + } + + $message = DeleteAllDanglingServerDockerImages::run($server); + + return response()->json(['message' => $message]); + } } diff --git a/routes/api.php b/routes/api.php index 59687d8a6..fc24abcc7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -131,7 +131,8 @@ Route::group([ Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]); Route::get('/docker/{server_uuid}/images', [DockerController::class, 'list_server_docker_images']); - Route::get('/docker/{server_uuid}/image/{id}', [DockerController::class, 'get_server_dcoker_image_details']); + Route::get('/docker/{server_uuid}/image/{id}', [DockerController::class, 'get_server_docker_image_details']); + Route::delete('/docker/{server_uuid}/images/delete/dangling', [DockerController::class, 'delete_all_dangling_server_docker_images']); });