class Blower::Host

Attributes

address[RW]

The host adress.

data[RW]
name[RW]
user[RW]

The default remote user.

via[RW]

The gateway host

Public Class Methods

new(address, data: {}, user: "root", via: nil, name: address) click to toggle source
Calls superclass method
# File lib/blower/host.rb, line 27
def initialize (address, data: {}, user: "root", via: nil, name: address)
  @address = address
  @name = name
  @user = user
  @data = data
  @via = via
  super()
end

Public Instance Methods

cp(froms, to, as: nil, quiet: false, delete: false) click to toggle source

Copy files or directories to the host. @api private

# File lib/blower/host.rb, line 57
def cp (froms, to, as: nil, quiet: false, delete: false)
  sleep DELAY if defined?(DELAY)
  as ||= @user
  output = ""
  synchronize do
    [froms].flatten.each do |from|
      if from.is_a?(String)
        to += "/" if to[-1] != "/" && from.is_a?(Array)
        command = ["rsync", "-e", ssh_command, "-r"]
        if File.exist?(".blowignore")
          command += ["--exclude-from", ".blowignore"]
        end
        command += ["--delete"] if delete
        command += [*from, "#{as}@#{@address}:#{to}"]
        log.trace command.shelljoin, quiet: quiet
        IO.popen(command, in: :close, err: %i(child out)) do |io|
          until io.eof?
            begin
              output << io.read_nonblock(100)
            rescue IO::WaitReadable
              IO.select([io])
              retry
            end
          end
          io.close
          if !$?.success?
            log.fatal "exit status #{$?.exitstatus}: #{command}", quiet: quiet
            log.fatal output, quiet: quiet
            fail "failed to copy files"
          end
        end
      elsif from.respond_to?(:read)
        cmd = "echo #{Base64.strict_encode64(from.read).shellescape} | base64 -d > #{to.shellescape}"
        sh cmd, quiet: quiet
      else
        fail "Don't know how to copy a #{from.class}: #{from}"
      end
    end
  end
  true
end
gateway() click to toggle source

Connect to the host as a Gateway @api private

# File lib/blower/host.rb, line 154
def gateway ()
  Net::SSH::Gateway.new(address, user)
end
log() click to toggle source

Produce a Logger prefixed with the host name. @api private

# File lib/blower/host.rb, line 148
def log
  @log ||= Logger.instance.with_prefix("on #{self}: ")
end
ping() click to toggle source

Attempt to connect to port 22 on the host. @return true @raise If it doesn't connect within 1 second. @api private

# File lib/blower/host.rb, line 45
def ping ()
  log.debug "Pinging"
  Timeout.timeout(1) do
    TCPSocket.new(address, 22).close
  end
  true
rescue Timeout::Error, Errno::ECONNREFUSED
  fail "Failed to ping #{self}"
end
read(filename, as: nil, quiet: false) click to toggle source

Read a host file. @api private

# File lib/blower/host.rb, line 107
def read (filename, as: nil, quiet: false)
  Base64.decode64 sh("cat #{filename.shellescape} | base64", as: as, quiet: quiet)
end
sh(command, as: nil, quiet: false) click to toggle source

Execute a command on the host and return its output. @api private

# File lib/blower/host.rb, line 113
def sh (command, as: nil, quiet: false)
  sleep DELAY if defined?(DELAY)
  as ||= @user
  output = ""
  synchronize do
    log.debug "sh #{command}", quiet: quiet
    result = nil
    ch = ssh(as).open_channel do |ch|
      ch.request_pty do |ch, success|
        "failed to acquire pty" unless success
        ch.exec(command) do |_, success|
          fail "failed to execute command" unless success
          ch.on_data do |_, data|
            log.trace "received #{data.bytesize} bytes stdout", quiet: quiet
            output << data
          end
          ch.on_extended_data do |_, _, data|
            log.trace "received #{data.bytesize} bytes stderr", quiet: quiet
            output << data
          end
          ch.on_request("exit-status") do |_, data|
            result = data.read_long
            log.trace "received exit-status #{result}", quiet: quiet
          end
        end
      end
    end
    ch.wait
    fail FailedCommand, output if result != 0
    output
  end
end
to_s() click to toggle source

Represent the host as a string.

# File lib/blower/host.rb, line 37
def to_s
  "#{@user}@#{@address}"
end
write(string, to, as: nil, quiet: false) click to toggle source

Write a string to a host file. @api private

# File lib/blower/host.rb, line 101
def write (string, to, as: nil, quiet: false)
  cp StringIO.new(string), to, as: as, quiet: quiet
end

Private Instance Methods

ssh(user) click to toggle source
# File lib/blower/host.rb, line 170
def ssh (user)
  @sessions ||= {}
  if @sessions[user] && @sessions[user].closed?
    log.warn "Discovered the connection to ssh:#{self} was lost"
    @sessions[user] = nil
  end
  @sessions[user] ||= begin
    if @via
      log.debug "Connecting to ssh:#{self} via ssh:#{via}"
      via.gateway.ssh(address, user)
    else
      log.debug "Connecting to ssh:#{self}"
      Timeout.timeout(5) do
        Net::SSH.start(address, user)
      end
    end
  end
end
ssh_command() click to toggle source
# File lib/blower/host.rb, line 162
def ssh_command
  if via
    "ssh -t -A -oStrictHostKeyChecking=no #{via} ssh -oStrictHostKeyChecking=no"
  else
    "ssh -oStrictHostKeyChecking=no"
  end
end