Rename to Kamal

This commit is contained in:
David Heinemeier Hansson
2023-08-22 08:24:31 -07:00
parent e2c3709d74
commit c4a203e648
104 changed files with 870 additions and 870 deletions

View File

@@ -1,10 +1,10 @@
module Mrsk
module Kamal
end
require "active_support"
require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
loader.ignore("#{__dir__}/mrsk/sshkit_with_ext.rb")
loader.ignore("#{__dir__}/kamal/sshkit_with_ext.rb")
loader.setup
loader.eager_load # We need all commands loaded.

View File

@@ -1,7 +1,7 @@
module Mrsk::Cli
module Kamal::Cli
class LockError < StandardError; end
class HookError < StandardError; end
end
# SSHKit uses instance eval, so we need a global const for ergonomics
MRSK = Mrsk::Commander.new
Kamal = Kamal::Commander.new

View File

@@ -1,17 +1,17 @@
class Mrsk::Cli::Accessory < Mrsk::Cli::Base
class Kamal::Cli::Accessory < Kamal::Cli::Base
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
def boot(name, login: true)
mutating do
if name == "all"
MRSK.accessory_names.each { |accessory_name| boot(accessory_name) }
KAMAL.accessory_names.each { |accessory_name| boot(accessory_name) }
else
with_accessory(name) do |accessory|
directories(name)
upload(name)
on(accessory.hosts) do
execute *MRSK.registry.login if login
execute *MRSK.auditor.record("Booted #{name} accessory"), verbosity: :debug
execute *KAMAL.registry.login if login
execute *KAMAL.auditor.record("Booted #{name} accessory"), verbosity: :debug
execute *accessory.run
end
end
@@ -54,7 +54,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
mutating do
with_accessory(name) do |accessory|
on(accessory.hosts) do
execute *MRSK.registry.login
execute *KAMAL.registry.login
end
stop(name)
@@ -69,7 +69,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
mutating do
with_accessory(name) do |accessory|
on(accessory.hosts) do
execute *MRSK.auditor.record("Started #{name} accessory"), verbosity: :debug
execute *KAMAL.auditor.record("Started #{name} accessory"), verbosity: :debug
execute *accessory.start
end
end
@@ -81,7 +81,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
mutating do
with_accessory(name) do |accessory|
on(accessory.hosts) do
execute *MRSK.auditor.record("Stopped #{name} accessory"), verbosity: :debug
execute *KAMAL.auditor.record("Stopped #{name} accessory"), verbosity: :debug
execute *accessory.stop, raise_on_non_zero_exit: false
end
end
@@ -101,7 +101,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
desc "details [NAME]", "Show details about accessory on host (use NAME=all to show all accessories)"
def details(name)
if name == "all"
MRSK.accessory_names.each { |accessory_name| details(accessory_name) }
KAMAL.accessory_names.each { |accessory_name| details(accessory_name) }
else
with_accessory(name) do |accessory|
on(accessory.hosts) { puts capture_with_info(*accessory.info) }
@@ -126,14 +126,14 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
when options[:reuse]
say "Launching command from existing container...", :magenta
on(accessory.hosts) do
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
capture_with_info(*accessory.execute_in_existing_container(cmd))
end
else
say "Launching command from new container...", :magenta
on(accessory.hosts) do
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
capture_with_info(*accessory.execute_in_new_container(cmd))
end
end
@@ -171,7 +171,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
def remove(name)
mutating do
if name == "all"
MRSK.accessory_names.each { |accessory_name| remove(accessory_name) }
KAMAL.accessory_names.each { |accessory_name| remove(accessory_name) }
else
if options[:confirmed] || ask("This will remove all containers, images and data directories for #{name}. Are you sure?", limited_to: %w( y N ), default: "N") == "y"
with_accessory(name) do
@@ -190,7 +190,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
mutating do
with_accessory(name) do |accessory|
on(accessory.hosts) do
execute *MRSK.auditor.record("Remove #{name} accessory container"), verbosity: :debug
execute *KAMAL.auditor.record("Remove #{name} accessory container"), verbosity: :debug
execute *accessory.remove_container
end
end
@@ -202,7 +202,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
mutating do
with_accessory(name) do |accessory|
on(accessory.hosts) do
execute *MRSK.auditor.record("Removed #{name} accessory image"), verbosity: :debug
execute *KAMAL.auditor.record("Removed #{name} accessory image"), verbosity: :debug
execute *accessory.remove_image
end
end
@@ -222,7 +222,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
private
def with_accessory(name)
if accessory = MRSK.accessory(name)
if accessory = KAMAL.accessory(name)
yield accessory
else
error_on_missing_accessory(name)
@@ -230,7 +230,7 @@ class Mrsk::Cli::Accessory < Mrsk::Cli::Base
end
def error_on_missing_accessory(name)
options = MRSK.accessory_names.presence
options = KAMAL.accessory_names.presence
error \
"No accessory by the name of '#{name}'" +

View File

