class Lunanode::API

Class for accessing the Lunanode API.

The class is instantiated by either passing it a JSON file containing the keys `api_id` and `api_key`, or specifying the ID and key directly:

api = Lunanode::API.new("credentials_file.json")
api = Lunanode::API.new(api_id: "ABCDEFG", api_key: "HIJKLMNOP")

Once instantiated, action methods follow the example:

api.vm_list # no parameters
api.vm_info(vm_id: "My-VM-ID") # required parameters
api.image_list(region: "Toronto") # optional parameters

Required and optional arguments are specified in the method definitions, but can can be queried via {API.params_for}. They follow the API specification atwiki.lunanode.com/index.php/API

Constants

API_ENDPOINT

Class-level definitions

Attributes

api_id[R]
api_key[R]
rest_client[R]

Public Class Methods

new(*args, **options) click to toggle source

Instantiate an API object

@return [API] an API instance.

@overload initialize(credentials_file)

Instantiate an API object from a credentials file.
@param credentials_file[String,File] a JSON credentials file
  (which contains the keys `api_id` and `api_key`)

@raise [JSON::ParserError] if the JSON file could not be read properly.
@raise [KeyError] if any required key could not be found.

@overload initialize(api_id: , api_key: )

Instantiate an API object from an API ID and API Key.
@param api_id[String] A LunaNode API ID
@param api_key[String] A LunaNode API Key

@raise [KeyError] if any required key could not be found.
# File lib/lunanode/api.rb, line 81
def initialize(*args, **options)
  credentials_file = args.compact.first.to_s
  if File.exist?(credentials_file)
    credentials_data = File.read(credentials_file)
    options = JSON.parse(credentials_data, symbolize_names: true)
  end
  @api_id = options.fetch(:api_id).to_s.dup.freeze
  @api_key = options.fetch(:api_key).to_s.dup.freeze
  @rest_client = RestClient::Resource.new(
    API_ENDPOINT,
    verify_ssl: OpenSSL::SSL::VERIFY_PEER
  )
end
params_for(method_name) click to toggle source

Show parameter info for any {API} instance method.

The keys of the hash results denote the type of parameter:

  • :keyreq => Required keyword argument

  • :key => Optional keyword argument

  • :keyrest => Arbitrary additional keyword arguments

  • :req => Required positional argument

  • :opt => Optional positional argument

  • :rest => Arbitrary additional positional arguments

@param method_name The name of the API method @return [Hash] information about the method parameters.

# File lib/lunanode/api.rb, line 44
def self.params_for(method_name)
  method_name = method_name.to_sym
  @params_for[method_name] ||= begin
    param_groups = instance_method(method_name).parameters.group_by(&:first)
    out = param_groups.map do |status, param_def|
      [status, param_def.map(&:last)]
    end
    out.to_h.each_value(&:freeze).freeze
  end
end

Public Instance Methods

action(category, action, **params) click to toggle source

Send an arbitrary API action without any parameter checks.

@param category The API action category (i.e. dns, image, vm…) @param action The API action name (i.e. create, list…) @param params Any parameters required for the action.

@return [Hash,Array,String] Response data from the API action. @raise [APIError] If there was a problem with the action.

# File lib/lunanode/api.rb, line 110
def action(category, action, **params)
  resp = call_api("#{category}/#{action}/", params)
  raise APIError, resp[:error] unless resp.delete(:success) == "yes"

  if resp.size == 1
    resp.values.first
  else
    resp
  end
end
inspect() click to toggle source

Display a string representation of the object without key information.

# File lib/lunanode/api.rb, line 96
def inspect
  "#<#{self.class.name}: api_id=#{api_id.inspect}>"
end
Also aliased as: to_s
to_s()
Alias for: inspect

Private Instance Methods

auth_request_formdata(handler_path, params) click to toggle source

Create signed request data

# File lib/lunanode/api.rb, line 157
def auth_request_formdata(handler_path, params)
  req_msg = raw_request_message(params)
  nonce = gen_nonce
  signature = gen_signature(handler_path, req_msg, nonce)
  {
    req: req_msg,
    signature: signature,
    nonce: nonce,
  }
end
call_api(handler_path, params) click to toggle source

Make an API call and return response.

# File lib/lunanode/api.rb, line 126
def call_api(handler_path, params)
  req_url = "#{rest_client.url}#{handler_path}"
  req_formdata = auth_request_formdata(handler_path, clean_params(params))

  if $DEBUG
    STDERR.puts "call_api()\n" + JSON.pretty_generate(
      path: req_url,
      req: JSON.parse(req_formdata[:req]),
      signature: req_formdata[:signature],
      nonce: req_formdata[:nonce]
    )
  end

  response = rest_client[handler_path].post(req_formdata)
  JSON.parse(response, symbolize_names: true)
rescue RestClient::Exception => err
  err.message += "\n  Request Path: #{req_url}" \
                 "\n  Request Data: #{req_formdata}"
  raise err
end
clean_params(params) click to toggle source

Clean empty request parameters

# File lib/lunanode/api.rb, line 148
def clean_params(params)
  params.each_with_object({}) do |(param, value), acc|
    next if param.nil? || value.nil?
    next unless param.respond_to?(:to_sym) && value.respond_to?(:to_s)
    acc[param.to_sym] = value.to_s
  end
end
gen_nonce() click to toggle source

Generate nonce for request

# File lib/lunanode/api.rb, line 176
def gen_nonce
  Time.now.utc.to_i.to_s
end
gen_signature(handler_path, req_msg, nonce) click to toggle source

Generate signature for request

# File lib/lunanode/api.rb, line 181
def gen_signature(handler_path, req_msg, nonce)
  message = "#{handler_path}|#{req_msg}|#{nonce}"
  OpenSSL::HMAC.hexdigest("SHA512", api_key, message)
end
raw_request_message(params) click to toggle source

Generate raw, unsigned request message.

# File lib/lunanode/api.rb, line 169
def raw_request_message(params)
  params[:api_id] = api_id
  params[:api_partialkey] = api_key.slice(0, 64)
  JSON.generate(params)
end