kubeclient 2.5.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kubeclient might be problematic. Click here for more details.

Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -5
  3. data/.travis.yml +2 -10
  4. data/CHANGELOG.md +21 -2
  5. data/README.md +112 -46
  6. data/Rakefile +2 -5
  7. data/kubeclient.gemspec +9 -7
  8. data/lib/kubeclient.rb +8 -8
  9. data/lib/kubeclient/common.rb +88 -105
  10. data/lib/kubeclient/config.rb +10 -10
  11. data/lib/kubeclient/http_error.rb +25 -0
  12. data/lib/kubeclient/missing_kind_compatibility.rb +1 -1
  13. data/lib/kubeclient/resource.rb +11 -0
  14. data/lib/kubeclient/resource_not_found_error.rb +4 -0
  15. data/lib/kubeclient/version.rb +1 -1
  16. data/lib/kubeclient/watch_stream.rb +17 -10
  17. data/test/test_common.rb +3 -3
  18. data/test/test_component_status.rb +16 -14
  19. data/test/test_config.rb +11 -11
  20. data/test/test_endpoint.rb +16 -12
  21. data/test/test_guestbook_go.rb +64 -62
  22. data/test/test_helper.rb +2 -0
  23. data/test/test_kubeclient.rb +268 -282
  24. data/test/test_limit_range.rb +11 -11
  25. data/test/test_missing_methods.rb +3 -3
  26. data/test/test_namespace.rb +27 -27
  27. data/test/test_node.rb +17 -15
  28. data/test/test_persistent_volume.rb +16 -14
  29. data/test/test_persistent_volume_claim.rb +16 -14
  30. data/test/test_pod.rb +16 -14
  31. data/test/test_pod_log.rb +4 -4
  32. data/test/test_process_template.rb +7 -7
  33. data/test/test_replication_controller.rb +29 -4
  34. data/test/test_resource_list_without_kind.rb +7 -7
  35. data/test/test_resource_quota.rb +4 -4
  36. data/test/test_secret.rb +11 -10
  37. data/test/test_service.rb +36 -31
  38. data/test/test_service_account.rb +4 -4
  39. data/test/test_watch.rb +52 -29
  40. data/test/test_watch_notice.rb +1 -1
  41. metadata +35 -26
  42. data/Gemfile-rest-client-1.8.rb +0 -11
  43. data/lib/kubeclient/kube_exception.rb +0 -14
data/Rakefile CHANGED
@@ -3,10 +3,7 @@ require 'rake/testtask'
3
3
  require 'rubocop/rake_task'
4
4
  require 'yaml'
5
5
 
6
- task default: [:test, :rubocop] # same as .travis.yml
7
-
8
- Rake::TestTask.new do |t|
9
- t.libs << 'test'
10
- end
6
+ task default: %i[test rubocop] # same as .travis.yml
11
7
 
8
+ Rake::TestTask.new
12
9
  RuboCop::RakeTask.new
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'kubeclient/version'
@@ -17,15 +18,16 @@ Gem::Specification.new do |spec|
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
20
  spec.require_paths = ['lib']
20
- spec.required_ruby_version = '>= 2.0.0'
21
+ spec.required_ruby_version = '>= 2.2.0'
21
22
 
22
23
  spec.add_development_dependency 'bundler', '~> 1.6'
23
- spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rake', '~> 12.0'
24
25
  spec.add_development_dependency 'minitest'
25
- spec.add_development_dependency 'webmock', '~> 1.24.2'
26
+ spec.add_development_dependency 'minitest-rg'
27
+ spec.add_development_dependency 'webmock', '~> 3.0.1'
26
28
  spec.add_development_dependency 'vcr'
27
- spec.add_development_dependency 'rubocop', '= 0.30.0'
28
- spec.add_dependency 'rest-client'
29
- spec.add_dependency 'recursive-open-struct', '~> 1.0.0'
30
- spec.add_dependency 'http', '< 3', '>= 0.98'
29
+ spec.add_development_dependency 'rubocop', '= 0.49.1'
30
+ spec.add_dependency 'rest-client', '~> 2.0'
31
+ spec.add_dependency 'recursive-open-struct', '~> 1.0.4'
32
+ spec.add_dependency 'http', '~> 2.2.2'
31
33
  end
