diff --git a/lib/kamal/commander.rb b/lib/kamal/commander.rb index 19e4c6cd..042e8429 100644 --- a/lib/kamal/commander.rb +++ b/lib/kamal/commander.rb @@ -3,11 +3,13 @@ require "active_support/core_ext/module/delegation" class Kamal::Commander attr_accessor :verbosity, :holding_lock, :hold_lock_on_error + delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :traefik_hosts, :accessory_hosts, to: :specifics def initialize self.verbosity = :info self.holding_lock = false self.hold_lock_on_error = false + @specifics = nil end def config @@ -24,10 +26,12 @@ class Kamal::Commander attr_reader :specific_roles, :specific_hosts def specific_primary! + @specifics = nil self.specific_hosts = [ config.primary_host ] end def specific_roles=(role_names) + @specifics = nil if role_names.present? @specific_roles = Kamal::Utils.filter_specific_items(role_names, config.roles) @@ -40,6 +44,7 @@ class Kamal::Commander end def specific_hosts=(hosts) + @specifics = nil if hosts.present? @specific_hosts = Kamal::Utils.filter_specific_items(hosts, config.all_hosts) @@ -51,39 +56,6 @@ class Kamal::Commander end end - def primary_host - # Given a list of specific roles, make an effort to match up with the primary_role - specific_hosts&.first || specific_roles&.detect { |role| role == config.primary_role }&.primary_host || specific_roles&.first&.primary_host || config.primary_host - end - - def primary_role - roles_on(primary_host).first - 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).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) } - end - - def traefik_hosts - specific_hosts || config.traefik_hosts - end - - def accessory_hosts - specific_hosts || config.accessories.flat_map(&:hosts) - end - def accessory_names config.accessories&.collect(&:name) || [] end @@ -181,4 +153,8 @@ class Kamal::Commander SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs SSHKit.config.output_verbosity = verbosity end + + def specifics + @specifics ||= Kamal::Commander::Specifics.new(config, specific_hosts, specific_roles) + end end diff --git a/lib/kamal/commander/specifics.rb b/lib/kamal/commander/specifics.rb new file mode 100644 index 00000000..88d89a72 --- /dev/null +++ b/lib/kamal/commander/specifics.rb @@ -0,0 +1,49 @@ +class Kamal::Commander::Specifics + attr_reader :primary_host, :primary_role, :hosts, :roles + delegate :stable_sort!, to: Kamal::Utils + + def initialize(config, specific_hosts, specific_roles) + @config, @specific_hosts, @specific_roles = config, specific_hosts, specific_roles + + @roles, @hosts = specified_roles, specified_hosts + + @primary_host = specific_hosts&.first || primary_specific_role&.primary_host || config.primary_host + @primary_role = primary_or_first_role(roles_on(primary_host)) + + stable_sort!(roles) { |role| role == primary_role ? 0 : 1 } + stable_sort!(hosts) { |host| roles_on(host).any? { |role| role == primary_role } ? 0 : 1 } + end + + def roles_on(host) + roles.select { |role| role.hosts.include?(host.to_s) } + end + + def traefik_hosts + specific_hosts || config.traefik_hosts + end + + def accessory_hosts + specific_hosts || config.accessories.flat_map(&:hosts) + end + + private + attr_reader :config, :specific_hosts, :specific_roles + + def primary_specific_role + primary_or_first_role(specific_roles) if specific_roles.present? + end + + def primary_or_first_role(roles) + roles.detect { |role| role == config.primary_role } || roles.first + end + + def specified_roles + (specific_roles || config.roles) \ + .select { |role| ((specific_hosts || config.all_hosts) & role.hosts).any? } + end + + def specified_hosts + (specific_hosts || config.all_hosts) \ + .select { |host| (specific_roles || config.roles).flat_map(&:hosts).include?(host) } + end +end diff --git a/lib/kamal/utils.rb b/lib/kamal/utils.rb index 0229b8cb..af35edea 100644 --- a/lib/kamal/utils.rb +++ b/lib/kamal/utils.rb @@ -74,4 +74,8 @@ module Kamal::Utils matches end + + def stable_sort!(elements, &block) + elements.sort_by!.with_index { |element, index| [ block.call(element), index ] } + end end diff --git a/test/commander_test.rb b/test/commander_test.rb index 55d6cb1d..9eda1754 100644 --- a/test/commander_test.rb +++ b/test/commander_test.rb @@ -119,9 +119,10 @@ class CommanderTest < ActiveSupport::TestCase configure_with(:deploy_primary_web_role_override) @kamal.specific_roles = [ "web_*" ] - assert_equal [ "web_chicago", "web_tokyo" ], @kamal.roles.map(&:name) + assert_equal [ "web_tokyo", "web_chicago" ], @kamal.roles.map(&:name) assert_equal "web_tokyo", @kamal.primary_role.name assert_equal "1.1.1.3", @kamal.primary_host + assert_equal [ "1.1.1.3", "1.1.1.4", "1.1.1.1", "1.1.1.2" ], @kamal.hosts end private