class Chef::RunContext
Value object that loads and tracks the context of a Chef
run
Attributes
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>
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.
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
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 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
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
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
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 a child RunContext
.
# File lib/chef/run_context.rb, line 613 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 298 def delayed_notifications(resource) delayed_notification_collection[resource.declared_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 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
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
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
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 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
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
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 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
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
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
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
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
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
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
# File lib/chef/run_context.rb, line 198 def node=(node) @node = node node.run_context = self node.set_cookbook_attribute 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 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
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
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 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
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
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 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
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
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
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
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
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