@@ -1,23 +1,23 @@
class Mrsk::Cli::App < Mrsk::Cli::Base
class Kamal::Cli::App < Kamal::Cli::Base
desc "boot", "Boot app on servers (or reboot app if already running)"
def boot
mutating do
hold_lock_on_error do
say "Get most recent version available as an image...", :magenta unless options[:version]
using_version(version_or_latest) do |version|
say "Start container with version #{version} using a #{MRSK.config.readiness_delay}s readiness delay (or reboot if already running)...", :magenta
say "Start container with version #{version} using a #{KAMAL.config.readiness_delay}s readiness delay (or reboot if already running)...", :magenta
on(MRSK.hosts) do
execute *MRSK.auditor.record("Tagging #{MRSK.config.absolute_image} as the latest image"), verbosity: :debug
execute *MRSK.app.tag_current_as_latest
on(KAMAL.hosts) do
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
execute *KAMAL.app.tag_current_as_latest
end
on(MRSK.hosts, **MRSK.boot_strategy) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts, **KAMAL.boot_strategy) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
app = MRSK.app(role: role)
auditor = MRSK.auditor(role: role)
app = KAMAL.app(role: role)
auditor = KAMAL.auditor(role: role)
if capture_with_info(*app.container_id_for_version(version, only_running: true), raise_on_non_zero_exit: false).present?
tmp_version = "#{version}_replaced_#{SecureRandom.hex(8)}"
@@ -31,7 +31,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
execute *app.start_or_run(hostname: "#{host}-#{SecureRandom.hex(6)}")
Mrsk::Utils::HealthcheckPoller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
Kamal::Utils::HealthcheckPoller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
execute *app.stop(version: old_version), raise_on_non_zero_exit: false if old_version.present?
end
@@ -44,12 +44,12 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "start", "Start existing app container on servers"
def start
mutating do
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
execute *MRSK.auditor.record("Started app version #{MRSK.config.version}"), verbosity: :debug
execute *MRSK.app(role: role).start, raise_on_non_zero_exit: false
execute *KAMAL.auditor.record("Started app version #{KAMAL.config.version}"), verbosity: :debug
execute *KAMAL.app(role: role).start, raise_on_non_zero_exit: false
end
end
end
@@ -58,12 +58,12 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "stop", "Stop app container on servers"
def stop
mutating do
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
execute *MRSK.auditor.record("Stopped app", role: role), verbosity: :debug
execute *MRSK.app(role: role).stop, raise_on_non_zero_exit: false
execute *KAMAL.auditor.record("Stopped app", role: role), verbosity: :debug
execute *KAMAL.app(role: role).stop, raise_on_non_zero_exit: false
end
end
end
@@ -72,11 +72,11 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
# FIXME: Drop in favor of just containers?
desc "details", "Show details about app containers"
def details
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
puts_by_host host, capture_with_info(*MRSK.app(role: role).info)
puts_by_host host, capture_with_info(*KAMAL.app(role: role).info)
end
end
end
@@ -89,15 +89,15 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
when options[:interactive] && options[:reuse]
say "Get current version of running container...", :magenta unless options[:version]
using_version(options[:version] || current_running_version) do |version|
say "Launching interactive command with version #{version} via SSH from existing container on #{MRSK.primary_host}...", :magenta
run_locally { exec MRSK.app(role: "web").execute_in_existing_container_over_ssh(cmd, host: MRSK.primary_host) }
say "Launching interactive command with version #{version} via SSH from existing container on #{KAMAL.primary_host}...", :magenta
run_locally { exec KAMAL.app(role: "web").execute_in_existing_container_over_ssh(cmd, host: KAMAL.primary_host) }
end
when options[:interactive]
say "Get most recent version available as an image...", :magenta unless options[:version]
using_version(version_or_latest) do |version|
say "Launching interactive command with version #{version} via SSH from new container on #{MRSK.primary_host}...", :magenta
run_locally { exec MRSK.app(role: MRSK.primary_host.roles.first).execute_in_new_container_over_ssh(cmd, host: MRSK.primary_host) }
say "Launching interactive command with version #{version} via SSH from new container on #{KAMAL.primary_host}...", :magenta
run_locally { exec KAMAL.app(role: KAMAL.primary_host.roles.first).execute_in_new_container_over_ssh(cmd, host: KAMAL.primary_host) }
end
when options[:reuse]
@@ -105,12 +105,12 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
using_version(options[:version] || current_running_version) do |version|
say "Launching command with version #{version} from existing container...", :magenta
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
puts_by_host host, capture_with_info(*MRSK.app(role: role).execute_in_existing_container(cmd))
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_existing_container(cmd))
end
end
end
@@ -119,9 +119,9 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
say "Get most recent version available as an image...", :magenta unless options[:version]
using_version(version_or_latest) do |version|
say "Launching command with version #{version} from new container...", :magenta
on(MRSK.hosts) do |host|
execute *MRSK.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
puts_by_host host, capture_with_info(*MRSK.app.execute_in_new_container(cmd))
on(KAMAL.hosts) do |host|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
puts_by_host host, capture_with_info(*KAMAL.app.execute_in_new_container(cmd))
end
end
end
@@ -129,7 +129,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "containers", "Show app containers on servers"
def containers
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_containers) }
on(KAMAL.hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.app.list_containers) }
end
desc "stale_containers", "Detect app stale containers"
@@ -140,16 +140,16 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
cli = self
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
cli.send(:stale_versions, host: host, role: role).each do |version|
if stop
puts_by_host host, "Stopping stale container for role #{role} with version #{version}"
execute *MRSK.app(role: role).stop(version: version), raise_on_non_zero_exit: false
execute *KAMAL.app(role: role).stop(version: version), raise_on_non_zero_exit: false
else
puts_by_host host, "Detected stale container for role #{role} with version #{version} (use `mrsk app stale_containers --stop` to stop)"
puts_by_host host, "Detected stale container for role #{role} with version #{version} (use `kamal app stale_containers --stop` to stop)"
end
end
end
@@ -159,7 +159,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "images", "Show app images on servers"
def images
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.list_images) }
on(KAMAL.hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.app.list_images) }
end
desc "logs", "Show log lines from app on servers (use --help to show options)"
@@ -174,24 +174,24 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
if options[:follow]
run_locally do
info "Following logs on #{MRSK.primary_host}..."
info "Following logs on #{KAMAL.primary_host}..."
MRSK.specific_roles ||= ["web"]
role = MRSK.roles_on(MRSK.primary_host).first
KAMAL.specific_roles ||= ["web"]
role = KAMAL.roles_on(KAMAL.primary_host).first
info MRSK.app(role: role).follow_logs(host: MRSK.primary_host, grep: grep)
exec MRSK.app(role: role).follow_logs(host: MRSK.primary_host, grep: grep)
info KAMAL.app(role: role).follow_logs(host: KAMAL.primary_host, grep: grep)
exec KAMAL.app(role: role).follow_logs(host: KAMAL.primary_host, grep: grep)
end
else
since = options[:since]
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
begin
puts_by_host host, capture_with_info(*MRSK.app(role: role).logs(since: since, lines: lines, grep: grep))
puts_by_host host, capture_with_info(*KAMAL.app(role: role).logs(since: since, lines: lines, grep: grep))
rescue SSHKit::Command::Failed
puts_by_host host, "Nothing found"
end
@@ -212,12 +212,12 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
def remove_container(version)
mutating do
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
execute *MRSK.auditor.record("Removed app container with version #{version}", role: role), verbosity: :debug
execute *MRSK.app(role: role).remove_container(version: version)
execute *KAMAL.auditor.record("Removed app container with version #{version}", role: role), verbosity: :debug
execute *KAMAL.app(role: role).remove_container(version: version)
end
end
end
@@ -226,12 +226,12 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "remove_containers", "Remove all app containers from servers", hide: true
def remove_containers
mutating do
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)
on(KAMAL.hosts) do |host|
roles = KAMAL.roles_on(host)
roles.each do |role|
execute *MRSK.auditor.record("Removed all app containers", role: role), verbosity: :debug
execute *MRSK.app(role: role).remove_containers
execute *KAMAL.auditor.record("Removed all app containers", role: role), verbosity: :debug
execute *KAMAL.app(role: role).remove_containers
end
end
end
@@ -240,18 +240,18 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
desc "remove_images", "Remove all app images from servers", hide: true
def remove_images
mutating do
on(MRSK.hosts) do
execute *MRSK.auditor.record("Removed all app images"), verbosity: :debug
execute *MRSK.app.remove_images
on(KAMAL.hosts) do
execute *KAMAL.auditor.record("Removed all app images"), verbosity: :debug
execute *KAMAL.app.remove_images
end
end
end
desc "version", "Show app version currently running on servers"
def version
on(MRSK.hosts) do |host|
role = MRSK.roles_on(host).first
puts_by_host host, capture_with_info(*MRSK.app(role: role).current_running_version).strip
on(KAMAL.hosts) do |host|
role = KAMAL.roles_on(host).first
puts_by_host host, capture_with_info(*KAMAL.app(role: role).current_running_version).strip
end
end
@@ -259,22 +259,22 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
def using_version(new_version)
if new_version
begin
old_version = MRSK.config.version
MRSK.config.version = new_version
old_version = KAMAL.config.version
KAMAL.config.version = new_version
yield new_version
ensure
MRSK.config.version = old_version
KAMAL.config.version = old_version
end
else
yield MRSK.config.version
yield KAMAL.config.version
end
end
def current_running_version(host: MRSK.primary_host)
def current_running_version(host: KAMAL.primary_host)
version = nil
on(host) do
role = MRSK.roles_on(host).first
version = capture_with_info(*MRSK.app(role: role).current_running_version).strip
role = KAMAL.roles_on(host).first
version = capture_with_info(*KAMAL.app(role: role).current_running_version).strip
end
version.presence
end
@@ -283,7 +283,7 @@ class Mrsk::Cli::App < Mrsk::Cli::Base
versions = nil
on(host) do
versions = \
capture_with_info(*MRSK.app(role: role).list_versions, raise_on_non_zero_exit: false)
capture_with_info(*KAMAL.app(role: role).list_versions, raise_on_non_zero_exit: false)
.split("\n")
.drop(1)
end

View File

