chef-api 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/CHANGELOG.md +17 -0
  4. data/README.md +42 -0
  5. data/chef-api.gemspec +1 -1
  6. data/lib/chef-api/configurable.rb +3 -2
  7. data/lib/chef-api/connection.rb +77 -47
  8. data/lib/chef-api/defaults.rb +28 -8
  9. data/lib/chef-api/errors.rb +32 -3
  10. data/lib/chef-api/resource.rb +1 -0
  11. data/lib/chef-api/resources/base.rb +30 -21
  12. data/lib/chef-api/resources/client.rb +6 -8
  13. data/lib/chef-api/resources/collection_proxy.rb +19 -2
  14. data/lib/chef-api/resources/data_bag.rb +1 -1
  15. data/lib/chef-api/resources/organization.rb +22 -0
  16. data/lib/chef-api/resources/user.rb +72 -1
  17. data/lib/chef-api/schema.rb +59 -21
  18. data/lib/chef-api/version.rb +1 -1
  19. data/lib/chef-api.rb +26 -5
  20. data/spec/integration/resources/client_spec.rb +54 -0
  21. data/spec/integration/resources/user_spec.rb +8 -0
  22. data/spec/spec_helper.rb +1 -1
  23. data/spec/support/chef_server.rb +1 -0
  24. data/spec/unit/errors_spec.rb +294 -0
  25. data/spec/unit/resources/client_spec.rb +0 -16
  26. data/spec/unit/resources/connection_spec.rb +51 -0
  27. data/templates/errors/abstract_method.erb +5 -0
  28. data/templates/errors/cannot_regenerate_key.erb +1 -0
  29. data/templates/errors/chef_api_error.erb +1 -0
  30. data/templates/errors/file_not_found.erb +1 -0
  31. data/templates/errors/http_bad_request.erb +3 -0
  32. data/templates/errors/http_forbidden_request.erb +3 -0
  33. data/templates/errors/http_gateway_timeout.erb +3 -0
  34. data/templates/errors/http_method_not_allowed.erb +3 -0
  35. data/templates/errors/http_not_acceptable.erb +3 -0
  36. data/templates/errors/http_not_found.erb +3 -0
  37. data/templates/errors/http_server_unavailable.erb +1 -0
  38. data/templates/errors/http_unauthorized_request.erb +3 -0
  39. data/templates/errors/insufficient_file_permissions.erb +1 -0
  40. data/templates/errors/invalid_resource.erb +1 -0
  41. data/templates/errors/invalid_validator.erb +1 -0
  42. data/templates/errors/missing_url_parameter.erb +1 -0
  43. data/templates/errors/not_a_directory.erb +1 -0
  44. data/templates/errors/resource_already_exists.erb +1 -0
  45. data/templates/errors/resource_not_found.erb +1 -0
  46. data/templates/errors/resource_not_mutable.erb +1 -0
  47. data/templates/errors/unknown_attribute.erb +1 -0
  48. metadata +43 -17
  49. data/lib/chef-api/logger.rb +0 -160
  50. data/lib/chef-api/proxy.rb +0 -72
  51. data/locales/en.yml +0 -89
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9d6c6244de045894dffca646b3cea3696645629
4
- data.tar.gz: 3e5a947c02535d537733ee9009906964d423af88
3
+ metadata.gz: 2eb1855e2b759c9728d8bb855286295c5051174a
4
+ data.tar.gz: e04becfd2a2fdbac4bfa2420810f4cedace3c4f4
5
5
  SHA512:
