chef 12.4.3 → 12.5.0.alpha.1

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -2
  3. data/lib/chef.rb +1 -1
  4. data/lib/chef/application/solo.rb +1 -1
  5. data/lib/chef/application/windows_service_manager.rb +17 -12
  6. data/lib/chef/chef_class.rb +7 -0
  7. data/lib/chef/chef_fs/config.rb +22 -24
  8. data/lib/chef/chef_fs/file_pattern.rb +4 -15
  9. data/lib/chef/chef_fs/file_system/cookbook_dir.rb +1 -0
  10. data/lib/chef/chef_fs/knife.rb +35 -7
  11. data/lib/chef/chef_fs/path_utils.rb +65 -34
  12. data/lib/chef/constants.rb +27 -0
  13. data/lib/chef/delayed_evaluator.rb +21 -0
  14. data/lib/chef/dsl/recipe.rb +20 -2
  15. data/lib/chef/event_dispatch/base.rb +40 -16
  16. data/lib/chef/event_dispatch/dsl.rb +64 -0
  17. data/lib/chef/exceptions.rb +6 -1
  18. data/lib/chef/formatters/doc.rb +3 -1
  19. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +3 -1
  20. data/lib/chef/http/http_request.rb +1 -1
  21. data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
  22. data/lib/chef/knife/ssl_check.rb +3 -2
  23. data/lib/chef/knife/user_edit.rb +1 -2
  24. data/lib/chef/mixin/params_validate.rb +362 -135
  25. data/lib/chef/node.rb +19 -0
  26. data/lib/chef/platform/handler_map.rb +0 -5
  27. data/lib/chef/platform/rebooter.rb +1 -1
  28. data/lib/chef/property.rb +539 -0
  29. data/lib/chef/provider.rb +129 -12
  30. data/lib/chef/provider/deploy.rb +3 -5
  31. data/lib/chef/provider/lwrp_base.rb +1 -75
  32. data/lib/chef/provider/package.rb +1 -1
  33. data/lib/chef/provider/powershell_script.rb +32 -19
  34. data/lib/chef/provider/registry_key.rb +5 -5
  35. data/lib/chef/provider/service/macosx.rb +5 -1
  36. data/lib/chef/recipe.rb +1 -8
  37. data/lib/chef/resource.rb +499 -84
  38. data/lib/chef/resource/file/verification.rb +7 -1
  39. data/lib/chef/resource/lwrp_base.rb +1 -7
  40. data/lib/chef/run_context.rb +404 -83
  41. data/lib/chef/version.rb +1 -1
  42. data/lib/chef/win32/registry.rb +10 -2
  43. data/lib/chef/workstation_config_loader.rb +3 -158
  44. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -0
  45. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -0
  46. data/spec/functional/rebooter_spec.rb +1 -1
  47. data/spec/functional/resource/{powershell_spec.rb → powershell_script_spec.rb} +3 -3
  48. data/spec/functional/win32/registry_helper_spec.rb +12 -0
  49. data/spec/functional/win32/service_manager_spec.rb +2 -2
  50. data/spec/integration/knife/chef_repo_path_spec.rb +13 -11
  51. data/spec/integration/recipes/recipe_dsl_spec.rb +0 -15
  52. data/spec/integration/recipes/resource_action_spec.rb +343 -0
  53. data/spec/spec_helper.rb +1 -0
  54. data/spec/support/shared/functional/win32_service.rb +2 -1
  55. data/spec/unit/application/solo_spec.rb +4 -3
  56. data/spec/unit/chef_class_spec.rb +23 -0
  57. data/spec/unit/chef_fs/path_util_spec.rb +108 -0
  58. data/spec/unit/event_dispatch/dsl_spec.rb +87 -0
  59. data/spec/unit/json_compat_spec.rb +4 -3
  60. data/spec/unit/knife/ssl_check_spec.rb +4 -0
  61. data/spec/unit/mixin/params_validate_spec.rb +4 -2
  62. data/spec/unit/node_spec.rb +7 -0
  63. data/spec/unit/property/state_spec.rb +506 -0
  64. data/spec/unit/property/validation_spec.rb +658 -0
  65. data/spec/unit/property_spec.rb +968 -0
  66. data/spec/unit/provider/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  67. data/spec/unit/provider/registry_key_spec.rb +12 -0
  68. data/spec/unit/provider/service/macosx_spec.rb +4 -4
  69. data/spec/unit/provider_spec.rb +1 -3
  70. data/spec/unit/recipe_spec.rb +0 -4
  71. data/spec/unit/registry_helper_spec.rb +15 -1
  72. data/spec/unit/resource/file/verification_spec.rb +33 -5
  73. data/spec/unit/resource/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  74. data/spec/unit/resource_spec.rb +2 -2
  75. data/spec/unit/run_context/child_run_context_spec.rb +133 -0
  76. data/spec/unit/run_context_spec.rb +7 -0
  77. metadata +25 -25
  78. data/spec/unit/workstation_config_loader_spec.rb +0 -283
