Skip to content

Commit

Permalink
Merge pull request #202 from basecamp/deploy-lock-acquisition
Browse files Browse the repository at this point in the history
Improved deploy lock acquisition
  • Loading branch information
dhh authored Apr 10, 2023
2 parents fb1718c + c4df440 commit 18031bc
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 8 deletions.
1 change: 1 addition & 0 deletions lib/mrsk/cli.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module Mrsk::Cli
class LockError < StandardError; end
end

# SSHKit uses instance eval, so we need a global const for ergonomics
Expand Down
12 changes: 7 additions & 5 deletions lib/mrsk/cli/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ module Mrsk::Cli
class Base < Thor
include SSHKit::DSL

class LockError < StandardError; end

def self.exit_on_failure?() true end

class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
Expand Down Expand Up @@ -82,8 +80,11 @@ def with_lock
acquire_lock

yield
ensure

release_lock
rescue
error " \e[31mDeploy lock was not released\e[0m" if MRSK.lock_count > 0
raise
end

def acquire_lock
Expand All @@ -95,9 +96,10 @@ def acquire_lock
rescue SSHKit::Runner::ExecuteError => e
if e.message =~ /cannot create directory/
invoke "mrsk:cli:lock:status", []
raise LockError, "Deploy lock found"
else
raise e
end

raise LockError, "Deploy lock found"
end

def release_lock
Expand Down
4 changes: 2 additions & 2 deletions lib/mrsk/cli/lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ def acquire
message = options[:message]
handle_missing_lock do
on(MRSK.primary_host) { execute *MRSK.lock.acquire(message, MRSK.config.version) }
say "Set the deploy lock"
say "Acquired the deploy lock"
end
end

desc "release", "Release the deploy lock"
def release
handle_missing_lock do
on(MRSK.primary_host) { execute *MRSK.lock.release }
say "Removed the deploy lock"
say "Released the deploy lock"
end
end

Expand Down
6 changes: 5 additions & 1 deletion test/cli/cli_test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ class CliTestCase < ActiveSupport::TestCase
def stdouted
capture(:stdout) { yield }.strip
end
end

def stderred
capture(:stderr) { yield }.strip
end
end
40 changes: 40 additions & 0 deletions test/cli/main_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,46 @@ class CliMainTest < CliTestCase
end
end

test "deploy when locked" do
Thread.report_on_exception = false

SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, :mrsk_lock] }
.raises(RuntimeError, "mkdir: cannot create directory ‘mrsk_lock’: File exists")

Mrsk::Cli::Base.any_instance.expects(:invoke).with("mrsk:cli:lock:status", [])

assert_raises(Mrsk::Cli::LockError) do
run_command("deploy")
end
end

test "deploy error when locking" do
Thread.report_on_exception = false

SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, :mrsk_lock] }
.raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known")

assert_raises(SSHKit::Runner::ExecuteError) do
run_command("deploy")
end
end

test "deploy errors leave lock in place" do
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }

Mrsk::Cli::Main.any_instance.expects(:invoke)
.with("mrsk:cli:server:bootstrap", [], invoke_options)
.raises(RuntimeError)

assert_equal 0, MRSK.lock_count
assert_raises(RuntimeError) do
stderred { run_command("deploy") }
end
assert_equal 1, MRSK.lock_count
end

test "redeploy" do
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "skip_broadcast" => false, "version" => "999" }

Expand Down

0 comments on commit 18031bc

Please sign in to comment.