summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <john@johnkeiser.com>2015-05-16 12:37:20 -0700
committerJohn Keiser <john@johnkeiser.com>2015-05-28 13:27:43 -0700
commita261c2c6954a6e3c682ff40a4c82d30ed994bdd8 (patch)
treeb736bc12e36ea55158a704be00d54b3f22e76916
parent678d696b169d6fbcf5966124f41847bfc3282c13 (diff)
downloadchef-a261c2c6954a6e3c682ff40a4c82d30ed994bdd8.tar.gz
Comment the heck out of Chef::Client
-rw-r--r--lib/chef/client.rb428
1 files changed, 384 insertions, 44 deletions
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index f3e7722509..428cf6c8d6 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -60,62 +60,105 @@ class Chef
class Client
include Chef::Mixin::PathSanity
+ #
# IO stream that will be used as 'STDOUT' for formatters. Formatters are
# configured during `initialize`, so this provides a convenience for
# setting alternative IO stream during tests.
+ #
+ # @api private
+ #
STDOUT_FD = STDOUT
+ #
# IO stream that will be used as 'STDERR' for formatters. Formatters are
# configured during `initialize`, so this provides a convenience for
# setting alternative IO stream during tests.
+ #
+ # @api private
+ #
STDERR_FD = STDERR
- # Clears all notifications for client run status events.
+ #
+ # Clears all listeners for client run status events.
+ #
# Primarily for testing purposes.
+ #
+ # @api private
+ #
def self.clear_notifications
@run_start_notifications = nil
@run_completed_successfully_notifications = nil
@run_failed_notifications = nil
end
- # The list of notifications to be run when the client run starts.
+ #
+ # Listeners to be run when the client run starts.
+ #
+ # @return [Array<Proc>]
+ #
+ # @api private
+ #
def self.run_start_notifications
@run_start_notifications ||= []
end
- # The list of notifications to be run when the client run completes
- # successfully.
+ #
+ # Listeners to be run when the client run completes successfully.
+ #
+ # @return [Array<Proc>]
+ #
+ # @api private
+ #
def self.run_completed_successfully_notifications
@run_completed_successfully_notifications ||= []
end
- # The list of notifications to be run when the client run fails.
+ #
+ # Listeners to be run when the client run fails.
+ #
+ # @return [Array<Proc>]
+ #
+ # @api private
+ #
def self.run_failed_notifications
@run_failed_notifications ||= []
end
- # Add a notification for the 'client run started' event. The notification
- # is provided as a block. The current Chef::RunStatus object will be passed
- # to the notification_block when the event is triggered.
+ #
+ # Add a listener for the 'client run started' event.
+ #
+ # @param notification_block The callback (takes |run_status| parameter).
+ # @yieldparam [Chef::RunStatus] run_status The run status.
+ #
def self.when_run_starts(&notification_block)
run_start_notifications << notification_block
end
- # Add a notification for the 'client run success' event. The notification
- # is provided as a block. The current Chef::RunStatus object will be passed
- # to the notification_block when the event is triggered.
+ #
+ # Add a listener for the 'client run success' event.
+ #
+ # @param notification_block The callback (takes |run_status| parameter).
+ # @yieldparam [Chef::RunStatus] run_status The run status.
+ #
def self.when_run_completes_successfully(&notification_block)
run_completed_successfully_notifications << notification_block
end
- # Add a notification for the 'client run failed' event. The notification
- # is provided as a block. The current Chef::RunStatus is passed to the
- # notification_block when the event is triggered.
+ #
+ # Add a listener for the 'client run failed' event.
+ #
+ # @param notification_block The callback (takes |run_status| parameter).
+ # @yieldparam [Chef::RunStatus] run_status The run status.
+ #
def self.when_run_fails(&notification_block)
run_failed_notifications << notification_block
end
+ #
# Callback to fire notifications that the Chef run is starting
+ #
+ # @api private
+ #
def run_started
self.class.run_start_notifications.each do |notification|
notification.call(run_status)
@@ -123,7 +166,11 @@ class Chef
@events.run_started(run_status)
end
+ #
# Callback to fire notifications that the run completed successfully
+ #
+ # @api private
+ #
def run_completed_successfully
success_handlers = self.class.run_completed_successfully_notifications
success_handlers.each do |notification|
@@ -131,7 +178,11 @@ class Chef
end
end
+ #
# Callback to fire notifications that the Chef run failed
+ #
+ # @api private
+ #
def run_failed
failure_handlers = self.class.run_failed_notifications
failure_handlers.each do |notification|
@@ -139,16 +190,81 @@ class Chef
end
end
+ #
+ # The node represented by this client.
+ #
+ # @return [Chef::Node]
+ #
attr_accessor :node
+
+ #
+ # The ohai system used by this client.
+ #
+ # @return [Ohai::System]
+ #
attr_accessor :ohai
+
+ #
+ # The rest object used to communicate with the Chef server.
+ #
+ # @return [Chef::REST]
+ #
attr_accessor :rest
+
+ #
+ # The runner used to converge.
+ #
+ # @return [Chef::Runner]
+ #
attr_accessor :runner
+ #
+ # Extra node attributes that were applied to the node.
+ #
+ # @return [Hash]
+ #
attr_reader :json_attribs
+
+ #
+ # The status of the Chef run.
+ #
+ # @return [Chef::RunStatus]
+ #
attr_reader :run_status
+
+ #
+ # The event dispatcher for the Chef run, including any configured output
+ # formatters and event loggers.
+ #
+ # @return [EventDispatch::Dispatcher]
+ #
+ # @see Chef::Formatters
+ # @see Chef::Config#formatters
+ # @see Chef::Config#stdout
+ # @see Chef::Config#stderr
+ # @see Chef::Config#force_logger
+ # @see Chef::Config#force_formatter
+ # TODO add stdout, stderr, and default formatters to Chef::Config so the
+ # defaults aren't calculated here. Remove force_logger and force_formatter
+ # from this code.
+ # @see Chef::EventLoggers
+ # @see Chef::Config#disable_event_logger
+ # @see Chef::Config#event_loggers
+ # @see Chef::Config#event_handlers
+ #
attr_reader :events
+ #
# Creates a new Chef::Client.
+ #
+ # @param json_attribs [Hash] Node attributes to layer into the node when it is
+ # fetched.
+ # @param args [Hash] Options:
+ # @option args [Array<RunList::RunListItem>] :override_runlist A runlist to
+ # use instead of the node's embedded run list.
+ # @option args [] :specific_recipes Recipes to run after all cookbooks have
+ # been compiled and loaded.
+ #
def initialize(json_attribs=nil, args={})
@json_attribs = json_attribs || {}
@node = nil
@@ -159,6 +275,7 @@ class Chef
event_handlers += Array(Chef::Config[:event_handlers])
@events = EventDispatch::Dispatcher.new(*event_handlers)
+ # TODO it seems like a bad idea to be deletin' other peoples' hashes.
@override_runlist = args.delete(:override_runlist)
@specific_recipes = args.delete(:specific_recipes)
@run_status = Chef::RunStatus.new(node, events)
@@ -175,6 +292,12 @@ class Chef
Chef.set_resource_priority_map(Chef::Platform::ResourcePriorityMap.instance)
end
+ #
+ # Private API
+ # TODO make this stuff protected or private
+ #
+
+ # @api private
def configure_formatters
formatters_for_run.map do |formatter_name, output_path|
if output_path.nil?
@@ -187,6 +310,7 @@ class Chef
end
end
+ # @api private
def formatters_for_run
if Chef::Config.formatters.empty?
[default_formatter]
@@ -195,6 +319,7 @@ class Chef
end
end
+ # @api private
def default_formatter
if (STDOUT.tty? && !Chef::Config[:force_logger]) || Chef::Config[:force_formatter]
[:doc]
@@ -203,6 +328,7 @@ class Chef
end
end
+ # @api private
def configure_event_loggers
if Chef::Config.disable_event_logger
[]
@@ -219,8 +345,9 @@ class Chef
end
end
- # Resource repoters send event information back to the chef server for processing.
- # Can only be called after we have a @rest object
+ # Resource reporters send event information back to the chef server for
+ # processing. Can only be called after we have a @rest object
+ # @api private
def register_reporters
[
Chef::ResourceReporter.new(rest),
@@ -230,11 +357,16 @@ class Chef
end
end
+ #
# Instantiates a Chef::Node object, possibly loading the node's prior state
- # when using chef-client. Delegates to policy_builder. Injects the built node
- # into the Chef class.
+ # when using chef-client. Sets Chef.node to the new node.
#
# @return [Chef::Node] The node object for this Chef run
+ #
+ # @see Chef::PolicyBuilder#load_node
+ #
+ # @api private
+ #
def load_node
policy_builder.load_node
@node = policy_builder.node
@@ -242,16 +374,42 @@ class Chef
node
end
- # Mutates the `node` object to prepare it for the chef run. Delegates to
- # policy_builder
+ #
+ # Mutates the `node` object to prepare it for the chef run.
#
# @return [Chef::Node] The updated node object
+ #
+ # @see Chef::PolicyBuilder#build_node
+ #
+ # @api private
+ #
def build_node
policy_builder.build_node
- @run_status.node = node
+ run_status.node = node
node
end
+ #
+ # Sync cookbooks to local cache.
+ #
+ # TODO this appears to be unused.
+ #
+ # @see Chef::PolicyBuilder#sync_cookbooks
+ #
+ # @api private
+ #
+ def sync_cookbooks
+ policy_builder.sync_cookbooks
+ end
+
+ #
+ # Sets up the run context.
+ #
+ # @see Chef::PolicyBuilder#setup_run_context
+ #
+ # @return The newly set up run context
+ #
+ # @api private
def setup_run_context
run_context = policy_builder.setup_run_context(@specific_recipes)
assert_cookbook_path_not_empty(run_context)
@@ -259,14 +417,27 @@ class Chef
run_context
end
- def sync_cookbooks
- policy_builder.sync_cookbooks
- end
-
+ #
+ # The PolicyBuilder strategy for figuring out run list and cookbooks.
+ #
+ # @return [Chef::PolicyBuilder::Policyfile, Chef::PolicyBuilder::ExpandNodeObject]
+ #
+ # @api private
+ #
def policy_builder
@policy_builder ||= Chef::PolicyBuilder.strategy.new(node_name, ohai.data, json_attribs, @override_runlist, events)
end
+ #
+ # Save the updated node to Chef.
+ #
+ # Does not save if we are in solo mode or using override_runlist.
+ #
+ # @see Chef::Node#save
+ # @see Chef::Config#solo
+ #
+ # @api private
+ #
def save_updated_node
if Chef::Config[:solo]
# nothing to do
@@ -278,12 +449,42 @@ class Chef
end
end
+ #
+ # Run ohai plugins. Runs all ohai plugins unless minimal_ohai is specified.
+ #
+ # Sends the ohai_completed event when finished.
+ #
+ # @see Chef::EventDispatcher#
+ # @see Chef::Config#minimal_ohai
+ #
+ # @api private
+ #
def run_ohai
filter = Chef::Config[:minimal_ohai] ? %w[fqdn machinename hostname platform platform_version os os_version] : nil
ohai.all_plugins(filter)
@events.ohai_completed(node)
end
+ #
+ # Figure out the node name we are working with.
+ #
+ # It tries these, in order:
+ # - Chef::Config.node_name
+ # - ohai[:fqdn]
+ # - ohai[:machinename]
+ # - ohai[:hostname]
+ #
+ # If we are running against a server with authentication protocol < 1.0, we
+ # *require* authentication protocol version 1.1.
+ #
+ # @raise [Chef::Exceptions::CannotDetermineNodeName] If the node name is not
+ # set and cannot be determined via ohai.
+ #
+ # @see Chef::Config#node_name
+ # @see Chef::Config#authentication_protocol_version
+ #
+ # @api private
+ #
def node_name
name = Chef::Config[:node_name] || ohai[:fqdn] || ohai[:machinename] || ohai[:hostname]
Chef::Config[:node_name] = name
@@ -292,6 +493,8 @@ class Chef
# node names > 90 bytes only work with authentication protocol >= 1.1
# see discussion in config.rb.
+ # TODO use a computed default in Chef::Config to determine this instead of
+ # setting it.
if name.bytesize > 90
Chef::Config[:authentication_protocol_version] = "1.1"
end
@@ -300,8 +503,26 @@ class Chef
end
#
- # === Returns
- # rest<Chef::REST>:: returns Chef::REST connection object
+ # Determine our private key and set up the connection to the Chef server.
+ #
+ # Skips registration and fires the `skipping_registration` event if
+ # Chef::Config.client_key is unspecified or already exists.
+ #
+ # If Chef::Config.client_key does not exist, we register the client with the
+ # Chef server and fire the registration_start and registration_completed events.
+ #
+ # @return [Chef::REST] The server connection object.
+ #
+ # @see Chef::Config#chef_server_url
+ # @see Chef::Config#client_key
+ # @see Chef::ApiClient::Registration#run
+ # @see Chef::EventDispatcher#skipping_registration
+ # @see Chef::EventDispatcher#registration_start
+ # @see Chef::EventDispatcher#registration_completed
+ # @see Chef::EventDispatcher#registration_failed
+ #
+ # @api private
+ #
def register(client_name=node_name, config=Chef::Config)
if !config[:client_key]
@events.skipping_registration(client_name, config)
@@ -319,16 +540,38 @@ class Chef
@rest = Chef::REST.new(config[:chef_server_url], client_name, config[:client_key])
register_reporters
rescue Exception => e
+ # TODO this should probably only ever fire if we *started* registration.
+ # Move it to the block above.
# TODO: munge exception so a semantic failure message can be given to the
# user
@events.registration_failed(client_name, e, config)
raise
end
- # Converges the node.
#
- # === Returns
- # The thrown exception, if there was one. If this returns nil the converge was successful.
+ # Converges all compiled resources.
+ #
+ # Fires the converge_start, converge_complete and converge_failed events.
+ #
+ # If the exception `:end_client_run_early` is thrown during convergence, it
+ # does not mark the run complete *or* failed, and returns `nil`
+ #
+ # @param run_context The run context.
+ #
+ # @return The thrown exception, if we are in audit mode. `nil` means the
+ # converge was successful or ended early.
+ #
+ # @raise Any converge exception, unless we are in audit mode, in which case
+ # we *return* the exception.
+ #
+ # @see Chef::Runner#converge
+ # @see Chef::Config#audit_mode
+ # @see Chef::EventDispatch#converge_start
+ # @see Chef::EventDispatch#converge_complete
+ # @see Chef::EventDispatch#converge_failed
+ #
+ # @api private
+ #
def converge(run_context)
converge_exception = nil
catch(:end_client_run_early) do
@@ -347,8 +590,28 @@ class Chef
converge_exception
end
+ #
+ # Converge the node via and then save it if successful.
+ #
+ # @param run_context The run context.
+ #
+ # @return The thrown exception, if we are in audit mode. `nil` means the
+ # converge was successful or ended early.
+ #
+ # @raise Any converge or node save exception, unless we are in audit mode,
+ # in which case we *return* the exception.
+ #
+ # @see #converge
+ # @see #save_updated_mode
+ # @see Chef::Config#audit_mode
+ #
+ # @api private
+ #
# We don't want to change the old API on the `converge` method to have it perform
# saving. So we wrap it in this method.
+ # TODO given this seems to be pretty internal stuff, how badly do we need to
+ # split this stuff up?
+ #
def converge_and_save(run_context)
converge_exception = converge(run_context)
unless converge_exception
@@ -362,6 +625,24 @@ class Chef
converge_exception
end
+ #
+ # Run the audit phase.
+ #
+ # Triggers the audit_phase_start, audit_phase_complete and
+ # audit_phase_failed events.
+ #
+ # @param run_context The run context. (Deprecated, don't bother passing
+ # this, it is already in the class.)
+ #
+ # @return Any thrown exceptions. `nil` if successful.
+ #
+ # @see Chef::Audit::Runner#run
+ # @see Chef::EventDispatch#audit_phase_start
+ # @see Chef::EventDispatch#audit_phase_complete
+ # @see Chef::EventDispatch#audit_phase_failed
+ #
+ # @api private
+ #
def run_audits(run_context)
audit_exception = nil
begin
@@ -383,18 +664,30 @@ class Chef
audit_exception
end
- # Expands the run list. Delegates to the policy_builder.
#
- # Normally this does not need to be called from here, it will be called by
- # build_node. This is provided so external users (like the chefspec
- # project) can inject custom behavior into the run process.
+ # Expands the run list.
+ #
+ # @return [Chef::RunListExpansion] The expanded run list.
+ #
+ # @see Chef::PolicyBuilder#expand_run_list
#
- # === Returns
- # RunListExpansion: A RunListExpansion or API compatible object.
def expanded_run_list
policy_builder.expand_run_list
end
+ #
+ # Check if the user has Administrator privileges on windows.
+ #
+ # Throws an error if the user is not an admin, and
+ # `Chef::Config.fatal_windows_admin_check` is true.
+ #
+ # @raise [Chef::Exceptions::WindowsNotAdmin] If the user is not an admin.
+ #
+ # @see Chef::platform#windows?
+ # @see Chef::Config#fatal_windows_admin_check
+ #
+ # @api private
+ #
def do_windows_admin_check
if Chef::Platform.windows?
Chef::Log.debug("Checking for administrator privileges....")
@@ -414,20 +707,67 @@ class Chef
end
end
- # Do a full run for this Chef::Client. Calls:
#
- # * run_ohai - Collect information about the system
- # * build_node - Get the last known state, merge with local changes
- # * register - If not in solo mode, make sure the server knows about this client
- # * sync_cookbooks - If not in solo mode, populate the local cache with the node's cookbooks
- # * converge - Bring this system up to date
+ # Do a full run for this Chef::Client.
+ #
+ # Locks the run while doing its job.
+ #
+ # Fires run_start before doing anything and fires run_completed or
+ # run_failed when finished. Also notifies client listeners of run_started
+ # at the beginning of Compile, and run_completed_successfully or run_failed
+ # when all is complete.
+ #
+ # Phase 1: Setup
+ # --------------
+ # Gets information about the system and the run we are doing.
+ #
+ # 1. Run ohai to collect system information.
+ # 2. Register / connect to the Chef server (unless in solo mode).
+ # 3. Retrieve the node (or create a new one).
+ # 4. Merge in json_attribs, Chef::Config.environment, and override_run_list.
+ #
+ # Phase 2: Compile
+ # ----------------
+ # Decides *what* we plan to converge by compiling recipes.
+ #
+ # 1. Sync required cookbooks to the local cache.
+ # 2. Load libraries from all cookbooks.
+ # 3. Load attributes from all cookbooks.
+ # 4. Load LWRPs from all cookbooks.
+ # 5. Load resource definitions from all cookbooks.
+ # 6. Load recipes in the run list.
+ # 7. Load recipes from the command line.
+ #
+ # Phase 3: Converge
+ # -----------------
+ # Brings the system up to date.
+ #
+ # 1. Converge the resources built from recipes in Phase 2.
+ # 2. Save the node.
+ # 3. Reboot if we were asked to.
+ #
+ # @raise [Chef::Exceptions::RunFailedWrappingError] If converge or audit failed.
+ #
+ # @see Chef::Config#lockfile
+ # @see Chef::Config#enforce_path_sanity
+ # @see Chef::Config#solo
+ # @see Chef::Config#audit_mode
+ #
+ # @see Chef::RunLock#acquire
+ # @see #run_ohai
+ # @see #load_node
+ # @see #build_node
+ # @see Chef::CookbookCompiler#compile
+ #
+ # @see #setup_run_context Syncs and compiles cookbooks.
+ #
+ # @return Always returns true.
#
- # === Returns
- # true:: Always returns true.
def run
run_error = nil
runlock = RunLock.new(Chef::Config.lockfile)
+ # TODO feels like acquire should have its own block arg for this
runlock.acquire
# don't add code that may fail before entering this section to be sure to release lock
begin