Add a pre-connect hook
This can be used for hooks that should run before connecting to remote hosts. An example use case is pre-warming DNS.
This commit is contained in:
14
README.md
14
README.md
@@ -880,17 +880,21 @@ fine-grained audit reporting, e.g. for triggering deployment reports or
|
||||
firing a JSON webhook. These variables include:
|
||||
- `MRSK_RECORDED_AT` - UTC timestamp in ISO 8601 format, e.g. `2023-04-14T17:07:31Z`
|
||||
- `MRSK_PERFORMER` - the local user performing the command (from `whoami`)
|
||||
- `MRSK_MESSAGE` - the full audit message, e.g. "Deployed app@150b24f"
|
||||
- `MRSK_SERVICE_VERSION` - an abbreviated version (for use in messages)
|
||||
- `MRSK_SERVICE_VERSION` - an abbreviated service and version for use in messages, e.g. app@150b24f
|
||||
- `MRSK_VERSION` - an full version being deployed
|
||||
- `MRSK_DESTINATION` - optional: destination, e.g. "staging"
|
||||
- `MRSK_HOSTS` - a comma separated list of the hosts targeted by the command
|
||||
- `MRSK_ROLE` - optional: role targeted, e.g. "web"
|
||||
|
||||
There are two hooks:
|
||||
There are three hooks:
|
||||
|
||||
1. pre-build
|
||||
1. pre-connect
|
||||
Called before taking the deploy lock. For checks that need to run before connecting to remote hosts - e.g. DNS warming.
|
||||
|
||||
2. pre-build
|
||||
Used for pre-build checks - e.g. there are no uncommitted changes or that CI has passed.
|
||||
|
||||
2. post-deploy - run after a deploy, redeploy or rollback
|
||||
3. post-deploy - run after a deploy, redeploy or rollback
|
||||
|
||||
This hook is also passed a `MRSK_RUNTIME` env variable.
|
||||
|
||||
|
||||
@@ -76,6 +76,8 @@ module Mrsk::Cli
|
||||
if MRSK.holding_lock?
|
||||
yield
|
||||
else
|
||||
run_hook "pre-connect"
|
||||
|
||||
acquire_lock
|
||||
|
||||
begin
|
||||
@@ -135,7 +137,7 @@ module Mrsk::Cli
|
||||
if !options[:skip_hooks] && MRSK.hook.hook_exists?(hook)
|
||||
say "Running the #{hook} hook...", :magenta
|
||||
run_locally do
|
||||
MRSK.with_verbosity(:debug) { execute *MRSK.hook.run(hook, **details) }
|
||||
MRSK.with_verbosity(:debug) { execute *MRSK.hook.run(hook, **details, hosts: MRSK.hosts.join(",")) }
|
||||
rescue SSHKit::Command::Failed
|
||||
raise HookError.new("Hook `#{hook}` failed")
|
||||
end
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# MRSK_RECORDED_AT
|
||||
# MRSK_PERFORMER
|
||||
# MRSK_VERSION
|
||||
# MRSK_HOSTS
|
||||
# MRSK_ROLE (if set)
|
||||
# MRSK_DESTINATION (if set)
|
||||
# MRSK_RUNTIME
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# MRSK_RECORDED_AT
|
||||
# MRSK_PERFORMER
|
||||
# MRSK_VERSION
|
||||
# MRSK_HOSTS
|
||||
# MRSK_ROLE (if set)
|
||||
# MRSK_DESTINATION (if set)
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
|
||||
47
lib/mrsk/cli/templates/sample_hooks/pre-connect.sample
Executable file
47
lib/mrsk/cli/templates/sample_hooks/pre-connect.sample
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# A sample pre-connect check
|
||||
#
|
||||
# Warms DNS before connecting to hosts in parallel
|
||||
#
|
||||
# These environment variables are available:
|
||||
# MRSK_RECORDED_AT
|
||||
# MRSK_PERFORMER
|
||||
# MRSK_VERSION
|
||||
# MRSK_HOSTS
|
||||
# MRSK_ROLE (if set)
|
||||
# MRSK_DESTINATION (if set)
|
||||
# MRSK_RUNTIME
|
||||
|
||||
hosts = ENV["MRSK_HOSTS"].split(",")
|
||||
results = nil
|
||||
max = 3
|
||||
|
||||
elapsed = Benchmark.realtime do
|
||||
results = hosts.map do |host|
|
||||
Thread.new do
|
||||
tries = 1
|
||||
|
||||
begin
|
||||
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
|
||||
rescue SocketError
|
||||
if tries < max
|
||||
puts "Retrying DNS warmup: #{host}"
|
||||
tries += 1
|
||||
sleep rand
|
||||
retry
|
||||
else
|
||||
puts "DNS warmup failed: #{host}"
|
||||
host
|
||||
end
|
||||
end
|
||||
|
||||
tries
|
||||
end
|
||||
end.map(&:value)
|
||||
end
|
||||
|
||||
retries = results.sum - hosts.size
|
||||
nopes = results.count { |r| r == max }
|
||||
|
||||
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
|
||||
@@ -23,6 +23,7 @@ class CliMainTest < CliTestCase
|
||||
Mrsk::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
|
||||
|
||||
run_command("deploy").tap do |output|
|
||||
assert_match /Running the pre-connect hook.../, output
|
||||
assert_match /Log into image registry/, output
|
||||
assert_match /Build and push app image/, output
|
||||
assert_match /Ensure Traefik is running/, output
|
||||
|
||||
8
test/integration/docker/deployer/app/.mrsk/hooks/pre-connect
Executable file
8
test/integration/docker/deployer/app/.mrsk/hooks/pre-connect
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "About to lock..."
|
||||
if [ "$MRSK_HOSTS" != "vm1,vm2" ]; then
|
||||
echo "Expected hosts to be 'vm1,vm2', got $MRSK_HOSTS"
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p /tmp/${TEST_ID} && touch /tmp/${TEST_ID}/pre-connect
|
||||
@@ -8,16 +8,16 @@ class MainTest < IntegrationTest
|
||||
|
||||
mrsk :deploy
|
||||
assert_app_is_up version: first_version
|
||||
assert_hooks_ran "pre-build", "post-deploy"
|
||||
assert_hooks_ran "pre-connect", "pre-build", "post-deploy"
|
||||
|
||||
second_version = update_app_rev
|
||||
|
||||
mrsk :redeploy
|
||||
assert_app_is_up version: second_version
|
||||
assert_hooks_ran "pre-build", "post-deploy"
|
||||
assert_hooks_ran "pre-connect", "pre-build", "post-deploy"
|
||||
|
||||
mrsk :rollback, first_version
|
||||
assert_hooks_ran "post-deploy"
|
||||
assert_hooks_ran "pre-connect", "post-deploy"
|
||||
assert_app_is_up version: first_version
|
||||
|
||||
details = mrsk :details, capture: true
|
||||
|
||||
Reference in New Issue
Block a user