diff --git a/lib/mrsk/cli/app.rb b/lib/mrsk/cli/app.rb index 7b003860..882c80c8 100644 --- a/lib/mrsk/cli/app.rb +++ b/lib/mrsk/cli/app.rb @@ -19,14 +19,14 @@ class Mrsk::Cli::App < Mrsk::Cli::Base roles.each do |role| execute *MRSK.auditor(role: role).record("Booted app version #{version}"), verbosity: :debug - if capture_with_info(*MRSK.app(role: role).container_id_for_version(version)).present? + if capture_with_info(*MRSK.app(role: role).container_id_for_version(version), raise_on_non_zero_exit: false).present? tmp_version = "#{version}_#{SecureRandom.hex(8)}" info "Renaming container #{version} to #{tmp_version} as already deployed on #{host}" execute *MRSK.auditor(role: role).record("Renaming container #{version} to #{tmp_version}"), verbosity: :debug execute *MRSK.app(role: role).rename_container(version: version, new_version: tmp_version) end - old_version = capture_with_info(*MRSK.app(role: role).current_running_version).strip + old_version = capture_with_info(*MRSK.app(role: role).current_running_version, raise_on_non_zero_exit: false).strip execute *MRSK.app(role: role).run sleep MRSK.config.readiness_delay execute *MRSK.app(role: role).stop(version: old_version), raise_on_non_zero_exit: false if old_version.present? @@ -127,6 +127,31 @@ class Mrsk::Cli::App < Mrsk::Cli::Base on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_containers) } end + desc "stale_containers", "Detect app stale containers" + option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found" + def stale_containers + with_lock do + stop = options[:stop] + + cli = self + + on(MRSK.hosts) do |host| + roles = MRSK.roles_on(host) + + roles.each do |role| + cli.send(:stale_versions, host: host, role: role).each do |version| + if stop + puts_by_host host, "Stopping stale container for role #{role} with version #{version}" + execute *MRSK.app(role: role).stop(version: version), raise_on_non_zero_exit: false + else + puts_by_host host, "Detected stale container for role #{role} with version #{version} (use `mrsk app stale_containers --stop` to stop)" + end + end + end + end + end + end + desc "images", "Show app images on servers" def images on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_images) } @@ -243,6 +268,17 @@ class Mrsk::Cli::App < Mrsk::Cli::Base version.presence end + def stale_versions(host:, role:) + versions = nil + on(host) do + versions = \ + capture_with_info(*MRSK.app(role: role).list_versions, raise_on_non_zero_exit: false) + .split("\n") + .drop(1) + end + versions + end + def version_or_latest options[:version] || "latest" end diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index d38e5a02..0a3d2ce0 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -37,6 +37,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base say "Ensure app can pass healthcheck...", :magenta invoke "mrsk:cli:healthcheck:perform", [], invoke_options + say "Detect stale containers...", :magenta + invoke "mrsk:cli:app:stale_containers", [], invoke_options + hold_lock_on_error do invoke "mrsk:cli:app:boot", [], invoke_options end @@ -67,6 +70,9 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base say "Ensure app can pass healthcheck...", :magenta invoke "mrsk:cli:healthcheck:perform", [], invoke_options + say "Detect stale containers...", :magenta + invoke "mrsk:cli:app:stale_containers", [], invoke_options + hold_lock_on_error do invoke "mrsk:cli:app:boot", [], invoke_options end diff --git a/lib/mrsk/commands/app.rb b/lib/mrsk/commands/app.rb index c6ddb858..08e6795d 100644 --- a/lib/mrsk/commands/app.rb +++ b/lib/mrsk/commands/app.rb @@ -29,7 +29,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base def stop(version: nil) pipe \ - version ? container_id_for_version(version) : current_container_id, + version ? container_id_for_version(version) : current_running_container_id, xargs(config.stop_wait_time ? docker(:stop, "-t", config.stop_wait_time) : docker(:stop)) end @@ -40,7 +40,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base def logs(since: nil, lines: nil, grep: nil) pipe \ - current_container_id, + current_running_container_id, "xargs docker logs#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1", ("grep '#{grep}'" if grep) end @@ -48,7 +48,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base def follow_logs(host:, grep: nil) run_over_ssh \ pipe( - current_container_id, + current_running_container_id, "xargs docker logs --timestamps --tail 10 --follow 2>&1", (%(grep "#{grep}") if grep) ), @@ -82,8 +82,8 @@ class Mrsk::Commands::App < Mrsk::Commands::Base end - def current_container_id - docker :ps, "--quiet", *filter_args + def current_running_container_id + docker :ps, "--quiet", *filter_args(status: :running), "--latest" end def container_id_for_version(version) @@ -91,11 +91,14 @@ class Mrsk::Commands::App < Mrsk::Commands::Base end def current_running_version - # FIXME: Find more graceful way to extract the version from "app-version" than using sed and tail! + list_versions("--latest", status: :running) + end + + def list_versions(*docker_args, status: nil) pipe \ - docker(:ps, *filter_args, "--format", '"{{.Names}}"'), - %(sed 's/-/\\n/g'), - "tail -n 1" + docker(:ps, *filter_args(status: status), *docker_args, "--format", '"{{.Names}}"'), + %(grep -oE "\\-[^-]+$"), # Extract SHA from "service-role-dest-SHA" + %(cut -c 2-) end def list_containers @@ -138,14 +141,15 @@ class Mrsk::Commands::App < Mrsk::Commands::Base [ config.service, role, config.destination, version || config.version ].compact.join("-") end - def filter_args - argumentize "--filter", filters + def filter_args(status: nil) + argumentize "--filter", filters(status: status) end - def filters + def filters(status: nil) [ "label=service=#{config.service}" ].tap do |filters| filters << "label=destination=#{config.destination}" if config.destination filters << "label=role=#{role}" if role + filters << "status=#{status}" if status end end end diff --git a/lib/mrsk/sshkit_with_ext.rb b/lib/mrsk/sshkit_with_ext.rb index df63fabe..075c2643 100644 --- a/lib/mrsk/sshkit_with_ext.rb +++ b/lib/mrsk/sshkit_with_ext.rb @@ -2,8 +2,8 @@ require "sshkit" require "sshkit/dsl" class SSHKit::Backend::Abstract - def capture_with_info(*args) - capture(*args, verbosity: Logger::INFO) + def capture_with_info(*args, **kwargs) + capture(*args, **kwargs, verbosity: Logger::INFO) end def puts_by_host(host, output, type: "App") diff --git a/test/cli/app_test.rb b/test/cli/app_test.rb index da032dab..78b166d4 100644 --- a/test/cli/app_test.rb +++ b/test/cli/app_test.rb @@ -16,11 +16,11 @@ class CliAppTest < CliTestCase run_command("details") # Preheat MRSK const SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) - .with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet") + .with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", raise_on_non_zero_exit: false) .returns("12345678") # running version SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) - .with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1") + .with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--filter", "status=running", "--latest", "--format", "\"{{.Names}}\"", "|", "grep -oE \"\\-[^-]+$\"", "|", "cut -c 2-", raise_on_non_zero_exit: false) .returns("123") # old version run_command("boot").tap do |output| @@ -41,7 +41,28 @@ class CliAppTest < CliTestCase test "stop" do run_command("stop").tap do |output| - assert_match "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker stop", output + assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker stop", output + end + end + + test "stale_containers" do + SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) + .with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "grep -oE \"\\-[^-]+$\"", "|", "cut -c 2-", raise_on_non_zero_exit: false) + .returns("12345678\n87654321") + + run_command("stale_containers").tap do |output| + assert_match /Detected stale container for role web with version 87654321/, output + end + end + + test "stop stale_containers" do + SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) + .with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "grep -oE \"\\-[^-]+$\"", "|", "cut -c 2-", raise_on_non_zero_exit: false) + .returns("12345678\n87654321") + + run_command("stale_containers", "--stop").tap do |output| + assert_match /Stopping stale container for role web with version 87654321/, output + assert_match /#{Regexp.escape("docker container ls --all --filter name=^app-web-87654321$ --quiet | xargs docker stop")}/, output end end @@ -53,7 +74,7 @@ class CliAppTest < CliTestCase test "remove" do run_command("remove").tap do |output| - assert_match /#{Regexp.escape("docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker stop")}/, output + assert_match /#{Regexp.escape("docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker stop")}/, output assert_match /#{Regexp.escape("docker container prune --force --filter label=service=app")}/, output assert_match /#{Regexp.escape("docker image prune --all --force --filter label=service=app")}/, output end @@ -85,7 +106,7 @@ class CliAppTest < CliTestCase test "exec with reuse" do run_command("exec", "--reuse", "ruby -v").tap do |output| - assert_match "docker ps --filter label=service=app --format \"{{.Names}}\" | sed 's/-/\\n/g' | tail -n 1", output # Get current version + assert_match "docker ps --filter label=service=app --filter status=running --latest --format \"{{.Names}}\" | grep -oE \"\\-[^-]+$\" | cut -c 2-", output # Get current version assert_match "docker exec app-web-999 ruby -v", output end end @@ -104,28 +125,28 @@ class CliAppTest < CliTestCase test "logs" do SSHKit::Backend::Abstract.any_instance.stubs(:exec) - .with("ssh -t root@1.1.1.1 'docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --timestamps --tail 10 2>&1'") + .with("ssh -t root@1.1.1.1 'docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest| xargs docker logs --timestamps --tail 10 2>&1'") - assert_match "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --tail 100 2>&1", run_command("logs") + assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --tail 100 2>&1", run_command("logs") end test "logs with follow" do SSHKit::Backend::Abstract.any_instance.stubs(:exec) - .with("ssh -t root@1.1.1.1 'docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --timestamps --tail 10 --follow 2>&1'") + .with("ssh -t root@1.1.1.1 'docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1'") - assert_match "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --timestamps --tail 10 --follow 2>&1", run_command("logs", "--follow") + assert_match "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1", run_command("logs", "--follow") end test "version" do run_command("version").tap do |output| - assert_match "docker ps --filter label=service=app --format \"{{.Names}}\" | sed 's/-/\\n/g' | tail -n 1", output + assert_match "docker ps --filter label=service=app --filter status=running --latest --format \"{{.Names}}\" | grep -oE \"\\-[^-]+$\" | cut -c 2-", output end end test "version through main" do stdouted { Mrsk::Cli::Main.start(["app", "version", "-c", "test/fixtures/deploy_with_accessories.yml", "--hosts", "1.1.1.1"]) }.tap do |output| - assert_match "docker ps --filter label=service=app --format \"{{.Names}}\" | sed 's/-/\\n/g' | tail -n 1", output + assert_match "docker ps --filter label=service=app --filter status=running --latest --format \"{{.Names}}\" | grep -oE \"\\-[^-]+$\" | cut -c 2-", output end end diff --git a/test/cli/main_test.rb b/test/cli/main_test.rb index 2f73abfe..0af67f43 100644 --- a/test/cli/main_test.rb +++ b/test/cli/main_test.rb @@ -17,6 +17,7 @@ class CliMainTest < CliTestCase Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:stale_containers", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:prune:all", [], invoke_options) @@ -26,6 +27,7 @@ class CliMainTest < CliTestCase assert_match /Build and push app image/, output assert_match /Ensure Traefik is running/, output assert_match /Ensure app can pass healthcheck/, output + assert_match /Detect stale containers/, output assert_match /Prune old containers and images/, output end end @@ -38,6 +40,7 @@ class CliMainTest < CliTestCase Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:pull", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:stale_containers", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:prune:all", [], invoke_options) @@ -48,6 +51,7 @@ class CliMainTest < CliTestCase assert_match /Pull app image/, output assert_match /Ensure Traefik is running/, output assert_match /Ensure app can pass healthcheck/, output + assert_match /Detect stale containers/, output assert_match /Prune old containers and images/, output assert_match /Releasing the deploy lock/, output end @@ -85,6 +89,7 @@ class CliMainTest < CliTestCase Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:server:bootstrap", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:registry:login", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:stale_containers", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:traefik:boot", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options).raises(RuntimeError) @@ -115,6 +120,7 @@ class CliMainTest < CliTestCase Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:deliver", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:stale_containers", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) run_command("redeploy").tap do |output| @@ -128,6 +134,7 @@ class CliMainTest < CliTestCase Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:build:pull", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:healthcheck:perform", [], invoke_options) + Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:stale_containers", [], invoke_options) Mrsk::Cli::Main.any_instance.expects(:invoke).with("mrsk:cli:app:boot", [], invoke_options) run_command("redeploy", "--skip_push").tap do |output| @@ -148,8 +155,8 @@ class CliMainTest < CliTestCase test "rollback good version" do Mrsk::Cli::Main.any_instance.stubs(:container_available?).returns(true) - SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1").returns("version-to-rollback\n").at_least_once - SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=workers", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1").returns("version-to-rollback\n").at_least_once + SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--filter", "status=running", "--latest", "--format", "\"{{.Names}}\"", "|", "grep -oE \"\\-[^-]+$\"", "|", "cut -c 2-").returns("version-to-rollback\n").at_least_once + SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=workers", "--filter", "status=running", "--latest", "--format", "\"{{.Names}}\"", "|", "grep -oE \"\\-[^-]+$\"", "|", "cut -c 2-").returns("version-to-rollback\n").at_least_once run_command("rollback", "123", config_file: "deploy_with_accessories").tap do |output| assert_match "Start version 123", output @@ -161,7 +168,7 @@ class CliMainTest < CliTestCase test "rollback without old version" do Mrsk::Cli::Main.any_instance.stubs(:container_available?).returns(true) - SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1").returns("").at_least_once + SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--filter", "status=running", "--latest", "--format", "\"{{.Names}}\"", "|", "grep -oE \"\\-[^-]+$\"", "|", "cut -c 2-").returns("").at_least_once run_command("rollback", "123").tap do |output| assert_match "Start version 123", output diff --git a/test/commands/app_test.rb b/test/commands/app_test.rb index 44177167..87afe3f1 100644 --- a/test/commands/app_test.rb +++ b/test/commands/app_test.rb @@ -63,14 +63,14 @@ class CommandsAppTest < ActiveSupport::TestCase test "stop" do assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker stop", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker stop", new_command.stop.join(" ") end test "stop with custom stop wait time" do @config[:stop_wait_time] = 30 assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker stop -t 30", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker stop -t 30", new_command.stop.join(" ") end @@ -96,37 +96,37 @@ class CommandsAppTest < ActiveSupport::TestCase test "logs" do assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs 2>&1", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs 2>&1", new_command.logs.join(" ") assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --since 5m 2>&1", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --since 5m 2>&1", new_command.logs(since: "5m").join(" ") assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --tail 100 2>&1", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --tail 100 2>&1", new_command.logs(lines: "100").join(" ") assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --since 5m --tail 100 2>&1", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --since 5m --tail 100 2>&1", new_command.logs(since: "5m", lines: "100").join(" ") assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs 2>&1 | grep 'my-id'", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs 2>&1 | grep 'my-id'", new_command.logs(grep: "my-id").join(" ") assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --since 5m 2>&1 | grep 'my-id'", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --since 5m 2>&1 | grep 'my-id'", new_command.logs(since: "5m", grep: "my-id").join(" ") end test "follow logs" do assert_match \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --timestamps --tail 10 --follow 2>&1", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1", new_command.follow_logs(host: "app-1") assert_match \ - "docker ps --quiet --filter label=service=app --filter label=role=web | xargs docker logs --timestamps --tail 10 --follow 2>&1 | grep \"Completed\"", + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest | xargs docker logs --timestamps --tail 10 --follow 2>&1 | grep \"Completed\"", new_command.follow_logs(host: "app-1", grep: "Completed") end @@ -178,17 +178,17 @@ class CommandsAppTest < ActiveSupport::TestCase end - test "current_container_id" do + test "current_running_container_id" do assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=role=web", - new_command.current_container_id.join(" ") + "docker ps --quiet --filter label=service=app --filter label=role=web --filter status=running --latest", + new_command.current_running_container_id.join(" ") end - test "current_container_id with destination" do + test "current_running_container_id with destination" do @destination = "staging" assert_equal \ - "docker ps --quiet --filter label=service=app --filter label=destination=staging --filter label=role=web", - new_command.current_container_id.join(" ") + "docker ps --quiet --filter label=service=app --filter label=destination=staging --filter label=role=web --filter status=running --latest", + new_command.current_running_container_id.join(" ") end test "container_id_for" do @@ -199,10 +199,20 @@ class CommandsAppTest < ActiveSupport::TestCase test "current_running_version" do assert_equal \ - "docker ps --filter label=service=app --filter label=role=web --format \"{{.Names}}\" | sed 's/-/\\n/g' | tail -n 1", + "docker ps --filter label=service=app --filter label=role=web --filter status=running --latest --format \"{{.Names}}\" | grep -oE \"\\-[^-]+$\" | cut -c 2-", new_command.current_running_version.join(" ") end + test "list_versions" do + assert_equal \ + "docker ps --filter label=service=app --filter label=role=web --format \"{{.Names}}\" | grep -oE \"\\-[^-]+$\" | cut -c 2-", + new_command.list_versions.join(" ") + + assert_equal \ + "docker ps --filter label=service=app --filter label=role=web --filter status=running --latest --format \"{{.Names}}\" | grep -oE \"\\-[^-]+$\" | cut -c 2-", + new_command.list_versions("--latest", status: :running).join(" ") + end + test "list_containers" do assert_equal \ "docker container ls --all --filter label=service=app --filter label=role=web",