Class: Chef::RunContext

Inherits:
Object
  • Object
show all
Includes:
Deprecated
Defined in:
lib/chef/run_context.rb,
lib/chef/run_context/cookbook_compiler.rb

Overview

Chef::RunContext

Value object that loads and tracks the context of a Chef run

Direct Known Subclasses

ChildRunContext

Defined Under Namespace

Modules: Deprecated Classes: ChildRunContext, CookbookCompiler

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Deprecated

#audits=, #delayed_notification_collection=, #immediate_notification_collection=

Constructor Details

#initialize(node, cookbook_collection, events) ⇒ RunContext

Creates a new Chef::RunContext object and populates its fields. This object gets used by the Chef Server to generate a fully compiled recipe list for a node.

Parameters:



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/chef/run_context.rb', line 161

def initialize(node, cookbook_collection, events)
  @node = node
  @cookbook_collection = cookbook_collection
  @events = events

  node.run_context = self
  node.set_cookbook_attribute

  @definitions = Hash.new
  @loaded_recipes_hash = {}
  @loaded_attributes_hash = {}
  @reboot_info = {}
  @cookbook_compiler = nil
  @delayed_actions = []

  initialize_child_state
end

Instance Attribute Details

#auditsObject (readonly)

The list of control groups to execute during the audit phase



111
112
113
# File 'lib/chef/run_context.rb', line 111

def audits
  @audits
end

#before_notification_collectionHash[String, Array[Chef::Resource::Notification]] (readonly)

A Hash containing the before notifications triggered by resources during the converge phase of the chef run.

Returns:



124
125
126
# File 'lib/chef/run_context.rb', line 124

def before_notification_collection
  @before_notification_collection
end

#cookbook_collectionChef::CookbookCollection (readonly)

The set of cookbooks involved in this run



51
52
53
# File 'lib/chef/run_context.rb', line 51

def cookbook_collection
  @cookbook_collection
end

#definitionsArray[Chef::ResourceDefinition] (readonly)

Resource Definitions for this run. Populated when the files in definitions/ are evaluated (this is triggered by #load).

Returns:



59
60
61
# File 'lib/chef/run_context.rb', line 59

def definitions
  @definitions
end

#delayed_actionsArray[Chef::Resource::Notification] (readonly)

An Array containing the delayed (end of run) notifications triggered by resources during the converge phase of the chef run.

Returns:



150
151
152
# File 'lib/chef/run_context.rb', line 150

def delayed_actions
  @delayed_actions
end

#delayed_notification_collectionHash[String, Array[Chef::Resource::Notification]] (readonly)

A Hash containing the delayed (end of run) notifications triggered by resources during the converge phase of the chef run.

Returns:



142
143
144
# File 'lib/chef/run_context.rb', line 142

def delayed_notification_collection
  @delayed_notification_collection
end

#eventsChef::EventDispatch::Dispatcher (readonly)

Event dispatcher for this run.



66
67
68
# File 'lib/chef/run_context.rb', line 66

def events
  @events
end

#immediate_notification_collectionHash[String, Array[Chef::Resource::Notification]] (readonly)

A Hash containing the immediate notifications triggered by resources during the converge phase of the chef run.

Returns:



133
134
135
# File 'lib/chef/run_context.rb', line 133

def immediate_notification_collection
  @immediate_notification_collection
end

#nodeChef::Node (readonly)

The node for this run

Returns:



44
45
46
# File 'lib/chef/run_context.rb', line 44

def node
  @node
end

#parent_run_contextChef::RunContext (readonly)

The parent run context.

Returns:

  • (Chef::RunContext)

    The parent run context, or nil if this is the root context.



85
86
87
# File 'lib/chef/run_context.rb', line 85

def parent_run_context
  @parent_run_context
end

#reboot_infoHash

Hash of factoids for a reboot request.

Returns:

  • (Hash)


73
74
75
# File 'lib/chef/run_context.rb', line 73

def reboot_info
  @reboot_info
end

#resource_collectionChef::ResourceCollection

The collection of resources intended to be converged (and able to be notified).



106
107
108
# File 'lib/chef/run_context.rb', line 106

def resource_collection
  @resource_collection
end

Instance Method Details

#add_delayed_action(notification) ⇒ Object

Adds a delayed action to the delayed_actions.



241
242
243
244
245
246
247
248
# File 'lib/chef/run_context.rb', line 241

def add_delayed_action(notification)
  if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) }
    Chef::Log.info( "#{notification.notifying_resource} not queuing delayed action #{notification.action} on #{notification.resource}"\
                   " (delayed), as it's already been queued")
  else
    delayed_actions << notification
  end
end

#before_notifications(resource) ⇒ Array[Notification]

Get the list of before notifications sent by the given resource.

Returns:

  • (Array[Notification])