@@ -1,8 +1,8 @@
require "thor"
require "dotenv"
require "mrsk/sshkit_with_ext"
require "kamal/sshkit_with_ext"
module Mrsk::Cli
module Kamal::Cli
class Base < Thor
include SSHKit::DSL
@@ -42,7 +42,7 @@ module Mrsk::Cli
end
def initialize_commander(options)
MRSK.tap do |commander|
KAMAL.tap do |commander|
if options[:verbose]
ENV["VERBOSE"] = "1" # For backtraces via cli/start
commander.verbosity = :debug
@@ -73,9 +73,9 @@ module Mrsk::Cli
end
def mutating
return yield if MRSK.holding_lock?
return yield if KAMAL.holding_lock?
MRSK.config.ensure_env_available
KAMAL.config.ensure_env_available
run_hook "pre-connect"
@@ -84,7 +84,7 @@ module Mrsk::Cli
begin
yield
rescue
if MRSK.hold_lock_on_error?
if KAMAL.hold_lock_on_error?
error " \e[31mDeploy lock was not released\e[0m"
else
release_lock
@@ -99,24 +99,24 @@ module Mrsk::Cli
def acquire_lock
raise_if_locked do
say "Acquiring the deploy lock...", :magenta
on(MRSK.primary_host) { execute *MRSK.lock.acquire("Automatic deploy lock", MRSK.config.version), verbosity: :debug }
on(KAMAL.primary_host) { execute *KAMAL.lock.acquire("Automatic deploy lock", KAMAL.config.version), verbosity: :debug }
end
MRSK.holding_lock = true
KAMAL.holding_lock = true
end
def release_lock
say "Releasing the deploy lock...", :magenta
on(MRSK.primary_host) { execute *MRSK.lock.release, verbosity: :debug }
on(KAMAL.primary_host) { execute *KAMAL.lock.release, verbosity: :debug }
MRSK.holding_lock = false
KAMAL.holding_lock = false
end
def raise_if_locked
yield
rescue SSHKit::Runner::ExecuteError => e
if e.message =~ /cannot create directory/
on(MRSK.primary_host) { puts capture_with_debug(*MRSK.lock.status) }
on(KAMAL.primary_host) { puts capture_with_debug(*KAMAL.lock.status) }
raise LockError, "Deploy lock found"
else
raise e
@@ -124,22 +124,22 @@ module Mrsk::Cli
end
def hold_lock_on_error
if MRSK.hold_lock_on_error?
if KAMAL.hold_lock_on_error?
yield
else
MRSK.hold_lock_on_error = true
KAMAL.hold_lock_on_error = true
yield
MRSK.hold_lock_on_error = false
KAMAL.hold_lock_on_error = false
end
end
def run_hook(hook, **extra_details)
if !options[:skip_hooks] && MRSK.hook.hook_exists?(hook)
details = { hosts: MRSK.hosts.join(","), command: command, subcommand: subcommand }
if !options[:skip_hooks] && KAMAL.hook.hook_exists?(hook)
details = { hosts: KAMAL.hosts.join(","), command: command, subcommand: subcommand }
say "Running the #{hook} hook...", :magenta
run_locally do
MRSK.with_verbosity(:debug) { execute *MRSK.hook.run(hook, **details, **extra_details) }
KAMAL.with_verbosity(:debug) { execute *KAMAL.hook.run(hook, **details, **extra_details) }
rescue SSHKit::Command::Failed
raise HookError.new("Hook `#{hook}` failed")
end
@@ -147,20 +147,20 @@ module Mrsk::Cli
end
def command
@mrsk_command ||= begin
@kamal_command ||= begin
invocation_class, invocation_commands = *first_invocation
if invocation_class == Mrsk::Cli::Main
if invocation_class == Kamal::Cli::Main
invocation_commands[0]
else
Mrsk::Cli::Main.subcommand_classes.find { |command, clazz| clazz == invocation_class }[0]
Kamal::Cli::Main.subcommand_classes.find { |command, clazz| clazz == invocation_class }[0]
end
end
end
def subcommand
@mrsk_subcommand ||= begin
@kamal_subcommand ||= begin
invocation_class, invocation_commands = *first_invocation
invocation_commands[0] if invocation_class != Mrsk::Cli::Main
invocation_commands[0] if invocation_class != Kamal::Cli::Main
end
end

View File

@@ -1,4 +1,4 @@
class Mrsk::Cli::Build < Mrsk::Cli::Base
class Kamal::Cli::Build < Kamal::Cli::Base
class BuildError < StandardError; end
desc "deliver", "Build app and push app image to registry then pull image on servers"
@@ -17,21 +17,21 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
verify_local_dependencies
run_hook "pre-build"
if (uncommitted_changes = Mrsk::Utils.uncommitted_changes).present?
if (uncommitted_changes = Kamal::Utils.uncommitted_changes).present?
say "The following paths have uncommitted changes:\n #{uncommitted_changes}", :yellow
end
run_locally do
begin
MRSK.with_verbosity(:debug) do
execute *MRSK.builder.push
KAMAL.with_verbosity(:debug) do
execute *KAMAL.builder.push
end
rescue SSHKit::Command::Failed => e
if e.message =~ /(no builder)|(no such file or directory)/
error "Missing compatible builder, so creating a new one first"
if cli.create
MRSK.with_verbosity(:debug) { execute *MRSK.builder.push }
KAMAL.with_verbosity(:debug) { execute *KAMAL.builder.push }
end
else
raise
@@ -44,10 +44,10 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
desc "pull", "Pull app image from registry onto servers"
def pull
mutating do
on(MRSK.hosts) do
execute *MRSK.auditor.record("Pulled image with version #{MRSK.config.version}"), verbosity: :debug
execute *MRSK.builder.clean, raise_on_non_zero_exit: false
execute *MRSK.builder.pull
on(KAMAL.hosts) do
execute *KAMAL.auditor.record("Pulled image with version #{KAMAL.config.version}"), verbosity: :debug
execute *KAMAL.builder.clean, raise_on_non_zero_exit: false
execute *KAMAL.builder.pull
end
end
end
@@ -57,8 +57,8 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
mutating do
run_locally do
begin
debug "Using builder: #{MRSK.builder.name}"
execute *MRSK.builder.create
debug "Using builder: #{KAMAL.builder.name}"
execute *KAMAL.builder.create
rescue SSHKit::Command::Failed => e
if e.message =~ /stderr=(.*)/
error "Couldn't create remote builder: #{$1}"
@@ -75,8 +75,8 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
def remove
mutating do
run_locally do
debug "Using builder: #{MRSK.builder.name}"
execute *MRSK.builder.remove
debug "Using builder: #{KAMAL.builder.name}"
execute *KAMAL.builder.remove
end
end
end
@@ -84,8 +84,8 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
desc "details", "Show build setup"
def details
run_locally do
puts "Builder: #{MRSK.builder.name}"
puts capture(*MRSK.builder.info)
puts "Builder: #{KAMAL.builder.name}"
puts capture(*KAMAL.builder.info)
end
end
@@ -93,7 +93,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
def verify_local_dependencies
run_locally do
begin
execute *MRSK.builder.ensure_local_dependencies_installed
execute *KAMAL.builder.ensure_local_dependencies_installed
rescue SSHKit::Command::Failed => e
build_error = e.message =~ /command not found/ ?
"Docker is not installed locally" :

View File

@@ -0,0 +1,20 @@
class Kamal::Cli::Healthcheck < Kamal::Cli::Base
default_command :perform
desc "perform", "Health check current app version"
def perform
on(KAMAL.primary_host) do
begin
execute *KAMAL.healthcheck.run
Kamal::Utils::HealthcheckPoller.wait_for_healthy { capture_with_info(*KAMAL.healthcheck.status) }
rescue Kamal::Utils::HealthcheckPoller::HealthcheckError => e
error capture_with_info(*KAMAL.healthcheck.logs)
error capture_with_pretty_json(*KAMAL.healthcheck.container_health_log)
raise
ensure
execute *KAMAL.healthcheck.stop, raise_on_non_zero_exit: false
execute *KAMAL.healthcheck.remove, raise_on_non_zero_exit: false
end
end
end
end

