Merge pull request #196 from handy-la/main

Configurable max_attempts for healthcheck
This commit is contained in:
David Heinemeier Hansson
2023-04-11 14:17:17 +02:00
committed by GitHub
5 changed files with 12 additions and 9 deletions

View File

@@ -646,18 +646,21 @@ That'll post a line like follows to a preconfigured chatbot in Basecamp:
[My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de [My App] [dhh] Rolled back to version d264c4e92470ad1bd18590f04466787262f605de
``` ```
### Using custom healthcheck path or port ### Custom healthcheck
MRSK defaults to checking the health of your application again `/up` on port 3000. You can tailor both with the `healthcheck` setting: MRSK defaults to checking the health of your application again `/up` on port 3000 up to 7 times. You can tailor the behaviour with the `healthcheck` setting:
```yaml ```yaml
healthcheck: healthcheck:
path: /healthz path: /healthz
port: 4000 port: 4000
max_attempts: 7
``` ```
This will ensure your application is configured with a traefik label for the healthcheck against `/healthz` and that the pre-deploy healthcheck that MRSK performs is done against the same path on port 4000. This will ensure your application is configured with a traefik label for the healthcheck against `/healthz` and that the pre-deploy healthcheck that MRSK performs is done against the same path on port 4000.
The healthcheck also allows for an optional `max_attempts` setting, which will attempt the healthcheck up to the specified number of times before failing the deploy. This is useful for applications that take a while to start up. The default is 7.
## Commands ## Commands
### Running commands on servers ### Running commands on servers

View File

@@ -1,5 +1,4 @@
class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
MAX_ATTEMPTS = 7
class HealthcheckError < StandardError; end class HealthcheckError < StandardError; end
@@ -13,6 +12,7 @@ class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
target = "Health check against #{MRSK.config.healthcheck["path"]}" target = "Health check against #{MRSK.config.healthcheck["path"]}"
attempt = 1 attempt = 1
max_attempts = MRSK.config.healthcheck["max_attempts"]
begin begin
status = capture_with_info(*MRSK.healthcheck.curl) status = capture_with_info(*MRSK.healthcheck.curl)
@@ -23,8 +23,8 @@ class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
raise HealthcheckError, "#{target} failed with status #{status}" raise HealthcheckError, "#{target} failed with status #{status}"
end end
rescue SSHKit::Command::Failed rescue SSHKit::Command::Failed
if attempt <= MAX_ATTEMPTS if attempt <= max_attempts
info "#{target} failed to respond, retrying in #{attempt}s..." info "#{target} failed to respond, retrying in #{attempt}s (attempt #{attempt}/#{max_attempts})..."
sleep attempt sleep attempt
attempt += 1 attempt += 1

View File

@@ -158,7 +158,7 @@ class Mrsk::Configuration
end end
def healthcheck def healthcheck
{ "path" => "/up", "port" => 3000 }.merge(raw_config.healthcheck || {}) { "path" => "/up", "port" => 3000, "max_attempts" => 7 }.merge(raw_config.healthcheck || {})
end end
def readiness_delay def readiness_delay

View File

@@ -23,8 +23,8 @@ class CliHealthcheckTest < CliTestCase
.returns("200") .returns("200")
run_command("perform").tap do |output| run_command("perform").tap do |output|
assert_match "Health check against /up failed to respond, retrying in 1s...", output assert_match "Health check against /up failed to respond, retrying in 1s (attempt 1/7)...", output
assert_match "Health check against /up failed to respond, retrying in 2s...", output assert_match "Health check against /up failed to respond, retrying in 2s (attempt 2/7)...", output
assert_match "Health check against /up succeeded with 200 OK!", output assert_match "Health check against /up succeeded with 200 OK!", output
end end
end end

View File

@@ -249,6 +249,6 @@ class ConfigurationTest < ActiveSupport::TestCase
end end
test "to_h" do test "to_h" do
assert_equal({ :roles=>["web"], :hosts=>["1.1.1.1", "1.1.1.2"], :primary_host=>"1.1.1.1", :version=>"missing", :repository=>"dhh/app", :absolute_image=>"dhh/app:missing", :service_with_version=>"app-missing", :env_args=>["-e", "REDIS_URL=\"redis://x/y\""], :ssh_options=>{:user=>"root", :auth_methods=>["publickey"]}, :volume_args=>["--volume", "/local/path:/container/path"], :logging=>["--log-opt", "max-size=\"10m\""], :healthcheck=>{"path"=>"/up", "port"=>3000 }}, @config.to_h) assert_equal({ :roles=>["web"], :hosts=>["1.1.1.1", "1.1.1.2"], :primary_host=>"1.1.1.1", :version=>"missing", :repository=>"dhh/app", :absolute_image=>"dhh/app:missing", :service_with_version=>"app-missing", :env_args=>["-e", "REDIS_URL=\"redis://x/y\""], :ssh_options=>{:user=>"root", :auth_methods=>["publickey"]}, :volume_args=>["--volume", "/local/path:/container/path"], :logging=>["--log-opt", "max-size=\"10m\""], :healthcheck=>{"path"=>"/up", "port"=>3000, "max_attempts" => 7 }}, @config.to_h)
end end
end end