Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Role aware container names #99

Merged
merged 20 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 54 additions & 26 deletions lib/mrsk/cli/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,26 @@ def boot

cli = self

MRSK.config.roles.each do |role|
on(role.hosts) do |host|
execute *MRSK.auditor.record("Booted app version #{version}"), verbosity: :debug
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)

roles.each do |role|
execute *MRSK.auditor(role: role).record("Booted app version #{version}"), verbosity: :debug

begin
old_version = capture_with_info(*MRSK.app.current_running_version).strip
execute *MRSK.app.run(role: role.name)
old_version = capture_with_info(*MRSK.app(role: role).current_running_version).strip
execute *MRSK.app(role: role).run
sleep MRSK.config.readiness_delay
execute *MRSK.app.stop(version: old_version), raise_on_non_zero_exit: false if old_version.present?
execute *MRSK.app(role: role).stop(version: old_version), raise_on_non_zero_exit: false if old_version.present?

rescue SSHKit::Command::Failed => e
if e.message =~ /already in use/
error "Rebooting container with same version #{version} already deployed on #{host} (may cause gap in zero-downtime promise!)"
execute *MRSK.auditor.record("Rebooted app version #{version}"), verbosity: :debug
execute *MRSK.auditor(role: role).record("Rebooted app version #{version}"), verbosity: :debug

execute *MRSK.app.stop(version: version)
execute *MRSK.app.remove_container(version: version)
execute *MRSK.app.run(role: role.name)
execute *MRSK.app(role: role).stop(version: version)
execute *MRSK.app(role: role).remove_container(version: version)
execute *MRSK.app(role: role).run
else
raise
end
Expand All @@ -36,24 +38,38 @@ def boot

desc "start", "Start existing app container on servers"
def start
on(MRSK.hosts) do
execute *MRSK.auditor.record("Started app version #{MRSK.config.version}"), verbosity: :debug
execute *MRSK.app.start, raise_on_non_zero_exit: false
on(MRSK.hosts) do |host|
roles = MRSK.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
end
end
end

desc "stop", "Stop app container on servers"
def stop
on(MRSK.hosts) do
execute *MRSK.auditor.record("Stopped app"), verbosity: :debug
execute *MRSK.app.stop, raise_on_non_zero_exit: false
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)

roles.each do |role|
execute *MRSK.auditor(role: role).record("Stopped app"), verbosity: :debug
execute *MRSK.app(role: role).stop, raise_on_non_zero_exit: false
end
end
end

# FIXME: Drop in favor of just containers?
desc "details", "Show details about app containers"
def details
on(MRSK.hosts) { |host| puts_by_host host, capture_with_info(*MRSK.app.info) }
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)

roles.each do |role|
puts_by_host host, capture_with_info(*MRSK.app(role: role).info)
end
end
end

desc "exec [CMD]", "Execute a custom command on servers (use --help to show options)"
Expand All @@ -65,7 +81,7 @@ def exec(cmd)
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.execute_in_existing_container_over_ssh(cmd, host: MRSK.primary_host) }
run_locally { exec MRSK.app(role: "web").execute_in_existing_container_over_ssh(cmd, host: MRSK.primary_host) }
end

when options[:interactive]
Expand All @@ -81,8 +97,12 @@ def exec(cmd)
say "Launching command with version #{version} from existing 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_existing_container(cmd))
roles = MRSK.roles_on(host)

roles.each do |role|
execute *MRSK.auditor(role: role).record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
puts_by_host host, capture_with_info(*MRSK.app(role: role).execute_in_existing_container(cmd))
end
end
end

Expand Down Expand Up @@ -147,17 +167,25 @@ def remove

desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
def remove_container(version)
on(MRSK.hosts) do
execute *MRSK.auditor.record("Removed app container with version #{version}"), verbosity: :debug
execute *MRSK.app.remove_container(version: version)
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)

roles.each do |role|
execute *MRSK.auditor(role: role).record("Removed app container with version #{version}"), verbosity: :debug
execute *MRSK.app(role: role).remove_container(version: version)
end
end
end

desc "remove_containers", "Remove all app containers from servers", hide: true
def remove_containers
on(MRSK.hosts) do
execute *MRSK.auditor.record("Removed all app containers"), verbosity: :debug
execute *MRSK.app.remove_containers
on(MRSK.hosts) do |host|
roles = MRSK.roles_on(host)

roles.each do |role|
execute *MRSK.auditor(role: role).record("Removed all app containers"), verbosity: :debug
execute *MRSK.app(role: role).remove_containers
end
end
end

Expand Down
32 changes: 23 additions & 9 deletions lib/mrsk/commander.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ def initialize
self.verbosity = :info
end


def config
@config ||= Mrsk::Configuration.create_from(**@config_kwargs).tap do |config|
@config_kwargs = nil
Expand All @@ -20,23 +19,38 @@ def configure(**kwargs)
@config, @config_kwargs = nil, kwargs
end