@@ -1,27 +1,27 @@
1
- require 'kubeclient/version'
2
1
  require 'json'
3
2
  require 'rest-client'
4
- require 'kubeclient/entity_list'
5
- require 'kubeclient/kube_exception'
6
- require 'kubeclient/watch_notice'
7
- require 'kubeclient/watch_stream'
3
+
8
4
  require 'kubeclient/common'
9
5
  require 'kubeclient/config'
6
+ require 'kubeclient/entity_list'
7
+ require 'kubeclient/http_error'
10
8
  require 'kubeclient/missing_kind_compatibility'
9
+ require 'kubeclient/resource'
10
+ require 'kubeclient/resource_not_found_error'
11
+ require 'kubeclient/version'
12
+ require 'kubeclient/watch_notice'
13
+ require 'kubeclient/watch_stream'
11
14
 
12
15
  module Kubeclient
13
16
  # Kubernetes Client
14
17
  class Client
15
18
  include ClientMixin
16
- # define a multipurpose resource class, available before discovery
17
- ClientMixin.resource_class(Kubeclient, 'Resource')
18
19
  def initialize(
19
20
  uri,
20
21
  version = 'v1',
21
22
  **options
22
23
  )
23
24
  initialize_client(
24
- Kubeclient,
25
25
  uri,
26
26
  '/api',
27
27
  version,
@@ -4,7 +4,7 @@ module Kubeclient
4
4
  # Common methods
5
5
  # this is mixed in by other gems
6
6
  module ClientMixin
7
- ENTITY_METHODS = %w(get watch delete create update patch)
7
+ ENTITY_METHODS = %w[get watch delete create update patch].freeze
8
8
 
9
9
  DEFAULT_SSL_OPTIONS = {
10
10
  client_cert: nil,
@@ -49,7 +49,6 @@ module Kubeclient
49
49
  attr_reader :discovered
50
50
 
51
51
  def initialize_client(
52
- class_owner,
53
52
  uri,
54
53
  path,
55
54
  version,
@@ -62,7 +61,6 @@ module Kubeclient
62
61
  validate_auth_options(auth_options)
63
62
  handle_uri(uri, path)
64
63
 
65
- @class_owner = class_owner
66
64
  @entities = {}
67
65
  @discovered = false
68
66
  @api_version = version
@@ -73,7 +71,7 @@ module Kubeclient
73
71
  # Allow passing partial timeouts hash, without unspecified
74
72
  # @timeouts[:foo] == nil resulting in infinite timeout.
75
73
  @timeouts = DEFAULT_TIMEOUTS.merge(timeouts)
76
- @http_proxy_uri = http_proxy_uri.to_s if http_proxy_uri
74
+ @http_proxy_uri = http_proxy_uri ? http_proxy_uri.to_s : nil
77
75
 
78
76
  if auth_options[:bearer_token]
79
77
  bearer_token(@auth_options[:bearer_token])
@@ -114,7 +112,8 @@ module Kubeclient
114
112
  {}
115
113
  end
116
114
  err_message = json_error_msg['message'] || e.message
117
- raise KubeException.new(e.http_code, err_message, e.response)
115
+ error_klass = e.http_code == 404 ? ResourceNotFoundError : HttpError
116
+ raise error_klass.new(e.http_code, err_message, e.response)
118
117
  end
119
118
 
120
119
  def discover
@@ -126,9 +125,9 @@ module Kubeclient
126
125
  def self.parse_definition(kind, name)
127
126
  # "name": "componentstatuses", networkpolicies, endpoints
128
127
  # "kind": "ComponentStatus" NetworkPolicy, Endpoints
129
- # maintain pre group api compatibility for endpoints.
128
+ # maintain pre group api compatibility for endpoints and securitycontextconstraints.
130
129
  # See: https://github.com/kubernetes/kubernetes/issues/8115
131
- kind = 'Endpoint' if kind == 'Endpoints'
130
+ kind = kind[0..-2] if %w[Endpoints SecurityContextConstraints].include?(kind)
132
131
 
133
132
  prefix = kind[0..kind.rindex(/[A-Z]/)] # NetworkP
134
133
  m = name.match(/^#{prefix.downcase}(.*)$/)
@@ -143,10 +142,10 @@ module Kubeclient
143
142
  end
144
143
 
145
144
  def handle_uri(uri, path)
146
- fail ArgumentError, 'Missing uri' unless uri
145
+ raise ArgumentError, 'Missing uri' unless uri
147
146
  @api_endpoint = (uri.is_a?(URI) ? uri : URI.parse(uri))
148
147
  @api_endpoint.path = path if @api_endpoint.path.empty?
149
- @api_endpoint.path = @api_endpoint.path.chop if @api_endpoint.path.end_with? '/'
148
+ @api_endpoint.path = @api_endpoint.path.chop if @api_endpoint.path.end_with?('/')
150
149
  components = @api_endpoint.path.to_s.split('/') # ["", "api"] or ["", "apis", batch]
151
150
  @api_group = components.length > 2 ? components[2] + '/' : ''
152
151
  end
@@ -155,28 +154,11 @@ module Kubeclient
155
154
  namespace.to_s.empty? ? '' : "namespaces/#{namespace}/"
156
155
  end
157
156
 
158
- def self.resource_class(class_owner, entity_type)
159
- if class_owner.const_defined?(entity_type, false)
160
- class_owner.const_get(entity_type, false)
161
- else
162
- class_owner.const_set(
163
- entity_type,
164
- Class.new(RecursiveOpenStruct) do
165
- def initialize(hash = nil, args = {})
166
- args[:recurse_over_arrays] = true
167
- super(hash, args)
168
- end
169
- end
170
- )
171
- end
172
- end
173
-
174
157
  def define_entity_methods
175
158
  @entities.values.each do |entity|
176
- klass = ClientMixin.resource_class(@class_owner, entity.entity_type)
177
159
  # get all entities of a type e.g. get_nodes, get_pods, etc.
178
160
  define_singleton_method("get_#{entity.method_names[1]}") do |options = {}|
179
- get_entities(entity.entity_type, klass, entity.resource_name, options)
161
+ get_entities(entity.entity_type, entity.resource_name, options)
180
162
  end
181
163
 
182
164
  # watch all entities of a type e.g. watch_nodes, watch_pods, etc.
@@ -190,16 +172,17 @@ module Kubeclient
190
172
 
191
173
  # get a single entity of a specific type by name
192
174
  define_singleton_method("get_#{entity.method_names[0]}") \
193
- do |name, namespace = nil, opts = {}|
194
- get_entity(klass, entity.resource_name, name, namespace, opts)
175
+ do |name, namespace = nil, opts = {}|
176
+ get_entity(entity.resource_name, name, namespace, opts)
195
177
  end
196
178
 
197
- define_singleton_method("delete_#{entity.method_names[0]}") do |name, namespace = nil|
198
- delete_entity(entity.resource_name, name, namespace)
179
+ define_singleton_method("delete_#{entity.method_names[0]}") \
180
+ do |name, namespace = nil, opts = {}|
181
+ delete_entity(entity.resource_name, name, namespace, opts)
199
182
  end
200
183
 
201
184
  define_singleton_method("create_#{entity.method_names[0]}") do |entity_config|
202
- create_entity(entity.entity_type, entity.resource_name, entity_config, klass)
185
+ create_entity(entity.entity_type, entity.resource_name, entity_config)
203
186
  end
204
187
 
205
188
  define_singleton_method("update_#{entity.method_names[0]}") do |entity_config|
@@ -228,7 +211,7 @@ module Kubeclient
228
211
  user: @auth_options[:username],
229
212
  password: @auth_options[:password],
230
213
  open_timeout: @timeouts[:open],
231
- ClientMixin.restclient_read_timeout_option => @timeouts[:read]
214
+ read_timeout: @timeouts[:read]
232
215
  }
233
216
  RestClient::Resource.new(@api_endpoint.merge(path).to_s, options)
234
217
  end
@@ -245,6 +228,9 @@ module Kubeclient
245
228
  # :label_selector (string) - a selector to restrict the list of returned objects by labels.
246
229
  # :field_selector (string) - a selector to restrict the list of returned objects by fields.
247
230
  # :resource_version (string) - shows changes that occur after passed version of a resource.
231
+ # :as (:raw|:ros) - defaults to :ros
232
+ # :raw - return the raw response body as a string
233
+ # :ros - return a collection of RecursiveOpenStruct objects
248
234
  def watch_entities(resource_name, options = {})
249
235
  ns = build_namespace_prefix(options[:namespace])
250
236
 
@@ -256,68 +242,73 @@ module Kubeclient
256
242
  WATCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] }
257
243
  uri.query = URI.encode_www_form(params) if params.any?
258
244
 
259
- Kubeclient::Common::WatchStream.new(uri, http_options(uri))
245
+ Kubeclient::Common::WatchStream.new(uri, http_options(uri), as: options[:as] || :ros)
260
246
  end
261
247
 
262
248
  # Accepts the following options:
263
249
  # :namespace (string) - the namespace of the entity.
264
250
  # :label_selector (string) - a selector to restrict the list of returned objects by labels.
265
251
  # :field_selector (string) - a selector to restrict the list of returned objects by fields.
266
- # :as (symbol) - if :raw, return the raw response body (as a string)
267
- #
268
- # Default response type will return a collection RecursiveOpenStruct
269
- # (:ros) objects, unless `:as` is passed with `:raw`.
270
- def get_entities(entity_type, klass, resource_name, options = {})
252
+ # :as (:raw|:ros) - defaults to :ros
253
+ # :raw - return the raw response body as a string
254
+ # :ros - return a collection of RecursiveOpenStruct objects
255
+ def get_entities(entity_type, resource_name, options = {})
271
256
  params = {}
272
257
  SEARCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] }
