class Cult::Commander

Attributes

node[R]
project[R]

Public Class Methods

new(project:, node:) click to toggle source
# File lib/cult/commander.rb, line 12
def initialize(project:, node:)
  @project = project
  @node = node
end

Public Instance Methods

bootstrap!() click to toggle source
# File lib/cult/commander.rb, line 128
def bootstrap!
  bootstrap_role = CLI.fetch_item('bootstrap', from: Role)
  install!(bootstrap_role)
end
connect(user: nil) { |ssh)| ... } click to toggle source
# File lib/cult/commander.rb, line 141
def connect(user: nil, &block)
  5.times do |attempt|
    begin
      user ||= node.user
      puts "Connecting with user=#{user}, host=#{node.host}, " +
           "key=#{node.ssh_private_key_file}"
      Net::SSH.start(node.host,
                     user,
                     port: node.ssh_port,
                     user_known_hosts_file: node.ssh_known_hosts_file,
                     timeout: 5,
                     auth_methods: ['publickey'],
                     keys_only: true,
                     keys: [node.ssh_private_key_file]) do |ssh|
        return (yield ssh)
      end
    rescue Errno::ECONNREFUSED, Net::SSH::ConnectionTimeout
      puts "Connection refused.  Retrying"
      sleep attempt * 3
    end
  end
end
create_build_tar(role) click to toggle source
# File lib/cult/commander.rb, line 31
def create_build_tar(role)
  io = StringIO.new
  Bundle.new(io) do |bundle|
    puts "Building bundle..."
    role.build_order.each do |r|
      (r.artifacts + r.build_tasks).each do |transferable|
        bundle.add_file(project, r, node, transferable)
      end
    end
  end

  io.rewind
  io
end
create_sync_tar(pass:, roles: nil) click to toggle source
# File lib/cult/commander.rb, line 102
def create_sync_tar(pass:, roles: nil)
  io = StringIO.new
  Bundle.new(io) do |bundle|
    find_sync_tasks(pass: pass, roles: roles).each do |task|
      bundle.add_file(project, task.role, node, task)
    end
  end

  io.rewind
  io
end
esc(s) click to toggle source
# File lib/cult/commander.rb, line 17
def esc(s)
  Shellwords.escape(s)
end
exec_remote!(ssh:, role:, task:) click to toggle source
# File lib/cult/commander.rb, line 47
    def exec_remote!(ssh:, role:, task:)
      token = SecureRandom.hex
      task_bin = role.relative_path(task.path)

      puts "Executing: #{task.remote_path} on #{node.name}"
      res = ssh.exec! <<~BASH
        cd #{esc(role.remote_path)}; \
        ./#{esc(task_bin)} && \
        echo #{esc(token)}
      BASH

      if res.chomp.end_with?(token)
        res = res.gsub(token, '')
        puts Rainbow(res.gsub(/^/, '    ')).darkgray.italic
        true
      else
        puts Rainbow(res).red
        puts "Failed"
        false
      end
    end
find_sync_tasks(pass:, roles: nil) click to toggle source
# File lib/cult/commander.rb, line 85
def find_sync_tasks(pass:, roles: nil)
  selected_roles = node.build_order

  if roles
    selected_roles.select! { |r| roles.include?(r) }
  end

  r = []
  selected_roles.each do |role|
    r += role.event_tasks.select do |t|
      t.event == :sync && t.pass == pass
    end
  end
  r
end
install!(role) click to toggle source
# File lib/cult/commander.rb, line 70
def install!(role)
  connect(user: role.user) do |ssh|
    io = create_build_tar(role)
    send_tar(io, ssh)

    role.build_order.each do |r|
      puts "Installing role: #{Rainbow(r.name).blue}"
      r.build_tasks.each do |task|
        exec_remote!(ssh: ssh, role: r, task: task)
      end
    end
  end
end
ping() click to toggle source
# File lib/cult/commander.rb, line 133
def ping
  connect do |ssh|
    ssh.exec!("uptime").chomp
  end
rescue
  nil
end
send_tar(io, ssh) click to toggle source
# File lib/cult/commander.rb, line 22
def send_tar(io, ssh)
  filename = SecureRandom.hex + ".tar"
  puts "Uploading bundle: #{filename}"
  scp = Net::SCP.new(ssh)
  scp.upload!(io, filename)
  ssh.exec! "tar -xf #{esc(filename)} && rm #{esc(filename)}"
end
sync!(pass:, roles: nil) click to toggle source
# File lib/cult/commander.rb, line 115
def sync!(pass:, roles: nil)
  io = create_sync_tar(pass: pass, roles: roles)
  return if io.eof?

  connect do |ssh|
    send_tar(io, ssh)
    find_sync_tasks(pass: pass, roles: roles).each do |task|
      exec_remote!(ssh: ssh, role: task.role, task: task)
    end
  end
end