6
- metadata.gz: 0e4c4aa13d550e8ffeabf24e0dae0bbd9695a276eb85097e265cf121137f9ef19ed96712f3b172b80716789c60504961760f2ee722b6c459731ef6b0e48121d2
7
- data.tar.gz: ade2f5cc5dfada170a6922aecf6a6acfe2b23a071a21bca2d062aa86f7d15f4dde1e5caa12e34cf28af7ee8f3c8faaff28b9aa7eb312bab1cff1d3b775f01edb
6
+ metadata.gz: f6bfa54343067d1ebe47610970206ed7c55701e01d942a4774fb1e1645a7d289bfc965975ad2b4d25ba468b5da6091ee8c94b2e21c86a46f88cdae74136cf2ee
7
+ data.tar.gz: 5bbfff5db04efb34eb49977bc62e89bdb3c21ba97c58194e5fb3b00aee44b271be2f33ef25919fea8db55376464bc52b39b0888f47745ee0ac6b19b3897538c7
data/.travis.yml CHANGED
@@ -3,8 +3,8 @@ rvm:
3
3
  - 2.0.0
4
4
  - 2.1.0
5
5
 
6
- # Don't install local development gems on Travis
7
- bundler_args: --without development
6
+ # Don't install local development gems on Travis, use parallel gem downloads
7
+ bundler_args: --without development --jobs 7
8
8
 
9
9
  # Only test master
10
10
  branches:
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ ChefAPI Changelog
2
+ =================
3
+
4
+ v0.2.1 (2014-04-17)
5
+ -------------------
6
+ - Fix a series of typographical errors
7
+ - Improved documentation for loading resources from disk
8
+ - Improved spec coverage
9
+ - Switch to Logify for logging
10
+ - Add HEC endpoint for authenticating users
11
+ - Change the default options for Hosted Chef
12
+ - Implement HTTPGatewayTimeout (504)
13
+ - Do not automatically inflate JSON objects
14
+ - Improved logging awesomeness
15
+ - Add "flavors" when defining the schema (OSC is different than HEC)
16
+ - Remove i18n in favor of ERB
17
+ - Fix an issue when providing a key at an unexpanded path
data/README.md CHANGED
@@ -48,6 +48,14 @@ ChefAPI.configure do |config|
48
48
  # Hosted Chef Server, or Enterprise Chef Server.
49
49
  config.endpoint = 'https://api.opscode.com/organizations/meats'
50
50
 
51
+ # ChefAPI will try to determine if you are running on an Enterprise Chef
52
+ # Server or Open Source Chef depending on the URL you provide for the
53
+ # +endpoint+ attribute. However, it may be incorrect. If is seems like the
54
+ # generated schema does not match the response from the server, it is
55
+ # possible this value was calculated incorrectly. Thus, you should set it
56
+ # manually. Possible values are +:enterprise+ and +:open_source+.
57
+ config.flavor = :enterprise
58
+
51
59
  # The client and key must also be specified (unless you are running Chef Zero
52
60
  # in no-authentication mode). The +key+ attribute may be the raw private key,
53
61
  # the path to the private key on disk, or an +OpenSSLL::PKey+ object.
@@ -90,6 +98,33 @@ export CHEF_API_CLIENT=bacon
90
98
  export CHEF_API_KEY=~/.chef/bacon.pem
91
99
  ```
92
100
 
101
+ If you prefer a more object-oriented approach (or if you want to support multiple simultaneous connections), you can create a raw `ChefAPI::Connection` object. All of the options that are available on the `ChefAPI` object are also available on a raw connection:
102
+
103
+ ```ruby
104
+ connection = ChefAPI::Connection.new(
105
+ endpoint: 'https://api.opscode.com/organizations/meats',
106
+ client: 'bacon',
107
+ key: '~/.chef/bacon.pem'
108
+ )
109
+
110
+ connection.clients.fetch('chef-webui')
111
+ connection.environments.delete('production')
112
+ ```
113
+
114
+ If you do not want to manage a `ChefAPI::Connection` object, or if you just prefer an alternative syntax, you can use the block-form:
115
+
116
+ ```ruby
117
+ ChefAPI::Connection.new do |connection|
118
+ connection.endpoint = 'https://api.opscode.com/organizations/meats'
119
+ connection.client = 'bacon'
120
+ connection.key = '~/.chef/bacon.pem'
121
+
122
+ # The connection object is now setup, so you can use it directly:
123
+ connection.clients.fetch('chef-webui')
124
+ connection.environments.delete('production')
125
+ end
126
+ ```
127
+
93
128
  ### Making Requests
94
129
  The ChefAPI gem attempts to wrap the Chef Server API in an object-oriented and Rubyesque way. All of the methods and API calls are heavily documented inline using YARD. For a full list of every possible option, please see the inline documentation.
95
130
 
@@ -223,6 +258,13 @@ You can also force ChefAPI to raise an exception if the validations fail, using
223
258
  client.save! #=> InvalidResource: There were errors saving your resource: `name' must be present