@@ -106,7 +106,13 @@ class Chef
106
106
  # We reuse Chef::GuardInterpreter in order to support
107
107
  # the same set of options that the not_if/only_if blocks do
108
108
  def verify_command(path, opts)
109
- command = @command % {:file => path}
109
+ # First implementation interpolated `file`; docs & RFC claim `path`
110
+ # is interpolated. Until `file` can be deprecated, interpolate both.
111
+ Chef::Log.deprecation(
112
+ '%{file} is deprecated in verify command and will not be '\
113
+ 'supported in Chef 13. Please use %{path} instead.'
114
+ ) if @command.include?('%{file}')
115
+ command = @command % {:file => path, :path => path}
110
116
  interpreter = Chef::GuardInterpreter.for_resource(@parent_resource, command, @command_opts)
111
117
  interpreter.evaluate
112
118
  end
@@ -74,13 +74,7 @@ class Chef
74
74
  resource_class
75
75
  end
76
76
 
77
- # Define an attribute on this resource, including optional validation
78
- # parameters.
79
- def attribute(attr_name, validation_opts={})
80
- define_method(attr_name) do |arg=nil|
81
- set_or_return(attr_name.to_sym, arg, validation_opts)
82
- end
83
- end
77
+ alias :attribute :property
84
78
 
85
79
  # Adds +action_names+ to the list of valid actions for this resource.
86
80
  # Does not include superclass's action list when appending.
@@ -25,118 +25,223 @@ require 'chef/log'
25
25
  require 'chef/recipe'
26
26
  require 'chef/run_context/cookbook_compiler'
27
27
  require 'chef/event_dispatch/events_output_stream'
28
+ require 'forwardable'
28
29
 
29
30
  class Chef
30
31
 
31
32
  # == Chef::RunContext
32
33
  # Value object that loads and tracks the context of a Chef run
33
34
  class RunContext
35
+ #
36
+ # Global state
37
+ #
34
38
 
35
- # Chef::Node object for this run
39
+ #
40
+ # The node for this run
41
+ #
42
+ # @return [Chef::Node]
43
+ #
36
44
  attr_reader :node
37
45
 
38
- # Chef::CookbookCollection for this run
46
+ #
47
+ # The set of cookbooks involved in this run
48
+ #
49
+ # @return [Chef::CookbookCollection]
50
+ #
39
51
  attr_reader :cookbook_collection
40
52
 
53
+ #
41
54
  # Resource Definitions for this run. Populated when the files in
42
55
  # +definitions/+ are evaluated (this is triggered by #load).
56
+ #
57
+ # @return [Array[Chef::ResourceDefinition]]
58
+ #
43
59
  attr_reader :definitions
44
60
 
45
- ###
46
- # These need to be settable so deploy can run a resource_collection
47
- # independent of any cookbooks via +recipe_eval+
61
+ #
62
+ # Event dispatcher for this run.
63
+ #
64
+ # @return [Chef::EventDispatch::Dispatcher]
65
+ #
66
+ attr_reader :events
67
+
68
+ #
69
+ # Hash of factoids for a reboot request.
70
+ #
71
+ # @return [Hash]
72
+ #
73
+ attr_accessor :reboot_info
74
+
75
+ #
76
+ # Scoped state
77
+ #
48
78
 
