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:



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/chef/run_context.rb', line 150

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



100
101
102
# File 'lib/chef/run_context.rb', line 100

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:



113
114
115
# File 'lib/chef/run_context.rb', line 113

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:



139
140
141
# File 'lib/chef/run_context.rb', line 139

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:



131
132
133
# File 'lib/chef/run_context.rb', line 131

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:



122
123
124
# File 'lib/chef/run_context.rb', line 122

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).



95
96
97
# File 'lib/chef/run_context.rb', line 95

def resource_collection
  @resource_collection
end

Instance Method Details

#add_delayed_action(notification) ⇒ Object

Adds a delayed action to the delayed_actions.



236
237
238
239
240
241
242
243
# File 'lib/chef/run_context.rb', line 236

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.

TODO seriously, this is actually wrong. resource.name is not unique, you need the type as well.

Returns:

  • (Array[Notification])


253
254
255
256
257
258
259
# File 'lib/chef/run_context.rb', line 253

def before_notifications(resource)
  if resource.instance_of?(Chef::Resource)
    return before_notification_collection[resource.name]
  else
    return before_notification_collection[resource.declared_key]
  end
end

#cancel_rebootObject



570
571
572
573
# File 'lib/chef/run_context.rb', line 570

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

#create_childObject

Create a child RunContext.



582
583
584
# File 'lib/chef/run_context.rb', line 582

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.

TODO seriously, this is actually wrong. resource.name is not unique, you need the type as well.

Returns:

  • (Array[Notification])


286
287
288
289
290
291
292
# File 'lib/chef/run_context.rb', line 286

def delayed_notifications(resource)
  if resource.instance_of?(Chef::Resource)
    return delayed_notification_collection[resource.name]
  else
    return delayed_notification_collection[resource.declared_key]
  end
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:



516
517
518
519
# File 'lib/chef/run_context.rb', line 516

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:



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

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.

TODO seriously, this is actually wrong. resource.name is not unique, you need the type as well.

Returns:

  • (Array[Notification])


269
270
271
272
273
274
275
# File 'lib/chef/run_context.rb', line 269

def immediate_notifications(resource)
  if resource.instance_of?(Chef::Resource)
    return immediate_notification_collection[resource.name]
  else
    return immediate_notification_collection[resource.declared_key]
  end
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:



307
308
309
310
311
312
313
314
315
# File 'lib/chef/run_context.rb', line 307

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



182
183
184
185
186
187
188
189
# File 'lib/chef/run_context.rb', line 182

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:



174
175
176
177
# File 'lib/chef/run_context.rb', line 174

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:



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/chef/run_context.rb', line 332

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("MissingCookbookDependency:\nRecipe `\#{recipe_name}` is not in the run_list, and cookbook '\#{cookbook_name}'\nis not a dependency of any cookbook in the run_list.  To load this recipe,\nfirst add a dependency on cookbook '\#{cookbook_name}' in the cookbook you're\nincluding it from in that cookbook's metadata.\n")
  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:



367
368
369
370
371
372
373
374
375
376
# File 'lib/chef/run_context.rb', line 367

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.



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

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”.



427
428
429
# File 'lib/chef/run_context.rb', line 427

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.



474
475
476
# File 'lib/chef/run_context.rb', line 474

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.



439
440
441
# File 'lib/chef/run_context.rb', line 439

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.



462
463
464
# File 'lib/chef/run_context.rb', line 462

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.



451
452
453
454
# File 'lib/chef/run_context.rb', line 451

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:



413
414
415
# File 'lib/chef/run_context.rb', line 413

def loaded_recipes
  loaded_recipes_hash.keys
end

#notifies_before(notification) ⇒ Object

Adds an before notification to the before_notification_collection.

Parameters:



196
197
198
199
200
201
202
203
# File 'lib/chef/run_context.rb', line 196

def notifies_before(notification)
  nr = notification.notifying_resource
  if nr.instance_of?(Chef::Resource)
    before_notification_collection[nr.name] << notification
  else
    before_notification_collection[nr.declared_key] << notification
  end
end

#notifies_delayed(notification) ⇒ Object

Adds a delayed notification to the delayed_notification_collection.

Parameters:



224
225
226
227
228
229
230
231
# File 'lib/chef/run_context.rb', line 224

def notifies_delayed(notification)
  nr = notification.notifying_resource
  if nr.instance_of?(Chef::Resource)
    delayed_notification_collection[nr.name] << notification
  else
    delayed_notification_collection[nr.declared_key] << notification
  end
end

#notifies_immediately(notification) ⇒ Object

Adds an immediate notification to the immediate_notification_collection.

Parameters:



210
211
212
213
214
215
216
217
# File 'lib/chef/run_context.rb', line 210

def notifies_immediately(notification)
  nr = notification.notifying_resource
  if nr.instance_of?(Chef::Resource)
    immediate_notification_collection[nr.name] << notification
  else
    immediate_notification_collection[nr.declared_key] << notification
  end
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:



545
546
547
548
549
550
551
552
553
554
555
556
# File 'lib/chef/run_context.rb', line 545

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)


575
576
577
# File 'lib/chef/run_context.rb', line 575

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. ?



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

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:



391
392
393
394
395
396
397
398
399
# File 'lib/chef/run_context.rb', line 391

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

#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?


529
530
531
# File 'lib/chef/run_context.rb', line 529

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