class Chef::RunContext

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

Attributes

action_collection[RW]
before_notification_collection[R]

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

@return [Hash[String, Array]] A hash from

<notifying resource name> => <list of notifications it sent>
cookbook_collection[RW]

The set of cookbooks involved in this run

@return [Chef::CookbookCollection]

cookbook_compiler[R]
definitions[R]

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

@return [Array]

delayed_actions[R]

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

@return [Array] An array of notification objects

delayed_notification_collection[R]

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

@return [Hash[String, Array]] A hash from

<notifying resource name> => <list of notifications it sent>
events[RW]

Event dispatcher for this run.

@return [Chef::EventDispatch::Dispatcher]

immediate_notification_collection[R]

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

@return [Hash[String, Array]] A hash from

<notifying resource name> => <list of notifications it sent>
loaded_attributes_hash[R]
loaded_recipes_hash[R]
logger[R]

A child of the root Chef::Log logging object.

@return Mixlib::Log::Child A child logger

node[R]

The node for this run

@return [Chef::Node]

parent_run_context[R]

The parent run context.

@return [Chef::RunContext] The parent run context, or `nil` if this is the

root context.
reboot_info[RW]

Hash of factoids for a reboot request.

@return [Hash]

resource_collection[RW]

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

@return [Chef::ResourceCollection]

@see CookbookCompiler

rest[RW]

Common rest object for using to talk to the Chef Server, this strictly 'validates' utf8 and will throw. (will be nil on solo-legacy runs)

@return [Chef::ServerAPI]

rest_clean[RW]

Common rest object for using to talk to the Chef Server, this has utf8 sanitization turned on and will replace invalid utf8 with valid characters. (will be nil on solo-legacy runs)

@return [Chef::ServerAPI]

runner[RW]

Pointer back to the Chef::Runner that created this

Public Class Methods

new(node = nil, cookbook_collection = {}, events = nil, logger = nil) click to toggle source

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.

@param node [Chef::Node] The node to run against. @param cookbook_collection [Chef::CookbookCollection] The cookbooks

involved in this run.

@param events [EventDispatch::Dispatcher] The event dispatcher for this

run.
# File lib/chef/run_context.rb, line 183
def initialize(node = nil, cookbook_collection = {}, events = nil, logger = nil)
  @events = events
  @logger = logger || Chef::Log.with_child
  @cookbook_collection = cookbook_collection
  self.node = node if node
  @definitions = Hash.new
  @loaded_recipes_hash = {}
  @loaded_attributes_hash = {}
  @reboot_info = {}
  @cookbook_compiler = nil
  @delayed_actions = []

  initialize_child_state
end

Public Instance Methods

add_delayed_action(notification) click to toggle source

Adds a delayed action to the delayed_actions.

# File lib/chef/run_context.rb, line 265
def add_delayed_action(notification)
  if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) }
    logger.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) click to toggle source

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

@return [Array]

# File lib/chef/run_context.rb, line 279
def before_notifications(resource)
  before_notification_collection[resource.declared_key]
end
cancel_reboot() click to toggle source

Cancels a pending reboot

# File lib/chef/run_context.rb, line 581
def cancel_reboot
  logger.info "Changing reboot status from #{reboot_info.inspect} to {}"
  @reboot_info = {}
end
create_child() click to toggle source

Create a child RunContext.

# File lib/chef/run_context.rb, line 613
def create_child
  ChildRunContext.new(self)
end
delayed_notifications(resource) click to toggle source

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

@return [Array]

# File lib/chef/run_context.rb, line 298
def delayed_notifications(resource)
  delayed_notification_collection[resource.declared_key]
end
has_cookbook_file_in_cookbook?(cookbook, cb_file_name) click to toggle source

Find out if the cookbook has the given file.

@param cookbook [String] Cookbook name. @param cb_file_name [String] File name.

@return [Boolean] `true` if the file is in the cookbook, `false`

otherwise.

@see Chef::CookbookVersion#has_cookbook_file_for_node?

# File lib/chef/run_context.rb, line 524
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) click to toggle source

Find out if the cookbook has the given template.

