diff options
author | John Keiser <john@johnkeiser.com> | 2015-05-16 12:37:20 -0700 |
---|---|---|
committer | John Keiser <john@johnkeiser.com> | 2015-05-28 13:27:43 -0700 |
commit | a261c2c6954a6e3c682ff40a4c82d30ed994bdd8 (patch) | |
tree | b736bc12e36ea55158a704be00d54b3f22e76916 | |
parent | 678d696b169d6fbcf5966124f41847bfc3282c13 (diff) | |
download | chef-a261c2c6954a6e3c682ff40a4c82d30ed994bdd8.tar.gz |
Comment the heck out of Chef::Client
-rw-r--r-- | lib/chef/client.rb | 428 |
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(¬ification_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(¬ification_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(¬ification_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 |