From ff7a1e6726d50fe263dc933c016c3d23fc273ee7 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 25 May 2023 16:32:54 +0100 Subject: [PATCH] Prune unused images correctly dangling=true doesn't prune any images, as we are not creating dangling images. Using --all should remove unused images, but it considers the Git SHA tag on the latest image to be unused (presumably because there are two tags, the SHA and latest and the running container is only considered to be using "latest"). As a result it deletes the tag, which means that we can't rollback to that SHA later. Its a bit more complicated to only remove images that are not referenced by any containers. First we find the tags we want to keep from the containers (running and stopped). Then we append the latest tag to that list. Then we get a full list of image tags and remove those tags from that list (using `grep -v -w`). Finally we pass the tags to `docker rmi`. That either deletes the tag if there are other references to the image or both the tag and the image if it is the only one. --- lib/mrsk/commands/prune.rb | 15 +++++++++++++-- test/cli/prune_test.rb | 2 +- test/commands/prune_test.rb | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/mrsk/commands/prune.rb b/lib/mrsk/commands/prune.rb index 79da58be..3b68fe3f 100644 --- a/lib/mrsk/commands/prune.rb +++ b/lib/mrsk/commands/prune.rb @@ -3,12 +3,15 @@ require "active_support/core_ext/numeric/time" class Mrsk::Commands::Prune < Mrsk::Commands::Base def images - docker :image, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true" + pipe \ + docker(:image, :ls, *service_filter, "--format", "'{{.Repository}}:{{.Tag}}'"), + "grep -v -w \"#{active_image_list}\"", + "while read tag; do docker rmi $tag; done" end def containers(keep_last: 5) pipe \ - docker(:ps, "-q", "-a", "--filter", "label=service=#{config.service}", *stopped_containers_filters), + docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters), "tail -n +#{keep_last + 1}", "while read container_id; do docker rm $container_id; done" end @@ -17,4 +20,12 @@ class Mrsk::Commands::Prune < Mrsk::Commands::Base def stopped_containers_filters [ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] } end + + def active_image_list + "$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=#{config.service} | tr -d '\\n')#{config.latest_image}" + end + + def service_filter + [ "--filter", "label=service=#{config.service}" ] + end end diff --git a/test/cli/prune_test.rb b/test/cli/prune_test.rb index e07f3172..85cdf159 100644 --- a/test/cli/prune_test.rb +++ b/test/cli/prune_test.rb @@ -10,7 +10,7 @@ class CliPruneTest < CliTestCase test "images" do run_command("images").tap do |output| - assert_match /docker image prune --force --filter label=service=app --filter dangling=true on 1.1.1.\d/, output + assert_match "docker image ls --filter label=service=app --format '{{.Repository}}:{{.Tag}}' | grep -v -w \"$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=app | tr -d '\\n')dhh/app:latest\" | while read tag; do docker rmi $tag; done on 1.1.1.", output end end diff --git a/test/commands/prune_test.rb b/test/commands/prune_test.rb index 436ed02f..f4025893 100644 --- a/test/commands/prune_test.rb +++ b/test/commands/prune_test.rb @@ -10,7 +10,7 @@ class CommandsPruneTest < ActiveSupport::TestCase test "images" do assert_equal \ - "docker image prune --force --filter label=service=app --filter dangling=true", + "docker image ls --filter label=service=app --format '{{.Repository}}:{{.Tag}}' | grep -v -w \"$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=app | tr -d '\\n')dhh/app:latest\" | while read tag; do docker rmi $tag; done", new_command.images.join(" ") end