255
256
257
# File 'lib/chef/run_context.rb', line 255

def before_notifications(resource)
  return before_notification_collection[resource.declared_key]
end

#cancel_rebootObject



554
555
556
557
# File 'lib/chef/run_context.rb', line 554

def cancel_reboot
  Chef::Log.info "Changing reboot status from #{reboot_info.inspect} to {}"
  @reboot_info = {}
end

#create_childObject

Create a child RunContext.



566
567
568
# File 'lib/chef/run_context.rb', line 566

def create_child
  ChildRunContext.new(self)
end

#delayed_notifications(resource) ⇒ Array[Notification]

Get the list of delayed (end of run) notifications sent by the given resource.

Returns:

  • (Array[Notification])


274
275
276
# File 'lib/chef/run_context.rb', line 274

def delayed_notifications(resource)
  return delayed_notification_collection[resource.declared_key]
end

#has_cookbook_file_in_cookbook?(cookbook, cb_file_name) ⇒ Boolean

Find out if the cookbook has the given file.

Parameters:

  • cookbook (String)

    Cookbook name.

  • cb_file_name (String)

    File name.

Returns:

  • (Boolean)

    true if the file is in the cookbook, false otherwise.

See Also:



500
501
502
503
# File 'lib/chef/run_context.rb', line 500

def has_cookbook_file_in_cookbook?(cookbook, cb_file_name)
  cookbook = cookbook_collection[cookbook]
  cookbook.has_cookbook_file_for_node?(node, cb_file_name)
end

#has_template_in_cookbook?(cookbook, template_name) ⇒ Boolean

Find out if the cookbook has the given template.

Parameters:

  • cookbook (String)

    Cookbook name.

  • template_name (String)

    Template name.

Returns:

  • (Boolean)

    true if the template is in the cookbook, false otherwise.

See Also:



485
486
487
488
# File 'lib/chef/run_context.rb', line 485

def has_template_in_cookbook?(cookbook, template_name)
  cookbook = cookbook_collection[cookbook]
  cookbook.has_template_for_node?(node, template_name)
end

#immediate_notifications(resource) ⇒ Array[Notification]

Get the list of immediate notifications sent by the given resource.

Returns:

  • (Array[Notification])


264
265
266
# File 'lib/chef/run_context.rb', line 264

def immediate_notifications(resource)
  return immediate_notification_collection[resource.declared_key]
end

#include_recipe(*recipe_names, current_cookbook: nil) ⇒ Object

Evaluates the recipes recipe_names. Used by DSL::IncludeRecipe

Parameters:

  • recipe_names (Array[String])

    The list of recipe names (e.g. ‘my_cookbook’ or ‘my_cookbook::my_resource’).

  • current_cookbook (defaults to: nil)

    The cookbook we are currently running in.

See Also:



291
292
293
294
295
296
297
298
299
# File 'lib/chef/run_context.rb', line 291

def include_recipe(*recipe_names, current_cookbook: nil)
  result_recipes = Array.new
  recipe_names.flatten.each do |recipe_name|
    if result = load_recipe(recipe_name, current_cookbook: current_cookbook)
      result_recipes << result
    end
  end
  result_recipes
end

#initialize_child_stateObject

Initialize state that applies to both Chef::RunContext and Chef::ChildRunContext



193
194
195
196
197
198
199
200
# File 'lib/chef/run_context.rb', line 193

def initialize_child_state
  @audits = {}
  @resource_collection = Chef::ResourceCollection.new(self)
  @before_notification_collection = Hash.new { |h, k| h[k] = [] }
  @immediate_notification_collection = Hash.new { |h, k| h[k] = [] }
  @delayed_notification_collection = Hash.new { |h, k| h[k] = [] }
  @delayed_actions = []
end

#load(run_list_expansion) ⇒ Object

Triggers the compile phase of the chef run.

Parameters:

See Also:



185
186
187
188
# File 'lib/chef/run_context.rb', line 185

def load(run_list_expansion)
  @cookbook_compiler = CookbookCompiler.new(self, run_list_expansion, events)
  cookbook_compiler.compile
end

#load_recipe(recipe_name, current_cookbook: nil) ⇒ Object

Evaluates the recipe recipe_name. Used by DSL::IncludeRecipe

TODO I am sort of confused why we have both this and include_recipe …

I don't see anything different beyond accepting and returning an
array of recipes.

Parameters:

  • recipe_names (Array[String])

    The recipe name (e.g ‘my_cookbook’ or ‘my_cookbook::my_resource’).

  • current_cookbook (defaults to: nil)

    The cookbook we are currently running in.

Returns:

  • A truthy value if the load occurred; false if already loaded.

See Also:



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/chef/run_context.rb', line 316

