chef 12.2.1-x86-mingw32 → 12.3.0.rc.0-x86-mingw32

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/ext/win32-eventlog/Rakefile +10 -6
  3. data/lib/chef.rb +1 -0
  4. data/lib/chef/application/apply.rb +5 -0
  5. data/lib/chef/application/client.rb +10 -0
  6. data/lib/chef/application/knife.rb +5 -1
  7. data/lib/chef/application/solo.rb +5 -0
  8. data/lib/chef/chef_class.rb +130 -0
  9. data/lib/chef/client.rb +15 -7
  10. data/lib/chef/config.rb +13 -0
  11. data/lib/chef/event_loggers/windows_eventlog.rb +11 -5
  12. data/lib/chef/http.rb +13 -3
  13. data/lib/chef/http/basic_client.rb +21 -4
  14. data/lib/chef/http/socketless_chef_zero_client.rb +207 -0
  15. data/lib/chef/knife.rb +3 -0
  16. data/lib/chef/knife/bootstrap.rb +1 -1
  17. data/lib/chef/knife/core/status_presenter.rb +12 -11
  18. data/lib/chef/knife/ssh.rb +3 -1
  19. data/lib/chef/knife/status.rb +32 -7
  20. data/lib/chef/local_mode.rb +13 -3
  21. data/lib/chef/mixin/provides.rb +32 -0
  22. data/lib/chef/platform/provider_priority_map.rb +16 -7
  23. data/lib/chef/platform/resource_priority_map.rb +37 -0
  24. data/lib/chef/policy_builder/expand_node_object.rb +14 -0
  25. data/lib/chef/policy_builder/policyfile.rb +0 -1
  26. data/lib/chef/provider.rb +5 -20
  27. data/lib/chef/provider/package/rubygems.rb +4 -1
  28. data/lib/chef/provider/service/macosx.rb +66 -30
  29. data/lib/chef/provider_resolver.rb +10 -5
  30. data/lib/chef/resource.rb +5 -39
  31. data/lib/chef/resource/gem_package.rb +5 -0
  32. data/lib/chef/resource/link.rb +1 -1
  33. data/lib/chef/resource/macosx_service.rb +59 -0
  34. data/lib/chef/resource/remote_file.rb +0 -4
  35. data/lib/chef/resource_resolver.rb +101 -0
  36. data/lib/chef/rest.rb +4 -5
  37. data/lib/chef/search/query.rb +1 -1
  38. data/lib/chef/server_api.rb +1 -0
  39. data/lib/chef/version.rb +1 -1
  40. data/spec/data/lwrp/providers/buck_passer.rb +2 -1
  41. data/spec/data/lwrp/resources/bar.rb +1 -1
  42. data/spec/data/{big_json.json → nested.json} +2 -2
  43. data/spec/functional/event_loggers/windows_eventlog_spec.rb +14 -0
  44. data/spec/functional/resource/execute_spec.rb +1 -1
  45. data/spec/integration/client/client_spec.rb +12 -1
  46. data/spec/integration/client/ipv6_spec.rb +1 -1
  47. data/spec/integration/knife/common_options_spec.rb +3 -3
  48. data/spec/integration/recipes/lwrp_inline_resources_spec.rb +1 -1
  49. data/spec/integration/solo/solo_spec.rb +7 -5
  50. data/spec/unit/application/client_spec.rb +10 -0
  51. data/spec/unit/chef_class_spec.rb +91 -0
  52. data/spec/unit/client_spec.rb +13 -0
  53. data/spec/unit/http/basic_client_spec.rb +43 -6
  54. data/spec/unit/http/socketless_chef_zero_client_spec.rb +174 -0
  55. data/spec/unit/http_spec.rb +14 -0
  56. data/spec/unit/json_compat_spec.rb +7 -20
  57. data/spec/unit/knife/ssh_spec.rb +18 -0
  58. data/spec/unit/knife/status_spec.rb +69 -3
  59. data/spec/unit/knife_spec.rb +5 -0
  60. data/spec/unit/provider/package/rubygems_spec.rb +19 -0
  61. data/spec/unit/provider/service/macosx_spec.rb +230 -203
  62. data/spec/unit/provider_resolver_spec.rb +1 -0
  63. data/spec/unit/recipe_spec.rb +48 -0
  64. data/spec/unit/resource/link_spec.rb +15 -0
  65. data/spec/unit/resource_spec.rb +6 -6
  66. data/spec/unit/rest_spec.rb +9 -0
  67. data/spec/unit/search/query_spec.rb +24 -0
  68. data/spec/unit/shell_spec.rb +3 -1
  69. metadata +16 -9
  70. data/spec/data/big_json_plus_one.json +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93766b12ac46c57581c4945e64dd79619e4dd40b