49
- # The Chef::ResourceCollection for this run. Populated by evaluating
50
- # recipes, which is triggered by #load. (See also: CookbookCompiler)
51
- attr_accessor :resource_collection
79
+ #
80
+ # The parent run context.
81
+ #
82
+ # @return [Chef::RunContext] The parent run context, or `nil` if this is the
83
+ # root context.
84
+ #
85
+ attr_reader :parent_run_context
52
86
 
87
+ #
88
+ # The collection of resources intended to be converged (and able to be
89
+ # notified).
90
+ #
91
+ # @return [Chef::ResourceCollection]
92
+ #
93
+ # @see CookbookCompiler
94
+ #
95
+ attr_reader :resource_collection
96
+
97
+ #
53
98
  # The list of control groups to execute during the audit phase
54
- attr_accessor :audits
99
+ #
100
+ attr_reader :audits
101
+
102
+ #
103
+ # Notification handling
104
+ #
55
105
 
106
+ #
56
107
  # A Hash containing the immediate notifications triggered by resources
57
108
  # during the converge phase of the chef run.
58
- attr_accessor :immediate_notification_collection
109
+ #
110
+ # @return [Hash[String, Array[Chef::Resource::Notification]]] A hash from
111
+ # <notifying resource name> => <list of notifications it sent>
112
+ #
113
+ attr_reader :immediate_notification_collection
59
114
 
115
+ #
60
116
  # A Hash containing the delayed (end of run) notifications triggered by
61
117
  # resources during the converge phase of the chef run.
62
- attr_accessor :delayed_notification_collection
63
-
64
- # Event dispatcher for this run.
65
- attr_reader :events
66
-
67
- # Hash of factoids for a reboot request.
68
- attr_reader :reboot_info
118
+ #
119
+ # @return [Hash[String, Array[Chef::Resource::Notification]]] A hash from
120
+ # <notifying resource name> => <list of notifications it sent>
121
+ #
122
+ attr_reader :delayed_notification_collection
69
123
 
70
124
  # Creates a new Chef::RunContext object and populates its fields. This object gets
71
125
  # used by the Chef Server to generate a fully compiled recipe list for a node.
72
126
  #
73
- # === Returns
74
- # object<Chef::RunContext>:: Duh. :)
127
+ # @param node [Chef::Node] The node to run against.
128
+ # @param cookbook_collection [Chef::CookbookCollection] The cookbooks
129
+ # involved in this run.
130
+ # @param events [EventDispatch::Dispatcher] The event dispatcher for this
131
+ # run.
132
+ #
75
133
  def initialize(node, cookbook_collection, events)
76
134
  @node = node
77
135
  @cookbook_collection = cookbook_collection
78
- @resource_collection = Chef::ResourceCollection.new
79
- @audits = {}
80
- @immediate_notification_collection = Hash.new {|h,k| h[k] = []}
81
- @delayed_notification_collection = Hash.new {|h,k| h[k] = []}
82
- @definitions = Hash.new
83
- @loaded_recipes = {}
84
- @loaded_attributes = {}
85
136
  @events = events
86
- @reboot_info = {}
87
137
 
88
- @node.run_context = self
89
- @node.set_cookbook_attribute
138
+ node.run_context = self
139
+ node.set_cookbook_attribute
140
+
141
+ @definitions = Hash.new
142
+ @loaded_recipes_hash = {}
143
+ @loaded_attributes_hash = {}
144
+ @reboot_info = {}
90
145
  @cookbook_compiler = nil
146
+
147
+ initialize_child_state
91
148
  end
92
149
 
93
- # Triggers the compile phase of the chef run. Implemented by
94
- # Chef::RunContext::CookbookCompiler
150
+ #
151
+ # Triggers the compile phase of the chef run.
152
+ #
153
+ # @param run_list_expansion [Chef::RunList::RunListExpansion] The run list.
154
+ # @see Chef::RunContext::CookbookCompiler
155
+ #
95
156
  def load(run_list_expansion)