273
258
 
274
259
  ns_prefix = build_namespace_prefix(options[:namespace])
275
260
  response = handle_exception do
276
261
  rest_client[ns_prefix + resource_name]
277
- .get({ 'params' => params }.merge(@headers))
262
+ .get({ 'params' => params }.merge(@headers))
278
263
  end
279
264
  return response.body if options[:as] == :raw
280
265
 
281
266
  result = JSON.parse(response)
282
267
 
283
- resource_version = result.fetch('resourceVersion', nil)
284
- if resource_version.nil?
285
- resource_version =
286
- result.fetch('metadata', {}).fetch('resourceVersion', nil)
287
- end
268
+ resource_version =
269
+ result.fetch('resourceVersion') do
270
+ result.fetch('metadata', {}).fetch('resourceVersion', nil)
271
+ end
288
272
 
289
273
  # result['items'] might be nil due to https://github.com/kubernetes/kubernetes/issues/13096
290
- collection = result['items'].to_a.map { |item| new_entity(item, klass) }
274
+ collection = result['items'].to_a.map { |item| Kubeclient::Resource.new(item) }
291
275
 
292
276
  Kubeclient::Common::EntityList.new(entity_type, resource_version, collection)
293
277
  end
294
278
 
295
279
  # Accepts the following options:
296
- # :as (symbol) - if :raw, return the raw response body (as a string)
297
- #
298
- # Default response type will return an entity as a RecursiveOpenStruct
299
- # (:ros) object, unless `:as` is passed with `:raw`.
300
- def get_entity(klass, resource_name, name, namespace = nil, options = {})
280
+ # :as (:raw|:ros) - defaults to :ros
281
+ # :raw - return the raw response body as a string
282
+ # :ros - return a collection of RecursiveOpenStruct objects
283
+ def get_entity(resource_name, name, namespace = nil, options = {})
301
284
  ns_prefix = build_namespace_prefix(namespace)
302
285
  response = handle_exception do
303
286
  rest_client[ns_prefix + resource_name + "/#{name}"]
304
- .get(@headers)
287
+ .get(@headers)
305
288
  end
306
- return response.body if options[:as] == :raw
307
-
308
- result = JSON.parse(response)
309
- new_entity(result, klass)
289
+ format_response(options[:as], response)
310
290
  end
311
291
 
312
- def delete_entity(resource_name, name, namespace = nil)
292
+ # delete_options are passed as a JSON payload in the delete request
293
+ def delete_entity(resource_name, name, namespace = nil, delete_options: {})
294
+ delete_options_hash = delete_options.to_hash
313
295
  ns_prefix = build_namespace_prefix(namespace)