4
- data.tar.gz: da93669cfe6fd813330a92b2eed059acefa81e89
3
+ metadata.gz: 91f2fc1f5461db9f00c5bb861ba643c8ecba728e
4
+ data.tar.gz: f520f5bbb0057bec905346f923dc3f6cabab4780
5
5
  SHA512:
6
- metadata.gz: 2d54a3105c9732293f92b326602fc6f3d22b7f2b33b71a7bd3689469e4afeae29179a46d0f0b8551b4cca48f9ba80d195a7b3a17f80b5e9834132a8bd4819b53
7
- data.tar.gz: c2985dfe159c46fc1631631a578ebd404a74f6379137a5289fae57e505597eb8b01f70242b128d43502d915a665f07415d9ec5072721e2461cde19316f7473e6
6
+ metadata.gz: 5fdc2cbedacc39566ce05e9b4b448f992d7766eda5f5585d075437987db7058a675147d689a624c1b8f50416740c21643393713ce1fc06ebef0e1ca177969083
7
+ data.tar.gz: 010f4fae462cdeaf79d1bbbbd82bd7d01d19af19af6880b1e8d607ed8aae8bd44d6fa1ea9bfd99dd9a41a052ce1c05c4c7aa2dbaab55035925d3affdd807c0f4
@@ -41,10 +41,14 @@ end
41
41
  task :register => EVT_SHARED_OBJECT do
42
42
  require 'win32/eventlog'
43
43
  dll_file = File.expand_path(EVT_SHARED_OBJECT)
44
- Win32::EventLog.add_event_source(
45
- :source => "Application",
46
- :key_name => "Chef",
47
- :event_message_file => dll_file,
48
- :category_message_file => dll_file
49
- )
44
+ begin
45
+ Win32::EventLog.add_event_source(
46
+ :source => "Application",
47
+ :key_name => "Chef",
48
+ :event_message_file => dll_file,
49
+ :category_message_file => dll_file
50
+ )
51
+ rescue Errno::EIO => e
52
+ puts "Skipping event log registration due to missing privileges: #{e}"
53
+ end
50
54
  end
@@ -32,3 +32,4 @@ require 'chef/run_status'
32
32
  require 'chef/handler'
33
33
  require 'chef/handler/json_file'
34
34
 
35
+ require 'chef/chef_class'
@@ -85,6 +85,11 @@ class Chef::Application::Apply < Chef::Application
85
85
  :default => !Chef::Platform.windows?,
86
86
  :description => "Use colored output, defaults to enabled"
87
87
 
88
+ option :minimal_ohai,
89
+ :long => "--minimal-ohai",
90
+ :description => "Only run the bare minimum ohai plugins chef needs to function",
91
+ :boolean => true
92
+
88
93
  attr_reader :json_attribs
89
94
 
90
95
  def initialize
@@ -253,6 +253,16 @@ class Chef::Application::Client < Chef::Application
253
253
  :description => "Enable audit-mode with `enabled`. Disable audit-mode with `disabled`. Skip converge and only perform audits with `audit-only`",
