Module: SSHKitDslRoles

Defined in:
lib/kamal/sshkit_with_ext.rb

Instance Method Summary collapse

Instance Method Details

#on_roles(roles, hosts:, parallel: true, rolling: false, &block) ⇒ Object

Execute on hosts grouped by role.

Unlike on() which deduplicates hosts, this allows the same host to have multiple concurrent connections when it appears in multiple roles.

Options:

hosts: The hosts to run on (required)
parallel: When true, each role runs in its own thread with separate
        connections. When false, hosts run in parallel but roles on each
        host run sequentially (default: true)
rolling:  When true, each role's own `boot` config paces its hosts through
        the SSHKit runner, so one role can boot serially while its
        siblings still boot in parallel. Only for commands that start
        containers — it would needlessly serialize read-only commands.
        Requires parallel: there is no per-role runner otherwise.

Example:

on_roles(roles) do |host, role|
# deploy role to host
end


261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/kamal/sshkit_with_ext.rb', line 261

def on_roles(roles, hosts:, parallel: true, rolling: false, &block)
  if parallel
    threads = roles.filter_map do |role|
      if (role_hosts = role.hosts & hosts).any?
        Thread.new do
          on(role_hosts, rolling ? role.boot_runner_options(role_hosts) : {}) { |host| instance_exec(host, role, &block) }
        rescue StandardError => e
          raise SSHKit::Runner::ExecuteError.new(e), "Exception while executing on #{role}: #{e.message}"
        end
      end
    end

    exceptions = []
    threads.each do |t|
      begin
        t.join
      rescue SSHKit::Runner::ExecuteError => e
        exceptions << e
      end
    end

    if exceptions.one?
      raise exceptions.first
    elsif exceptions.many?
      raise exceptions.first, [ "Exceptions on #{exceptions.count} roles:", exceptions.map(&:message) ].join("\n")
    end
  else
    # Host-first iteration: hosts run in parallel, roles on each host run sequentially
    on(hosts) do |host|
      roles.each do |role|
        instance_exec(host, role, &block) if role.hosts.include?(host.to_s)
      end
    end
  end
end