96
157
  @cookbook_compiler = CookbookCompiler.new(self, run_list_expansion, events)
97
- @cookbook_compiler.compile
158
+ cookbook_compiler.compile
98
159
  end
99
160
 
100
- # Adds an immediate notification to the
101
- # +immediate_notification_collection+. The notification should be a
102
- # Chef::Resource::Notification or duck type.
161
+ #
162
+ # Initialize state that applies to both Chef::RunContext and Chef::ChildRunContext
163
+ #
164
+ def initialize_child_state
165
+ @audits = {}
166
+ @resource_collection = Chef::ResourceCollection.new
167
+ @immediate_notification_collection = Hash.new {|h,k| h[k] = []}
168
+ @delayed_notification_collection = Hash.new {|h,k| h[k] = []}
169
+ end
170
+
171
+ #
172
+ # Adds an immediate notification to the +immediate_notification_collection+.
173
+ #
174
+ # @param [Chef::Resource::Notification] The notification to add.
175
+ #
103
176
  def notifies_immediately(notification)
104
177
  nr = notification.notifying_resource
105
178
  if nr.instance_of?(Chef::Resource)
106
- @immediate_notification_collection[nr.name] << notification
179
+ immediate_notification_collection[nr.name] << notification
107
180
  else
108
- @immediate_notification_collection[nr.declared_key] << notification
181
+ immediate_notification_collection[nr.declared_key] << notification
109
182
  end
110
183
  end
111
184
 
112
- # Adds a delayed notification to the +delayed_notification_collection+. The
113
- # notification should be a Chef::Resource::Notification or duck type.
185
+ #
186
+ # Adds a delayed notification to the +delayed_notification_collection+.
187
+ #
188
+ # @param [Chef::Resource::Notification] The notification to add.
189
+ #
114
190
  def notifies_delayed(notification)
115
191
  nr = notification.notifying_resource
116
192
  if nr.instance_of?(Chef::Resource)
117
- @delayed_notification_collection[nr.name] << notification
193
+ delayed_notification_collection[nr.name] << notification
118
194
  else
119
- @delayed_notification_collection[nr.declared_key] << notification
195
+ delayed_notification_collection[nr.declared_key] << notification
120
196
  end
121
197
  end
122
198
 
199
+ #
200
+ # Get the list of immediate notifications sent by the given resource.
201
+ #
202
+ # TODO seriously, this is actually wrong. resource.name is not unique,
203
+ # you need the type as well.
204
+ #
205
+ # @return [Array[Notification]]
206
+ #
123
207
  def immediate_notifications(resource)
124
208
  if resource.instance_of?(Chef::Resource)
125
- return @immediate_notification_collection[resource.name]
209
+ return immediate_notification_collection[resource.name]
126
210
  else
127
- return @immediate_notification_collection[resource.declared_key]
211
+ return immediate_notification_collection[resource.declared_key]
128
212
  end
129
213
  end
130
214
 
215
+ #
216
+ # Get the list of delayed (end of run) notifications sent by the given
217
+ # resource.
218
+ #
219
+ # TODO seriously, this is actually wrong. resource.name is not unique,
220
+ # you need the type as well.
221
+ #
222
+ # @return [Array[Notification]]
223
+ #
131
224
  def delayed_notifications(resource)
132
225
  if resource.instance_of?(Chef::Resource)
133
- return @delayed_notification_collection[resource.name]
226
+ return delayed_notification_collection[resource.name]
134
227
  else
135
- return @delayed_notification_collection[resource.declared_key]
228
+ return delayed_notification_collection[resource.declared_key]
136
229
  end
137
230
  end
138
231
 
232
+ #
233
+ # Cookbook and recipe loading
234
+ #
235
+
236
+ #
139
237
  # Evaluates the recipes +recipe_names+. Used by DSL::IncludeRecipe