View File

@@ -1,8 +1,8 @@
class Mrsk::Cli::Lock < Mrsk::Cli::Base
class Kamal::Cli::Lock < Kamal::Cli::Base
desc "status", "Report lock status"
def status
handle_missing_lock do
on(MRSK.primary_host) { puts capture_with_debug(*MRSK.lock.status) }
on(KAMAL.primary_host) { puts capture_with_debug(*KAMAL.lock.status) }
end
end
@@ -11,7 +11,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
def acquire
message = options[:message]
raise_if_locked do
on(MRSK.primary_host) { execute *MRSK.lock.acquire(message, MRSK.config.version), verbosity: :debug }
on(KAMAL.primary_host) { execute *KAMAL.lock.acquire(message, KAMAL.config.version), verbosity: :debug }
say "Acquired the deploy lock"
end
end
@@ -19,7 +19,7 @@ class Mrsk::Cli::Lock < Mrsk::Cli::Base
desc "release", "Release the deploy lock"
def release
handle_missing_lock do
on(MRSK.primary_host) { execute *MRSK.lock.release, verbosity: :debug }
on(KAMAL.primary_host) { execute *KAMAL.lock.release, verbosity: :debug }
say "Released the deploy lock"
end
end

View File

@@ -1,10 +1,10 @@
class Mrsk::Cli::Main < Mrsk::Cli::Base
class Kamal::Cli::Main < Kamal::Cli::Base
desc "setup", "Setup all accessories and deploy app to servers"
def setup
print_runtime do
mutating do
invoke "mrsk:cli:server:bootstrap"
invoke "mrsk:cli:accessory:boot", [ "all" ]
invoke "kamal:cli:server:bootstrap"
invoke "kamal:cli:accessory:boot", [ "all" ]
deploy
end
end
@@ -18,31 +18,31 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
invoke_options = deploy_options
say "Log into image registry...", :magenta
invoke "mrsk:cli:registry:login", [], invoke_options
invoke "kamal:cli:registry:login", [], invoke_options
if options[:skip_push]
say "Pull app image...", :magenta
invoke "mrsk:cli:build:pull", [], invoke_options
invoke "kamal:cli:build:pull", [], invoke_options
else
say "Build and push app image...", :magenta
invoke "mrsk:cli:build:deliver", [], invoke_options
invoke "kamal:cli:build:deliver", [], invoke_options
end
run_hook "pre-deploy"
say "Ensure Traefik is running...", :magenta
invoke "mrsk:cli:traefik:boot", [], invoke_options
invoke "kamal:cli:traefik:boot", [], invoke_options
say "Ensure app can pass healthcheck...", :magenta
invoke "mrsk:cli:healthcheck:perform", [], invoke_options
invoke "kamal:cli:healthcheck:perform", [], invoke_options
say "Detect stale containers...", :magenta
invoke "mrsk:cli:app:stale_containers", [], invoke_options
invoke "kamal:cli:app:stale_containers", [], invoke_options
invoke "mrsk:cli:app:boot", [], invoke_options
invoke "kamal:cli:app:boot", [], invoke_options
say "Prune old containers and images...", :magenta
invoke "mrsk:cli:prune:all", [], invoke_options
invoke "kamal:cli:prune:all", [], invoke_options
end
end
@@ -58,21 +58,21 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
if options[:skip_push]
say "Pull app image...", :magenta
invoke "mrsk:cli:build:pull", [], invoke_options
invoke "kamal:cli:build:pull", [], invoke_options
else
say "Build and push app image...", :magenta
invoke "mrsk:cli:build:deliver", [], invoke_options
invoke "kamal:cli:build:deliver", [], invoke_options
end
run_hook "pre-deploy"
say "Ensure app can pass healthcheck...", :magenta
invoke "mrsk:cli:healthcheck:perform", [], invoke_options
invoke "kamal:cli:healthcheck:perform", [], invoke_options
say "Detect stale containers...", :magenta
invoke "mrsk:cli:app:stale_containers", [], invoke_options
invoke "kamal:cli:app:stale_containers", [], invoke_options
invoke "mrsk:cli:app:boot", [], invoke_options
invoke "kamal:cli:app:boot", [], invoke_options
end
end
@@ -86,16 +86,16 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
mutating do
invoke_options = deploy_options
MRSK.config.version = version
KAMAL.config.version = version
old_version = nil
if container_available?(version)
run_hook "pre-deploy"
invoke "mrsk:cli:app:boot", [], invoke_options.merge(version: version)
invoke "kamal:cli:app:boot", [], invoke_options.merge(version: version)
rolled_back = true
else
say "The app version '#{version}' is not available as a container (use 'mrsk app containers' for available versions)", :red
say "The app version '#{version}' is not available as a container (use 'kamal app containers' for available versions)", :red
end
end
end
@@ -105,27 +105,27 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
desc "details", "Show details about all containers"
def details
invoke "mrsk:cli:traefik:details"
invoke "mrsk:cli:app:details"
invoke "mrsk:cli:accessory:details", [ "all" ]
invoke "kamal:cli:traefik:details"
invoke "kamal:cli:app:details"
invoke "kamal:cli:accessory:details", [ "all" ]
end
desc "audit", "Show audit log from servers"
def audit
on(MRSK.hosts) do |host|
puts_by_host host, capture_with_info(*MRSK.auditor.reveal)
on(KAMAL.hosts) do |host|
puts_by_host host, capture_with_info(*KAMAL.auditor.reveal)
end
end
desc "config", "Show combined config (including secrets!)"
def config
run_locally do
puts Mrsk::Utils.redacted(MRSK.config.to_h).to_yaml
puts Kamal::Utils.redacted(KAMAL.config.to_h).to_yaml
end
end
desc "init", "Create config stub in config/deploy.yml and env stub in .env"
option :bundle, type: :boolean, default: false, desc: "Add MRSK to the Gemfile and create a bin/mrsk binstub"
option :bundle, type: :boolean, default: false, desc: "Add Kamal to the Gemfile and create a bin/kamal binstub"
def init
require "fileutils"
@@ -142,24 +142,24 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
puts "Created .env file"
end
unless (hooks_dir = Pathname.new(File.expand_path(".mrsk/hooks"))).exist?
unless (hooks_dir = Pathname.new(File.expand_path(".kamal/hooks"))).exist?
hooks_dir.mkpath
Pathname.new(File.expand_path("templates/sample_hooks", __dir__)).each_child do |sample_hook|
FileUtils.cp sample_hook, hooks_dir, preserve: true
end
puts "Created sample hooks in .mrsk/hooks"
puts "Created sample hooks in .kamal/hooks"
end
if options[:bundle]
if (binstub = Pathname.new(File.expand_path("bin/mrsk"))).exist?
puts "Binstub already exists in bin/mrsk (remove first to create a new one)"
if (binstub = Pathname.new(File.expand_path("bin/kamal"))).exist?
puts "Binstub already exists in bin/kamal (remove first to create a new one)"
else
puts "Adding MRSK to Gemfile and bundle..."
puts "Adding Kamal to Gemfile and bundle..."
run_locally do
execute :bundle, :add, :mrsk
execute :bundle, :binstubs, :mrsk
execute :bundle, :add, :kamal
execute :bundle, :binstubs, :kamal
end
puts "Created binstub file in bin/mrsk"
puts "Created binstub file in bin/kamal"
end
end
end
@@ -182,52 +182,52 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
def remove
mutating do
if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w( y N ), default: "N") == "y"
invoke "mrsk:cli:traefik:remove", [], options.without(:confirmed)
invoke "mrsk:cli:app:remove", [], options.without(:confirmed)
invoke "mrsk:cli:accessory:remove", [ "all" ], options
invoke "mrsk:cli:registry:logout", [], options.without(:confirmed)
invoke "kamal:cli:traefik:remove", [], options.without(:confirmed)
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
invoke "kamal:cli:accessory:remove", [ "all" ], options
invoke "kamal:cli:registry:logout", [], options.without(:confirmed)
end
end
end
desc "version", "Show MRSK version"
desc "version", "Show Kamal version"
def version
puts Mrsk::VERSION
puts Kamal::VERSION
end
desc "accessory", "Manage accessories (db/redis/search)"
subcommand "accessory", Mrsk::Cli::Accessory
subcommand "accessory", Kamal::Cli::Accessory
desc "app", "Manage application"
subcommand "app", Mrsk::Cli::App
subcommand "app", Kamal::Cli::App
desc "build", "Build application image"
subcommand "build", Mrsk::Cli::Build
subcommand "build", Kamal::Cli::Build
desc "healthcheck", "Healthcheck application"
subcommand "healthcheck", Mrsk::Cli::Healthcheck
subcommand "healthcheck", Kamal::Cli::Healthcheck
desc "lock", "Manage the deploy lock"
subcommand "lock", Mrsk::Cli::Lock
subcommand "lock", Kamal::Cli::Lock
desc "prune", "Prune old application images and containers"
subcommand "prune", Mrsk::Cli::Prune
subcommand "prune", Kamal::Cli::Prune
desc "registry", "Login and -out of the image registry"
subcommand "registry", Mrsk::Cli::Registry
subcommand "registry", Kamal::Cli::Registry
desc "server", "Bootstrap servers with curl and Docker"
subcommand "server", Mrsk::Cli::Server
subcommand "server", Kamal::Cli::Server
desc "traefik", "Manage Traefik load balancer"
subcommand "traefik", Mrsk::Cli::Traefik
subcommand "traefik", Kamal::Cli::Traefik
private
def container_available?(version)
begin
on(MRSK.hosts) do
MRSK.roles_on(host).each do |role|
container_id = capture_with_info(*MRSK.app(role: role).container_id_for_version(version))
on(KAMAL.hosts) do
KAMAL.roles_on(host).each do |role|
container_id = capture_with_info(*KAMAL.app(role: role).container_id_for_version(version))
raise "Container not found" unless container_id.present?
end
end
@@ -244,6 +244,6 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
end
def deploy_options
{ "version" => MRSK.config.version }.merge(options.without("skip_push"))
{ "version" => KAMAL.config.version }.merge(options.without("skip_push"))
end
end

