Class: CloudQueues::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/cloud-queues/client.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Client

Returns a new instance of Client.



11
12
13
14
15
16
17
18
19
20
# File 'lib/cloud-queues/client.rb', line 11

def initialize(options = {})
  [:username, :api_key].each do |arg|
    raise ArgumentError.new "#{arg} is a required argument." unless options[arg]
  end if options[:token].nil? and options[:tenant].nil?
  
  @client_id = SecureRandom.uuid
  
  options.each_pair {|k, v| instance_variable_set("@#{k}".to_sym, v) }
  authenticate!
end

Instance Attribute Details

#api_hostObject (readonly)

Returns the value of attribute api_host.



9
10
11
# File 'lib/cloud-queues/client.rb', line 9

def api_host
  @api_host
end

#client_idObject

Returns the value of attribute client_id.



4
5
6
# File 'lib/cloud-queues/client.rb', line 4

def client_id
  @client_id
end

#default_regionObject (readonly)

Returns the value of attribute default_region.



8
9
10
# File 'lib/cloud-queues/client.rb', line 8

def default_region
  @default_region
end

#tenantObject

Returns the value of attribute tenant.



6
7
8
# File 'lib/cloud-queues/client.rb', line 6

def tenant
  @tenant
end

#tokenObject

Returns the value of attribute token.



5
6
7
# File 'lib/cloud-queues/client.rb', line 5

def token
  @token
end

Instance Method Details

#authenticate!Object



42
43
44
45
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
# File 'lib/cloud-queues/client.rb', line 42

def authenticate!
  @client = Excon.new("https://identity.api.rackspacecloud.com")
  @base_path = nil

  if @token.nil?
    request = {auth: {"RAX-KSKEY:apiKeyCredentials" => {username: @username, apiKey: @api_key}}}
    response = request(method: :post, path: "/v2.0/tokens", body: request)
    @token = response.body["access"]["token"]["id"]
  else
    # try the current token
    request = {auth: {tenantId: @tenant, token: {id: @token}}}
    response = request(method: :post, path: "/v2.0/tokens", body: request)
  end

  @default_region = response.body["access"]["user"]["RAX-AUTH:defaultRegion"]

  url_type = @internal ? "internalURL" : "publicURL"
  queues = response.body["access"]["serviceCatalog"].select{|service| service["name"] == "cloudQueues" }
  endpoints = queues[0]["endpoints"]

  # default to the account's preferred region
  unless @region
    @region = @default_region
  end

  endpoint = endpoints.select { |endpoint| endpoint["region"] == @region.to_s.upcase }
  raise ArgumentError.new "Region #{@region.to_s.upcase} does not exist!" if endpoint.count == 0
  url = endpoint[0][url_type].split('/')

  @api_host = url[0..2].join('/')
  @base_path = "/" + url[3..-1].join('/')
  @tenant = url[-1]
  
  @client = Excon.new(@api_host, tcp_nodelay: true)
end

#create(name) ⇒ Object



22
23
24
25
# File 'lib/cloud-queues/client.rb', line 22

def create(name)
  request(method: :put, path: "/queues/#{name}", expects: 201)
  Queue.new(self, name)
end

#get(name) ⇒ Object



27
28
29
30
# File 'lib/cloud-queues/client.rb', line 27

def get(name)
  request(method: :head, path: "/queues/#{name}", expects: 204)
  Queue.new(self, name)
end

#queuesObject



32
33
34
35
36
37
38
39
40
# File 'lib/cloud-queues/client.rb', line 32

def queues
  response = request_all("queues", method: :get, path: "/queues", expects: [200, 204])

  return [] if response.status == 204

  response.body["queues"].map do |queue|
    Queue.new(self, queue["name"])
  end
end

#request(options = {}, second_try = []) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/cloud-queues/client.rb', line 78

def request(options = {}, second_try = [])
  if options[:body] and options[:body].class != String
    options[:body] = options[:body].to_json
  end
  
  options[:path] = "#{@base_path}#{options[:path]}" unless options[:path].start_with?(@base_path)
  
  options[:headers] ||= {}
  options[:headers]["Content-Type"] = "application/json" if options[:body]
  options[:headers]["Accept"] = "application/json"
  options[:headers]["Client-ID"] = @client_id
  options[:headers]["X-Auth-Token"] = @token if @token
  options[:headers]["X-Project-ID"] = @tenant
  
  options[:expects] ||= 200
  
  puts options if @debug
  
  begin
    response = @client.request(options)
  rescue Excon::Errors::SocketError => e
    raise unless e.message.include?("EOFError") or second_try.include?(:socketerror)

    # this happens when the server closes the keep-alive socket and
    # Excon doesn't realize it yet.
    @client.reset
    return request(options, second_try + [:socketerror])
  rescue Excon::Errors::Unauthorized => e
    raise if second_try.include?(:unauth) or @token.nil?

    # Our @token probably expired, re-auth and try again
    @token = nil
    authenticate!
    @client.reset # for good measure
    return request(options, second_try + [:unauth])
  end

  begin
    response.body = JSON.load(response.body) if (response.get_header("Content-Type") || "").include?("application/json")
  rescue
    # the api seems to like to give a json content type for a "204 no content" response
  end

  return response
end

#request_all(collection_key, options = {}) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/cloud-queues/client.rb', line 124

def request_all(collection_key, options = {})
  begin
    absolute_limit = options[:query][:limit]
    limit = [absolute_limit, 10].min
    options[:query][:limit] = limit if options[:query][:limit]
  rescue
    absolute_limit = Float::INFINITY
    limit = 10
  end

  first_response = response = request(options)

  if collection_key and first_response.status != 204
    # the next href link will have the query represented in it
    options.delete :query

    collection = first_response.body[collection_key]
    last_links = first_response.body["links"]

    while response.body[collection_key].count >= limit and collection.count < absolute_limit
      next_link = response.body["links"].select{|l| l["rel"] == "next" }[0]["href"]
      options[:path] = set_query_from(options[:path], next_link)

      response = request(options)

      break if response.status == 204
      collection += response.body[collection_key]
      last_links = response.body["links"]
    end

    first_response.body[collection_key] = collection
    first_response.body["links"] = last_links
  end

  return first_response
end