Add a deploy lock for commands that are unsafe to run concurrently.
The lock is taken by creating a `mrsk_lock` directory on the primary
host. Details of who took the lock are added to a details file in that
directory.
Additional CLI commands have been added to manual release and acquire
the lock and to check its status.
```
Commands:
mrsk lock acquire -m, --message=MESSAGE # Acquire the deploy lock
mrsk lock help [COMMAND] # Describe subcommands or one specific subcommand
mrsk lock release # Release the deploy lock
mrsk lock status # Report lock status
Options:
-v, [--verbose], [--no-verbose] # Detailed logging
-q, [--quiet], [--no-quiet] # Minimal logging
[--version=VERSION] # Run commands against a specific app version
-p, [--primary], [--no-primary] # Run commands only on primary host instead of all
-h, [--hosts=HOSTS] # Run commands on these hosts instead of all (separate by comma)
-r, [--roles=ROLES] # Run commands on these roles instead of all (separate by comma)
-c, [--config-file=CONFIG_FILE] # Path to config file
# Default: config/deploy.yml
-d, [--destination=DESTINATION] # Specify destination to be used for config file (staging -> deploy.staging.yml)
-B, [--skip-broadcast], [--no-skip-broadcast] # Skip audit broadcasts
```
If we add support for running multiple deployments on a single server
we'll need to extend the locking to lock per deployment.
112 lines
2.3 KiB
Ruby
112 lines
2.3 KiB
Ruby
require "active_support/core_ext/enumerable"
|
|
require "active_support/core_ext/module/delegation"
|
|
|
|
class Mrsk::Commander
|
|
attr_accessor :verbosity, :lock_count
|
|
|
|
def initialize
|
|
self.verbosity = :info
|
|
self.lock_count = 0
|
|
end
|
|
|
|
|
|
def config
|
|
@config ||= Mrsk::Configuration.create_from(**@config_kwargs).tap do |config|
|
|
@config_kwargs = nil
|
|
configure_sshkit_with(config)
|
|
end
|
|
end
|
|
|
|
def configure(**kwargs)
|
|
@config, @config_kwargs = nil, kwargs
|
|
end
|
|
|
|
|
|
attr_accessor :specific_hosts
|
|
|
|
def specific_primary!
|
|
self.specific_hosts = [ config.primary_web_host ]
|
|
end
|
|
|
|
def specific_roles=(role_names)
|
|
self.specific_hosts = config.roles.select { |r| role_names.include?(r.name) }.flat_map(&:hosts) if role_names.present?
|
|
end
|
|
|
|
def primary_host
|
|
specific_hosts&.first || config.primary_web_host
|
|
end
|
|
|
|
def hosts
|
|
specific_hosts || config.all_hosts
|
|
end
|
|
|
|
def traefik_hosts
|
|
specific_hosts || config.traefik_hosts
|
|
end
|
|
|
|
def accessory_hosts
|
|
specific_hosts || config.accessories.collect(&:host)
|
|
end
|
|
|
|
def accessory_names
|
|
config.accessories&.collect(&:name) || []
|
|
end
|
|
|
|
|
|
def app
|
|
@app ||= Mrsk::Commands::App.new(config)
|
|
end
|
|
|
|
def accessory(name)
|
|
Mrsk::Commands::Accessory.new(config, name: name)
|
|
end
|
|
|
|
def auditor
|
|
@auditor ||= Mrsk::Commands::Auditor.new(config)
|
|
end
|
|
|
|
def builder
|
|
@builder ||= Mrsk::Commands::Builder.new(config)
|
|
end
|
|
|
|
def healthcheck
|
|
@healthcheck ||= Mrsk::Commands::Healthcheck.new(config)
|
|
end
|
|
|
|
def prune
|
|
@prune ||= Mrsk::Commands::Prune.new(config)
|
|
end
|
|
|
|
def registry
|
|
@registry ||= Mrsk::Commands::Registry.new(config)
|
|
end
|
|
|
|
def traefik
|
|
@traefik ||= Mrsk::Commands::Traefik.new(config)
|
|
end
|
|
|
|
def lock
|
|
@lock ||= Mrsk::Commands::Lock.new(config)
|
|
end
|
|
|
|
def with_verbosity(level)
|
|
old_level = self.verbosity
|
|
|
|
self.verbosity = level
|
|
SSHKit.config.output_verbosity = level
|
|
|
|
yield
|
|
ensure
|
|
self.verbosity = old_level
|
|
SSHKit.config.output_verbosity = old_level
|
|
end
|
|
|
|
private
|
|
# Lazy setup of SSHKit
|
|
def configure_sshkit_with(config)
|
|
SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = config.ssh_options }
|
|
SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs
|
|
SSHKit.config.output_verbosity = verbosity
|
|
end
|
|
end
|