30
lib/kamal/cli/prune.rb Normal file
View File

@@ -0,0 +1,30 @@
class Kamal::Cli::Prune < Kamal::Cli::Base
desc "all", "Prune unused images and stopped containers"
def all
mutating do
containers
images
end
end
desc "images", "Prune dangling images"
def images
mutating do
on(KAMAL.hosts) do
execute *KAMAL.auditor.record("Pruned images"), verbosity: :debug
execute *KAMAL.prune.dangling_images
execute *KAMAL.prune.tagged_images
end
end
end
desc "containers", "Prune all stopped containers, except the last 5"
def containers
mutating do
on(KAMAL.hosts) do
execute *KAMAL.auditor.record("Pruned containers"), verbosity: :debug
execute *KAMAL.prune.containers
end
end
end
end

View File

@@ -1,8 +1,8 @@
class Mrsk::Cli::Registry < Mrsk::Cli::Base
class Kamal::Cli::Registry < Kamal::Cli::Base
desc "login", "Log in to registry locally and remotely"
def login
run_locally { execute *MRSK.registry.login }
on(MRSK.hosts) { execute *MRSK.registry.login }
run_locally { execute *KAMAL.registry.login }
on(KAMAL.hosts) { execute *KAMAL.registry.login }
# FIXME: This rescue needed?
rescue ArgumentError => e
puts e.message
@@ -10,7 +10,7 @@ class Mrsk::Cli::Registry < Mrsk::Cli::Base
desc "logout", "Log out of registry remotely"
def logout
on(MRSK.hosts) { execute *MRSK.registry.logout }
on(KAMAL.hosts) { execute *KAMAL.registry.logout }
# FIXME: This rescue needed?
rescue ArgumentError => e
puts e.message

View File

@@ -1,13 +1,13 @@
class Mrsk::Cli::Server < Mrsk::Cli::Base
desc "bootstrap", "Set up Docker to run MRSK apps"
class Kamal::Cli::Server < Kamal::Cli::Base
desc "bootstrap", "Set up Docker to run Kamal apps"
def bootstrap
missing = []
on(MRSK.hosts | MRSK.accessory_hosts) do |host|
unless execute(*MRSK.docker.installed?, raise_on_non_zero_exit: false)
if execute(*MRSK.docker.superuser?, raise_on_non_zero_exit: false)
on(KAMAL.hosts | KAMAL.accessory_hosts) do |host|
unless execute(*KAMAL.docker.installed?, raise_on_non_zero_exit: false)
if execute(*KAMAL.docker.superuser?, raise_on_non_zero_exit: false)
info "Missing Docker on #{host}. Installing…"
execute *MRSK.docker.install
execute *KAMAL.docker.install
else
missing << host
end

View File

@@ -16,7 +16,7 @@ registry:
# Always use an access token rather than real password when possible.
password:
- MRSK_REGISTRY_PASSWORD
- KAMAL_REGISTRY_PASSWORD
# Inject ENV variables into containers (secrets come from .env).
# env:

View File

@@ -0,0 +1,14 @@
#!/bin/sh
# A sample post-deploy hook
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"

View File

@@ -9,12 +9,12 @@
# 4. The version we are deploying matches the remote
#
# These environment variables are available:
# MRSK_RECORDED_AT
# MRSK_PERFORMER
# MRSK_VERSION
# MRSK_HOSTS
# MRSK_ROLE (if set)
# MRSK_DESTINATION (if set)
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
if [ -n "$(git status --porcelain)" ]; then
echo "Git checkout is not clean, aborting..." >&2
@@ -43,8 +43,8 @@ if [ -z "$remote_head" ]; then
exit 1
fi
if [ "$MRSK_VERSION" != "$remote_head" ]; then
echo "Version ($MRSK_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
exit 1
fi

View File

@@ -5,15 +5,15 @@
# 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
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME
hosts = ENV["MRSK_HOSTS"].split(",")
hosts = ENV["KAMAL_HOSTS"].split(",")
results = nil
max = 3

View File

@@ -7,17 +7,17 @@
# Fails unless the combined status is "success"
#
# These environment variables are available:
# MRSK_RECORDED_AT
# MRSK_PERFORMER
# MRSK_VERSION
# MRSK_HOSTS
# MRSK_COMMAND
# MRSK_SUBCOMMAND
# MRSK_ROLE (if set)
# MRSK_DESTINATION (if set)
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_COMMAND
# KAMAL_SUBCOMMAND
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# Only check the build status for production deployments
if ENV["MRSK_COMMAND"] == "rollback" || ENV["MRSK_DESTINATION"] != "production"
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
exit 0
end

View File

@@ -0,0 +1,2 @@
KAMAL_REGISTRY_PASSWORD=change-this
RAILS_MASTER_KEY=another-env

View File

