right_api_client 1.5.14 → 1.5.15
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +6 -6
- data/lib/right_api_client/client.rb +163 -132
- data/lib/right_api_client/version.rb +1 -1
- metadata +4 -4
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# CHANGELOG.md
|
2
2
|
|
3
|
+
## 1.5.15
|
4
|
+
- \#50 Ivory 14 02 acu148161 Harden client against spotty networks
|
5
|
+
|
3
6
|
## 1.5.14
|
4
7
|
- \#51 Add type aliases for some Exception subclasses whose name changed; restores interface compatibility with 1.5.9
|
8
|
+
|
5
9
|
## 1.5.13
|
6
10
|
- \#44 Charcoal 13 18 account id in header acu103549
|
7
11
|
- \#42 Salmon 13 17 acu135785 add current url
|
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require File.expand_path('../lib/right_api_client', __FILE__)
|
2
|
+
require 'rubygems'
|
2
3
|
require 'rake'
|
3
|
-
require '
|
4
|
+
require 'rspec/core/rake_task'
|
4
5
|
|
5
6
|
task :build do
|
6
7
|
system "gem build right_api_client.gemspec"
|
@@ -10,8 +11,7 @@ task :release => :build do
|
|
10
11
|
system "gem push right_api_client-#{RightApi::Client::VERSION}.gem"
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
t.
|
15
|
-
t.
|
16
|
-
|
17
|
-
end
|
14
|
+
RSpec::Core::RakeTask.new('spec') do |t|
|
15
|
+
t.pattern= 'spec/**/*_spec.rb'
|
16
|
+
t.rspec_opts = ['--format nested','--colour']
|
17
|
+
end
|
@@ -16,6 +16,10 @@ module RightApi
|
|
16
16
|
class Client
|
17
17
|
include Helper
|
18
18
|
|
19
|
+
DEFAULT_OPEN_TIMEOUT = nil
|
20
|
+
DEFAULT_TIMEOUT = -1
|
21
|
+
DEFAULT_MAX_ATTEMPTS = 5
|
22
|
+
|
19
23
|
ROOT_RESOURCE = '/api/session'
|
20
24
|
ROOT_INSTANCE_RESOURCE = '/api/session/instance'
|
21
25
|
DEFAULT_API_URL = 'https://my.rightscale.com'
|
@@ -23,10 +27,10 @@ module RightApi
|
|
23
27
|
# permitted parameters for initializing
|
24
28
|
AUTH_PARAMS = %w[
|
25
29
|
email password_base64 password account_id api_url api_version
|
26
|
-
cookies instance_token
|
30
|
+
cookies instance_token timeout open_timeout max_attempts enable_retry
|
27
31
|
]
|
28
32
|
|
29
|
-
attr_reader :cookies, :instance_token, :last_request
|
33
|
+
attr_reader :cookies, :instance_token, :last_request, :timeout, :open_timeout, :max_attempts, :enable_retry
|
30
34
|
attr_accessor :account_id, :api_url
|
31
35
|
|
32
36
|
def initialize(args)
|
@@ -34,6 +38,8 @@ module RightApi
|
|
34
38
|
raise 'This API client is only compatible with Ruby 1.8.7 and upwards.' if (RUBY_VERSION < '1.8.7')
|
35
39
|
|
36
40
|
@api_url, @api_version = DEFAULT_API_URL, API_VERSION
|
41
|
+
@open_timeout, @timeout, @max_attempts = DEFAULT_OPEN_TIMEOUT, DEFAULT_TIMEOUT, DEFAULT_MAX_ATTEMPTS
|
42
|
+
@enable_retry = false
|
37
43
|
|
38
44
|
# Initializing all instance variables from hash
|
39
45
|
args.each { |key,value|
|
@@ -42,7 +48,7 @@ module RightApi
|
|
42
48
|
|
43
49
|
raise 'This API client is only compatible with the RightScale API 1.5 and upwards.' if (Float(@api_version) < 1.5)
|
44
50
|
|
45
|
-
@rest_client = RestClient::Resource.new(@api_url, :timeout =>
|
51
|
+
@rest_client = RestClient::Resource.new(@api_url, :open_timeout => @open_timeout, :timeout => @timeout)
|
46
52
|
@last_request = {}
|
47
53
|
|
48
54
|
# There are three options for login: credentials, instance token,
|
@@ -114,6 +120,44 @@ module RightApi
|
|
114
120
|
end
|
115
121
|
|
116
122
|
protected
|
123
|
+
# Users shouldn't need to call the following methods directly
|
124
|
+
|
125
|
+
def retry_request(is_read_only = false)
|
126
|
+
attempts = 0
|
127
|
+
begin
|
128
|
+
yield
|
129
|
+
rescue OpenSSL::SSL::SSLError => e
|
130
|
+
raise e unless @enable_retry
|
131
|
+
# These errors pertain to the SSL handshake. Since no data has been
|
132
|
+
# exchanged its always safe to retry
|
133
|
+
raise e if attempts >= @max_attempts
|
134
|
+
attempts += 1
|
135
|
+
retry
|
136
|
+
rescue Errno::ECONNRESET, RestClient::ServerBrokeConnection, RestClient::RequestTimeout => e
|
137
|
+
raise e unless @enable_retry
|
138
|
+
# Packetloss related.
|
139
|
+
# There are two timeouts on the ssl negotiation and data read with different
|
140
|
+
# times. Unfortunately the standard timeout class is used for both and the
|
141
|
+
# exceptions are caught and reraised so you can't distinguish between them.
|
142
|
+
# Unfortunate since ssl negotiation timeouts should always be retryable
|
143
|
+
# whereas data may not.
|
144
|
+
if is_read_only
|
145
|
+
raise e if attempts >= @max_attempts
|
146
|
+
attempts += 1
|
147
|
+
retry
|
148
|
+
else
|
149
|
+
raise e
|
150
|
+
end
|
151
|
+
rescue ApiError => e
|
152
|
+
if re_login?(e)
|
153
|
+
# Session cookie is expired or invalid
|
154
|
+
login()
|
155
|
+
retry
|
156
|
+
else
|
157
|
+
raise e
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
117
161
|
|
118
162
|
def login
|
119
163
|
params, path = if @instance_token
|
@@ -128,13 +172,22 @@ module RightApi
|
|
128
172
|
end
|
129
173
|
params['account_href'] = "/api/accounts/#{@account_id}"
|
130
174
|
|
131
|
-
response =
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
175
|
+
response = nil
|
176
|
+
attempts = 0
|
177
|
+
begin
|
178
|
+
response = @rest_client[path].post(params, 'X_API_VERSION' => @api_version) do |response, request, result, &block|
|
179
|
+
if response.code == 302
|
180
|
+
update_api_url(response)
|
181
|
+
response.follow_redirection(request, result, &block)
|
182
|
+
else
|
183
|
+
response.return!(request, result)
|
184
|
+
end
|
137
185
|
end
|
186
|
+
rescue Errno::ECONNRESET, RestClient::RequestTimeout, OpenSSL::SSL::SSLError, RestClient::ServerBrokeConnection
|
187
|
+
raise unless @enable_retry
|
188
|
+
raise if attempts >= @max_attempts
|
189
|
+
attempts += 1
|
190
|
+
retry
|
138
191
|
end
|
139
192
|
|
140
193
|
update_cookies(response)
|
@@ -157,43 +210,37 @@ module RightApi
|
|
157
210
|
# Resource id is a special param as it needs to be added to the path
|
158
211
|
path = add_id_and_params_to_path(path, params)
|
159
212
|
|
160
|
-
req, res = nil
|
213
|
+
req, res, resource_type, body = nil
|
161
214
|
|
162
215
|
begin
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
216
|
+
retry_request(true) do
|
217
|
+
# Return content type so the resulting resource object knows what kind of resource it is.
|
218
|
+
resource_type, body = @rest_client[path].get(headers) do |response, request, result, &block|
|
219
|
+
req, res = request, response
|
220
|
+
update_cookies(response)
|
221
|
+
update_last_request(request, response)
|
222
|
+
|
223
|
+
case response.code
|
224
|
+
when 200
|
225
|
+
# Get the resource_type from the content_type, the resource_type
|
226
|
+
# will be used later to add relevant methods to relevant resources
|
227
|
+
type = if result.content_type.index('rightscale')
|
228
|
+
get_resource_type(result.content_type)
|
229
|
+
else
|
230
|
+
''
|
231
|
+
end
|
232
|
+
|
233
|
+
[type, response.body]
|
234
|
+
when 301, 302
|
235
|
+
update_api_url(response)
|
236
|
+
response.follow_redirection(request, result, &block)
|
237
|
+
when 404
|
238
|
+
raise UnknownRouteError.new(request, response)
|
175
239
|
else
|
176
|
-
|
240
|
+
raise ApiError.new(request, response)
|
177
241
|
end
|
178
|
-
|
179
|
-
[type, response.body]
|
180
|
-
when 301, 302
|
181
|
-
update_api_url(response)
|
182
|
-
response.follow_redirection(request, result, &block)
|
183
|
-
when 404
|
184
|
-
raise UnknownRouteError.new(request, response)
|
185
|
-
else
|
186
|
-
raise ApiError.new(request, response)
|
187
242
|
end
|
188
243
|
end
|
189
|
-
rescue ApiError => e
|
190
|
-
if re_login?(e)
|
191
|
-
# session cookie is expired or invalid
|
192
|
-
login()
|
193
|
-
retry
|
194
|
-
else
|
195
|
-
raise wrap(e, :get, path, params, req, res)
|
196
|
-
end
|
197
244
|
rescue => e
|
198
245
|
raise wrap(e, :get, path, params, req, res)
|
199
246
|
end
|
@@ -211,58 +258,52 @@ module RightApi
|
|
211
258
|
def do_post(path, params={})
|
212
259
|
params = fix_array_of_hashes(params)
|
213
260
|
|
214
|
-
req, res = nil
|
261
|
+
req, res, resource_type, body = nil
|
215
262
|
|
216
263
|
begin
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
264
|
+
retry_request do
|
265
|
+
@rest_client[path].post(params, headers) do |response, request, result|
|
266
|
+
req, res = request, response
|
267
|
+
update_cookies(response)
|
268
|
+
update_last_request(request, response)
|
269
|
+
|
270
|
+
case response.code
|
271
|
+
when 201, 202
|
272
|
+
# Create and return the resource
|
273
|
+
href = response.headers[:location]
|
274
|
+
relative_href = href.split(@api_url)[-1]
|
275
|
+
# Return the resource that was just created
|
276
|
+
# Determine the resource_type from the href (eg. api/clouds/id).
|
277
|
+
# This is based on the assumption that we can determine the resource_type without doing a do_get
|
278
|
+
resource_type = get_singular(relative_href.split('/')[-2])
|
279
|
+
RightApi::Resource.process(self, resource_type, relative_href)
|
280
|
+
when 204
|
281
|
+
nil
|
282
|
+
when 200..299
|
283
|
+
# This is needed for the tags Resource -- which returns a 200 and has a content type
|
284
|
+
# therefore, ResourceDetail objects needs to be returned
|
285
|
+
if response.code == 200 && response.headers[:content_type].index('rightscale')
|
286
|
+
resource_type = get_resource_type(response.headers[:content_type])
|
287
|
+
data = JSON.parse(response)
|
288
|
+
# Resource_tag is returned after querying tags.by_resource or tags.by_tags.
|
289
|
+
# You cannot do a show on a resource_tag, but that is basically what we want to do
|
290
|
+
data.map { |obj|
|
291
|
+
RightApi::ResourceDetail.new(self, resource_type, path, obj)
|
292
|
+
}
|
293
|
+
else
|
294
|
+
response.return!(request, result)
|
295
|
+
end
|
296
|
+
when 301, 302
|
297
|
+
update_api_url(response)
|
298
|
+
do_post(path, params)
|
299
|
+
when 404
|
300
|
+
raise UnknownRouteError.new(request, response)
|
245
301
|
else
|
246
|
-
|
302
|
+
raise ApiError.new(request, response)
|
247
303
|
end
|
248
|
-
when 301, 302
|
249
|
-
update_api_url(response)
|
250
|
-
do_post(path, params)
|
251
|
-
when 404
|
252
|
-
raise UnknownRouteError.new(request, response)
|
253
|
-
else
|
254
|
-
raise ApiError.new(request, response)
|
255
304
|
end
|
256
305
|
end
|
257
|
-
|
258
306
|
rescue ApiError => e
|
259
|
-
if re_login?(e)
|
260
|
-
login()
|
261
|
-
retry
|
262
|
-
else
|
263
|
-
raise wrap(e, :post, path, params, req, res)
|
264
|
-
end
|
265
|
-
rescue => e
|
266
307
|
raise wrap(e, :post, path, params, req, res)
|
267
308
|
end
|
268
309
|
end
|
@@ -272,34 +313,29 @@ module RightApi
|
|
272
313
|
# Resource id is a special param as it needs to be added to the path
|
273
314
|
path = add_id_and_params_to_path(path, params)
|
274
315
|
|
275
|
-
req, res = nil
|
316
|
+
req, res, resource_type, body = nil
|
276
317
|
|
277
318
|
begin
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
319
|
+
retry_request do
|
320
|
+
@rest_client[path].delete(headers) do |response, request, result|
|
321
|
+
req, res = request, response
|
322
|
+
update_cookies(response)
|
323
|
+
update_last_request(request, response)
|
324
|
+
|
325
|
+
case response.code
|
326
|
+
when 200
|
327
|
+
when 204
|
328
|
+
nil
|
329
|
+
when 301, 302
|
330
|
+
update_api_url(response)
|
331
|
+
do_delete(path, params)
|
332
|
+
when 404
|
333
|
+
raise UnknownRouteError.new(request, response)
|
334
|
+
else
|
335
|
+
raise ApiError.new(request, response)
|
336
|
+
end
|
294
337
|
end
|
295
338
|
end
|
296
|
-
rescue ApiError => e
|
297
|
-
if re_login?(e)
|
298
|
-
login()
|
299
|
-
retry
|
300
|
-
else
|
301
|
-
raise wrap(e, :delete, path, params, req, res)
|
302
|
-
end
|
303
339
|
rescue => e
|
304
340
|
raise wrap(e, :delete, path, params, req, res)
|
305
341
|
end
|
@@ -309,33 +345,28 @@ module RightApi
|
|
309
345
|
def do_put(path, params={})
|
310
346
|
params = fix_array_of_hashes(params)
|
311
347
|
|
312
|
-
req, res = nil
|
348
|
+
req, res, resource_type, body = nil
|
313
349
|
|
314
350
|
begin
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
351
|
+
retry_request do
|
352
|
+
@rest_client[path].put(params, headers) do |response, request, result|
|
353
|
+
req, res = request, response
|
354
|
+
update_cookies(response)
|
355
|
+
update_last_request(request, response)
|
356
|
+
|
357
|
+
case response.code
|
358
|
+
when 204
|
359
|
+
nil
|
360
|
+
when 301, 302
|
361
|
+
update_api_url(response)
|
362
|
+
do_put(path, params)
|
363
|
+
when 404
|
364
|
+
raise UnknownRouteError.new(request, response)
|
365
|
+
else
|
366
|
+
raise ApiError.new(request, response)
|
367
|
+
end
|
330
368
|
end
|
331
369
|
end
|
332
|
-
rescue ApiError => e
|
333
|
-
if re_login?(e)
|
334
|
-
login()
|
335
|
-
retry
|
336
|
-
else
|
337
|
-
raise wrap(e, :put, path, params, req, res)
|
338
|
-
end
|
339
370
|
rescue => e
|
340
371
|
raise wrap(e, :put, path, params, req, res)
|
341
372
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_api_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 1.5.
|
9
|
+
- 15
|
10
|
+
version: 1.5.15
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- RightScale, Inc.
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2014-01-
|
18
|
+
date: 2014-01-30 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
requirement: &id001 !ruby/object:Gem::Requirement
|