238
+ #
239
+ # @param recipe_names [Array[String]] The list of recipe names (e.g.
240
+ # 'my_cookbook' or 'my_cookbook::my_resource').
241
+ # @param current_cookbook The cookbook we are currently running in.
242
+ #
243
+ # @see DSL::IncludeRecipe#include_recipe
244
+ #
140
245
  def include_recipe(*recipe_names, current_cookbook: nil)
141
246
  result_recipes = Array.new
142
247
  recipe_names.flatten.each do |recipe_name|
@@ -147,7 +252,21 @@ class Chef
147
252
  result_recipes
148
253
  end
149
254
 
255
+ #
150
256
  # Evaluates the recipe +recipe_name+. Used by DSL::IncludeRecipe
257
+ #
258
+ # TODO I am sort of confused why we have both this and include_recipe ...
259
+ # I don't see anything different beyond accepting and returning an
260
+ # array of recipes.
261
+ #
262
+ # @param recipe_names [Array[String]] The recipe name (e.g 'my_cookbook' or
263
+ # 'my_cookbook::my_resource').
264
+ # @param current_cookbook The cookbook we are currently running in.
265
+ #
266
+ # @return A truthy value if the load occurred; `false` if already loaded.
267
+ #
268
+ # @see DSL::IncludeRecipe#load_recipe
269
+ #
151
270
  def load_recipe(recipe_name, current_cookbook: nil)
152
271
  Chef::Log.debug("Loading Recipe #{recipe_name} via include_recipe")
153
272
 
@@ -175,6 +294,15 @@ ERROR_MESSAGE
175
294
  end
176
295
  end
177
296
 
297
+ #
298
+ # Load the given recipe from a filename.
299
+ #
300
+ # @param recipe_file [String] The recipe filename.
301
+ #
302
+ # @return [Chef::Recipe] The loaded recipe.
303
+ #
304
+ # @raise [Chef::Exceptions::RecipeNotFound] If the file does not exist.
305
+ #
178
306
  def load_recipe_file(recipe_file)
179
307
  if !File.exist?(recipe_file)
180
308
  raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
@@ -186,8 +314,19 @@ ERROR_MESSAGE
186
314
  recipe
187
315
  end
188
316
 
189
- # Looks up an attribute file given the +cookbook_name+ and
190
- # +attr_file_name+. Used by DSL::IncludeAttribute
317
+ #
318
+ # Look up an attribute filename.
319
+ #
320
+ # @param cookbook_name [String] The cookbook name of the attribute file.
321
+ # @param attr_file_name [String] The attribute file's name (not path).
322
+ #
323
+ # @return [String] The filename.
324
+ #
325
+ # @see DSL::IncludeAttribute#include_attribute
326
+ #
327
+ # @raise [Chef::Exceptions::CookbookNotFound] If the cookbook could not be found.
328
+ # @raise [Chef::Exceptions::AttributeNotFound] If the attribute file could not be found.
329
+ #
191
330
  def resolve_attribute(cookbook_name, attr_file_name)
192
331
  cookbook = cookbook_collection[cookbook_name]
193
332
  raise Chef::Exceptions::CookbookNotFound, "could not find cookbook #{cookbook_name} while loading attribute #{name}" unless cookbook
@@ -198,76 +337,152 @@ ERROR_MESSAGE
198
337
  attribute_filename
199
338
  end
200
339
 
201
- # An Array of all recipes that have been loaded. This is stored internally
202
- # as a Hash, so ordering is predictable.
203
340
  #
204
- # Recipe names are given in fully qualified form, e.g., the recipe "nginx"
205
- # will be given as "nginx::default"
341
+ # A list of all recipes that have been loaded.
342
+ #
343
+ # This is stored internally as a Hash, so ordering is predictable.
344
+ #
345
+ # TODO is the above statement true in a 1.9+ ruby world? Is it relevant?
346
+ #
347
+ # @return [Array[String]] A list of recipes in fully qualified form, e.g.
348
+ # the recipe "nginx" will be given as "nginx::default".
349
+ #
350
+ # @see #loaded_recipe? To determine if a particular recipe has been loaded.
206
351
  #
