class Chef::RunContext
Value object that loads and tracks the context of a Chef
run
Attributes
Handle to the global action_collection
of executed actions for reporting / data_collector /etc
@return [Chef::ActionCollection]
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>
The set of cookbooks involved in this run
@return [Chef::CookbookCollection]
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
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>
Event dispatcher for this run.
@return [Chef::EventDispatch::Dispatcher]
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>
Handle to the global input_collection
of inspec input files for the compliance phase
@return [Chef::Compliance::inputCollection]
A child of the root Chef::Log
logging object.
@return Mixlib::Log::Child A child logger
The node for this run
@return [Chef::Node]
The parent run context.
@return [Chef::RunContext] The parent run context, or `nil` if this is the
root context.
Handle to the global profile_collection
of inspec profiles for the compliance phase
@return [Chef::Compliance::ProfileCollection]
Hash of factoids for a reboot request.
@return [Hash]
The collection of resources intended to be converged (and able to be notified).
@return [Chef::ResourceCollection]
@see CookbookCompiler
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]
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]
Pointer back to the Chef::Runner
that created this
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.
Handle to the global waiver_collection
of inspec waiver files for the compliance phase
@return [Chef::Compliance::WaiverCollection]
Public Class Methods
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 212 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) initialize_child_state end
Public Instance Methods
Adds a delayed action to the delayed_actions
collection
# File lib/chef/run_context.rb, line 307 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
Get the list of before notifications sent by the given resource.
@return [Array]
# File lib/chef/run_context.rb, line 320 def before_notifications(resource) key = resource.is_a?(String) ? resource : resource.declared_key before_notification_collection[key] end
Cancels a pending reboot
# File lib/chef/run_context.rb, line 636 def cancel_reboot logger.info "Changing reboot status from #{reboot_info.inspect} to {}" @reboot_info = {} end
# File lib/chef/run_context.rb, line 234 def cookbook_collection=(cookbook_collection) @cookbook_collection = cookbook_collection node.set_cookbook_attribute end
Create a child RunContext
.
# File lib/chef/run_context.rb, line 668 def create_child ChildRunContext.new(self) end
Get the list of delayed (end of run) notifications sent by the given resource.
@return [Array]
# File lib/chef/run_context.rb, line 352 def delayed_notifications(resource) key = resource.is_a?(String) ? resource : resource.declared_key delayed_notification_collection[key] end
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 579 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
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 564 def has_template_in_cookbook?(cookbook, template_name) cookbook = cookbook_collection[cookbook] cookbook.has_template_for_node?(node, template_name) end
Get the list of immediate notifications sent by the given resource.
@return [Array]
# File lib/chef/run_context.rb, line 329 def immediate_notifications(resource) key = resource.is_a?(String) ? resource : resource.declared_key immediate_notification_collection[key] end
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 370 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 state that applies to both Chef::RunContext
and Chef::ChildRunContext
# File lib/chef/run_context.rb, line 253 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
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 245 def load(run_list_expansion) @cookbook_compiler = CookbookCompiler.new(self, run_list_expansion, events) cookbook_compiler.compile end
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 395 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 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 430 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
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 547 def loaded_attribute(cookbook, attribute_file) loaded_attributes_hash["#{cookbook}::#{attribute_file}"] = true end
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 490 def loaded_attributes loaded_attributes_hash.keys end
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 537 def loaded_fully_qualified_attribute?(cookbook, attribute_file) loaded_attributes_hash.key?("#{cookbook}::#{attribute_file}") end
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 502 def loaded_fully_qualified_recipe?(cookbook, recipe) loaded_recipes_hash.key?("#{cookbook}::#{recipe}") end
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 514 def loaded_recipe?(recipe) cookbook, recipe_name = Chef::Recipe.parse_recipe_name(recipe) loaded_fully_qualified_recipe?(cookbook, recipe_name) end
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 476 def loaded_recipes loaded_recipes_hash.keys end
# File lib/chef/run_context.rb, line 229 def node=(node) @node = node node.run_context = self end
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 267 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
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 295 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
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 283 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 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 608 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
Checks to see if a reboot has been requested @return [Boolean]
# File lib/chef/run_context.rb, line 645 def reboot_requested? reboot_info.size > 0 end
there are options for how to handle multiple calls to these functions:
-
first call always wins (never change
reboot_info
once set). -
last call always wins (happily change
reboot_info
whenever). -
raise an exception on the first conflict.
-
disable reboot after this run if anyone ever calls :cancel.
-
raise an exception on any second call.
-
?
# File lib/chef/run_context.rb, line 628 def request_reboot(reboot_info) logger.info "Changing reboot status from #{self.reboot_info.inspect} to #{reboot_info.inspect}" @reboot_info = reboot_info end
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 454 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
Get the list of immediate notifications pending to the given resource
@return [Array]
# File lib/chef/run_context.rb, line 338 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
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
Remote transport from Train
@return [Train::Plugins::Transport] The child class for our train transport.
# File lib/chef/run_context.rb, line 653 def transport @transport ||= Chef::TrainTransport.new(logger).build_transport end
Remote connection object from Train
@return [Train::Plugins::Transport::BaseConnection]
# File lib/chef/run_context.rb, line 661 def transport_connection @transport_connection ||= transport&.connection end
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 592 def unreachable_cookbook?(cookbook_name) cookbook_compiler.unreachable_cookbook?(cookbook_name) end