module Vanagon::Utilities

Public Instance Methods

erb_file(erbfile, outfile = nil, remove_orig = false, opts = { :binding => binding }) click to toggle source

Helper method that takes a template and writes the evaluated contents to a file on disk

@param erbfile [String] @param outfile [String] @param remove_orig [true, false] @param opts [Hash]

# File lib/vanagon/utilities.rb, line 305
def erb_file(erbfile, outfile = nil, remove_orig = false, opts = { :binding => binding })
  outfile ||= File.join(Dir.mktmpdir, File.basename(erbfile).sub(File.extname(erbfile), ""))
  output = erb_string(erbfile, opts[:binding])
  File.open(outfile, 'w') { |f| f.write output }
  VanagonLogger.info "Generated: #{outfile}"
  FileUtils.rm_rf erbfile if remove_orig
  outfile
end
erb_string(erbfile, b = binding) click to toggle source

Helper method that takes a template file and runs it through ERB

@param erbfile [String] template to be evaluated @param b [Binding] binding to evaluate the template under @return [String] the evaluated template

# File lib/vanagon/utilities.rb, line 291
def erb_string(erbfile, b = binding)
  template = File.read(erbfile)
  message  = ERB.new(template, trim_mode: "-")
  message.result(b)
    .gsub(/[\n]+{3,}/, "\n\n")
    .squeeze("\s")
end
ex(command) click to toggle source

Similar to rake’s sh, the passed command will be executed and an exception will be raised on command failure. However, in contrast to rake’s sh, this method returns the output of the command instead of a boolean.

@param command [String] The command to be executed @return [String] The standard output of the executed command @raise [Vanagon::Error] If the command fails an exception is raised