@@ -1,10 +1,10 @@
class Mrsk::Cli::Traefik < Mrsk::Cli::Base
class Kamal::Cli::Traefik < Kamal::Cli::Base
desc "boot", "Boot Traefik on servers"
def boot
mutating do
on(MRSK.traefik_hosts) do
execute *MRSK.registry.login
execute *MRSK.traefik.start_or_run
on(KAMAL.traefik_hosts) do
execute *KAMAL.registry.login
execute *KAMAL.traefik.start_or_run
end
end
end
@@ -13,12 +13,12 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
def reboot
mutating do
on(MRSK.traefik_hosts, in: options[:rolling] ? :sequence : :parallel) do
execute *MRSK.auditor.record("Rebooted traefik"), verbosity: :debug
execute *MRSK.registry.login
execute *MRSK.traefik.stop
execute *MRSK.traefik.remove_container
execute *MRSK.traefik.run
on(KAMAL.traefik_hosts, in: options[:rolling] ? :sequence : :parallel) do
execute *KAMAL.auditor.record("Rebooted traefik"), verbosity: :debug
execute *KAMAL.registry.login
execute *KAMAL.traefik.stop
execute *KAMAL.traefik.remove_container
execute *KAMAL.traefik.run
end
end
end
@@ -26,9 +26,9 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
desc "start", "Start existing Traefik container on servers"
def start
mutating do
on(MRSK.traefik_hosts) do
execute *MRSK.auditor.record("Started traefik"), verbosity: :debug
execute *MRSK.traefik.start
on(KAMAL.traefik_hosts) do
execute *KAMAL.auditor.record("Started traefik"), verbosity: :debug
execute *KAMAL.traefik.start
end
end
end
@@ -36,9 +36,9 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
desc "stop", "Stop existing Traefik container on servers"
def stop
mutating do
on(MRSK.traefik_hosts) do
execute *MRSK.auditor.record("Stopped traefik"), verbosity: :debug
execute *MRSK.traefik.stop
on(KAMAL.traefik_hosts) do
execute *KAMAL.auditor.record("Stopped traefik"), verbosity: :debug
execute *KAMAL.traefik.stop
end
end
end
@@ -53,7 +53,7 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
desc "details", "Show details about Traefik container from servers"
def details
on(MRSK.traefik_hosts) { |host| puts_by_host host, capture_with_info(*MRSK.traefik.info), type: "Traefik" }
on(KAMAL.traefik_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.traefik.info), type: "Traefik" }
end
desc "logs", "Show log lines from Traefik on servers"
@@ -66,16 +66,16 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
if options[:follow]
run_locally do
info "Following logs on #{MRSK.primary_host}..."
info MRSK.traefik.follow_logs(host: MRSK.primary_host, grep: grep)
exec MRSK.traefik.follow_logs(host: MRSK.primary_host, grep: grep)
info "Following logs on #{KAMAL.primary_host}..."
info KAMAL.traefik.follow_logs(host: KAMAL.primary_host, grep: grep)
exec KAMAL.traefik.follow_logs(host: KAMAL.primary_host, grep: grep)
end
else
since = options[:since]
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
on(MRSK.traefik_hosts) do |host|
puts_by_host host, capture(*MRSK.traefik.logs(since: since, lines: lines, grep: grep)), type: "Traefik"
on(KAMAL.traefik_hosts) do |host|
puts_by_host host, capture(*KAMAL.traefik.logs(since: since, lines: lines, grep: grep)), type: "Traefik"
end
end
end
@@ -92,9 +92,9 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
desc "remove_container", "Remove Traefik container from servers", hide: true
def remove_container
mutating do
on(MRSK.traefik_hosts) do
execute *MRSK.auditor.record("Removed traefik container"), verbosity: :debug
execute *MRSK.traefik.remove_container
on(KAMAL.traefik_hosts) do
execute *KAMAL.auditor.record("Removed traefik container"), verbosity: :debug
execute *KAMAL.traefik.remove_container
end
end
end
@@ -102,9 +102,9 @@ class Mrsk::Cli::Traefik < Mrsk::Cli::Base
desc "remove_image", "Remove Traefik image from servers", hide: true
def remove_image
mutating do
on(MRSK.traefik_hosts) do
execute *MRSK.auditor.record("Removed traefik image"), verbosity: :debug
execute *MRSK.traefik.remove_image
on(KAMAL.traefik_hosts) do
execute *KAMAL.auditor.record("Removed traefik image"), verbosity: :debug
execute *KAMAL.traefik.remove_image
end
end
end

View File

@@ -1,7 +1,7 @@
require "active_support/core_ext/enumerable"
require "active_support/core_ext/module/delegation"
class Mrsk::Commander
class Kamal::Commander
attr_accessor :verbosity, :holding_lock, :hold_lock_on_error
def initialize
@@ -11,7 +11,7 @@ class Mrsk::Commander
end
def config
@config ||= Mrsk::Configuration.create_from(**@config_kwargs).tap do |config|
@config ||= Kamal::Configuration.create_from(**@config_kwargs).tap do |config|
@config_kwargs = nil
configure_sshkit_with(config)
end
@@ -77,47 +77,47 @@ class Mrsk::Commander
def app(role: nil)
Mrsk::Commands::App.new(config, role: role)
Kamal::Commands::App.new(config, role: role)
end
def accessory(name)
Mrsk::Commands::Accessory.new(config, name: name)
Kamal::Commands::Accessory.new(config, name: name)
end
def auditor(**details)
Mrsk::Commands::Auditor.new(config, **details)
Kamal::Commands::Auditor.new(config, **details)
end
def builder
@builder ||= Mrsk::Commands::Builder.new(config)
@builder ||= Kamal::Commands::Builder.new(config)
end
def docker
@docker ||= Mrsk::Commands::Docker.new(config)
@docker ||= Kamal::Commands::Docker.new(config)
end
def healthcheck
@healthcheck ||= Mrsk::Commands::Healthcheck.new(config)
@healthcheck ||= Kamal::Commands::Healthcheck.new(config)
end
def hook
@hook ||= Mrsk::Commands::Hook.new(config)
@hook ||= Kamal::Commands::Hook.new(config)
end
def lock
@lock ||= Mrsk::Commands::Lock.new(config)
@lock ||= Kamal::Commands::Lock.new(config)
end
def prune
@prune ||= Mrsk::Commands::Prune.new(config)
@prune ||= Kamal::Commands::Prune.new(config)
end
def registry
@registry ||= Mrsk::Commands::Registry.new(config)
@registry ||= Kamal::Commands::Registry.new(config)
end
def traefik
@traefik ||= Mrsk::Commands::Traefik.new(config)
@traefik ||= Kamal::Commands::Traefik.new(config)
end
def with_verbosity(level)

2
lib/kamal/commands.rb Normal file
View File

@@ -0,0 +1,2 @@
module Kamal::Commands
end

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Accessory < Mrsk::Commands::Base
class Kamal::Commands::Accessory < Kamal::Commands::Base
attr_reader :accessory_config
delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
:publish_args, :env_args, :volume_args, :label_args, :option_args, to: :accessory_config

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::App < Mrsk::Commands::Base
class Kamal::Commands::App < Kamal::Commands::Base
ACTIVE_DOCKER_STATUSES = [ :running, :restarting ]
attr_reader :role
@@ -20,7 +20,7 @@ class Mrsk::Commands::App < Mrsk::Commands::Base
"--restart unless-stopped",
"--name", container_name,
*(["--hostname", hostname] if hostname),
"-e", "MRSK_CONTAINER_NAME=\"#{container_name}\"",
"-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
*role.env_args,
*role.health_check_args,
*config.logging_args,

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Auditor < Mrsk::Commands::Base
class Kamal::Commands::Auditor < Kamal::Commands::Base
attr_reader :details
def initialize(config, **details)
@@ -19,7 +19,7 @@ class Mrsk::Commands::Auditor < Mrsk::Commands::Base
private
def audit_log_file
[ "mrsk", config.service, config.destination, "audit.log" ].compact.join("-")
[ "kamal", config.service, config.destination, "audit.log" ].compact.join("-")
end
def audit_tags(**details)