@param cookbook [String] Cookbook name. @param template_name [String] Template name.

@return [Boolean] `true` if the template is in the cookbook, `false`

otherwise.

@see Chef::CookbookVersion#has_template_for_node?

# File lib/chef/run_context.rb, line 509
def has_template_in_cookbook?(cookbook, template_name)
  cookbook = cookbook_collection[cookbook]
  cookbook.has_template_for_node?(node, template_name)
end
immediate_notifications(resource) click to toggle source

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

@return [Array]

# File lib/chef/run_context.rb, line 288
def immediate_notifications(resource)
  immediate_notification_collection[resource.declared_key]
end
include_recipe(*recipe_names, current_cookbook: nil) click to toggle source

Evaluates the recipes recipe_names. Used by DSL::IncludeRecipe

@param recipe_names [Array] The list of recipe names (e.g.

'my_cookbook' or 'my_cookbook::my_resource').

@param current_cookbook The cookbook we are currently running in.

@see DSL::IncludeRecipe#include_recipe

# File lib/chef/run_context.rb, line 315
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_state() click to toggle source

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

# File lib/chef/run_context.rb, line 218
def initialize_child_state
  @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) click to toggle source

Triggers the compile phase of the chef run.

@param run_list_expansion [Chef::RunList::RunListExpansion] The run list. @see Chef::RunContext::CookbookCompiler

# File lib/chef/run_context.rb, line 210
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) click to toggle source

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.

@param recipe_name [Array] The recipe name (e.g 'my_cookbook' or

'my_cookbook::my_resource').

@param current_cookbook [String] The cookbook we are currently running in.

@return A truthy value if the load occurred; `false` if already loaded.

@see DSL::IncludeRecipe#load_recipe

# File lib/chef/run_context.rb, line 340
    def load_recipe(recipe_name, current_cookbook: nil)
      logger.trace("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
        logger.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)
        logger.trace("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) click to toggle source

Load the given recipe from a filename.

@param recipe_file [String] The recipe filename.

@return [Chef::Recipe] The loaded recipe.

@raise [Chef::Exceptions::RecipeNotFound] If the file does not exist.

# File lib/chef/run_context.rb, line 375
def load_recipe_file(recipe_file)
  if !File.exist?(recipe_file)
    raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
  end

  logger.trace("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) click to toggle source

Mark a given attribute file as having been loaded.

@param cookbook [String] Cookbook name. @param attribute_file [String] Attribute file name.

# File lib/chef/run_context.rb, line 492
def loaded_attribute(cookbook, attribute_file)
  loaded_attributes_hash["#{cookbook}::#{attribute_file}"] = true
end
loaded_attributes() click to toggle source

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?

@return [Array] A list of attribute file names in fully qualified

form, e.g. the "nginx" will be given as "nginx::default".
# File lib/chef/run_context.rb, line 435
def loaded_attributes
  loaded_attributes_hash.keys
end
loaded_fully_qualified_attribute?(cookbook, attribute_file) click to toggle source

Find out if a given attribute file has been loaded.

@param cookbook [String] Cookbook name. @param attribute_file [String] Attribute file name.

@return [Boolean] `true` if the recipe has been loaded, `false` otherwise.

# File lib/chef/run_context.rb, line 482
def loaded_fully_qualified_attribute?(cookbook, attribute_file)
  loaded_attributes_hash.key?("#{cookbook}::#{attribute_file}")
end
loaded_fully_qualified_recipe?(cookbook, recipe) click to toggle source

Find out if a given recipe has been loaded.

@param cookbook [String] Cookbook name. @param recipe [String] Recipe name.

@return [Boolean] `true` if the recipe has been loaded, `false` otherwise.

# File lib/chef/run_context.rb, line 447
def loaded_fully_qualified_recipe?(cookbook, recipe)
  loaded_recipes_hash.key?("#{cookbook}::#{recipe}")
end
loaded_recipe(cookbook, recipe) click to toggle source

Mark a given recipe as having been loaded.

@param cookbook [String] Cookbook name. @param recipe [String] Recipe name.

# File lib/chef/run_context.rb, line 470
def loaded_recipe(cookbook, recipe)
  loaded_recipes_hash["#{cookbook}::#{recipe}"] = true
end
loaded_recipe?(recipe) click to toggle source

Find out if a given recipe has been loaded.

@param recipe [String] Recipe name. “nginx” and “nginx::default” yield

the same results.

@return [Boolean] `true` if the recipe has been loaded, `false` otherwise.

# File lib/chef/run_context.rb, line 459
def loaded_recipe?(recipe)
  cookbook, recipe_name = Chef::Recipe.parse_recipe_name(recipe)
  loaded_fully_qualified_recipe?(cookbook, recipe_name)
end
loaded_recipes() click to toggle source

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?

@return [Array] A list of recipes in fully qualified form, e.g.

the recipe "nginx" will be given as "nginx::default".

@see loaded_recipe? To determine if a particular recipe has been loaded.

# File lib/chef/run_context.rb, line 421
def loaded_recipes
  loaded_recipes_hash.keys
end
node=(node) click to toggle source
# File lib/chef/run_context.rb, line 198
def node=(node)
  @node = node
  node.run_context = self
  node.set_cookbook_attribute
end
notifies_before(notification) click to toggle source

Adds an before notification to the before_notification_collection.

@param [Chef::Resource::Notification] notification The notification to add.

# File lib/chef/run_context.rb, line 231
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) click to toggle source

Adds a delayed notification to the delayed_notification_collection.

@param [Chef::Resource::Notification] notification The notification to add.

# File lib/chef/run_context.rb, line 255
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) click to toggle source