254
254
  :proc => lambda { |mo| mo.gsub("-", "_").to_sym }
255
255
 
256
+ option :minimal_ohai,
257
+ :long => "--minimal-ohai",
258
+ :description => "Only run the bare minimum ohai plugins chef needs to function",
259
+ :boolean => true
260
+
261
+ option :listen,
262
+ :long => "--[no-]listen",
263
+ :description => "Whether a local mode (-z) server binds to a port",
264
+ :boolean => true
265
+
256
266
  IMMEDIATE_RUN_SIGNAL = "1".freeze
257
267
 
258
268
  attr_reader :chef_client_json
@@ -121,6 +121,11 @@ class Chef::Application::Knife < Chef::Application
121
121
  :long => "--chef-zero-port PORT",
122
122
  :description => "Port (or port range) to start chef-zero on. Port ranges like 1000,1010 or 8889-9999 will try all given ports until one works."
123
123
 
124
+ option :listen,
125
+ :long => "--[no-]listen",
126
+ :description => "Whether a local mode (-z) server binds to a port",
127
+ :boolean => true
128
+
124
129
  option :version,
125
130
  :short => "-v",
126
131
  :long => "--version",
@@ -129,7 +134,6 @@ class Chef::Application::Knife < Chef::Application
129
134
  :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"},
130
135
  :exit => 0
131
136
 
132
-
133
137
  # Run knife
134
138
  def run
135
139
  Mixlib::Log::Formatter.show_time = false
@@ -180,6 +180,11 @@ class Chef::Application::Solo < Chef::Application
180
180
  :description => "Set maximum duration to wait for another client run to finish, default is indefinitely.",
181
181
  :proc => lambda { |s| s.to_i }
182
182
 
183
+ option :minimal_ohai,
184
+ :long => "--minimal-ohai",
185
+ :description => "Only run the bare minimum ohai plugins chef needs to function",
186
+ :boolean => true
187
+
183
188
  attr_reader :chef_client_json
184
189
 
185
190
  def initialize