314
- handle_exception do
315
- rest_client[ns_prefix + resource_name + "/#{name}"]
316
- .delete(@headers)
296
+ payload = delete_options_hash.to_json unless delete_options_hash.empty?
297
+ response = handle_exception do
298
+ rs = rest_client[ns_prefix + resource_name + "/#{name}"]
299
+ RestClient::Request.execute(
300
+ rs.options.merge(
301
+ method: :delete,
302
+ url: rs.url,
303
+ headers: { 'Content-Type' => 'application/json' }.merge(@headers),
304
+ payload: payload
305
+ )
306
+ )
317
307
  end
308
+ format_response(:ros, response)
318
309
  end
319
310
 
320
- def create_entity(entity_type, resource_name, entity_config, klass)
311
+ def create_entity(entity_type, resource_name, entity_config)
321
312
  # Duplicate the entity_config to a hash so that when we assign
322
313
  # kind and apiVersion, this does not mutate original entity_config obj.
323
314
  hash = entity_config.to_hash
@@ -329,42 +320,35 @@ module Kubeclient
329
320
  # https://github.com/GoogleCloudPlatform/kubernetes/issues/6439
330
321
  # TODO: #2 solution for
331
322
  # https://github.com/kubernetes/kubernetes/issues/8115
332
- if entity_type.eql? 'Endpoint'
333
- hash[:kind] = 'Endpoints'
334
- else
335
- hash[:kind] = entity_type
336
- end
323
+ hash[:kind] = (entity_type.eql?('Endpoint') ? 'Endpoints' : entity_type)
337
324
  hash[:apiVersion] = @api_group + @api_version
338
325
  response = handle_exception do
339
326
  rest_client[ns_prefix + resource_name]