def load_recipe(recipe_name, current_cookbook: nil)
  Chef::Log.debug("Loading recipe #{recipe_name} via include_recipe")

  cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name, current_cookbook: current_cookbook)

  if unreachable_cookbook?(cookbook_name) # CHEF-4367
    Chef::Log.warn(<<-ERROR_MESSAGE)
MissingCookbookDependency:
Recipe `#{recipe_name}` is not in the run_list, and cookbook '#{cookbook_name}'
is not a dependency of any cookbook in the run_list.  To load this recipe,
first add a dependency on cookbook '#{cookbook_name}' in the cookbook you're
including it from in that cookbook's metadata.
ERROR_MESSAGE
  end

  if loaded_fully_qualified_recipe?(cookbook_name, recipe_short_name)
    Chef::Log.debug("I am not loading #{recipe_name}, because I have already seen it.")
    false
  else
    loaded_recipe(cookbook_name, recipe_short_name)
    node.loaded_recipe(cookbook_name, recipe_short_name)
    cookbook = cookbook_collection[cookbook_name]
    cookbook.load_recipe(recipe_short_name, self)
  end
end

#load_recipe_file(recipe_file) ⇒ Chef::Recipe

Load the given recipe from a filename.

Parameters:

  • recipe_file (String)

    The recipe filename.

Returns:

Raises:



351
352
353
354
355
356
357
358
359
360
# File 'lib/chef/run_context.rb', line 351

def load_recipe_file(recipe_file)
  if !File.exist?(recipe_file)
    raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
  end

  Chef::Log.debug("Loading recipe file #{recipe_file}")
  recipe = Chef::Recipe.new("@recipe_files", recipe_file, self)
  recipe.from_file(recipe_file)
  recipe
end

#loaded_attribute(cookbook, attribute_file) ⇒ Object

Mark a given attribute file as having been loaded.

Parameters:

  • cookbook (String)

    Cookbook name.

  • attribute_file (String)

    Attribute file name.



468
469
470
# File 'lib/chef/run_context.rb', line 468

def loaded_attribute(cookbook, attribute_file)
  loaded_attributes_hash["#{cookbook}::#{attribute_file}"] = true
end

#loaded_attributesArray[String]

A list of all attributes files that have been loaded.

Stored internally using a Hash, so order is predictable.

TODO is the above statement true in a 1.9+ ruby world? Is it relevant?

Returns:

  • (Array[String])

    A list of attribute file names in fully qualified form, e.g. the “nginx” will be given as “nginx::default”.



411
412
413
# File 'lib/chef/run_context.rb', line 411

def loaded_attributes
  loaded_attributes_hash.keys
end

#loaded_fully_qualified_attribute?(cookbook, attribute_file) ⇒ Boolean

Find out if a given attribute file has been loaded.

Parameters:

  • cookbook (String)

    Cookbook name.

  • attribute_file (String)

    Attribute file name.

Returns:

  • (Boolean)

    true if the recipe has been loaded, false otherwise.



458
459
460
# File 'lib/chef/run_context.rb', line 458

def loaded_fully_qualified_attribute?(cookbook, attribute_file)
  loaded_attributes_hash.has_key?("#{cookbook}::#{attribute_file}")
end

#loaded_fully_qualified_recipe?(cookbook, recipe) ⇒ Boolean

Find out if a given recipe has been loaded.

Parameters:

  • cookbook (String)

    Cookbook name.

  • recipe (String)

    Recipe name.

Returns:

  • (Boolean)

    true if the recipe has been loaded, false otherwise.



423
424
425
# File 'lib/chef/run_context.rb', line 423

def loaded_fully_qualified_recipe?(cookbook, recipe)
  loaded_recipes_hash.has_key?("#{cookbook}::#{recipe}")
end

#loaded_recipe(cookbook, recipe) ⇒ Object

Mark a given recipe as having been loaded.

Parameters:

  • cookbook (String)

    Cookbook name.

  • recipe (String)

    Recipe name.



446
447
448
# File 'lib/chef/run_context.rb', line 446

def loaded_recipe(cookbook, recipe)
  loaded_recipes_hash["#{cookbook}::#{recipe}"] = true
end

#loaded_recipe?(recipe) ⇒ Boolean

Find out if a given recipe has been loaded.

Parameters:

  • recipe (String)

    Recipe name. “nginx” and “nginx::default” yield the same results.

Returns:

  • (Boolean)

    true if the recipe has been loaded, false otherwise.



435
436
437
438
# File 'lib/chef/run_context.rb', line 435

def loaded_recipe?(recipe)
  cookbook, recipe_name = Chef::Recipe.parse_recipe_name(recipe)
  loaded_fully_qualified_recipe?(cookbook, recipe_name)
end

#loaded_recipesArray[String]

A list of all recipes that have been loaded.

This is stored internally as a Hash, so ordering is predictable.

