class SupplyDrop::Rsync

Public Class Methods

command(from, to, options={}) click to toggle source
# File lib/supply_drop/rsync.rb, line 4
def command(from, to, options={})
  flags = ['-az']
  flags << '--delete' if options[:delete]
  flags << excludes(options[:excludes]) if options.has_key?(:excludes)
  flags << ssh_options(options[:ssh]) if options.has_key?(:ssh)

  "rsync #{flags.compact.join(' ')} #{from} #{to}"
end
excludes(patterns) click to toggle source
# File lib/supply_drop/rsync.rb, line 18
def excludes(patterns)
  [patterns].flatten.map { |p| "--exclude=#{p}" }
end
remote_address(user, host, path) click to toggle source
# File lib/supply_drop/rsync.rb, line 13
def remote_address(user, host, path)
  user_with_host = [user, host].compact.join('@')
  [user_with_host, path].join(':')
end
ssh_options(options) click to toggle source

Convert Net::SSH options into OpenSSH options.

For a list of the options normally support by Net::SSH (and thus Capistrano), see net-ssh.github.com/net-ssh/classes/Net/SSH.html#method-c-start

Also, to see how Net::SSH does the opposite of the conversion we are doing here, check out: github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/config.rb

API mismatch:

  • many OpenSSH options not supported

  • some options only make sense for Net::SSH

  • compression: for Net::SSH, this option is supposed to accept true, false, or algorithm. OpenSSH accepts 'yes' or 'no'

# File lib/supply_drop/rsync.rb, line 37
def ssh_options(options)
  mapped_options = options.map do |key, value|
    next if value.nil?

    case key
    when :auth_methods            then opt_auth_methods(value)
    when :bind_address            then opt('BindAddress', value)
    when :compression             then opt('Compression', value ? 'yes' : 'no')
    when :compression_level       then opt('CompressionLevel', value.to_i)
    when :config                  then value ? "-F '#{value}'" : nil
    when :encryption              then opt('Ciphers', [value].flatten.join(','))
    when :forward_agent           then opt('ForwardAgent', value)
    when :global_known_hosts_file then opt('GlobalKnownHostsFile', value)
    when :hmac                    then opt('MACs', [value].flatten.join(','))
    when :host_key                then opt('HostKeyAlgorithms', [value].flatten.join(','))
    when :host_key_alias          then opt('HostKeyAlias', value)
    when :host_name               then opt('HostName', value)
    when :kex                     then opt('KexAlgorithms', [value].flatten.join(','))
    when :key_data                then nil # not supported
    when :keys                    then [value].flatten.select { |k| File.exist?(k) }.map { |k| "-i '#{k}'" }
    when :keys_only               then opt('IdentitiesOnly', value ? 'yes' : 'no')
    when :languages               then nil # not applicable
    when :logger                  then nil # not applicable
    when :paranoid                then opt('StrictHostKeyChecking', value ? 'yes' : 'no')
    when :passphrase              then nil # not supported
    when :password                then nil # not supported
    when :port                    then "-p #{value.to_i}"
    when :properties              then nil # not applicable
    when :proxy                   then nil # not applicable
    when :rekey_blocks_limit      then nil # not supported
    when :rekey_limit             then opt('RekeyLimit', reverse_interpret_size(value))
    when :rekey_packet_limit      then nil # not supported
    when :timeout                 then opt('ConnectTimeout', value.to_i)
    when :user                    then "-l #{value}"
    when :user_known_hosts_file   then multi_opt('UserKnownHostsFile', value)
    when :verbose                 then opt('LogLevel', interpret_log_level(value))
    end
  end.compact

  %[-e "ssh #{mapped_options.join(' ')}"] unless mapped_options.empty?
end

Private Class Methods

interpret_log_level(level) click to toggle source
# File lib/supply_drop/rsync.rb, line 140
def interpret_log_level(level)
  if level.is_a? Symbol
    case level
      when :debug then "DEBUG"
      when :info then "INFO"
      when :warn then "ERROR"
      when :error then "ERROR"
      when :fatal then "FATAL"
      else "INFO"
    end
  elsif level.is_a? Integer
    case level
      when Logger::DEBUG then "DEBUG"
      when Logger::INFO then "INFO"
      when Logger::WARN then "ERROR"
      when Logger::ERROR then "ERROR"
      when Logger::FATAL then "FATAL"
      else "INFO"
    end
  else
    "INFO"
  end
end
multi_opt(option_name, option_values) click to toggle source
# File lib/supply_drop/rsync.rb, line 85
def multi_opt(option_name, option_values)
  [option_values].flatten.map do |value|
    opt(option_name, value)
  end.join(' ')
end
opt(option_name, option_value) click to toggle source
# File lib/supply_drop/rsync.rb, line 81
def opt(option_name, option_value)
  "-o #{option_name}='#{option_value}'"
end
opt_auth_methods(value) click to toggle source

In OpenSSH, password and pubkey default to 'yes', hostbased defaults to 'no'. Regardless, if :auth_method is configured, then we explicitly disable methods not included.

# File lib/supply_drop/rsync.rb, line 95
def opt_auth_methods(value)
  value = [value].flatten
  opts = []
  if value.any?
    if value.include? 'password'
      opts << opt('PasswordAuthentication', 'yes')
    else
      opts << opt('PasswordAuthentication', 'no')
    end
    if value.include? 'publickey'
      opts << opt('PubkeyAuthentication', 'yes')
    else
      opts << opt('PubkeyAuthentication', 'no')
    end
    if value.include? 'hostbased'
      opts << opt('HostbasedAuthentication', 'yes')
    else
      opts << opt('HostbasedAuthentication', 'no')
    end
  end
  if opts.any?
    return opts.join(' ')
  else
    nil
  end
end
reverse_interpret_size(size) click to toggle source

Converts the given integer size in bytes into a string with 'K', 'M', 'G' suffix, as appropriate.

reverse of interpret_size in github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/config.rb

# File lib/supply_drop/rsync.rb, line 127
def reverse_interpret_size(size)
  size = size.to_i
  if size < 1024
    "#{size}"
  elsif size < 1024 * 1024
    "#{size/1024}K"
  elsif size < 1024 * 1024 * 1024
    "#{size/(1024*1024)}M"
  else
    "#{size/(1024*1024*1024)}G"
  end
end