340
- .post(hash.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
327
+ .post(hash.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
341
328
  end
342
- result = JSON.parse(response)
343
- new_entity(result, klass)
329
+ format_response(:ros, response)
344
330
  end
345
331
 
346
332
  def update_entity(resource_name, entity_config)
347
333
  name = entity_config[:metadata][:name]
348
334
  ns_prefix = build_namespace_prefix(entity_config[:metadata][:namespace])
349
- handle_exception do
335
+ response = handle_exception do
350
336
  rest_client[ns_prefix + resource_name + "/#{name}"]
351
337
  .put(entity_config.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
352
338
  end
339
+ format_response(:ros, response)
353
340
  end
354
341
 
355
342
  def patch_entity(resource_name, name, patch, namespace = nil)
356
343
  ns_prefix = build_namespace_prefix(namespace)
357
- handle_exception do
344
+ response = handle_exception do
358
345
  rest_client[ns_prefix + resource_name + "/#{name}"]
359
346
  .patch(
360
347
  patch.to_json,
361
348
  { 'Content-Type' => 'application/strategic-merge-patch+json' }.merge(@headers)
362
349
  )
363
350
  end
364
- end
365
-
366
- def new_entity(hash, klass)
367
- klass.new(hash)
351
+ format_response(:ros, response)
368
352
  end
369
353
 
370
354
  def all_entities(options = {})
@@ -375,7 +359,7 @@ module Kubeclient
375
359
  method_name = "get_#{entity.method_names[1]}"
376
360
  begin
377
361
  result_hash[entity.method_names[0]] = send(method_name, options)
378
- rescue KubeException
362
+ rescue Kubeclient::HttpError
379
363
  next # do not fail due to resources not supporting get
380
364
  end
381
365
  end
@@ -405,16 +389,17 @@ module Kubeclient
405
389
  uri.path += "/#{@api_version}/#{ns}pods/#{pod_name}/log"
406
390
  uri.query = URI.encode_www_form(params)
407
391
 
408
- Kubeclient::Common::WatchStream.new(uri, http_options(uri), format: :text)
392
+ Kubeclient::Common::WatchStream.new(uri, http_options(uri), as: :raw)
409
393
  end
410
394
 
411
395
  def proxy_url(kind, name, port, namespace = '')
412
396
  discover unless @discovered
413
- entity_name_plural = if %w(services pods nodes).include?(kind.to_s)
414
- kind.to_s
415
- else
416
- @entities[kind.to_s].resource_name
417
- end
397
+ entity_name_plural =
398
+ if %w[services pods nodes].include?(kind.to_s)
399
+ kind.to_s
400
+ else
401
+ @entities[kind.to_s].resource_name
402
+ end
418
403
  ns_prefix = build_namespace_prefix(namespace)
419
404
  # TODO: Change this once services supports the new scheme
420
405
  if entity_name_plural == 'pods'
@@ -428,7 +413,7 @@ module Kubeclient
428
413
  ns_prefix = build_namespace_prefix(template[:metadata][:namespace])
429
414
  response = handle_exception do
430
415
  rest_client[ns_prefix + 'processedtemplates']
431
- .post(template.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
416
+ .post(template.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers))
432
417
  end
433
418
  JSON.parse(response)
434
419
  end
@@ -436,7 +421,7 @@ module Kubeclient
436
421
  def api_valid?
437
422
  result = api
438
423
  result.is_a?(Hash) && (result['versions'] || []).any? do |group|
439
- @api_group.empty? ? group.include?(@api_version) : group['version'] == (@api_version)
424
+ @api_group.empty? ? group.include?(@api_version) : group['version'] == @api_version
440
425
  end
441
426
  end
442
427
 
@@ -445,26 +430,21 @@ module Kubeclient
445
430
  JSON.parse(response)
446
431
  end
447
432
 
448
- def self.restclient_read_timeout_option
449
- @restclient_read_timeout_option ||=
450
- # RestClient silently accepts unknown options, so check accessors instead.
451
- if RestClient::Resource.instance_methods.include?(:read_timeout) # rest-client 2.0
452
- :read_timeout
453
- elsif RestClient::Resource.instance_methods.include?(:timeout) # rest-client 1.x
454
- :timeout
455
- else
456
- fail ArgumentError("RestClient doesn't support neither :read_timeout nor :timeout")
457
- end
458
- end
459
-
460
433
  private
461
434
 
435
+ def format_response(as, response)
436
+ return response.body if as == :raw
437
+
438
+ result = JSON.parse(response)
439
+ Kubeclient::Resource.new(result)
440
+ end
441
+
462
442
  def load_entities
463
443
  @entities = {}
464
444
  fetch_entities['resources'].each do |resource|
465
445
  next if resource['name'].include?('/')
466
- resource['kind'] = Kubeclient::Common::MissingKindCompatibility
467
- .resource_kind(resource['name']) if resource['kind'].nil?
446
+ resource['kind'] ||=
447
+ Kubeclient::Common::MissingKindCompatibility.resource_kind(resource['name'])
468
448
  entity = ClientMixin.parse_definition(resource['kind'], resource['name'])
469
449
  @entities[entity.method_names[0]] = entity if entity
470
450
  end
@@ -483,20 +463,23 @@ module Kubeclient
483
463
  # maintain backward compatibility:
484
464
  opts[:username] = opts[:user] if opts[:user]
485
465
 
486
- if [:bearer_token, :bearer_token_file, :username].count { |key| opts[key] } > 1
487
- fail(ArgumentError, 'Invalid auth options: specify only one of username/password,' \
488
- ' bearer_token or bearer_token_file')
489
- elsif [:username, :password].count { |key| opts[key] } == 1
490
- fail(ArgumentError, 'Basic auth requires both username & password')
466
+ if %i[bearer_token bearer_token_file username].count { |key| opts[key] } > 1
467
+ raise(
468
+ ArgumentError,
469
+ 'Invalid auth options: specify only one of username/password,' \
470
+ ' bearer_token or bearer_token_file'
471
+ )
472
+ elsif %i[username password].count { |key| opts[key] } == 1
473
+ raise ArgumentError, 'Basic auth requires both username & password'
491
474
  end
492
475
  end
493
476
 
494
477
  def validate_bearer_token_file
495
478
  msg = "Token file #{@auth_options[:bearer_token_file]} does not exist"
496
- fail ArgumentError, msg unless File.file?(@auth_options[:bearer_token_file])
479
+ raise ArgumentError, msg unless File.file?(@auth_options[:bearer_token_file])
497
480
 
498
481
  msg = "Cannot read token file #{@auth_options[:bearer_token_file]}"
499
- fail ArgumentError, msg unless File.readable?(@auth_options[:bearer_token_file])
482
+ raise ArgumentError, msg unless File.readable?(@auth_options[:bearer_token_file])
500
483
  end
501
484
 
502
485
  def http_options(uri)