diff --git a/root/app/www/public/ajax/orphans.php b/root/app/www/public/ajax/orphans.php index 347ef46..96b4b3d 100644 --- a/root/app/www/public/ajax/orphans.php +++ b/root/app/www/public/ajax/orphans.php @@ -10,18 +10,19 @@ require 'shared.php'; if ($_POST['m'] == 'init') { - $images = apiRequest('docker-getOrphanContainers'); - $images = json_decode($images['result'], true); - $volumes = apiRequest('docker-getOrphanVolumes'); - $volumes = json_decode($volumes['result'], true); - $networks = apiRequest('docker-getOrphanNetworks'); - $networks = json_decode($networks['result'], true); - + $images = apiRequest('docker-getOrphanContainers'); + $images = json_decode($images['result'], true); + $volumes = apiRequest('docker-getOrphanVolumes'); + $volumes = json_decode($volumes['result'], true); + $networks = apiRequest('docker-getOrphanNetworks'); + $networks = json_decode($networks['result'], true); + $unusedContainers = apiRequest('docker-getUnusedContainers'); + $unusedContainers = json_decode($unusedContainers['result'], true); ?>

Images

- docker images -f dangling=true +
@@ -50,8 +51,36 @@
+

Unused containers

+ +
+ + + + + + + + + + + + + + + + + + + +
IDRepositoryTag
+

Volumes

- docker volume ls -qf dangling=true +
@@ -77,7 +106,7 @@

Networks

- docker network ls -qf dangling=true +
@@ -125,21 +154,21 @@ if ($_POST['m'] == 'removeOrphans') { switch ($_POST['action']) { case 'remove': - if ($_POST['type'] == 'image') { + if ($_POST['type'] == 'image' || $_POST['type'] == 'unused') { $apiRequest = apiRequest('docker-removeImage', [], ['image' => $_POST['orphan']])['result']; - if (stripos($apiRequest, 'error') !== false || stripos($apiRequest, 'help') !== false) { + if (stri_contains($apiRequest, 'error') || stri_contains($apiRequest, 'help')) { echo $apiRequest; } } if ($_POST['type'] == 'network') { $apiRequest = apiRequest('docker-removeNetwork', [], ['id' => $_POST['orphan']])['result']; - if (stripos($apiRequest, 'error') !== false || stripos($apiRequest, 'help') !== false) { + if (stri_contains($apiRequest, 'error') || stri_contains($apiRequest, 'help')) { echo $apiRequest; } } if ($_POST['type'] == 'volume') { $apiRequest = apiRequest('docker-removeVolume', [], ['name' => $_POST['orphan']])['result']; - if (stripos($apiRequest, 'error') !== false || stripos($apiRequest, 'help') !== false) { + if (stri_contains($apiRequest, 'error') || stri_contains($apiRequest, 'help')) { echo $apiRequest; } } diff --git a/root/app/www/public/classes/interfaces/Docker.php b/root/app/www/public/classes/interfaces/Docker.php index 25a1c31..b3851d6 100644 --- a/root/app/www/public/classes/interfaces/Docker.php +++ b/root/app/www/public/classes/interfaces/Docker.php @@ -32,6 +32,7 @@ interface DockerSock public const START_CONTAINER = '/usr/bin/docker container start %s'; public const STOP_CONTAINER = '/usr/bin/docker container stop %s%s'; public const ORPHAN_CONTAINERS = '/usr/bin/docker images -f dangling=true --format="{{json . }}" | jq -s --tab .'; + public const UNUSED_CONTAINERS = '/usr/bin/docker images --format \'{{.ID}}:{{.Repository}}:{{.Tag}}\' | grep -v "$(docker ps --format {{.Image}})"'; public const CONTAINER_PORT = '/usr/bin/docker port %s %s'; //-- IMAGE SPECIFIC public const REMOVE_IMAGE = '/usr/bin/docker image rm %s'; diff --git a/root/app/www/public/classes/traits/Docker/Container.php b/root/app/www/public/classes/traits/Docker/Container.php index 817c112..52d982f 100644 --- a/root/app/www/public/classes/traits/Docker/Container.php +++ b/root/app/www/public/classes/traits/Docker/Container.php @@ -42,6 +42,28 @@ public function stopContainer($containerName) return $this->shell->exec($cmd . ' 2>&1'); } + public function getUnusedContainers() + { + $unused = []; + $cmd = DockerSock::UNUSED_CONTAINERS; + $containers = $this->shell->exec($cmd . ' 2>&1'); + + if ($containers) { + $containerList = explode("\n", $containers); + foreach ($containerList as $container) { + list($id, $image, $tag) = explode(':', $container); + + if (!$id) { + continue; + } + + $unused[] = ['ID' => $id, 'Repository' => $image, 'Tag' => $tag]; + } + } + + return json_encode($unused); + } + public function getOrphanContainers() { $cmd = DockerSock::ORPHAN_CONTAINERS; diff --git a/root/app/www/public/functions/api.php b/root/app/www/public/functions/api.php index 0e4174f..1523120 100644 --- a/root/app/www/public/functions/api.php +++ b/root/app/www/public/functions/api.php @@ -204,6 +204,8 @@ function apiRequestLocal($endpoint, $parameters = [], $payload = []) return $docker->getOrphanNetworks(); case 'docker-getOrphanVolumes': return $docker->getOrphanVolumes(); + case 'docker-getUnusedContainers': + return $docker->getUnusedContainers(); case 'docker-imageSizes': return $docker->getImageSizes(); case 'docker-inspect': diff --git a/root/app/www/public/functions/helpers/strings.php b/root/app/www/public/functions/helpers/strings.php index 2a187b8..f4c26b5 100644 --- a/root/app/www/public/functions/helpers/strings.php +++ b/root/app/www/public/functions/helpers/strings.php @@ -7,6 +7,11 @@ ---------------------------------- */ +function stri_contains(string|null $haystack, string $needle) +{ + return str_contains(strtolower($haystack), strtolower($needle)); +} + function str_equals_any(string|null $haystack, array $needles): bool { if (!$haystack) { diff --git a/root/app/www/public/js/orphans.js b/root/app/www/public/js/orphans.js index 9e0e79e..f66074b 100644 --- a/root/app/www/public/js/orphans.js +++ b/root/app/www/public/js/orphans.js @@ -28,6 +28,8 @@ function removeOrphans() type = 'volume'; } else if (split[0] == 'orphanNetwork') { type = 'network'; + } else if (split[0] == 'unusedContainer') { + type = 'unused'; } selectedOrphans.push({'orphan': split[1], 'type': type});