View File

@@ -1,6 +1,6 @@
module Mrsk::Commands
module Kamal::Commands
class Base
delegate :sensitive, :argumentize, to: Mrsk::Utils
delegate :sensitive, :argumentize, to: Kamal::Utils
DOCKER_HEALTH_STATUS_FORMAT = "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'"
DOCKER_HEALTH_LOG_FORMAT = "'{{json .State.Health}}'"
@@ -59,7 +59,7 @@ module Mrsk::Commands
end
def tags(**details)
Mrsk::Tags.from_config(config, **details)
Kamal::Tags.from_config(config, **details)
end
end
end

View File

@@ -1,8 +1,8 @@
class Mrsk::Commands::Builder < Mrsk::Commands::Base
class Kamal::Commands::Builder < Kamal::Commands::Base
delegate :create, :remove, :push, :clean, :pull, :info, to: :target
def name
target.class.to_s.remove("Mrsk::Commands::Builder::").underscore.inquiry
target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
end
def target
@@ -21,23 +21,23 @@ class Mrsk::Commands::Builder < Mrsk::Commands::Base
end
def native
@native ||= Mrsk::Commands::Builder::Native.new(config)
@native ||= Kamal::Commands::Builder::Native.new(config)
end
def native_cached
@native ||= Mrsk::Commands::Builder::Native::Cached.new(config)
@native ||= Kamal::Commands::Builder::Native::Cached.new(config)
end
def native_remote
@native ||= Mrsk::Commands::Builder::Native::Remote.new(config)
@native ||= Kamal::Commands::Builder::Native::Remote.new(config)
end
def multiarch
@multiarch ||= Mrsk::Commands::Builder::Multiarch.new(config)
@multiarch ||= Kamal::Commands::Builder::Multiarch.new(config)
end
def multiarch_remote
@multiarch_remote ||= Mrsk::Commands::Builder::Multiarch::Remote.new(config)
@multiarch_remote ||= Kamal::Commands::Builder::Multiarch::Remote.new(config)
end

View File

@@ -1,8 +1,8 @@
class Mrsk::Commands::Builder::Base < Mrsk::Commands::Base
class Kamal::Commands::Builder::Base < Kamal::Commands::Base
class BuilderError < StandardError; end
delegate :argumentize, to: Mrsk::Utils
delegate :argumentize, to: Kamal::Utils
delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, to: :builder_config
def clean

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Builder::Multiarch < Mrsk::Commands::Builder::Base
class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
def create
docker :buildx, :create, "--use", "--name", builder_name
end
@@ -24,6 +24,6 @@ class Mrsk::Commands::Builder::Multiarch < Mrsk::Commands::Builder::Base
private
def builder_name
"mrsk-#{config.service}-multiarch"
"kamal-#{config.service}-multiarch"
end
end

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Multiarch
class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Multiarch
def create
combine \
create_contexts,

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Builder::Native < Mrsk::Commands::Builder::Base
class Kamal::Commands::Builder::Native < Kamal::Commands::Builder::Base
def create
# No-op on native without cache
end

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Builder::Native::Cached < Mrsk::Commands::Builder::Native
class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Native
def create
docker :buildx, :create, "--use", "--driver=docker-container"
end

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Builder::Native::Remote < Mrsk::Commands::Builder::Native
class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Native
def create
chain \
create_context,
@@ -29,7 +29,7 @@ class Mrsk::Commands::Builder::Native::Remote < Mrsk::Commands::Builder::Native
private
def builder_name
"mrsk-#{config.service}-native-remote"
"kamal-#{config.service}-native-remote"
end
def builder_name_with_arch

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Docker < Mrsk::Commands::Base
class Kamal::Commands::Docker < Kamal::Commands::Base
# Install Docker using the https://github.com/docker/docker-install convenience script.
def install
pipe [ :curl, "-fsSL", "https://get.docker.com" ], :sh

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Healthcheck < Mrsk::Commands::Base
class Kamal::Commands::Healthcheck < Kamal::Commands::Base
EXPOSED_PORT = 3999
def run
@@ -9,7 +9,7 @@ class Mrsk::Commands::Healthcheck < Mrsk::Commands::Base
"--name", container_name_with_version,
"--publish", "#{EXPOSED_PORT}:#{config.healthcheck["port"]}",
"--label", "service=#{container_name}",
"-e", "MRSK_CONTAINER_NAME=\"#{container_name}\"",
"-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
*web.env_args,
*web.health_check_args,
*config.volume_args,

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Hook < Mrsk::Commands::Base
class Kamal::Commands::Hook < Kamal::Commands::Base
def run(hook, **details)
[ hook_file(hook), env: tags(**details).env ]
end

View File

@@ -1,7 +1,7 @@
require "active_support/duration"
require "time"
class Mrsk::Commands::Lock < Mrsk::Commands::Base
class Kamal::Commands::Lock < Kamal::Commands::Base
def acquire(message, version)
combine \
[:mkdir, lock_dir],
@@ -40,7 +40,7 @@ class Mrsk::Commands::Lock < Mrsk::Commands::Base
end
def lock_dir
"mrsk_lock-#{config.service}"
"kamal_lock-#{config.service}"
end
def lock_details_file

View File

@@ -1,7 +1,7 @@
require "active_support/duration"
require "active_support/core_ext/numeric/time"
class Mrsk::Commands::Prune < Mrsk::Commands::Base
class Kamal::Commands::Prune < Kamal::Commands::Base
def dangling_images
docker :image, :prune, "--force", "--filter", "label=service=#{config.service}", "--filter", "dangling=true"
end

View File

@@ -1,4 +1,4 @@
class Mrsk::Commands::Registry < Mrsk::Commands::Base
class Kamal::Commands::Registry < Kamal::Commands::Base
delegate :registry, to: :config
def login

View File

@@ -1,5 +1,5 @@
class Mrsk::Commands::Traefik < Mrsk::Commands::Base
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
class Kamal::Commands::Traefik < Kamal::Commands::Base
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Kamal::Utils
DEFAULT_IMAGE = "traefik:v2.9"
CONTAINER_PORT = 80

View File