207
- # To determine if a particular recipe has been loaded, use #loaded_recipe?
208
352
  def loaded_recipes
209
- @loaded_recipes.keys
353
+ loaded_recipes_hash.keys
210
354
  end
211
355
 
212
- # An Array of all attributes files that have been loaded. Stored internally
213
- # using a Hash, so order is predictable.
214
356
  #
215
- # Attribute file names are given in fully qualified form, e.g.,
216
- # "nginx::default" instead of "nginx".
357
+ # A list of all attributes files that have been loaded.
358
+ #
359
+ # Stored internally using a Hash, so order is predictable.
360
+ #
361
+ # TODO is the above statement true in a 1.9+ ruby world? Is it relevant?
362
+ #
363
+ # @return [Array[String]] A list of attribute file names in fully qualified
364
+ # form, e.g. the "nginx" will be given as "nginx::default".
365
+ #
217
366
  def loaded_attributes
218
- @loaded_attributes.keys
367
+ loaded_attributes_hash.keys
219
368
  end
220
369
 
370
+ #
371
+ # Find out if a given recipe has been loaded.
372
+ #
373
+ # @param cookbook [String] Cookbook name.
374
+ # @param recipe [String] Recipe name.
375
+ #
376
+ # @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
377
+ #
221
378
  def loaded_fully_qualified_recipe?(cookbook, recipe)
222
- @loaded_recipes.has_key?("#{cookbook}::#{recipe}")
379
+ loaded_recipes_hash.has_key?("#{cookbook}::#{recipe}")
223
380
  end
224
381
 
225
- # Returns true if +recipe+ has been loaded, false otherwise. Default recipe
226
- # names are expanded, so `loaded_recipe?("nginx")` and
227
- # `loaded_recipe?("nginx::default")` are valid and give identical results.
382
+ #
383
+ # Find out if a given recipe has been loaded.
384
+ #
385
+ # @param recipe [String] Recipe name. "nginx" and "nginx::default" yield
386
+ # the same results.
387
+ #
388
+ # @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
389
+ #
228
390
  def loaded_recipe?(recipe)
229
391
  cookbook, recipe_name = Chef::Recipe.parse_recipe_name(recipe)
230
392
  loaded_fully_qualified_recipe?(cookbook, recipe_name)
231
393
  end
232
394
 
395
+ #
396
+ # Mark a given recipe as having been loaded.
397
+ #
398
+ # @param cookbook [String] Cookbook name.
399
+ # @param recipe [String] Recipe name.
400
+ #
401
+ def loaded_recipe(cookbook, recipe)
402
+ loaded_recipes_hash["#{cookbook}::#{recipe}"] = true
403
+ end
404
+
405
+ #
406
+ # Find out if a given attribute file has been loaded.
407
+ #
408
+ # @param cookbook [String] Cookbook name.
409
+ # @param attribute_file [String] Attribute file name.
410
+ #
411
+ # @return [Boolean] `true` if the recipe has been loaded, `false` otherwise.
412
+ #
233
413
  def loaded_fully_qualified_attribute?(cookbook, attribute_file)
234
- @loaded_attributes.has_key?("#{cookbook}::#{attribute_file}")
414
+ loaded_attributes_hash.has_key?("#{cookbook}::#{attribute_file}")
235
415
  end
236
416
 
417
+ #
418
+ # Mark a given attribute file as having been loaded.
419
+ #
420
+ # @param cookbook [String] Cookbook name.
421
+ # @param attribute_file [String] Attribute file name.
422
+ #
237
423
  def loaded_attribute(cookbook, attribute_file)
238
- @loaded_attributes["#{cookbook}::#{attribute_file}"] = true
424
+ loaded_attributes_hash["#{cookbook}::#{attribute_file}"] = true
239
425
  end
240
426
 
241
427
  ##
242
428
  # Cookbook File Introspection
243
429
 
