Skip to content

Commit

Permalink
Make rollbacks role-aware
Browse files Browse the repository at this point in the history
Rollbacks stopped working after basecamp#99.

We'll confirm that a container is available for the first role on the
primary host before attempting to rollback.
  • Loading branch information
djmb authored and ncreuschling committed Apr 12, 2023
1 parent 6047a59 commit 3e2ca33
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 18 deletions.
30 changes: 20 additions & 10 deletions lib/mrsk/cli/main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,26 @@ def rollback(version)
with_lock do
MRSK.config.version = version

if container_name_available?(MRSK.config.service_with_version)
if container_available?(version)
say "Start version #{version}, then wait #{MRSK.config.readiness_delay}s for app to boot before stopping the old version...", :magenta

cli = self
old_version = nil

on(MRSK.hosts) do |host|
old_version = capture_with_info(*MRSK.app.current_running_version).strip.presence
roles = MRSK.roles_on(host)

execute *MRSK.app.start
roles.each do |role|
app = MRSK.app(role: role)
old_version = capture_with_info(*app.current_running_version).strip.presence

if old_version
sleep MRSK.config.readiness_delay
execute *app.start

execute *MRSK.app.stop(version: old_version), raise_on_non_zero_exit: false
if old_version
sleep MRSK.config.readiness_delay

execute *app.stop(version: old_version), raise_on_non_zero_exit: false
end
end
end

Expand Down Expand Up @@ -214,10 +219,15 @@ def version
subcommand "lock", Mrsk::Cli::Lock

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)
def container_available?(version, host: MRSK.primary_host)
available = nil

on(host) do
first_role = MRSK.roles_on(host).first
available = capture_with_info(*MRSK.app(role: first_role).container_id_for_version(version)).present?
end

available
end

def deploy_options
Expand Down
18 changes: 10 additions & 8 deletions test/cli/main_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,32 +120,34 @@ class CliMainTest < CliTestCase
end

test "rollback bad version" do
# Mrsk::Cli::Main.any_instance.stubs(:container_available?).returns(false)
run_command("details") # Preheat MRSK const

run_command("rollback", "nonsense").tap do |output|
assert_match /docker container ls --all --filter label=service=app --format '{{ .Names }}'/, output
assert_match /docker container ls --all --filter name=\^app-web-nonsense\$ --quiet/, output
assert_match /The app version 'nonsense' is not available as a container/, output
end
end

test "rollback good version" do
Mrsk::Cli::Main.any_instance.stubs(:container_name_available?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1").returns("version-to-rollback\n").at_least_once
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

run_command("rollback", "123", config_file: "deploy_with_accessories").tap do |output|
assert_match "Start version 123", output
assert_match "docker start app-123", output
assert_match "docker container ls --all --filter name=^app-version-to-rollback$ --quiet | xargs docker stop", output, "Should stop the container that was previously running"
assert_match "docker start app-web-123", output
assert_match "docker container ls --all --filter name=^app-web-version-to-rollback$ --quiet | xargs docker stop", output, "Should stop the container that was previously running"
end
end

test "rollback without old version" do
Mrsk::Cli::Main.any_instance.stubs(:container_name_available?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info).with(:docker, :ps, "--filter", "label=service=app", "--format", "\"{{.Names}}\"", "|", "sed 's/-/\\n/g'", "|", "tail -n 1").returns("").at_least_once
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

run_command("rollback", "123").tap do |output|
assert_match "Start version 123", output
assert_match "docker start app-123", output
assert_match "docker start app-web-123", output
assert_no_match "docker stop", output
end
end
Expand Down

0 comments on commit 3e2ca33

Please sign in to comment.