Class: VagrantPlugins::Skytap::API::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-skytap/api/client.rb

Constant Summary collapse

MAX_RATE_LIMIT_RETRIES =
3
DEFAULT_RETRY_AFTER_SECONDS =
10

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Client

Returns a new instance of Client.


12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/vagrant-skytap/api/client.rb', line 12

def initialize(config)
  @logger = Log4r::Logger.new("vagrant_skytap::api_client")

  @config = config
  uri = URI.parse(config.base_url)
  @http = Net::HTTP.new(uri.host, uri.port)

  #TODO:NLA Turn this on conditionally based on configuration value.
  @http.verify_mode = OpenSSL::SSL::VERIFY_NONE

  @http.use_ssl = uri.port == 443 || uri.scheme == 'https'
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config


7
8
9
# File 'lib/vagrant-skytap/api/client.rb', line 7

def config
  @config
end

#httpObject (readonly)

Returns the value of attribute http


7
8
9
# File 'lib/vagrant-skytap/api/client.rb', line 7

def http
  @http
end

Instance Method Details

#delete(path, options = {}) ⇒ Object


37
38
39
# File 'lib/vagrant-skytap/api/client.rb', line 37

def delete(path, options={})
  req('DELETE', path, nil, options)
end

#error_string_from_body(resp) ⇒ Object


96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/vagrant-skytap/api/client.rb', line 96

def error_string_from_body(resp)
  resp = resp.body if resp.respond_to?(:body)
  begin
    resp = JSON.load(resp)
    errors = resp['error'] || resp['errors']
    errors = errors.join('; ') if errors.respond_to? :join
  rescue
    # treat non-JSON string as error text
    errors = resp
  end
  errors if errors.present?
end

#get(path, options = {}) ⇒ Object


25
26
27
# File 'lib/vagrant-skytap/api/client.rb', line 25

def get(path, options={})
  req('GET', path, nil, options)
end

#post(path, body = nil, options = {}) ⇒ Object


29
30
31
# File 'lib/vagrant-skytap/api/client.rb', line 29

def post(path, body=nil, options={})
  req('POST', path, body, options)
end

#put(path, body = nil, options = {}) ⇒ Object


33
34
35
# File 'lib/vagrant-skytap/api/client.rb', line 33

def put(path, body=nil, options={})
  req('PUT', path, body, options)
end

#req(method, path, body, options = {}) ⇒ Object

path may optionally include query and fragment parts

options are:

query: A string or hash of the query string
extra_headers: A hash of extra headers

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/vagrant-skytap/api/client.rb', line 46

def req(method, path, body, options={})
  @logger.info("REST API call: #{method} #{path} #{'body: ' + body if body.present?}")

  uri = URI.parse(path)

  if qq = options[:query]
    if qq.is_a?(Hash)
      extra_query = qq.collect{|k,v| [k,v].join('=')}.join('&')
    else
      extra_query = qq.to_s
    end
  end

  if (query = [uri.query, extra_query].compact.join('&')).present?
    path = [uri.path, query].join('?')
  end

  headers = default_headers.merge(options[:extra_headers] || {})
  tries = 0
  retry_after = DEFAULT_RETRY_AFTER_SECONDS
  begin
    tries += 1
    http.send_request(method, URI.encode(path), body, headers).tap do |ret|
      @logger.debug("REST API response: #{ret.body}")
      unless ret.code =~ /^2\d\d/
        raise Errors::DoesNotExist, object_name: "Object '#{path}'" if ret.code == '404'
        error_class = case ret.code
        when '403'
          Errors::Unauthorized
        when '422'
          Errors::UnprocessableEntity
        when '423'
          Errors::ResourceBusy
        when '429'
          retry_after = ret['Retry-After'] || DEFAULT_RETRY_AFTER_SECONDS
          Errors::RateLimited
        else
          Errors::OperationFailed
        end
        raise error_class, err: error_string_from_body(ret)
      end
    end
  rescue Errors::RateLimited => ex
    raise if tries > MAX_RATE_LIMIT_RETRIES
    @logger.info("Rate limited, wil retry in #{retry_after} seconds")
    sleep retry_after.to_f + 0.1
    retry
  end
end