@@ -0,0 +1,130 @@
1
+ #
2
+ # Author:: Lamont Granquist (<lamont@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ # NOTE: This class is not intended for internal use by the chef-client code. Classes in
19
+ # chef-client should still have objects like the node and run_context injected into them
20
+ # via their initializers. This class is still global state and will complicate writing
21
+ # unit tests for internal Chef objects. It is intended to be used only by recipe code.
22
+
23
+ # NOTE: When adding require lines here you are creating tight coupling on a global that may be
24
+ # included in many different situations and ultimately that ends in tears with circular requires.
25
+ # Note the way that the run_context, provider_priority_map and resource_priority_map are "dependency
26
+ # injected" into this class by other objects and do not reference the class symbols in those files
27
+ # directly and we do not need to require those files here.
28
+
29
+ class Chef
30
+ class << self
31
+
32
+ #
33
+ # Public API
34
+ #
35
+
36
+ # Get the node object
37
+ #
38
+ # @return [Chef::Node] node object of the chef-client run
39
+ attr_reader :node
40
+
41
+ # Get the run context
42
+ #
43
+ # @return [Chef::RunContext] run_context of the chef-client run
44
+ attr_reader :run_context
45
+
46
+ # Get the array of providers associated with a resource_name for the current node
47
+ #
48
+ # @param resource_name [Symbol] name of the resource as a symbol
49
+ # @return [Array<Class>] Priority Array of Provider Classes to use for the resource_name on the node
50
+ def get_provider_priority_array(resource_name)
51
+ @provider_priority_map.get_priority_array(node, resource_name).dup
52
+ end
53
+
54
+ # Get the array of resources associated with a resource_name for the current node
55
+ #
56
+ # @param resource_name [Symbol] name of the resource as a symbol
57
+ # @return [Array<Class>] Priority Array of Resource Classes to use for the resource_name on the node
58
+ def get_resource_priority_array(resource_name)
59
+ @resource_priority_map.get_priority_array(node, resource_name).dup
60
+ end
61
+
62
+ # Set the array of providers associated with a resource_name for the current node
63
+ #
64
+ # @param resource_name [Symbol] name of the resource as a symbol
65
+ # @param priority_array [Array<Class>] Array of Classes to set as the priority for resource_name on the node
66
+ # @param filter [Hash] Chef::Nodearray-style filter
67
+ # @return [Array<Class>] Modified Priority Array of Provider Classes to use for the resource_name on the node
68
+ def set_provider_priority_array(resource_name, priority_array, *filter)
69
+ @provider_priority_map.set_priority_array(resource_name, priority_array, *filter).dup
70
+ end
71
+
72
+ # Get the array of resources associated with a resource_name for the current node
73
+ #
74
+ # @param resource_name [Symbol] name of the resource as a symbol
75
+ # @param priority_array [Array<Class>] Array of Classes to set as the priority for resource_name on the node
76
+ # @param filter [Hash] Chef::Nodearray-style filter
77
+ # @return [Array<Class>] Modified Priority Array of Resource Classes to use for the resource_name on the node
78
+ def set_resource_priority_array(resource_name, priority_array, *filter)
79
+ @resource_priority_map.set_priority_array(resource_name, priority_array, *filter).dup
80
+ end
81
+
82
+ #
83
+ # Dependency Injection API (Private not Public)
84
+ # [ in the ruby sense these have to be public methods, but they are
85
+ # *NOT* for public consumption ]
86
+ #
87
+
88
+ # Sets the resource_priority_map
89
+ #
90
+ # @api private
91
+ # @param resource_priority_map [Chef::Platform::ResourcePriorityMap]
92
+ def set_resource_priority_map(resource_priority_map)
93
+ @resource_priority_map = resource_priority_map
94
+ end
95
+
96
+ # Sets the provider_priority_map
97
+ #
98
+ # @api private
99
+ # @param provider_priority_map [Chef::Platform::providerPriorityMap]
100
+ def set_provider_priority_map(provider_priority_map)
101
+ @provider_priority_map = provider_priority_map
102
+ end
103
+
104
+ # Sets the node object
105
+ #
106
+ # @api private
107
+ # @param node [Chef::Node]
108
+ def set_node(node)
109
+ @node = node
110
+ end
111
+
112
+ # Sets the run_context object
113
+ #
114
+ # @api private
115
+ # @param run_context [Chef::RunContext]
116
+ def set_run_context(run_context)
117
+ @run_context = run_context
118
+ end
119
+
120
+ # Resets the internal state
121
+ #
122
+ # @api private
123
+ def reset!
124
+ @run_context = nil
125
+ @node = nil
126
+ @provider_priority_map = nil
127
+ @resource_priority_map = nil
128
+ end
129
+ end
130
+ end
@@ -166,6 +166,13 @@ class Chef
166
166
  if new_runlist = args.delete(:runlist)
167
167
  @json_attribs["run_list"] = new_runlist
168
168
  end
169
+
170
+ # these slurp in the resource+provider world, so be exceedingly lazy about requiring them
171
+ require 'chef/platform/provider_priority_map' unless defined? Chef::Platform::ProviderPriorityMap
172
+ require 'chef/platform/resource_priority_map' unless defined? Chef::Platform::ResourcePriorityMap
173
+
174
+ Chef.set_provider_priority_map(Chef::Platform::ProviderPriorityMap.instance)
175
+ Chef.set_resource_priority_map(Chef::Platform::ResourcePriorityMap.instance)
169
176
  end
170
177
 
171
178
  def configure_formatters
@@ -224,21 +231,21 @@ class Chef
224
231
  end
225
232
 
226
233
  # Instantiates a Chef::Node object, possibly loading the node's prior state