224
259
  ```
225
260
 
261
+ ### Objects on Disk
262
+ ChefAPI also has the ability to read and manipulate objects on disk. This varies from resource-to-resource, but the `.from_file` method accepts a path to a resource on disk and loads as much information about the object on disk as it can. The attributes are then merged with the remote resource, if one exists. For example, you can read a Client resource from disk:
263
+
264
+ ```ruby
265
+ client = Client.from_file('~/.chef/bacon.pem') #=> #<Resource::Client name: "bacon", admin: false, public_key: nil, private_key: "..." ...>
266
+ ```
267
+
226
268
 
227
269
  FAQ
228
270
  ---
data/chef-api.gemspec CHANGED
@@ -20,6 +20,6 @@ Gem::Specification.new do |spec|
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'i18n', '~> 0.6'
23
+ spec.add_dependency 'logify', '~> 0.1'
24
24
  spec.add_dependency 'mixlib-authentication', '~> 1.3'
25
25
  end
@@ -1,7 +1,7 @@
1
1
  module ChefAPI
2
2
  #
3
- # A re-usable class containing configuration information for the {Client}. See
4
- # {Defaults} for a list of default values.
3
+ # A re-usable class containing configuration information for the {Connection}.
4
+ # See {Defaults} for a list of default values.
5
5
  #
6
6
  module Configurable
7
7
  class << self
@@ -13,6 +13,7 @@ module ChefAPI
13
13
  def keys
14
14
  @keys ||= [
15
15
  :endpoint,
16
+ :flavor,
16
17
  :client,
17
18
  :key,
18
19
  :proxy_address,
@@ -16,33 +16,54 @@ module ChefAPI
16
16
  #
17
17
  # @macro proxy
18
18
  # @method $1
19
- # Get a proxied collection for +$1+. The proxy automatically injects
20
- # the current connection into the $2, providing a very Rubyesque way
21
- # for handling multiple connection objects.
19
+ # Get the list of $1 for this {Connection}. This method is threadsafe.
22
20
  #
23
- # @example Get the $1 from the connection object
21
+ # @example Get the $1 from this {Connection} object
24
22
  # connection = ChefAPI::Connection.new('...')
25
- # connection.$1 #=> $2 (with the connection object pre-populated)
23
+ # connection.$1 #=> $2(attribute1, attribute2, ...)
26
24
  #
27
- # @return [ChefAPI::Proxy<$2>]
28
- # a collection proxy for the $2
25
+ # @return [Class<$2>]
29
26
  #
30
27
  def proxy(name, klass)
31
28
  class_eval <<-EOH, __FILE__, __LINE__ + 1
32
29
  def #{name}
33
- @#{name} ||= ChefAPI::Proxy.new(self, #{klass})
30
+ Thread.current['chefapi.connection'] = self
31
+ #{klass}
34
32
  end
35
33
  EOH
36
34
  end
37
35
  end
38
36
 
37
+ include Logify
39
38
  include ChefAPI::Configurable
40
- include ChefAPI::Logger
39
+
40
+ proxy :clients, 'Resource::Client'
41
+ proxy :cookbooks, 'Resource::Cookbook'
42
+ proxy :data_bags, 'Resource::DataBag'
43
+ proxy :environments, 'Resource::Environment'
44
+ proxy :nodes, 'Resource::Node'
45
+ proxy :principals, 'Resource::Principal'
46
+ proxy :roles, 'Resource::Role'
47
+ proxy :users, 'Resource::User'
41
48
 
42
49
  #
43
50
  # Create a new ChefAPI Connection with the given options. Any options
44
51
  # given take precedence over the default options.
45
52
  #
53
+ # @example Create a connection object from a list of options
54
+ # ChefAPI::Connection.new(
55
+ # endpoint: 'https://...',
56
+ # client: 'bacon',
57
+ # key: '~/.chef/bacon.pem',
58
+ # )
59
+ #
60
+ # @example Create a connection object using a block
61
+ # ChefAPI::Connection.new do |connection|
62
+ # connection.endpoint = 'https://...'
63
+ # connection.client = 'bacon'
64
+ # connection.key = '~/.chef/bacon.pem'
65
+ # end
66
+ #
46
67
  # @return [ChefAPI::Connection]
47
68
  #
48
69
  def initialize(options = {})
@@ -56,6 +77,8 @@ module ChefAPI
56
77
 
57
78
  instance_variable_set(:"@#{key}", value)
58
79
  end
80
+
81
+ yield self if block_given?
59
82
  end
60
83
 
61
84
  #
@@ -149,14 +172,13 @@ module ChefAPI
149
172
  # request against
150
173
  # @param [#read, Hash, nil] data
151
174
  # the data to use (varies based on the +verb+)
152
- # @param [Hash] headers
153
- # the list of headers to use
154
175
  #
155
176
  # @return [String, Hash]
156
177
  # the response body
157
178
  #
158
179
  def request(verb, path, data = {})
159
- log.info "===> #{verb.to_s.upcase} #{path}..."
180
+ log.info "#{verb.to_s.upcase} #{path}..."
181
+ log.debug "Chef flavor: #{flavor.inspect}"
160
182
 
161
183
  # Build the URI and request object from the given information
162
184
  uri = build_uri(verb, path, data)
@@ -201,7 +223,7 @@ module ChefAPI
201
223
  # Naughty, naughty, naughty! Don't blame when when someone hops in
202
224
  # and executes a MITM attack!
203
225
  unless ssl_verify
204
- log.warn "===> Disabling SSL verification..."
226
+ log.warn "Disabling SSL verification..."
205
227
  log.warn "Neither ChefAPI nor the maintainers are responsible for " \
206
228
  "damanges incurred as a result of disabling SSL verification. " \
207
229
  "Please use this with extreme caution, or consider specifying " \
@@ -215,11 +237,14 @@ module ChefAPI
215
237
  connection.start do |http|
216
238
  response = http.request(request)
217
239
 
240
+ log.debug "Raw response:"
241
+ log.debug response.body
242
+
218
243
  case response
219
244
  when Net::HTTPRedirection
220
- redirect = URI.parse(response['location'])
221
- log.debug "===> Performing HTTP redirect to #{redirect}"
222
- request(verb, redirect, params)
245
+ redirect = URI.parse(response['location']).to_s
246
+ log.debug "Performing HTTP redirect to #{redirect}"
247
+ request(verb, redirect, data)
223
248
  when Net::HTTPSuccess
224
249
  success(response)
225
250
  else
@@ -227,7 +252,7 @@ module ChefAPI
227
252
  end
228
253
  end
229
254
  rescue SocketError, Errno::ECONNREFUSED, EOFError
230
- log.warn " Unable to reach the Chef Server"
255
+ log.warn "Unable to reach the Chef Server"
231
256
  raise Error::HTTPServerUnavailable.new
232
257
  end
233
258
 
@@ -250,13 +275,16 @@ module ChefAPI
250
275
  # @return [URI]
251
276
  #
252
277
  def build_uri(verb, path, params = {})
253
- log.info "===> Building URI..."
278
+ log.info "Building URI..."
254
279
 
255
280
  # Add any query string parameters
256
281
  if [:delete, :get].include?(verb)
257
- log.debug " Detected verb deserves a querystring"
258
- log.debug " Building querystring using #{params.inspect}"
259
- path = [path, to_query_string(params)].compact.join('?')
282
+ if querystring = to_query_string(params)
283
+ log.debug "Detected verb deserves a querystring"
284
+ log.debug "Building querystring using #{params.inspect}"
285
+ log.debug "Compiled querystring is #{querystring.inspect}"
286
+ path = [path, querystring].compact.join('?')
287
+ end
260
288
  end
261
289
 
262
290
  # Parse the URI
@@ -264,8 +292,8 @@ module ChefAPI
264
292
 
265
293
  # Don't merge absolute URLs
266
294
  unless uri.absolute?
267
- log.debug " Detected URI is relative"
268
- log.debug " Appending #{endpoint} to #{path}"
295
+ log.debug "Detected URI is relative"
296
+ log.debug "Appending #{path} to #{endpoint}"
269
297
  uri = URI.parse(File.join(endpoint, path))
270
298
  end
271
299
 
@@ -274,7 +302,7 @@ module ChefAPI
274
302
  end
275
303
 
276
304
  #
277
- # Helper method to get the corresponding {Net::HTTP} class from the given
305
+ # Helper method to get the corresponding +Net::HTTP+ class from the given
278
306
  # HTTP verb.
279
307
  #
280
308
  # @param [#to_s] verb
@@ -323,24 +351,24 @@ module ChefAPI
323
351
  def parsed_key
324
352
  return @parsed_key if @parsed_key
325
353
 
326
- log.info "===> Parsing private key..."
354
+ log.info "Parsing private key..."
327
355
 
328
356
  if key.nil?
329
- log.warn " No private key given!"
357
+ log.warn "No private key given!"
330
358
  raise 'No private key given!'
331
359
  end
332
360
 
333
361
  if key.is_a?(OpenSSL::PKey::RSA)
334
- log.debug " Detected private key is an OpenSSL Ruby object"
362
+ log.debug "Detected private key is an OpenSSL Ruby object"
335
363
  @parsed_key = key
336
364
  end
337
365
 
338
- if key =~ /(.+)\.pem$/ || File.exists?(key)
339
- log.debug " Detected private key is the path to a file"
366
+ if key =~ /(.+)\.pem$/ || File.exists?(File.expand_path(key))
367
+ log.debug "Detected private key is the path to a file"
340
368
  contents = File.read(File.expand_path(key))
341
369
  @parsed_key = OpenSSL::PKey::RSA.new(contents)
342
370
  else
343
- log.debug " Detected private key was the literal string key"
371
+ log.debug "Detected private key was the literal string key"
344
372
  @parsed_key = OpenSSL::PKey::RSA.new(key)
345
373
  end
346
374
 
@@ -359,15 +387,15 @@ module ChefAPI
359
387
  # the parsed response, as an object
360
388
  #
361
389
  def success(response)
362
- log.info "===> Parsing response as success..."
390
+ log.info "Parsing response as success..."
363
391
 
364
392
  case response['Content-Type']
365
- when 'application/json'
366
- log.debug " Detected response as JSON"
367
- log.debug " Parsing response body as JSON"
393
+ when /json/
394
+ log.debug "Detected response as JSON"
395
+ log.debug "Parsing response body as JSON"
368
396
  JSON.parse(response.body)
369
397
  else
370
- log.debug " Detected response as text/plain"
398
+ log.debug "Detected response as text/plain"
371
399
  response.body
372
400
  end
373
401
  end
@@ -380,15 +408,15 @@ module ChefAPI
380
408
  # the response object from the request
381
409
  #
382
410
  def error(response)
383
- log.info "===> Parsing response as error..."
411
+ log.info "Parsing response as error..."
384
412
 
385
413
  case response['Content-Type']
386
- when 'application/json'
387
- log.debug " Detected error response as JSON"
388
- log.debug " Parsing error response as JSON"
389
- message = JSON.parse(response.body)['error'].first
414
+ when /json/
415
+ log.debug "Detected error response as JSON"
416
+ log.debug "Parsing error response as JSON"
417
+ message = Array(JSON.parse(response.body)['error']).join(', ')
390
418
  else
391
- log.debug " Detected response as text/plain"
419
+ log.debug "Detected response as text/plain"
392
420
  message = response.body
393
421
  end
394
422
 
@@ -405,6 +433,8 @@ module ChefAPI
405
433
  raise Error::HTTPMethodNotAllowed.new(message: message)
406
434
  when 406
407
435
  raise Error::HTTPNotAcceptable.new(message: message)
436
+ when 504
437
+ raise Error::HTTPGatewayTimeout.new(message: message)
408
438
  when 500..600
409
439
  raise Error::HTTPServerUnavailable.new
410
440
  else
@@ -418,7 +448,7 @@ module ChefAPI
418
448
  # @param [Net::HTTP::Request] request
419
449
  #
420
450
  def add_request_headers(request)
421
- log.info "===> Adding request headers..."
451
+ log.info "Adding request headers..."
422
452
 
423
453
  headers = {
424
454
  'Accept' => 'application/json',
@@ -430,7 +460,7 @@ module ChefAPI
430
460
  }
431
461
 
432
462
  headers.each do |key, value|
433
- log.debug " #{key}: #{value}"
463
+ log.debug "#{key}: #{value}"
434
464
  request[key] = value
435
465
  end
436
466
  end
@@ -441,9 +471,9 @@ module ChefAPI
441
471
  # @param [Net::HTTP::Request] request
442
472
  #
443
473
  def add_signing_headers(verb, uri, request, key)
444
- log.info "===> Adding signed header authentication..."
474
+ log.info "Adding signed header authentication..."
445
475
 
446
- unless defined?(Mixlib::Authentication)
476
+ unless defined?(Mixlib::Authentication::SignedHeaderAuth)
447
477
  require 'mixlib/authentication/signedheaderauth'
448
478
  end
449
479
 
@@ -451,14 +481,14 @@ module ChefAPI
451
481
  :http_method => verb,
452
482
  :body => request.body || '',
453
483
  :host => "#{uri.host}:#{uri.port}",
454
- :path => request.path,
484
+ :path => uri.path,
455
485
  :timestamp => Time.now.utc.iso8601,
456
486
  :user_id => client,
457
487
  :file => '',
458
488
  ).sign(key)
459
489
 
460
490
  headers.each do |key, value|
461
- log.debug " #{key}: #{value}"
491
+ log.debug "#{key}: #{value}"
462
492
  request[key] = value
463
493
  end
464
494
  end
@@ -3,7 +3,7 @@ require 'chef-api/version'
3
3
  module ChefAPI
4
4
  module Defaults
5
5
  # Default API endpoint
6
- ENDPOINT = 'http://localhost:4000/'.freeze
6
+ ENDPOINT = 'https://api.opscode.com/'.freeze
7
7
 
8
8
  # Default User Agent header string
9
9
  USER_AGENT = "ChefAPI Ruby Gem #{ChefAPI::VERSION}".freeze
@@ -21,22 +21,42 @@ module ChefAPI
21
21
  #
22
22
  # The endpoint where the Chef Server lives. This is equivalent to the
23
23
  # +chef_server_url+ in Chef terminology. If you are using Enterprise
24
- # Hosted Chef or Enterprise Chef on premise, this endpoint includes your
25
- # organization name, such as:
24
+ # Hosted Chef or Enterprise Chef on premise, this endpoint should include
25
+ # your organization name. For example:
26
26
  #
27
- # https://api.opscode.com/organizations/NAME
27
+ # https://api.opscode.com/organizations/bacon
28
28
  #
29
- # If you are running Open Source Chef Server or Chef Zero, this is just
30
- # the URL to your Chef Server instance, such as:
29
+ # If you are running Open Source Chef Server or Chef Zero, this is the
30
+ # full URL to your Chef Server instance, including the server port and
31
+ # FQDN.
31
32
  #
32
- # http://chef.example.com/
33
+ # https://chef.server.local:4567/
33
34
  #
34
- # @return [String]
35
+ # @return [String] (default: +https://api.opscode.com/+)
35
36
  #
36
37
  def endpoint
37
38
  ENV['CHEF_API_ENDPOINT'] || ENDPOINT
38
39
  end
39
40
 
41
+ #
42
+ # The flavor of the target Chef Server. There are two possible values:
43
+ #
44
+ # - enterprise
45
+ # - open_source
46
+ #
47
+ # "Enterprise" covers both Hosted Chef and Enterprise Chef. "Open Source"
48
+ # covers both Chef Zero and Open Source Chef Server.
49
+ #
50
+ # @return [true, false]
51
+ #
52
+ def flavor
53
+ if ENV['CHEF_API_FLAVOR']
54
+ ENV['CHEF_API_FLAVOR'].to_sym
55
+ else
56
+ endpoint.include?('/organizations') ? :enterprise : :open_source
57
+ end
58
+ end
59
+
40
60
  #
41
61
  # The User Agent header to send along.
42
62
  #
@@ -1,11 +1,39 @@
1
+ require 'erb'
2
+
1
3
  module ChefAPI
2
4
  module Error
5
+ class ErrorBinding
6
+ def initialize(options = {})
7
+ options.each do |key, value|
8
+ instance_variable_set(:"@#{key}", value)
9
+ end
10
+ end
11
+
12
+ def get_binding
13
+ binding
14
+ end
15
+ end
16
+
3
17
  class ChefAPIError < StandardError
4
18
  def initialize(options = {})
5
- class_name = self.class.to_s.split('::').last
6
- error_key = Util.underscore(class_name)
19
+ @options = options
20
+ @filename = options.delete(:_template)
21
+
22
+ super()
23
+ end
7
24
 
8
- super I18n.t("chef_api.errors.#{error_key}", options)
25
+ def message
26
+ erb = ERB.new(File.read(template))
27
+ erb.result(ErrorBinding.new(@options).get_binding)
28
+ end
29
+ alias_method :to_s, :message
30
+
31
+ private
32
+
33
+ def template
34
+ class_name = self.class.to_s.split('::').last
35
+ filename = @filename || Util.underscore(class_name)
36
+ ChefAPI.root.join('templates', 'errors', "#{filename}.erb")
9
37
  end
10
38
  end
11
39
 
@@ -16,6 +44,7 @@ module ChefAPI
16
44
  class HTTPError < ChefAPIError; end
17
45
  class HTTPBadRequest < HTTPError; end
18
46
  class HTTPForbiddenRequest < HTTPError; end
47
+ class HTTPGatewayTimeout < HTTPError; end
19
48
  class HTTPNotAcceptable < HTTPError; end
20
49
  class HTTPNotFound < HTTPError; end
21
50
  class HTTPMethodNotAllowed < HTTPError; end
@@ -9,6 +9,7 @@ module ChefAPI
9
9
  autoload :DataBagItem, 'chef-api/resources/data_bag_item'
10
10
  autoload :Environment, 'chef-api/resources/environment'
11
11
  autoload :Node, 'chef-api/resources/node'
12
+ autoload :Organization, 'chef-api/resources/organization'
12
13
  autoload :Principal, 'chef-api/resources/principal'
13
14
  autoload :Role, 'chef-api/resources/role'
14
15
  autoload :User, 'chef-api/resources/user'