TODO is the above statement true in a 1.9+ ruby world? Is it relevant?

Returns:

  • (Array[String])

    A list of recipes in fully qualified form, e.g. the recipe “nginx” will be given as “nginx::default”.

See Also:



397
398
399
# File 'lib/chef/run_context.rb', line 397

def loaded_recipes
  loaded_recipes_hash.keys
end

#notifies_before(notification) ⇒ Object

Adds an before notification to the before_notification_collection.

Parameters:



207
208
209
210
211
212
# File 'lib/chef/run_context.rb', line 207

def notifies_before(notification)
  # Note for the future, notification.notifying_resource may be an instance
  # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
  # with a string value.
  before_notification_collection[notification.notifying_resource.declared_key] << notification
end

#notifies_delayed(notification) ⇒ Object

Adds a delayed notification to the delayed_notification_collection.

Parameters:



231
232
233
234
235
236
# File 'lib/chef/run_context.rb', line 231

def notifies_delayed(notification)
  # Note for the future, notification.notifying_resource may be an instance
  # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
  # with a string value.
  delayed_notification_collection[notification.notifying_resource.declared_key] << notification
end

#notifies_immediately(notification) ⇒ Object

Adds an immediate notification to the immediate_notification_collection.

Parameters:



219
220
221
222
223
224
# File 'lib/chef/run_context.rb', line 219

def notifies_immediately(notification)
  # Note for the future, notification.notifying_resource may be an instance
  # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
  # with a string value.
  immediate_notification_collection[notification.notifying_resource.declared_key] << notification
end

#open_stream(name: nil, **options) {|stream| ... } ⇒ EventDispatch::EventsOutputStream

Open a stream object that can be printed into and will dispatch to events

Parameters:

  • name (String) (defaults to: nil)

    The name of the stream.

  • options (Hash)

    Other options for the stream.

Yields:

  • If a block is passed, it will be run and the stream will be closed afterwards.

Yield Parameters:

Returns:



529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/chef/run_context.rb', line 529

def open_stream(name: nil, **options)
  stream = EventDispatch::EventsOutputStream.new(events, name: name, **options)
  if block_given?
    begin
      yield stream
    ensure
      stream.close
    end
  else
    stream
  end
end

#reboot_requested?Boolean

Returns:

  • (Boolean)


559
560
561
# File 'lib/chef/run_context.rb', line 559

def reboot_requested?
  reboot_info.size > 0
end

#request_reboot(reboot_info) ⇒ Object

there are options for how to handle multiple calls to these functions:

  1. first call always wins (never change reboot_info once set).

  2. last call always wins (happily change reboot_info whenever).

  3. raise an exception on the first conflict.

  4. disable reboot after this run if anyone ever calls :cancel.

  5. raise an exception on any second call.

  6. ?



549
550
551
552
# File 'lib/chef/run_context.rb', line 549

def request_reboot(reboot_info)
  Chef::Log.info "Changing reboot status from #{self.reboot_info.inspect} to #{reboot_info.inspect}"
  @reboot_info = reboot_info
end

#resolve_attribute(cookbook_name, attr_file_name) ⇒ String

Look up an attribute filename.

Parameters:

  • cookbook_name (String)

    The cookbook name of the attribute file.

  • attr_file_name (String)

    The attribute file’s name (not path).

Returns:

Raises:

See Also:



375
376
377
378
379
380
381
382
383
# File 'lib/chef/run_context.rb', line 375

def resolve_attribute(cookbook_name, attr_file_name)
  cookbook = cookbook_collection[cookbook_name]
  raise Chef::Exceptions::CookbookNotFound, "could not find cookbook #{cookbook_name} while loading attribute #{name}" unless cookbook

  attribute_filename = cookbook.attribute_filenames_by_short_filename[attr_file_name]
  raise Chef::Exceptions::AttributeNotFound, "could not find filename for attribute #{attr_file_name} in cookbook #{cookbook_name}" unless attribute_filename

  attribute_filename
end

#root_run_contextChef::RunContext

The root run context.

Returns:



92
93
94
95
96
# File 'lib/chef/run_context.rb', line 92

def root_run_context
  rc = self
  rc = rc.parent_run_context until rc.parent_run_context.nil?
  rc
end

#unreachable_cookbook?(cookbook_name) ⇒ Boolean

Find out whether the given cookbook is in the cookbook dependency graph.

Parameters:

  • cookbook_name (String)

    Cookbook name.

Returns:

  • (Boolean)

    true if the cookbook is reachable, false otherwise.

See Also:

  • CookbookCompiler#unreachable_cookbook?


513
514
515
# File 'lib/chef/run_context.rb', line 513

def unreachable_cookbook?(cookbook_name)
  cookbook_compiler.unreachable_cookbook?(cookbook_name)
end