class Bosh::Cpi::Cli

Constants

INVALID_CALL_ERROR_TYPE
KNOWN_RPC_METHODS
RPC_METHOD_TO_RUBY_METHOD
UNKNOWN_ERROR_TYPE

Public Class Methods

new(cpi, logs_string_io, result_io) click to toggle source
# File lib/bosh/cpi/cli.rb, line 38
def initialize(cpi, logs_string_io, result_io)
  @cpi = cpi
  @logs_string_io = logs_string_io
  @result_io = result_io
  @logger = Bosh::Cpi::Logger.new(STDERR)
end

Public Instance Methods

run(json) click to toggle source
# File lib/bosh/cpi/cli.rb, line 45
def run(json)
  begin
    request = JSON.load(json)
  rescue JSON::ParserError => e
    return error_response(INVALID_CALL_ERROR_TYPE, "Request cannot be deserialized, details: #{e.message}", false, e.backtrace)
  end

  method = request['method']
  unless method.is_a?(String)
    return error_response(INVALID_CALL_ERROR_TYPE, "Method must be a String, got: '#{method.inspect}'", false)
  end

  unless KNOWN_RPC_METHODS.include?(method)
    return error_response(Bosh::Clouds::NotImplemented.name, "Method is not known, got: '#{method}'", false)
  end

  arguments = request['arguments']
  unless arguments.is_a?(Array)
    return error_response(INVALID_CALL_ERROR_TYPE, "Arguments must be an Array", false)
  end

  context = request['context']
  unless context.is_a?(Hash) && context['director_uuid'].is_a?(String)
    return error_response(INVALID_CALL_ERROR_TYPE, "Request should include context with director uuid", false)
  end

  req_id = context['request_id']
  @logger.set_request_id(req_id)

  configure_director(context['director_uuid'])

  ruby_method = RPC_METHOD_TO_RUBY_METHOD[method] || method

  begin
    start_time = Time.now.utc
    @logger.info("Starting #{method}...")

    cpi = @cpi.call(context)

    result = cpi.public_send(ruby_method, *arguments)
  rescue Bosh::Clouds::RetriableCloudError => e
    return error_response(error_name(e), e.message, e.ok_to_retry, e.backtrace)
  rescue Bosh::Clouds::CloudError, Bosh::Clouds::CpiError => e
    return error_response(error_name(e), e.message, false, e.backtrace)
  rescue ArgumentError => e
    return error_response(INVALID_CALL_ERROR_TYPE, "Arguments are not correct, details: '#{e.message}'", false, e.backtrace)
  rescue Exception => e
    return error_response(UNKNOWN_ERROR_TYPE, e.message, false, e.backtrace)
  ensure
    end_time = Time.now.utc
    @logger.info("Finished #{method} in #{(end_time - start_time).round(2)} seconds")
  end

  result_response(result)
end

Private Instance Methods

configure_director(director_uuid) click to toggle source
# File lib/bosh/cpi/cli.rb, line 103
def configure_director(director_uuid)
  Bosh::Clouds::Config.uuid = director_uuid
end
encode_string_as_utf8(src) click to toggle source
# File lib/bosh/cpi/cli.rb, line 137
def encode_string_as_utf8(src)
  log = @logs_string_io.string.force_encoding(Encoding::UTF_8)
  unless log.valid_encoding?
    # the src encoding hint of Encoding::BINARY is only required for ruby 1.9.3
    log = @logs_string_io.string.encode(Encoding::UTF_8, Encoding::BINARY, undef: :replace, invalid: :replace)
  end
  log
end
error_name(error) click to toggle source
# File lib/bosh/cpi/cli.rb, line 133
def error_name(error)
  error.class.name
end
error_response(type, message, ok_to_retry, bt=[]) click to toggle source
# File lib/bosh/cpi/cli.rb, line 107
def error_response(type, message, ok_to_retry, bt=[])
  if !bt.empty?
    @logs_string_io.print("Rescued #{type}: #{message}. backtrace: #{bt.join("\n")}")
  end

  hash = {
    result: nil,
    error: {
      type: type,
      message: message,
      ok_to_retry: ok_to_retry,
    },
    log: encode_string_as_utf8(@logs_string_io.string)
  }
  @result_io.print(JSON.dump(hash)); nil
end
result_response(result) click to toggle source
# File lib/bosh/cpi/cli.rb, line 124
def result_response(result)
  hash = {
    result: result,
    error: nil,
    log: encode_string_as_utf8(@logs_string_io.string)
  }
  @result_io.print(JSON.dump(hash)); nil
end