# File lib/vanagon/utilities.rb, line 121
def ex(command)
  ret = %x(#{command})
  unless $CHILD_STATUS.success?
    raise Vanagon::Error, "'#{command}' did not succeed"
  end
  ret
end
find_program_on_path(command, required = true) click to toggle source

Similar to the command-line utility which, the method will search the PATH for the passed command and return the full path to the command if it exists.

@param command [String] Command to search for on PATH @param required [true, false] Whether or not to raise an exception if the command cannot be found @return [String, false] Returns either the full path to the command or false if the command cannot be found @raise [RuntimeError] If the command is required and cannot be found

# File lib/vanagon/utilities.rb, line 137
def find_program_on_path(command, required = true)
  extensions = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path_elem|
    extensions.each do |ext|
      location = File.join(path_elem, "#{command}#{ext}")
      return location if FileTest.executable?(location)
    end
  end

  if required
    fail "Could not find '#{command}'. Please install (or ensure it is on $PATH), and try again."
  else
    return false
  end
end
Also aliased as: which
get_md5sum(file) click to toggle source

Utility to get the md5 sum of a file

@deprecated Please use get_sum instead, this will be removed in a future vanagon release. @param file [String] file to md5sum @return [String] md5sum of the given file

# File lib/vanagon/utilities.rb, line 24
def get_md5sum(file)
  get_sum(file, 'md5')
end
get_sum(file, type = 'md5') click to toggle source

Generic file summing utility

@param file [String] file to sum @param type [String] type of sum to provide, defaults to md5 @return [String] sum of the given file @raise [RuntimeError] raises an exception if the given sum type is not supported

# File lib/vanagon/utilities.rb, line 34
def get_sum(file, type = 'md5')
  Digest.const_get(type.upcase).file(file).hexdigest.to_s

# If Digest::const_get fails, it'll raise a LoadError when it tries to
# pull in the subclass `type`. We catch that error, and fail instead.
rescue LoadError
  fail "Don't know how to produce a sum of type: '#{type}' for '#{file}'"
end
http_request(url, type, payload = {}.to_json, header = nil) click to toggle source

uses http_request_generic and returns the body as parsed by JSON. @param url [String] The url to make the request against (needs to be parsable by URI @param type [String] One of the supported request types (currently ‘get’, ‘post’, ‘delete’) @param payload [String] The request body data payload used for POST and PUT @param header [Hash] Send additional information in the HTTP request header @return [Hash] The response in JSON format @raise [RuntimeError, Vanagon::Error] an exception is raised if the response body cannot be parsed as JSON

# File lib/vanagon/utilities.rb, line 95
def http_request(url, type, payload = {}.to_json, header = nil)
  response = http_request_generic(url, type, payload, header)
  JSON.parse(response.body)
rescue JSON::ParserError => e
  raise Vanagon::Error.wrap(e, "#{url} handed us a response that doesn't look like JSON.")
end
http_request_code(url, type, payload = {}.to_json, header = nil) click to toggle source

uses http_request_generic and returns the response code. @param url [String] The url to make the request against (needs to be parsable by URI @param type [String] One of the supported request types (currently ‘get’, ‘post’, ‘delete’) @param payload [String] The request body data payload used for POST and PUT @param header [Hash] Send additional information in the HTTP request header @return [String] The response code eg 202, 200 etc

# File lib/vanagon/utilities.rb, line 108
def http_request_code(url, type, payload = {}.to_json, header = nil)
  response = http_request_generic(url, type, payload, header)
  response.code
end
http_request_generic(url, type, payload = {}.to_json, header = nil) click to toggle source

Simple wrapper around Net::HTTP. Will make a request of the given type to the given url and return the response object

@param url [String] The url to make the request against (needs to be parsable by URI @param type [String] One of the supported request types (currently ‘get’, ‘post’, ‘delete’) @param payload [String] The request body data payload used for POST and PUT @param header [Hash] Send additional information in the HTTP request header @return [Net::HTTPAccepted] The response object @raise [RuntimeError, Vanagon::Error] an exception is raised if the action is not supported, or if there is a problem with the http request

# File lib/vanagon/utilities.rb, line 53
def http_request_generic(url, type, payload = {}.to_json, header = nil) # rubocop:disable Metrics/AbcSize
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true if uri.scheme == 'https'

  case type.downcase
  when "get"
    request = Net::HTTP::Get.new(uri.request_uri)
  when "post"
    request = Net::HTTP::Post.new(uri.request_uri)
    request.body = payload
  when "put"
    request = Net::HTTP::Put.new(uri.request_uri)
    request.body = payload
  when "delete"
    request = Net::HTTP::Delete.new(uri.request_uri)
  else
    fail "ACTION: #{type} not supported by #http_request method. Maybe you should add it?"
  end

  # Add any headers to the request
  if header && header.is_a?(Hash)
    header.each do |key, val|
      request[key] = val
    end
  end

  http.request(request)
rescue Errno::ETIMEDOUT, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
  EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
  Net::ProtocolError => e
  raise Vanagon::Error.wrap(e, "Problem reaching #{url}. Is #{uri.host} down?")
end
local_command(command, return_command_output: false) click to toggle source

Runs the command on the local host

@param command [String] command to run locally @param return_command_output [Boolean] whether or not command output should be returned @return [true, String] Returns true if the command was successful or the

output of the command if return_command_output is true

@raise [RuntimeError] If the command fails an exception is raised

# File lib/vanagon/utilities.rb, line 263
def local_command(command, return_command_output: false)
  clean_environment do
    VanagonLogger.info "Executing '#{command}' locally"
    if return_command_output
      ret = %x(#{command}).chomp
      if $CHILD_STATUS.success?
        return ret
      else
        raise "Local command (#{command}) failed."
      end
    else
      Kernel.system(command)
      $CHILD_STATUS.success? or raise "Local command (#{command}) failed."
    end
  end
end
remote_ssh_command(target, command, port = 22, return_command_output: false) click to toggle source

Runs the command on the given host via ssh call

@param target [String] ssh host to run command on (user@machine) @param command [String] command to run on the target @param port [Integer] port number for ssh (default 22) @param return_command_output [Boolean] whether or not command output should be returned @return [true, String] Returns true if the command was successful or the

output of the command if return_command_output is true

@raise [RuntimeError] If there is no target given or the command fails an exception is raised

# File lib/vanagon/utilities.rb, line 241
def remote_ssh_command(target, command, port = 22, return_command_output: false)
  VanagonLogger.info "Executing '#{command}' on '#{target}'"
  if return_command_output
    ret = %x(#{ssh_command(port)} -T #{target} '#{command.gsub("'", "'\\\\''")}').chomp
    if $CHILD_STATUS.success?
      return ret
    else
      raise "Remote ssh command (#{command}) failed on '#{target}'."
    end
  else
    Kernel.system("#{ssh_command(port)} -T #{target} '#{command.gsub("'", "'\\\\''")}'")
    $CHILD_STATUS.success? or raise "Remote ssh command (#{command}) failed on '#{target}'."
  end
end
retry_with_timeout(tries = 5, timeout = 1) { || ... } click to toggle source

Method to retry a ruby block and fail if the command does not succeed within the number of tries and timeout.

@param tries [Integer] number of times to try calling the block @param timeout [Integer] number of seconds to run the block before timing out @return [true] If the block succeeds, true is returned @raise [Vanagon::Error] if the block fails after the retries are exhausted, an error is raised

# File lib/vanagon/utilities.rb, line 161
def retry_with_timeout(tries = 5, timeout = 1, &blk)
  error = nil
  tries.to_i.times do
    Timeout::timeout(timeout.to_i) do
      begin
        yield
        return true
      rescue StandardError => e
        VanagonLogger.error 'An error was encountered evaluating block. Retrying..'
        error = e
      end
    end
  end

  message = "Block failed maximum number of #{tries} tries"
  unless error.nil?
    message += "\n with error #{error.message}" + "\n#{error.backtrace.join("\n")}"
  end
  message += "\nExiting..."
  raise error, message unless error.nil?
  raise Vanagon::Error, "Block failed maximum number of #{tries} tries"
end
rsync_from(source, target, dest, port = 22, extra_flags = []) click to toggle source

Retrieves the desired file/directory from the destination using rsync

@param source [String] path on target to retrieve from @param target [String] ssh host to retrieve from (user@machine) @param dest [String] path on local host to place the source @param port [Integer] port number for ssh (default 22) @param extra_flags [Array] any additional flags to pass to rsync @return [String] output of rsync command

# File lib/vanagon/utilities.rb, line 223
def rsync_from(source, target, dest, port = 22, extra_flags = [])
  rsync = find_program_on_path('rsync')
  flags = "-rHlv -O --no-perms --no-owner --no-group"
  unless extra_flags.empty?
    flags << " " << extra_flags.join(" ")
  end
  ex("#{rsync} -e '#{ssh_command(port)}' #{flags} #{target}:#{source} #{dest}")
end
rsync_to(source, target, dest, port = 22, extra_flags = ["--ignore-existing"]) click to toggle source

Sends the desired file/directory to the destination using rsync

@param source [String] file or directory to send @param target [String] ssh host to send to (user@machine) @param dest [String] path on target to place the source @param extra_flags [Array] any additional flags to pass to rsync @param port [Integer] Port number for ssh (default 22) @return [String] output of rsync command

# File lib/vanagon/utilities.rb, line 192
def rsync_to(source, target, dest, port = 22, extra_flags = ["--ignore-existing"])
  rsync = find_program_on_path('rsync')
  flags = "-rHlv --no-perms --no-owner --no-group"
  unless extra_flags.empty?
    flags << " " << extra_flags.join(" ")
  end
  ex("#{rsync} -e '#{ssh_command(port)}' #{flags} #{source} #{target}:#{dest}")
end
ssh_command(port = 22) click to toggle source

Hacky wrapper to add on the correct flags for ssh to be used in ssh and rsync methods

@param port [Integer] Port number for ssh (default 22) @return [String] start of ssh command, including flags for ssh keys

# File lib/vanagon/utilities.rb, line 205
def ssh_command(port = 22)
  ssh = find_program_on_path('ssh')
  args = ENV['VANAGON_SSH_KEY'] ? " -i #{ENV['VANAGON_SSH_KEY']}" : ""
  args << " -p #{port} "
  args << " -o UserKnownHostsFile=/dev/null"
  args << " -o StrictHostKeyChecking=no"
  args << " -o ForwardAgent=yes" if ENV['VANAGON_SSH_AGENT']
  return ssh + args
end
which(command, required = true)

Private Instance Methods

clean_environment() { || ... } click to toggle source
# File lib/vanagon/utilities.rb, line 280
def clean_environment(&block)
  return Bundler.with_clean_env(&block) if defined?(Bundler)
  yield
end