attr_accessor :specific_hosts
attr_reader :specific_roles, :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?
@specific_roles = config.roles.select { |r| role_names.include?(r.name) } if role_names.present?
end

def specific_hosts=(hosts)
@specific_hosts = config.all_hosts & hosts if hosts.present?
end

def primary_host
specific_hosts&.first || config.primary_web_host
end

def roles
(specific_roles || config.roles).select do |role|
((specific_hosts || config.all_hosts) & role.hosts).any?
end
end

def hosts
specific_hosts || config.all_hosts
(specific_hosts || config.all_hosts).select do |host|
(specific_roles || config.roles).flat_map(&:hosts).include?(host)
end
end

def roles_on(host)
roles.select { |role| role.hosts.include?(host.to_s) }.map(&:name)
end

def traefik_hosts
Expand All @@ -52,16 +66,16 @@ def accessory_names
end


def app
@app ||= Mrsk::Commands::App.new(config)
def app(role: nil)
Mrsk::Commands::App.new(config, role: role)
end

def accessory(name)
Mrsk::Commands::Accessory.new(config, name: name)
end

def auditor
@auditor ||= Mrsk::Commands::Auditor.new(config)
def auditor(role: nil)
Mrsk::Commands::Auditor.new(config, role: role)
end

def builder
Expand Down
28 changes: 18 additions & 10 deletions lib/mrsk/commands/app.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
class Mrsk::Commands::App < Mrsk::Commands::Base
def run(role: :web)
role = config.role(role)
attr_reader :role

def initialize(config, role: nil)
super(config)
@role = role
end

def run
role = config.role(self.role)

docker :run,
"--detach",
"--restart unless-stopped",
"--log-opt", "max-size=#{MAX_LOG_SIZE}",
"--name", service_with_version_and_destination,
"-e", "MRSK_CONTAINER_NAME=\"#{service_with_version_and_destination}\"",
"--name", service_with_version_and_destination_and_role,
"-e", "MRSK_CONTAINER_NAME=\"#{service_with_version_and_destination_and_role}\"",
*role.env_args,
*config.volume_args,
*role.label_args,
Expand All @@ -17,7 +24,7 @@ def run(role: :web)
end

def start
docker :start, service_with_version_and_destination
docker :start, service_with_version_and_destination_and_role
end

def stop(version: nil)
Expand Down Expand Up @@ -52,7 +59,7 @@ def follow_logs(host:, grep: nil)
def execute_in_existing_container(*command, interactive: false)
docker :exec,
("-it" if interactive),
service_with_version_and_destination,
service_with_version_and_destination_and_role,
*command
end

Expand Down Expand Up @@ -97,7 +104,7 @@ def list_container_names

def remove_container(version:)
pipe \
container_id_for(container_name: service_with_version_and_destination(version)),
container_id_for(container_name: service_with_version_and_destination_and_role(version)),
xargs(docker(:container, :rm))
end

Expand All @@ -115,12 +122,12 @@ def remove_images


private
def service_with_version_and_destination(version = nil)
[ config.service, config.destination, version || config.version ].compact.join("-")
def service_with_version_and_destination_and_role(version = nil)
[ config.service, role, config.destination, version || config.version ].compact.join("-")
end

def container_id_for_version(version)
container_id_for(container_name: service_with_version_and_destination(version))
container_id_for(container_name: service_with_version_and_destination_and_role(version))
end

def filter_args
Expand All @@ -130,6 +137,7 @@ def filter_args
def filters
[ "label=service=#{config.service}" ].tap do |filters|
filters << "label=destination=#{config.destination}" if config.destination
filters << "label=role=#{role}" if role
end
end
end
19 changes: 17 additions & 2 deletions lib/mrsk/commands/auditor.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
require "active_support/core_ext/time/conversions"

class Mrsk::Commands::Auditor < Mrsk::Commands::Base
attr_reader :role

def initialize(config, role: nil)
super(config)
@role = role
end

# Runs remotely
def record(line)
append \
Expand All @@ -25,11 +32,15 @@ def audit_log_file
end

def tagged_record_line(line)
"'#{recorded_at_tag} #{performer_tag} #{line}'"
quote [recorded_at_tag, performer_tag, role_tag, line].compact.join(" ")
end

def tagged_broadcast_line(line)
"'#{performer_tag} #{line}'"
quote [performer_tag, role_tag, line].compact.join(" ")
end

def role_tag
"[#{role}]" if role
end

def performer_tag
Expand All @@ -39,4 +50,8 @@ def performer_tag
def recorded_at_tag
"[#{Time.now.to_fs(:db)}]"
end

def quote(tagged_line)
"'#{tagged_line}'"
end
end
4 changes: 2 additions & 2 deletions lib/mrsk/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ def accessory(name)


def all_hosts
roles.flat_map(&:hosts)
roles.flat_map(&:hosts).uniq
end

def primary_web_host
role(:web).primary_host
end

def traefik_hosts
roles.select(&:running_traefik?).flat_map(&:hosts)
roles.select(&:running_traefik?).flat_map(&:hosts).uniq
end


Expand Down
Loading