From db67e06a368fd65ee6eb70a57ef7b4157046abf5 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 9 Feb 2016 14:44:27 -0500 Subject: [PATCH 1/2] Add BATS tests --- README.md | 13 +++-- circle.yml | 15 ++++++ dockerclean | 6 +-- tests/docker-compose.yml | 14 +++++ tests/dockerclean-test.bats | 101 ++++++++++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 circle.yml create mode 100644 tests/docker-compose.yml create mode 100755 tests/dockerclean-test.bats diff --git a/README.md b/README.md index 549907c..ef26501 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ -dockerclean -=========== +# dockerclean A simple utility to make it easier to cleanup Docker images and containers. -Usage ------ +## Usage `dockerclean [OPTIONS]` - `-c PATTERN`: Remove all containers matched by `PATTERN` @@ -11,5 +9,10 @@ Usage - `-r`: By default `dockerclean` executes in `dry-run` mode to give you a chance to preview any images or containers that will get deleted. Use the `-r` flag to remove matched images and containers. -- `-t PATTERN`: Remove all images matched by `PATTERN` +- `-i PATTERN`: Remove all images matched by `PATTERN` - `-h`: Print usage and options + +## Build status +![master](https://circleci.com/gh/michael-yx-wu/dockerclean/tree/master.png?style=shield&circle-token=810386c47ffeb705bf8c4e52a88c0d2177e82230) (master) + +![develop](https://circleci.com/gh/michael-yx-wu/dockerclean/tree/develop.png?style=shield&circle-token=810386c47ffeb705bf8c4e52a88c0d2177e82230) (develop) diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..7f0b288 --- /dev/null +++ b/circle.yml @@ -0,0 +1,15 @@ +machine: + pre: + - sudo curl -L -o /usr/bin/docker 'https://s3-external-1.amazonaws.com/circle-downloads/docker-1.9.1-circleci' + - sudo chmod 0755 /usr/bin/docker + services: + - docker + +dependencies: + pre: + - git clone https://github.com/sstephenson/bats.git; cd bats; sudo ./install.sh /usr/local + - docker -v + +test: + override: + - bats tests diff --git a/dockerclean b/dockerclean index ba5ce7c..b305745 100755 --- a/dockerclean +++ b/dockerclean @@ -26,7 +26,7 @@ function displayHelp { optionsTable["1,1"]='Remove all images tagged as ' optionsTable["2,0"]='-r' optionsTable["2,1"]='If the -r option is not given, display all images or containers that would be removed' - optionsTable["3,0"]='-t PATTERN' + optionsTable["3,0"]='-i PATTERN' optionsTable["3,1"]='Remove all images matched by PATTERN' optionsTable["4,0"]='-h' optionsTable["4,1"]='Print usage and options' @@ -102,7 +102,7 @@ function removeContainers { docker rm -f $(docker ps -a | grep $grepContainerPattern | awk '{print $NF}') } -while getopts ":c:drt:h" option; do +while getopts ":c:dri:h" option; do case "$option" in h) displayHelp @@ -118,7 +118,7 @@ while getopts ":c:drt:h" option; do grepContainer=true grepContainerPattern="$OPTARG" ;; - t) + i) grepImage=true grepImagePattern="$OPTARG" ;; diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml new file mode 100644 index 0000000..398a606 --- /dev/null +++ b/tests/docker-compose.yml @@ -0,0 +1,14 @@ +alpine26: + container_name: alpine.26.dockerclean + image: alpine:2.6 + command: sleep 300 + +alpine32: + container_name: alpine.32.dockerclean + image: alpine:3.2 + command: sleep 300 + +cirros030: + container_name: cirros.030.dockerclean + image: cirros:0.3.0 + command: sleep 300 diff --git a/tests/dockerclean-test.bats b/tests/dockerclean-test.bats new file mode 100755 index 0000000..dbe0ed1 --- /dev/null +++ b/tests/dockerclean-test.bats @@ -0,0 +1,101 @@ +#!/usr/bin/env bats + +nothingRemoved() { + run docker inspect alpine:2.6 + [ "$status" -eq 0 ] + run docker inspect alpine:3.2 + [ "$status" -eq 0 ] + run docker inspect cirros:0.3.0 + [ "$status" -eq 0 ] + run docker inspect alpine.26.dockerclean + [ "$status" -eq 0 ] + run docker inspect alpine.32.dockerclean + [ "$status" -eq 0 ] + run docker inspect cirros.030.dockerclean + [ "$status" -eq 0 ] +} + +alpineImagesRemoved() { + run docker inspect alpine:2.6 + [ "$status" -eq 1 ] + run docker inspect alpine:3.2 + [ "$status" -eq 1 ] + run docker inspect cirros:0.3.0 + [ "$status" -eq 0 ] + run docker inspect alpine.26.dockerclean + [ "$status" -eq 1 ] + run docker inspect alpine.32.dockerclean + [ "$status" -eq 1 ] + run docker inspect cirros.030.dockerclean + [ "$status" -eq 0 ] +} + +alpineContainersRemoved() { + run docker inspect alpine:2.6 + [ "$status" -eq 0 ] + run docker inspect alpine:3.2 + [ "$status" -eq 0 ] + run docker inspect cirros:0.3.0 + [ "$status" -eq 0 ] + run docker inspect alpine.26.dockerclean + [ "$status" -eq 1 ] + run docker inspect alpine.32.dockerclean + [ "$status" -eq 1 ] + run docker inspect cirros.030.dockerclean + [ "$status" -eq 0 ] +} + +setup() { + docker-compose -f tests/docker-compose.yml -p dockerclean-tests up -d + sleep 5 + export alpine_26_image_id=$(docker inspect --format={{.Id}} alpine:2.6) + export alpine_latest_image_id=$(docker inspect --format={{.Id}} alpine:3.2) + export cirros_latest_image_id=$(docker inspect --format={{.Id}} cirros:0.3.0) + export alpine_26_container_id=$(docker inspect --format={{.Id}} alpine.26.dockerclean) + export alpine_latest_container_id=$(docker inspect --format={{.Id}} alpine.32.dockerclean) + export cirros_latest_container_id=$(docker inspect --format={{.Id}} cirros.030.dockerclean) +} + +@test "matches containers without removing" { + results=$(./dockerclean -c alpine) + num_results=$(echo "$results" | grep alpine | wc -l) + [ "$num_results" -eq 2 ] + + alpine_26_matched_container_id=$(docker inspect --format={{.Id}} $(echo "$results" | grep alpine | grep "2\.6" | awk '{print $1}')) + alpine_latest_matched_container_id=$(docker inspect --format={{.Id}} $(echo "$results" | grep alpine | grep "3\.2" | awk '{print $1}')) + [ "$alpine_26_matched_container_id" = "$alpine_26_container_id" ] + [ "$alpine_latest_matched_container_id" = "$alpine_latest_container_id" ] + + nothingRemoved +} + +@test "matches images without removing" { + results=$(./dockerclean -i alpine) + num_results=$(echo "$results" | grep alpine | wc -l) + [ "$num_results" -eq 2 ] + + alpine_26_matched_image_id=$(docker inspect --format={{.Id}} $(echo "$results" | grep alpine | grep "2\.6" | awk '{print $3}')) + alpine_latest_matched_image_id=$(docker inspect --format={{.Id}} $(echo "$results" | grep alpine | grep "3\.2" | awk '{print $3}')) + [ "$alpine_26_matched_image_id" = "$alpine_26_image_id" ] + [ "$alpine_latest_matched_image_id" = "$alpine_latest_image_id" ] + + nothingRemoved +} + +@test "removes containers" { + skip "CircleCI does not allow deletion of containers" + + results=$(./dockerclean -r -c alpine) + alpineContainersRemoved +} + +@test "removes images (after removing containers)" { + skip "CircleCI does not allow deletion of containers" + + run ./dockerclean -r -c alpine + [ "$status" -eq 0 ] + + results=$(./dockerclean -r -i alpine) + sleep 1 + alpineImagesRemoved +} From 8d284d7ce000b285dc8a4ba4b3f36d267d980f73 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 9 Feb 2016 16:21:04 -0500 Subject: [PATCH 2/2] Add options to remove all exited containers Use the `-e` flag to remove all exited containers. If used in conjunction with `-c`, it will find all containers that exited or match the `PATTERN` provided. --- README.md | 4 ++++ dockerclean | 26 ++++++++++++++++++-------- tests/docker-compose.yml | 5 +++++ tests/dockerclean-test.bats | 11 +++++++++++ 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ef26501..06780e7 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,16 @@ A simple utility to make it easier to cleanup Docker images and containers. - `-c PATTERN`: Remove all containers matched by `PATTERN` - `-d`: Remove all images tagged as `` +- `-e`: Remove all exited containers - `-r`: By default `dockerclean` executes in `dry-run` mode to give you a chance to preview any images or containers that will get deleted. Use the `-r` flag to remove matched images and containers. - `-i PATTERN`: Remove all images matched by `PATTERN` - `-h`: Print usage and options +If `-e` and `-c` are used in conjunction, `dockerclean` will find all containers +that have exited or match the `PATTERN` provided. + ## Build status ![master](https://circleci.com/gh/michael-yx-wu/dockerclean/tree/master.png?style=shield&circle-token=810386c47ffeb705bf8c4e52a88c0d2177e82230) (master) diff --git a/dockerclean b/dockerclean index b305745..c3e0b0a 100755 --- a/dockerclean +++ b/dockerclean @@ -10,6 +10,7 @@ grepContainer=false grepContainerPattern='' grepImage=false grepImagePattern='' +exitedContainers=false function displayUsageAndSummary { printf "%s\n\n%s\n" "$usage" "$summary" @@ -19,7 +20,7 @@ function displayHelp { displayUsageAndSummary echo '' declare -A optionsTable - local numFlags=5 + local numFlags=6 optionsTable["0,0"]='-c PATTERN' optionsTable["0,1"]='Remove all containers matched by PATTERN' optionsTable["1,0"]='-d' @@ -28,6 +29,8 @@ function displayHelp { optionsTable["2,1"]='If the -r option is not given, display all images or containers that would be removed' optionsTable["3,0"]='-i PATTERN' optionsTable["3,1"]='Remove all images matched by PATTERN' + optionsTable["5,0"]='-e' + optionsTable["5,1"]='Remove all exited containers' optionsTable["4,0"]='-h' optionsTable["4,1"]='Print usage and options' i=0 @@ -83,15 +86,19 @@ function removeImages { } function gatherContainersToRemove { - if ! "$grepContainer"; then + if "$grepContainer" && "$exitedContainers"; then + docker ps -a | grep -E "$grepContainerPattern|Exited" + elif "$grepContainer"; then + docker ps -a | grep -E "$grepContainerPattern" + elif "$exitedContainers"; then + docker ps -a | grep -E "Exited" + else echo '' - return 0 fi - docker ps -a | grep "$grepContainerPattern" } function removeContainers { - if ! "$grepContainer"; then + if ! "$grepContainer" && ! "$exitedContainers"; then echo '' return 0 fi @@ -99,10 +106,10 @@ function removeContainers { echo 'No matching containers to remove' return 0 fi - docker rm -f $(docker ps -a | grep $grepContainerPattern | awk '{print $NF}') + docker rm -f $(echo "$1" | awk '{print $1}') } -while getopts ":c:dri:h" option; do +while getopts ":c:deri:h" option; do case "$option" in h) displayHelp @@ -118,6 +125,9 @@ while getopts ":c:dri:h" option; do grepContainer=true grepContainerPattern="$OPTARG" ;; + e) + exitedContainers=true + ;; i) grepImage=true grepImagePattern="$OPTARG" @@ -137,7 +147,7 @@ while getopts ":c:dri:h" option; do esac done -if ! { "$dangling" || "$grepContainer" || "$grepImage"; } then +if ! { "$dangling" || "$grepContainer" || "$grepImage" || "$exitedContainers"; } then echo 'Nothing will be removed: no removal strategy specified' echo '' displayHelpPrompt diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 398a606..22f1860 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -12,3 +12,8 @@ cirros030: container_name: cirros.030.dockerclean image: cirros:0.3.0 command: sleep 300 + +cirrosexited: + container_name: cirros.exited.dockerclean + image: cirros:0.3.0 + command: sleep 0 diff --git a/tests/dockerclean-test.bats b/tests/dockerclean-test.bats index dbe0ed1..219368b 100755 --- a/tests/dockerclean-test.bats +++ b/tests/dockerclean-test.bats @@ -54,6 +54,7 @@ setup() { export alpine_26_container_id=$(docker inspect --format={{.Id}} alpine.26.dockerclean) export alpine_latest_container_id=$(docker inspect --format={{.Id}} alpine.32.dockerclean) export cirros_latest_container_id=$(docker inspect --format={{.Id}} cirros.030.dockerclean) + export cirros_exited_container_id=$(docker inspect --format={{.Id}} cirros.exited.dockerclean) } @test "matches containers without removing" { @@ -69,6 +70,16 @@ setup() { nothingRemoved } +@test "matches exited containers without removing" { + results=$(./dockerclean -e) + num_results=$(echo "$results" | grep cirros | grep Exited | wc -l) + [ "$num_results" -eq 1 ] + cirros_exited_matched_container_id=$(docker inspect --format={{.Id}} $(echo "$results" | grep cirros | grep Exited | awk '{print $1}')) + [ "$alpine_exited_matched_container_id" = "$alpine_exited_container_id" ] + + nothingRemoved +} + @test "matches images without removing" { results=$(./dockerclean -i alpine) num_results=$(echo "$results" | grep alpine | wc -l)