class QB::IPC::RPC::Server

@todo document Server class.

Constants

CONTENT_TYPE_JSON

Constants

Attributes

http_server[R]

TODO document `http_server` attribute.

@return [Unicorn::HTTPServer]

socket_dir[R]

Temp dir where the socket goes.

@return [Pathname]

socket_path[R]

Absolute path to the socket file.

@return [Pathname]

Public Class Methods

instance() click to toggle source

@todo Document instance method.

@param [type] arg_name

@todo Add name param description.

@return [return_type]

@todo Document return value.
# File lib/qb/ipc/rpc/server.rb, line 66
def self.instance
  @instance
end
new(unicorn_log_level: :warn) click to toggle source

Instantiate a new `Server`, which wraps a {::Unicorn::HttpServer} instance.

@param [::Symbol] unicorn_log_level

Log level for the Unicorn HTTP server. Defaults to `:warn` so that we 
don't see it's info log in `qb` `STDOUT`.
# File lib/qb/ipc/rpc/server.rb, line 135
def initialize unicorn_log_level: :warn
  @socket_dir = Dir.mktmpdir( 'qb-ipc-rpc' ).to_pn
  @socket_path = socket_dir + 'socket'
  
  unicorn_logger = NRSER::Log[ "#{ self.class.name }#http_server" ]
  unicorn_logger.level = unicorn_log_level
  
  @http_server = ::Unicorn::HttpServer.new self,
    listeners: socket_path.to_s,
    logger: unicorn_logger
end
run_around(&block) click to toggle source

@todo Document run! method.

@param [type] arg_name

@todo Add name param description.

@return [return_type]

@todo Document return value.
# File lib/qb/ipc/rpc/server.rb, line 78
def self.run_around &block
  unless ENV[QB::IPC::RPC::ENV_VAR_NAME].to_s == ''
    raise NRSER::ConflictError.new \
      "RPC ENV var already set",
      var_name:   QB::IPC::RPC::ENV_VAR_NAME,
      var_value:  ENV[QB::IPC::RPC::ENV_VAR_NAME]
  end

  @instance = new.start!

  ENV[QB::IPC::RPC::ENV_VAR_NAME] = instance.socket_path.to_s

  begin
    block_result = block.call
  ensure
    instance.stop!
    @instance = nil
    ENV.delete QB::IPC::RPC::ENV_VAR_NAME
  end

  block_result
end

Public Instance Methods

call(env) click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 262
def call env
  begin
    request = Rack::Request.new env
    body = request.body.read
    payload = JSON.load body

    logger.trace "Received request",
      path: request.path,
      body: body,
      payload: payload
    
    route request.path, payload
    
  rescue StandardError => error
    logger.error "Error processing request",
      { request: request },
      error
    
    respond_error message: error.message
  end
end
handle_plugins_filters() click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 202
def handle_plugins_filters
  map = {}
  
  QB::Ansible::Plugins::Filters.methods( false ).each { |method|
    map[method] = {
      receiver: 'QB::Ansible::Plugins::Filters',
      method:   method.to_s,
    }
  }
  
  respond_ok data: map
end
handle_send(payload) click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 216
def handle_send payload
  receiver = payload.fetch 'receiver'
  method = payload.fetch 'method'
  args = payload.fetch 'args', []

  if payload['kwds'] && !payload['kwds'].empty?
    args = [*args, payload['kwds'].to_options]
  end

  logger.trace "Unpacked /send payload",
    receiver: receiver,
    method: method,
    args: args

  if  Hash === receiver &&
      receiver.key?( NRSER::Props::DEFAULT_CLASS_KEY )
    
    logger.trace "Loading payload into a " + 
      "#{ receiver[NRSER::Props::DEFAULT_CLASS_KEY] }..."

    receiver = NRSER::Props.UNSAFE_load_instance_from_data receiver
  elsif   String === receiver &&
          receiver =~ /\A(?:(?:\:\:)?[A-Z]\w*)+\z/
    if (const = receiver.to_const)
      receiver = const
    end
  end

  logger.trace "Sending...",
    receiver: receiver,
    method: method,
    args: args

  # For some reason this doesn't work:
  # result = receiver.send method, *args, **kwds
  #
  # So we do this (after conditionally appending kwds up top)
  result = receiver.send method, *args

  logger.trace "Got result, responding",
    result: result

  respond_ok data: result
end
respond(code, values = {}) click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 163
def respond code, values = {}
  [
    code.to_s,
    CONTENT_TYPE_JSON,
    [ values.to_json ]
].tap { |response|
  logger.trace "Responding",
    response: response
}
end
respond_error(code: 500, message: 'Server error') click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 180
def respond_error code: 500, message: 'Server error'
  respond code, message: message
end
respond_not_found(message: 'Not found') click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 185
def respond_not_found message: 'Not found'
  respond 404, message: message
end
respond_ok(values = {}) click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 175
def respond_ok values = {}
  respond 200, values
end
route(path, payload) click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 190
def route path, payload
  case path
  when '/send'
    handle_send payload
  when '/plugins/filters'
    handle_plugins_filters
  else
    respond_not_found
  end
end
start!() click to toggle source

Instance Methods

# File lib/qb/ipc/rpc/server.rb, line 151
def start!
  http_server.start
  self
end
stop!(graceful: true) click to toggle source
# File lib/qb/ipc/rpc/server.rb, line 157
def stop! graceful: true
  http_server.stop graceful
  self
end