430
+ #
431
+ # Find out if the cookbook has the given template.
432
+ #
433
+ # @param cookbook [String] Cookbook name.
434
+ # @param template_name [String] Template name.
435
+ #
436
+ # @return [Boolean] `true` if the template is in the cookbook, `false`
437
+ # otherwise.
438
+ # @see Chef::CookbookVersion#has_template_for_node?
439
+ #
244
440
  def has_template_in_cookbook?(cookbook, template_name)
245
441
  cookbook = cookbook_collection[cookbook]
246
442
  cookbook.has_template_for_node?(node, template_name)
247
443
  end
248
444
 
445
+ #
446
+ # Find out if the cookbook has the given file.
447
+ #
448
+ # @param cookbook [String] Cookbook name.
449
+ # @param cb_file_name [String] File name.
450
+ #
451
+ # @return [Boolean] `true` if the file is in the cookbook, `false`
452
+ # otherwise.
453
+ # @see Chef::CookbookVersion#has_cookbook_file_for_node?
454
+ #
249
455
  def has_cookbook_file_in_cookbook?(cookbook, cb_file_name)
250
456
  cookbook = cookbook_collection[cookbook]
251
457
  cookbook.has_cookbook_file_for_node?(node, cb_file_name)
252
458
  end
253
459
 
254
- # Delegates to CookbookCompiler#unreachable_cookbook?
255
- # Used to raise an error when attempting to load a recipe belonging to a
256
- # cookbook that is not in the dependency graph. See also: CHEF-4367
460
+ #
461
+ # Find out whether the given cookbook is in the cookbook dependency graph.
462
+ #
463
+ # @param cookbook_name [String] Cookbook name.
464
+ #
465
+ # @return [Boolean] `true` if the cookbook is reachable, `false` otherwise.
466
+ #
467
+ # @see Chef::CookbookCompiler#unreachable_cookbook?
257
468
  def unreachable_cookbook?(cookbook_name)
258
- @cookbook_compiler.unreachable_cookbook?(cookbook_name)
469
+ cookbook_compiler.unreachable_cookbook?(cookbook_name)
259
470
  end
260
471
 
472
+ #
261
473
  # Open a stream object that can be printed into and will dispatch to events
262
474
  #
263
- # == Arguments
264
- # options is a hash with these possible options:
265
- # - name: a string that identifies the stream to the user. Preferably short.
475
+ # @param name [String] The name of the stream.
476
+ # @param options [Hash] Other options for the stream.
477
+ #
478
+ # @return [EventDispatch::EventsOutputStream] The created stream.
479
+ #
480
+ # @yield If a block is passed, it will be run and the stream will be closed
481
+ # afterwards.
482
+ # @yieldparam stream [EventDispatch::EventsOutputStream] The created stream.
266
483
  #
267
- # Pass a block and the stream will be yielded to it, and close on its own
268
- # at the end of the block.
269
- def open_stream(options = {})
270
- stream = EventDispatch::EventsOutputStream.new(events, options)
484
+ def open_stream(name: nil, **options)
485
+ stream = EventDispatch::EventsOutputStream.new(events, name: name, **options)
271
486
  if block_given?
272
487
  begin
273
488
  yield stream
@@ -280,31 +495,137 @@ ERROR_MESSAGE
280
495
  end
281
496
 
282
497
  # there are options for how to handle multiple calls to these functions:
283
- # 1. first call always wins (never change @reboot_info once set).
284
- # 2. last call always wins (happily change @reboot_info whenever).
498
+ # 1. first call always wins (never change reboot_info once set).
499
+ # 2. last call always wins (happily change reboot_info whenever).
285
500
  # 3. raise an exception on the first conflict.
286
501
  # 4. disable reboot after this run if anyone ever calls :cancel.
287
502
  # 5. raise an exception on any second call.
288
503
  # 6. ?
289
504
  def request_reboot(reboot_info)
290
- Chef::Log::info "Changing reboot status from #{@reboot_info.inspect} to #{reboot_info.inspect}"
505
+ Chef::Log::info "Changing reboot status from #{self.reboot_info.inspect} to #{reboot_info.inspect}"
291
506
  @reboot_info = reboot_info