227
- # when using chef-client. Delegates to policy_builder
228
- #
234
+ # when using chef-client. Delegates to policy_builder. Injects the built node
235
+ # into the Chef class.
229
236
  #
230
- # === Returns
231
- # Chef::Node:: The node object for this chef run
237
+ # @return [Chef::Node] The node object for this Chef run
232
238
  def load_node
233
239
  policy_builder.load_node
234
240
  @node = policy_builder.node
241
+ Chef.set_node(@node)
242
+ node
235
243
  end
236
244
 
237
245
  # Mutates the `node` object to prepare it for the chef run. Delegates to
238
246
  # policy_builder
239
247
  #
240
- # === Returns
241
- # Chef::Node:: The updated node object
248
+ # @return [Chef::Node] The updated node object
242
249
  def build_node
243
250
  policy_builder.build_node
244
251
  @run_status = Chef::RunStatus.new(node, events)
@@ -272,7 +279,8 @@ class Chef
272
279
  end
273
280
 
274
281
  def run_ohai
275
- ohai.all_plugins
282
+ filter = Chef::Config[:minimal_ohai] ? %w[fqdn machinename hostname platform platform_version os os_version] : nil
283
+ ohai.all_plugins(filter)
276
284
  @events.ohai_completed(node)
277
285
  end
278
286
 
@@ -305,6 +305,14 @@ class Chef
305
305
 
306
306
  default :pid_file, nil
307
307
 
308
+ # Whether Chef Zero local mode should bind to a port. All internal requests
309
+ # will go through the socketless code path regardless, so the socket is
310
+ # only needed if other processes will connect to the local mode server.
311
+ #
312
+ # For compatibility this is set to true but it will be changed to false in
313
+ # the future.
314
+ default :listen, true
315
+
308
316
  config_context :chef_zero do
309
317
  config_strict_mode true
310
318
  default(:enabled) { Chef::Config.local_mode }
@@ -333,6 +341,11 @@ class Chef
333
341
  # This can be removed when audit-mode is enabled by default.
334
342
  default :audit_mode, :disabled
335
343
 
344
+ # Chef only needs ohai to run the hostname plugin for the most basic
345
+ # functionality. If the rest of the ohai plugins are not needed (like in
346
+ # most of our testing scenarios)
347
+ default :minimal_ohai, false
348
+
336
349
  # Policyfile is an experimental feature where a node gets its run list and
337
350
  # cookbook version set from a single document on the server instead of
338
351
  # expanding the run list and having the server compute the cookbook version
@@ -88,15 +88,21 @@ class Chef
88
88
  #Exception message: %4
89
89
  #Exception backtrace: %5
90
90
  def run_failed(e)
91
+ data =
92
+ if @run_status
93
+ [@run_status.run_id,
94
+ @run_status.elapsed_time.to_s]
95
+ else
96
+ ["UNKNOWN", "UNKNOWN"]
97
+ end
98
+
91
99
  @eventlog.report_event(
92
100
  :event_type => ::Win32::EventLog::ERROR_TYPE,
93
101
  :source => SOURCE,
94
102
  :event_id => RUN_FAILED_EVENT_ID,
95
- :data => [@run_status.run_id,
96
- @run_status.elapsed_time.to_s,
97
- e.class.name,
98
- e.message,
99
- e.backtrace.join("\n")]
103
+ :data => data + [e.class.name,
104
+ e.message,
105
+ e.backtrace.join("\n")]
100
106
  )
101
107
  end
102
108
 
@@ -25,6 +25,7 @@ require 'tempfile'
25
25
  require 'net/https'
26
26
  require 'uri'
27
27
  require 'chef/http/basic_client'
28
+ require 'chef/http/socketless_chef_zero_client'
28
29
  require 'chef/monkey_patches/net_http'
29
30
  require 'chef/config'
30
31
  require 'chef/platform/query_helpers'
@@ -196,14 +197,18 @@ class Chef
196
197
 