Adds an immediate notification to the immediate_notification_collection.

@param [Chef::Resource::Notification] notification The notification to add.

# File lib/chef/run_context.rb, line 243
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| ... } click to toggle source

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

@param name [String] The name of the stream. @param options [Hash] Other options for the stream.

@return [EventDispatch::EventsOutputStream] The created stream.

@yield If a block is passed, it will be run and the stream will be closed

afterwards.

@yieldparam stream [EventDispatch::EventsOutputStream] The created stream.

# File lib/chef/run_context.rb, line 553
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?() click to toggle source

Checks to see if a reboot has been requested @return [Boolean]

# File lib/chef/run_context.rb, line 590
def reboot_requested?
  reboot_info.size > 0
end
request_reboot(reboot_info) click to toggle source

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

# File lib/chef/run_context.rb, line 573
def request_reboot(reboot_info)
  logger.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) click to toggle source

Look up an attribute filename.

@param cookbook_name [String] The cookbook name of the attribute file. @param attr_file_name [String] The attribute file's name (not path).

@return [String] The filename.

@see DSL::IncludeAttribute#include_attribute

@raise [Chef::Exceptions::CookbookNotFound] If the cookbook could not be found. @raise [Chef::Exceptions::AttributeNotFound] If the attribute file could not be found.

# File lib/chef/run_context.rb, line 399
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_context() click to toggle source

The root run context.

@return [Chef::RunContext] The root run context.

# File lib/chef/run_context.rb, line 106
def root_run_context
  rc = self
  rc = rc.parent_run_context until rc.parent_run_context.nil?
  rc
end
transport() click to toggle source

Remote transport from Train

@return [Train::Plugins::Transport] The child class for our train transport.

# File lib/chef/run_context.rb, line 598
def transport
  @transport ||= Chef::TrainTransport.build_transport(logger)
end
transport_connection() click to toggle source

Remote connection object from Train

@return [Train::Plugins::Transport::BaseConnection]

# File lib/chef/run_context.rb, line 606
def transport_connection
  @transport_connection ||= transport.connection
end
unreachable_cookbook?(cookbook_name) click to toggle source

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

@param cookbook_name [String] Cookbook name.

@return [Boolean] `true` if the cookbook is reachable, `false` otherwise.

@see Chef::CookbookCompiler#unreachable_cookbook?

# File lib/chef/run_context.rb, line 537
def unreachable_cookbook?(cookbook_name)
  cookbook_compiler.unreachable_cookbook?(cookbook_name)
end