292
507
  end
293
508
 
294
509
  def cancel_reboot
295
- Chef::Log::info "Changing reboot status from #{@reboot_info.inspect} to {}"
510
+ Chef::Log::info "Changing reboot status from #{reboot_info.inspect} to {}"
296
511
  @reboot_info = {}
297
512
  end
298
513
 
299
514
  def reboot_requested?
300
- @reboot_info.size > 0
515
+ reboot_info.size > 0
301
516
  end
302
517
 
303
- private
518
+ #
519
+ # Create a child RunContext.
520
+ #
521
+ def create_child
522
+ ChildRunContext.new(self)
523
+ end
304
524
 
305
- def loaded_recipe(cookbook, recipe)
306
- @loaded_recipes["#{cookbook}::#{recipe}"] = true
525
+ protected
526
+
527
+ attr_reader :cookbook_compiler
528
+ attr_reader :loaded_attributes_hash
529
+ attr_reader :loaded_recipes_hash
530
+
531
+ module Deprecated
532
+ ###
533
+ # These need to be settable so deploy can run a resource_collection
534
+ # independent of any cookbooks via +recipe_eval+
535
+ def resource_collection=(value)
536
+ Chef::Log.deprecation("Setting run_context.resource_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
537
+ @resource_collection = value
538
+ end
539
+
540
+ def audits=(value)
541
+ Chef::Log.deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
542
+ @audits = value
543
+ end
544
+
545
+ def immediate_notification_collection=(value)
546
+ Chef::Log.deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
547
+ @immediate_notification_collection = value
548
+ end
549
+
550
+ def delayed_notification_collection=(value)
551
+ Chef::Log.deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
552
+ @delayed_notification_collection = value
553
+ end
307
554
  end
555
+ prepend Deprecated
556
+
557
+
558
+ #
559
+ # A child run context. Delegates all root context calls to its parent.
560
+ #
561
+ # @api private
562
+ #
563
+ class ChildRunContext < RunContext
564
+ extend Forwardable
565
+ def_delegators :parent_run_context, *%w(
566
+ cancel_reboot
567
+ config
568
+ cookbook_collection
569
+ cookbook_compiler
570
+ definitions
571
+ events
572
+ has_cookbook_file_in_cookbook?
573
+ has_template_in_cookbook?
574
+ load
575
+ loaded_attribute
576
+ loaded_attributes
577
+ loaded_attributes_hash
578
+ loaded_fully_qualified_attribute?
579
+ loaded_fully_qualified_recipe?
580
+ loaded_recipe
581
+ loaded_recipe?
582
+ loaded_recipes
583
+ loaded_recipes_hash
584
+ node
585
+ open_stream
586
+ reboot_info
587
+ reboot_info=
588
+ reboot_requested?
589
+ request_reboot
590
+ resolve_attribute
591
+ unreachable_cookbook?
592
+ )
593
+
594
+ def initialize(parent_run_context)
595
+ @parent_run_context = parent_run_context
596
+
597
+ # We don't call super, because we don't bother initializing stuff we're
598
+ # going to delegate to the parent anyway. Just initialize things that
599
+ # every instance needs.
600
+ initialize_child_state
601
+ end
308
602
 
603
+ CHILD_STATE = %w(
604
+ audits
605
+ audits=
606
+ create_child
607
+ delayed_notification_collection
608
+ delayed_notification_collection=
609
+ delayed_notifications
610
+ immediate_notification_collection
611
+ immediate_notification_collection=
612
+ immediate_notifications
613
+ include_recipe
614
+ initialize_child_state
615
+ load_recipe
616
+ load_recipe_file
617
+ notifies_immediately
618
+ notifies_delayed
619
+ parent_run_context
620
+ resource_collection
621
+ resource_collection=
622
+ ).map { |x| x.to_sym }
623
+
624
+ # Verify that we didn't miss any methods
625
+ missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE
626
+ if !missing_methods.empty?
627
+ raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}."
628
+ end
629
+ end
309
630
  end
310
631
  end