197
198
  def http_client(base_url=nil)
198
199
  base_url ||= url
199
- BasicClient.new(base_url)
200
+ if chef_zero_uri?(base_url)
201
+ SocketlessChefZeroClient.new(base_url)
202
+ else
203
+ BasicClient.new(base_url, :ssl_policy => Chef::HTTP::APISSLPolicy)
204
+ end
200
205
  end
201
206
 
202
207
  protected
203
208
 
204
209
  def create_url(path)
205
210
  return path if path.is_a?(URI)
206
- if path =~ /^(http|https):\/\//i
211
+ if path =~ /^(http|https|chefzero):\/\//i
207
212
  URI.parse(path)
208
213
  elsif path.nil? or path.empty?
209
214
  URI.parse(@url)
@@ -292,7 +297,7 @@ class Chef
292
297
  http_attempts += 1
293
298
  response, request, return_value = yield
294
299
  # handle HTTP 50X Error
295
- if response.kind_of?(Net::HTTPServerError)
300
+ if response.kind_of?(Net::HTTPServerError) && !Chef::Config.local_mode
296
301
  if http_retry_count - http_attempts + 1 > 0
297
302
  sleep_time = 1 + (2 ** http_attempts) + rand(2 ** http_attempts)
298
303
  Chef::Log.error("Server returned error #{response.code} for #{url}, retrying #{http_attempts}/#{http_retry_count} in #{sleep_time}s")
@@ -351,6 +356,11 @@ class Chef
351
356
 
352
357
  private
353
358
 
359
+ def chef_zero_uri?(uri)
360
+ uri = URI.parse(uri) unless uri.respond_to?(:scheme)
361
+ uri.scheme == "chefzero"
362
+ end
363
+
354
364
  def redirected_to(response)
355
365
  return nil unless response.kind_of?(Net::HTTPRedirection)
356
366
  # Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this
@@ -97,7 +97,9 @@ class Chef
97
97
 
98
98
  #adapted from buildr/lib/buildr/core/transports.rb
99
99
  def proxy_uri
100
- proxy = Chef::Config["#{url.scheme}_proxy"]
100
+ proxy = Chef::Config["#{url.scheme}_proxy"] ||
101
+ env["#{url.scheme.upcase}_PROXY"] || env["#{url.scheme}_proxy"]
102
+
101
103
  # Check if the proxy string contains a scheme. If not, add the url's scheme to the
102
104
  # proxy before parsing. The regex /^.*:\/\// matches, for example, http://.
