Class: Chef::RunContext

Inherits:
Object
  • Object
show all
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

Classes: ChildRunContext, CookbookCompiler

Instance Attribute Summary collapse

Instance Method Summary collapse

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:


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/chef/run_context.rb', line 166

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:


129
130
131
# File 'lib/chef/run_context.rb', line 129

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:


155
156
157
# File 'lib/chef/run_context.rb', line 155

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:


147
148
149
# File 'lib/chef/run_context.rb', line 147

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:


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

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

#runnerObject

Pointer back to the Chef::Runner that created this


116
117
118
# File 'lib/chef/run_context.rb', line 116

def runner
  @runner
end

Instance Method Details

#add_delayed_action(notification) ⇒ Object

Adds a delayed action to the +delayed_actions+.


246
247
248
249
250
251
252
253
# File 'lib/chef/run_context.rb', line 246

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

260
261
262
# File 'lib/chef/run_context.rb', line 260

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

#cancel_rebootObject


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

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

#create_childObject

Create a child RunContext.


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

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

279
280
281
# File 'lib/chef/run_context.rb', line 279

def delayed_notifications(resource)
  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:


505
506
507
508
# File 'lib/chef/run_context.rb', line 505

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:


490
491
492
493
# File 'lib/chef/run_context.rb', line 490

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

269
270
271
# File 'lib/chef/run_context.rb', line 269

def immediate_notifications(resource)
  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:


296
297
298
299
300
301
302
303
304
# File 'lib/chef/run_context.rb', line 296

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


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

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:


190
191
192
193
# File 'lib/chef/run_context.rb', line 190

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:


321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/chef/run_context.rb', line 321

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:


356
357
358
359
360
361
362
363
364
365
# File 'lib/chef/run_context.rb', line 356

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.


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

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


416
417
418
# File 'lib/chef/run_context.rb', line 416

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.


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

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.


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

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.


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

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.


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

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:


402
403
404
# File 'lib/chef/run_context.rb', line 402

def loaded_recipes
  loaded_recipes_hash.keys
end

#notifies_before(notification) ⇒ Object

Adds an before notification to the +before_notification_collection+.

Parameters:


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

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:


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

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:


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

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:


534
535
536
537
538
539
540
541
542
543
544
545
# File 'lib/chef/run_context.rb', line 534

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)

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

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

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

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:


380
381
382
383
384
385
386
387
388
# File 'lib/chef/run_context.rb', line 380

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?

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

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