right_api_client 1.5.14 → 1.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|