103
105
  proxy = if proxy.match(/^.*:\/\//)
@@ -105,7 +107,8 @@ class Chef
105
107
  else
106
108
  URI.parse("#{url.scheme}://#{proxy}")
107
109
  end if String === proxy
108
- excludes = Chef::Config[:no_proxy].to_s.split(/\s*,\s*/).compact
110
+ no_proxy = Chef::Config[:no_proxy] || env['NO_PROXY'] || env['no_proxy']
111
+ excludes = no_proxy.to_s.split(/\s*,\s*/).compact
109
112
  excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" }
110
113
  return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") }
111
114
  end
@@ -126,18 +129,32 @@ class Chef
126
129
  Chef::Config
127
130
  end
128
131
 
132
+ def env
133
+ ENV
134
+ end
135
+
129
136
  def http_client_builder
130
137
  http_proxy = proxy_uri
131
138
  if http_proxy.nil?
132
139
  Net::HTTP
133
140
  else
134
141
  Chef::Log.debug("Using #{http_proxy.host}:#{http_proxy.port} for proxy")
135
- user = Chef::Config["#{url.scheme}_proxy_user"]
136
- pass = Chef::Config["#{url.scheme}_proxy_pass"]
142
+ user = http_proxy_user(http_proxy)
143
+ pass = http_proxy_pass(http_proxy)
137
144
  Net::HTTP.Proxy(http_proxy.host, http_proxy.port, user, pass)
138
145
  end
139
146
  end
140
147
 
148
+ def http_proxy_user(http_proxy)
149
+ http_proxy.user || Chef::Config["#{url.scheme}_proxy_user"] ||
150
+ env["#{url.scheme.upcase}_PROXY_USER"] || env["#{url.scheme}_proxy_user"]
151
+ end
152
+
153
+ def http_proxy_pass(http_proxy)
154
+ http_proxy.password || Chef::Config["#{url.scheme}_proxy_pass"] ||
155
+ env["#{url.scheme.upcase}_PROXY_PASS"] || env["#{url.scheme}_proxy_pass"]
156
+ end
157
+
141
158
  def configure_ssl(http_client)
142
159
  http_client.use_ssl = true
143
160
  ssl_policy.apply_to(http_client)
@@ -0,0 +1,207 @@
1
+ #--
2
+ # Author:: Daniel DeLeo (<dan@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ # ---
19
+ # Some portions of the code in this file are verbatim copies of code from the
20
+ # fakeweb project: https://github.com/chrisk/fakeweb
21
+ #
22
+ # fakeweb is distributed under the MIT license, which is copied below:
23
+ # ---
24
+ #
25
+ # Copyright 2006-2010 Blaine Cook, Chris Kampmeier, and other contributors
26
+ #
27
+ # Permission is hereby granted, free of charge, to any person obtaining
28
+ # a copy of this software and associated documentation files (the
29
+ # "Software"), to deal in the Software without restriction, including
30
+ # without limitation the rights to use, copy, modify, merge, publish,
31
+ # distribute, sublicense, and/or sell copies of the Software, and to
32
+ # permit persons to whom the Software is furnished to do so, subject to
33
+ # the following conditions:
34
+
35
+ # The above copyright notice and this permission notice shall be
36
+ # included in all copies or substantial portions of the Software.
37
+
38
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
39
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
40
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
41
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
42
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
43
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
44
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45
+
46
+ require 'chef_zero/server'
47
+
48
+ class Chef
49
+ class HTTP
50
+
51
+ # HTTP Client class that talks directly to Zero via the Rack interface.
52
+ class SocketlessChefZeroClient
53
+
54
+ # This module is extended into Net::HTTP Response objects created from
55
+ # Socketless Chef Zero responses.
56
+ module ResponseExts
57
+
58
+ # Net::HTTP raises an error if #read_body is called with a block or
59
+ # file argument after the body has already been read from the network.
60
+ #
61
+ # Since we always set the body to the string response from Chef Zero
62
+ # and set the `@read` indicator variable, we have to patch this method
63
+ # or else streaming-style responses won't work.
64
+ def read_body(dest = nil, &block)
65
+ if dest
66
+ raise "responses from socketless chef zero can't be written to specific destination"
67
+ end
68
+
69
+ if block_given?
70
+ block.call(@body)
71
+ else
72
+ super
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ attr_reader :url
79
+
80
+ # copied verbatim from webrick (2-clause BSD License)
81
+ #
82
+ # HTTP status codes and descriptions
83
+ STATUS_MESSAGE = {
84
+ 100 => 'Continue',
85
+ 101 => 'Switching Protocols',
86
+ 200 => 'OK',
87
+ 201 => 'Created',
88
+ 202 => 'Accepted',
89
+ 203 => 'Non-Authoritative Information',
90
+ 204 => 'No Content',
91
+ 205 => 'Reset Content',
92
+ 206 => 'Partial Content',
93
+ 207 => 'Multi-Status',
94
+ 300 => 'Multiple Choices',
95
+ 301 => 'Moved Permanently',
96
+ 302 => 'Found',
97
+ 303 => 'See Other',
98
+ 304 => 'Not Modified',
99
+ 305 => 'Use Proxy',
100
+ 307 => 'Temporary Redirect',
101
+ 400 => 'Bad Request',
102
+ 401 => 'Unauthorized',
103
+ 402 => 'Payment Required',
104
+ 403 => 'Forbidden',
105
+ 404 => 'Not Found',
106
+ 405 => 'Method Not Allowed',
107
+ 406 => 'Not Acceptable',
108
+ 407 => 'Proxy Authentication Required',
109
+ 408 => 'Request Timeout',
110
+ 409 => 'Conflict',
111
+ 410 => 'Gone',
112
+ 411 => 'Length Required',
113
+ 412 => 'Precondition Failed',
114
+ 413 => 'Request Entity Too Large',
115
+ 414 => 'Request-URI Too Large',
116
+ 415 => 'Unsupported Media Type',
117
+ 416 => 'Request Range Not Satisfiable',
118
+ 417 => 'Expectation Failed',
119
+ 422 => 'Unprocessable Entity',
120
+ 423 => 'Locked',
121
+ 424 => 'Failed Dependency',
122
+ 426 => 'Upgrade Required',
123
+ 428 => 'Precondition Required',
124
+ 429 => 'Too Many Requests',
125
+ 431 => 'Request Header Fields Too Large',
126
+ 500 => 'Internal Server Error',
127
+ 501 => 'Not Implemented',
128
+ 502 => 'Bad Gateway',
129
+ 503 => 'Service Unavailable',
130
+ 504 => 'Gateway Timeout',
131
+ 505 => 'HTTP Version Not Supported',
132
+ 507 => 'Insufficient Storage',
133
+ 511 => 'Network Authentication Required',
134
+ }
135
+
136
+ STATUS_MESSAGE.values.each {|v| v.freeze }
137
+ STATUS_MESSAGE.freeze
138
+
139
+ def initialize(base_url)
140
+ @url = base_url
141
+ end
142
+
143
+ def host
144
+ @url.hostname
145
+ end
146
+
147
+ def port
148
+ @url.port
149
+ end
150
+
151
+ def request(method, url, body, headers, &handler_block)
152
+ request = req_to_rack(method, url, body, headers)
153
+ res = ChefZero::SocketlessServerMap.request(port, request)
154
+
155
+ net_http_response = to_net_http(res[0], res[1], res[2])
156
+
157
+ yield net_http_response if block_given?
158
+
159
+ [self, net_http_response]
160
+ end
161
+
162
+ def req_to_rack(method, url, body, headers)
163
+ body_str = body || ""
164
+ {
165
+ "SCRIPT_NAME" => "",
166
+ "SERVER_NAME" => "localhost",
167
+ "REQUEST_METHOD" => method.to_s.upcase,
168
+ "PATH_INFO" => url.path,
169
+ "QUERY_STRING" => url.query,
170
+ "SERVER_PORT" => url.port,
171
+ "HTTP_HOST" => "localhost:#{url.port}",
172
+ "rack.url_scheme" => "chefzero",
173
+ "rack.input" => StringIO.new(body_str),
174
+ }
175
+ end
176
+
177
+ def to_net_http(code, headers, chunked_body)
178
+ body = chunked_body.join('')
179
+ msg = STATUS_MESSAGE[code] or raise "Cannot determine HTTP status message for code #{code}"
180
+ response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
181
+ response.instance_variable_set(:@body, body)
182
+ headers.each do |name, value|
183
+ if value.respond_to?(:each)
184
+ value.each { |v| response.add_field(name, v) }
185
+ else
186
+ response[name] = value
187
+ end
188
+ end
189
+
190
+ response.instance_variable_set(:@read, true)
191
+ response.extend(ResponseExts)
192
+ response
193
+ end
194
+
195
+ private
196
+
197
+ def headers_extracted_from_options
198
+ options.reject {|name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value|
199
+ [name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value]
200
+ }
201
+ end
202
+
203
+
204
+ end
205
+
206
+ end
207
+ end