@@ -5,9 +5,9 @@ require "pathname"
require "erb"
require "net/ssh/proxy/jump"
class Mrsk::Configuration
class Kamal::Configuration
delegate :service, :image, :servers, :env, :labels, :registry, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Kamal::Utils
attr_accessor :destination
attr_accessor :raw_config
@@ -54,7 +54,7 @@ class Mrsk::Configuration
end
def abbreviated_version
Mrsk::Utils.abbreviate_version(version)
Kamal::Utils.abbreviate_version(version)
end
@@ -67,7 +67,7 @@ class Mrsk::Configuration
end
def accessories
@accessories ||= raw_config.accessories&.keys&.collect { |name| Mrsk::Configuration::Accessory.new(name, config: self) } || []
@accessories ||= raw_config.accessories&.keys&.collect { |name| Kamal::Configuration::Accessory.new(name, config: self) } || []
end
def accessory(name)
@@ -88,7 +88,7 @@ class Mrsk::Configuration
end
def boot
Mrsk::Configuration::Boot.new(config: self)
Kamal::Configuration::Boot.new(config: self)
end
@@ -136,11 +136,11 @@ class Mrsk::Configuration
def ssh
Mrsk::Configuration::Ssh.new(config: self)
Kamal::Configuration::Ssh.new(config: self)
end
def sshkit
Mrsk::Configuration::Sshkit.new(config: self)
Kamal::Configuration::Sshkit.new(config: self)
end
@@ -157,7 +157,7 @@ class Mrsk::Configuration
end
def valid?
ensure_required_keys_present && ensure_valid_mrsk_version
ensure_required_keys_present && ensure_valid_kamal_version
end
@@ -186,11 +186,11 @@ class Mrsk::Configuration
end
def hooks_path
raw_config.hooks_path || ".mrsk/hooks"
raw_config.hooks_path || ".kamal/hooks"
end
def builder
Mrsk::Configuration::Builder.new(config: self)
Kamal::Configuration::Builder.new(config: self)
end
# Will raise KeyError if any secret ENVs are missing
@@ -225,9 +225,9 @@ class Mrsk::Configuration
true
end
def ensure_valid_mrsk_version
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Mrsk::VERSION)
raise ArgumentError, "Current version is #{Mrsk::VERSION}, minimum required is #{minimum_version}"
def ensure_valid_kamal_version
if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Kamal::VERSION)
raise ArgumentError, "Current version is #{Kamal::VERSION}, minimum required is #{minimum_version}"
end
true
@@ -241,7 +241,7 @@ class Mrsk::Configuration
def git_version
@git_version ||=
if system("git rev-parse")
uncommitted_suffix = Mrsk::Utils.uncommitted_changes.present? ? "_uncommitted_#{SecureRandom.hex(8)}" : ""
uncommitted_suffix = Kamal::Utils.uncommitted_changes.present? ? "_uncommitted_#{SecureRandom.hex(8)}" : ""
"#{`git rev-parse HEAD`.strip}#{uncommitted_suffix}"
else

View File

@@ -1,5 +1,5 @@
class Mrsk::Configuration::Accessory
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
class Kamal::Configuration::Accessory
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Kamal::Utils
attr_accessor :name, :specifics

View File

@@ -1,4 +1,4 @@
class Mrsk::Configuration::Boot
class Kamal::Configuration::Boot
def initialize(config:)
@options = config.raw_config.boot || {}
@host_count = config.all_hosts.count

View File

@@ -1,4 +1,4 @@
class Mrsk::Configuration::Builder
class Kamal::Configuration::Builder
def initialize(config:)
@options = config.raw_config.builder || {}
@image = config.image

View File

@@ -1,5 +1,5 @@
class Mrsk::Configuration::Role
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Mrsk::Utils
class Kamal::Configuration::Role
delegate :argumentize, :argumentize_env_with_secrets, :optionize, to: Kamal::Utils
attr_accessor :name

View File

@@ -1,4 +1,4 @@
class Mrsk::Configuration::Ssh
class Kamal::Configuration::Ssh
LOGGER = ::Logger.new(STDERR)
def initialize(config:)

View File

@@ -1,4 +1,4 @@
class Mrsk::Configuration::Sshkit
class Kamal::Configuration::Sshkit
def initialize(config:)
@options = config.raw_config.sshkit || {}
end

View File

@@ -1,6 +1,6 @@
require "time"
class Mrsk::Tags
class Kamal::Tags
attr_reader :config, :tags
class << self
@@ -26,7 +26,7 @@ class Mrsk::Tags
end
def env
tags.transform_keys { |detail| "MRSK_#{detail.upcase}" }
tags.transform_keys { |detail| "KAMAL_#{detail.upcase}" }
end
def to_s

View File

@@ -1,4 +1,4 @@
module Mrsk::Utils
module Kamal::Utils
extend self
DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX = /\$(?!{[^\}]*\})/
@@ -46,7 +46,7 @@ module Mrsk::Utils
# Pass `redaction:` to change the default `"[REDACTED]"` redaction, e.g.
# `sensitive "#{arg}=#{secret}", redaction: "#{arg}=xxxx"
def sensitive(...)
Mrsk::Utils::Sensitive.new(...)
Kamal::Utils::Sensitive.new(...)
end
def redacted(value)

View File

@@ -1,4 +1,4 @@
class Mrsk::Utils::HealthcheckPoller
class Kamal::Utils::HealthcheckPoller
TRAEFIK_HEALTHY_DELAY = 2
class HealthcheckError < StandardError; end
@@ -6,14 +6,14 @@ class Mrsk::Utils::HealthcheckPoller
class << self
def wait_for_healthy(pause_after_ready: false, &block)
attempt = 1
max_attempts = MRSK.config.healthcheck["max_attempts"]
max_attempts = KAMAL.config.healthcheck["max_attempts"]
begin
case status = block.call
when "healthy"
sleep TRAEFIK_HEALTHY_DELAY if pause_after_ready
when "running" # No health check configured
sleep MRSK.config.readiness_delay if pause_after_ready
sleep KAMAL.config.readiness_delay if pause_after_ready
else
raise HealthcheckError, "container not ready (#{status})"
end

View File

@@ -1,6 +1,6 @@
require "active_support/core_ext/module/delegation"
class Mrsk::Utils::Sensitive
class Kamal::Utils::Sensitive
# So SSHKit knows to redact these values.
include SSHKit::Redaction

View File

@@ -1,3 +1,3 @@
module Mrsk
module Kamal
VERSION = "0.15.1"
end

View File

@@ -1,20 +0,0 @@
class Mrsk::Cli::Healthcheck < Mrsk::Cli::Base
default_command :perform
desc "perform", "Health check current app version"
def perform
on(MRSK.primary_host) do
begin
execute *MRSK.healthcheck.run
Mrsk::Utils::HealthcheckPoller.wait_for_healthy { capture_with_info(*MRSK.healthcheck.status) }
rescue Mrsk::Utils::HealthcheckPoller::HealthcheckError => e
error capture_with_info(*MRSK.healthcheck.logs)
error capture_with_pretty_json(*MRSK.healthcheck.container_health_log)
raise
ensure
execute *MRSK.healthcheck.stop, raise_on_non_zero_exit: false
execute *MRSK.healthcheck.remove, raise_on_non_zero_exit: false
end
end
end
end

View File

@@ -1,30 +0,0 @@
class Mrsk::Cli::Prune < Mrsk::Cli::Base
desc "all", "Prune unused images and stopped containers"
def all
mutating do
containers
images
end
end
desc "images", "Prune dangling images"
def images
mutating do
on(MRSK.hosts) do
execute *MRSK.auditor.record("Pruned images"), verbosity: :debug
execute *MRSK.prune.dangling_images
execute *MRSK.prune.tagged_images
end
end
end
desc "containers", "Prune all stopped containers, except the last 5"
def containers
mutating do
on(MRSK.hosts) do
execute *MRSK.auditor.record("Pruned containers"), verbosity: :debug
execute *MRSK.prune.containers
end
end
end
end

View File

@@ -1,14 +0,0 @@
#!/bin/sh
# A sample post-deploy hook
#
# These environment variables are available:
# MRSK_RECORDED_AT
# MRSK_PERFORMER
# MRSK_VERSION
# MRSK_HOSTS
# MRSK_ROLE (if set)
# MRSK_DESTINATION (if set)
# MRSK_RUNTIME
echo "$MRSK_PERFORMER deployed $MRSK_VERSION to $MRSK_DESTINATION in $MRSK_RUNTIME seconds"

View File

@@ -1,2 +0,0 @@
MRSK_REGISTRY_PASSWORD=change-this
RAILS_MASTER_KEY=another-env

View File

@@ -1,2 +0,0 @@
module Mrsk::Commands
end