class Chef::RunContext

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

Attributes

action_collection[RW]

Handle to the global action_collection of executed actions for reporting / data_collector /etc

@return [Chef::ActionCollection]

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[R]

The set of cookbooks involved in this run

@return [Chef::CookbookCollection]

cookbook_compiler[R]
default_secret_config[RW]

@return [Hash<Symbol,Object>]

default_secret_service[RW]

@return [Symbol, nil]

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>
input_collection[RW]

Handle to the global input_collection of inspec input files for the compliance phase

@return [Chef::Compliance::inputCollection]

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.
profile_collection[RW]

Handle to the global profile_collection of inspec profiles for the compliance phase

@return [Chef::Compliance::ProfileCollection]

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

updated_resources[R]

A Set keyed by the string name, of all the resources that are updated. We do not track actions or individual resource objects, since this matches the behavior of the notification collections which are keyed by Strings.

waiver_collection[RW]

Handle to the global waiver_collection of inspec waiver files for the compliance phase

@return [Chef::Compliance::WaiverCollection]

Public Class Methods

new(node = nil, cookbook_collection = nil, 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 222
def initialize(node = nil, cookbook_collection = nil, events = nil, logger = nil)
  @events = events
  @logger = logger || Chef::Log.with_child
  self.node = node if node
  self.cookbook_collection = cookbook_collection if cookbook_collection
  @definitions = {}
  @loaded_recipes_hash = {}
  @loaded_attributes_hash = {}
  @reboot_info = {}
  @cookbook_compiler = nil
  @input_collection = Chef::Compliance::InputCollection.new(events)
  @waiver_collection = Chef::Compliance::WaiverCollection.new(events)
  @profile_collection = Chef::Compliance::ProfileCollection.new(events)
  @default_secret_service = nil
  @default_secret_config = {}

  initialize_child_state
end

Public Instance Methods

add_delayed_action(notification) click to toggle source

Adds a delayed action to the delayed_actions collection

# File lib/chef/run_context.rb, line 319
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 332
def before_notifications(resource)
  key = resource.is_a?(String) ? resource : resource.declared_key
  before_notification_collection[key]
end
cancel_reboot() click to toggle source

Cancels a pending reboot

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

Create a child RunContext.

# File lib/chef/run_context.rb, line 680
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 364
def delayed_notifications(resource)
  key = resource.is_a?(String) ? resource : resource.declared_key
  delayed_notification_collection[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 591
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 576
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 341
def immediate_notifications(resource)
  key = resource.is_a?(String) ? resource : resource.declared_key
  immediate_notification_collection[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 382
def include_recipe(*recipe_names, current_cookbook: nil)
  result_recipes = []
  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 265
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 = []
  @updated_resources = Set.new
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 257
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 407
    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 of the cookbook '#{cookbook_name}' into the metadata
                  of the cookbook which depends on '#{cookbook_name}'.
        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 442
def load_recipe_file(recipe_file)
  unless 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 559
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 502
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 549
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 514
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 537
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 526
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 488
def loaded_recipes
  loaded_recipes_hash.keys
end
node=(node) click to toggle source
# File lib/chef/run_context.rb, line 241
def node=(node)
  @node = node
  node.run_context = self
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 279
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.
  if unified_mode && updated_resources.include?(notification.notifying_resource.declared_key)
    raise Chef::Exceptions::UnifiedModeBeforeSubscriptionEarlierResource.new(notification)
  end

  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 307
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.
  if unified_mode && updated_resources.include?(notification.notifying_resource.declared_key)
    add_delayed_action(notification)
  end
  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 295
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 620
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 657
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 640
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 466
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
reverse_immediate_notifications(resource) click to toggle source

Get the list of immediate notifications pending to the given resource

@return [Array]

# File lib/chef/run_context.rb, line 350
def reverse_immediate_notifications(resource)
  immediate_notification_collection.map do |k, v|
    v.select do |n|
      (n.resource.is_a?(String) && n.resource == resource.declared_key) ||
        n.resource == resource
    end
  end.flatten
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 109
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 665
def transport
  @transport ||= Chef::TrainTransport.new(logger).build_transport
end
transport_connection() click to toggle source

Remote connection object from Train

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

# File lib/chef/run_context.rb, line 673
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 604
def unreachable_cookbook?(cookbook_name)
  cookbook_compiler.unreachable_cookbook?(cookbook_name)
end