From 2e06bf59a48a7cfab5da98065e643edf0e1c97f0 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 18 Feb 2023 14:33:47 +0100 Subject: [PATCH] Protect against rolling back to a bad version --- lib/mrsk/cli/main.rb | 24 +++++++++++++++++------- lib/mrsk/commands/app.rb | 10 ++++++++++ test/cli/main_test.rb | 14 ++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/lib/mrsk/cli/main.rb b/lib/mrsk/cli/main.rb index ad8d4959..d69e64fc 100644 --- a/lib/mrsk/cli/main.rb +++ b/lib/mrsk/cli/main.rb @@ -48,15 +48,18 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base def rollback(version) MRSK.version = version - cli = self + if container_name_available?(MRSK.config.service_with_version) + say "Stop current version, then start version #{version}...", :magenta - cli.say "Stop current version, then start version #{version}...", :magenta - on(MRSK.hosts) do - execute *MRSK.app.stop, raise_on_non_zero_exit: false - execute *MRSK.app.start + on(MRSK.hosts) do |host| + execute *MRSK.app.stop, raise_on_non_zero_exit: false + execute *MRSK.app.start + end + + audit_broadcast "Rolled back app to version #{version}" + else + say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red end - - audit_broadcast "Rolled back app to version #{version}" end desc "details", "Display details about Traefik and app containers" @@ -155,4 +158,11 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base desc "traefik", "Manage the Traefik load balancer" subcommand "traefik", Mrsk::Cli::Traefik + + private + def container_name_available?(container_name, host: MRSK.primary_host) + container_names = nil + on(host) { container_names = capture_with_info(*MRSK.app.list_container_names).split("\n") } + Array(container_names).include?(container_name) + end end diff --git a/lib/mrsk/commands/app.rb b/lib/mrsk/commands/app.rb index e17e7230..711be42e 100644 --- a/lib/mrsk/commands/app.rb +++ b/lib/mrsk/commands/app.rb @@ -93,11 +93,21 @@ class Mrsk::Commands::App < Mrsk::Commands::Base "head -n 1" end + def all_versions_from_available_containers + pipe \ + docker(:image, :ls, "--format", '"{{.Tag}}"', config.repository), + "head -n 1" + end + def list_containers docker :container, :ls, "-a", *service_filter end + def list_container_names + [ *list_containers, "--format", "'{{ .Names }}'" ] + end + def remove_container(version:) pipe \ container_id_for(container_name: service_with_version(version)), diff --git a/test/cli/main_test.rb b/test/cli/main_test.rb index 9abac435..a73a093c 100644 --- a/test/cli/main_test.rb +++ b/test/cli/main_test.rb @@ -5,4 +5,18 @@ class CliMainTest < CliTestCase version = stdouted { Mrsk::Cli::Main.new.version } assert_equal Mrsk::VERSION, version end + + test "rollback bad version" do + run_command("details") # Preheat MRSK const + + run_command("rollback", "nonsense").tap do |output| + assert_match /docker container ls -a --filter label=service=app --format '{{ .Names }}'/, output + assert_match /The app version 'nonsense' is not available as a container/, output + end + end + + private + def run_command(*command) + stdouted { Mrsk::Cli::Main.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } + end end