diff options
author | John Keiser <john@johnkeiser.com> | 2015-05-16 12:48:10 -0700 |
---|---|---|
committer | John Keiser <john@johnkeiser.com> | 2015-05-28 13:27:43 -0700 |
commit | 2ef63f2228a44ca2b81a231249b7a56b226e10d3 (patch) | |
tree | 6c8d991c66d60fcf39faab479c9b424554e7bac1 /lib | |
parent | a261c2c6954a6e3c682ff40a4c82d30ed994bdd8 (diff) | |
download | chef-2ef63f2228a44ca2b81a231249b7a56b226e10d3.tar.gz |
Move public APIs first, private second
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/client.rb | 475 | ||||
-rw-r--r-- | lib/chef/client/notification_registry.rb | 107 |
2 files changed, 308 insertions, 274 deletions
diff --git a/lib/chef/client.rb b/lib/chef/client.rb index 428cf6c8d6..a6321f6cca 100644 --- a/lib/chef/client.rb +++ b/lib/chef/client.rb @@ -50,6 +50,7 @@ require 'chef/run_lock' require 'chef/policy_builder' require 'chef/request_id' require 'chef/platform/rebooter' +require 'chef/client/notification_registry' require 'ohai' require 'rbconfig' @@ -60,135 +61,14 @@ 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 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 - - # - # Listeners to be run when the client run starts. - # - # @return [Array<Proc>] - # - # @api private - # - def self.run_start_notifications - @run_start_notifications ||= [] - end - - # - # 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 - - # - # 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 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 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 + extend NotificationRegistry # - # 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) - end - @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| - notification.call(run_status) - end - end - - # - # Callback to fire notifications that the Chef run failed + # The status of the Chef run. # - # @api private + # @return [Chef::RunStatus] # - def run_failed - failure_handlers = self.class.run_failed_notifications - failure_handlers.each do |notification| - notification.call(run_status) - end - end + attr_reader :run_status # # The node represented by this client. @@ -226,13 +106,6 @@ class Chef 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. # @@ -293,6 +166,153 @@ class Chef end # + # 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. + # + 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 + runlock.save_pid + + request_id = Chef::RequestID.instance.request_id + run_context = nil + @events.run_start(Chef::VERSION) + Chef::Log.info("*** Chef #{Chef::VERSION} ***") + Chef::Log.info "Chef-client pid: #{Process.pid}" + Chef::Log.debug("Chef-client request_id: #{request_id}") + enforce_path_sanity + run_ohai + + register unless Chef::Config[:solo] + + load_node + + build_node + + run_status.run_id = request_id + run_status.start_clock + Chef::Log.info("Starting Chef Run for #{node.name}") + run_started + + do_windows_admin_check + + run_context = setup_run_context + + if Chef::Config[:audit_mode] != :audit_only + converge_error = converge_and_save(run_context) + end + + if Chef::Config[:why_run] == true + # why_run should probably be renamed to why_converge + Chef::Log.debug("Not running controls in 'why_run' mode - this mode is used to see potential converge changes") + elsif Chef::Config[:audit_mode] != :disabled + audit_error = run_audits(run_context) + end + + # Raise converge_error so run_failed reporters/events are processed. + raise converge_error if converge_error + + run_status.stop_clock + Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds") + run_completed_successfully + @events.run_completed(node) + + # rebooting has to be the last thing we do, no exceptions. + Chef::Platform::Rebooter.reboot_if_needed!(node) + rescue Exception => run_error + # CHEF-3336: Send the error first in case something goes wrong below and we don't know why + Chef::Log.debug("Re-raising exception: #{run_error.class} - #{run_error.message}\n#{run_error.backtrace.join("\n ")}") + # If we failed really early, we may not have a run_status yet. Too early for these to be of much use. + if run_status + run_status.stop_clock + run_status.exception = run_error + run_failed + end + @events.run_failed(run_error) + ensure + Chef::RequestID.instance.reset_request_id + request_id = nil + @run_status = nil + run_context = nil + runlock.release + GC.start + end + + # Raise audit, converge, and other errors here so that we exit + # with the proper exit status code and everything gets raised + # as a RunFailedWrappingError + if run_error || converge_error || audit_error + error = if run_error == converge_error + Chef::Exceptions::RunFailedWrappingError.new(converge_error, audit_error) + else + Chef::Exceptions::RunFailedWrappingError.new(run_error, converge_error, audit_error) + end + error.fill_backtrace + Chef::Application.debug_stacktrace(error) + raise error + end + + true + end + + # # Private API # TODO make this stuff protected or private # @@ -358,6 +378,42 @@ class Chef 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) + end + @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| + notification.call(run_status) + 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| + notification.call(run_status) + end + end + + # # Instantiates a Chef::Node object, possibly loading the node's prior state # when using chef-client. Sets Chef.node to the new node. # @@ -579,7 +635,7 @@ class Chef @events.converge_start(run_context) Chef::Log.debug("Converging node #{node_name}") @runner = Chef::Runner.new(run_context) - @runner.converge + runner.converge @events.converge_complete rescue Exception => e @events.converge_failed(e) @@ -658,7 +714,7 @@ class Chef end rescue Exception => e Chef::Log.error("Audit phase failed with error message: #{e.message}") - @events.audit_phase_failed(e) + events.audit_phase_failed(e) audit_exception = e end audit_exception @@ -708,151 +764,23 @@ class Chef end # - # 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. + # 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. # - # @see Chef::Config#lockfile - # @see Chef::Config#enforce_path_sanity - # @see Chef::Config#solo - # @see Chef::Config#audit_mode + # @api private # - # @see Chef::RunLock#acquire - # @see #run_ohai - # @see #load_node - # @see #build_node - # @see Chef::CookbookCompiler#compile + STDOUT_FD = STDOUT + # - # @see #setup_run_context Syncs and compiles cookbooks. + # 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. # - # @return Always returns true. + # @api private # - 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 - runlock.save_pid - - request_id = Chef::RequestID.instance.request_id - run_context = nil - @events.run_start(Chef::VERSION) - Chef::Log.info("*** Chef #{Chef::VERSION} ***") - Chef::Log.info "Chef-client pid: #{Process.pid}" - Chef::Log.debug("Chef-client request_id: #{request_id}") - enforce_path_sanity - run_ohai - - register unless Chef::Config[:solo] - - load_node - - build_node - - run_status.run_id = request_id - run_status.start_clock - Chef::Log.info("Starting Chef Run for #{node.name}") - run_started - - do_windows_admin_check - - run_context = setup_run_context - - if Chef::Config[:audit_mode] != :audit_only - converge_error = converge_and_save(run_context) - end - - if Chef::Config[:why_run] == true - # why_run should probably be renamed to why_converge - Chef::Log.debug("Not running controls in 'why_run' mode - this mode is used to see potential converge changes") - elsif Chef::Config[:audit_mode] != :disabled - audit_error = run_audits(run_context) - end - - # Raise converge_error so run_failed reporters/events are processed. - raise converge_error if converge_error - - run_status.stop_clock - Chef::Log.info("Chef Run complete in #{run_status.elapsed_time} seconds") - run_completed_successfully - @events.run_completed(node) - - # rebooting has to be the last thing we do, no exceptions. - Chef::Platform::Rebooter.reboot_if_needed!(node) - rescue Exception => run_error - # CHEF-3336: Send the error first in case something goes wrong below and we don't know why - Chef::Log.debug("Re-raising exception: #{run_error.class} - #{run_error.message}\n#{run_error.backtrace.join("\n ")}") - # If we failed really early, we may not have a run_status yet. Too early for these to be of much use. - if run_status - run_status.stop_clock - run_status.exception = run_error - run_failed - end - @events.run_failed(run_error) - ensure - Chef::RequestID.instance.reset_request_id - request_id = nil - @run_status = nil - run_context = nil - runlock.release - GC.start - end - - # Raise audit, converge, and other errors here so that we exit - # with the proper exit status code and everything gets raised - # as a RunFailedWrappingError - if run_error || converge_error || audit_error - error = if run_error == converge_error - Chef::Exceptions::RunFailedWrappingError.new(converge_error, audit_error) - else - Chef::Exceptions::RunFailedWrappingError.new(run_error, converge_error, audit_error) - end - error.fill_backtrace - Chef::Application.debug_stacktrace(error) - raise error - end + STDERR_FD = STDERR - true - end private @@ -879,7 +807,6 @@ class Chef else Chef::Log.warn("Node #{node_name} has an empty run list.") if run_context.node.run_list.empty? end - end def has_admin_privileges? diff --git a/lib/chef/client/notification_registry.rb b/lib/chef/client/notification_registry.rb new file mode 100644 index 0000000000..e34115c5dd --- /dev/null +++ b/lib/chef/client/notification_registry.rb @@ -0,0 +1,107 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Walters (<cw@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) +# Author:: Tim Hinderliter (<tim@opscode.com>) +# Copyright:: Copyright (c) 2008-2011 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'chef/client/notification_registry' + +class Chef + class Client + module NotificationRegistry + # + # 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 when_run_starts(¬ification_block) + run_start_notifications << notification_block + end + + # + # 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 when_run_completes_successfully(¬ification_block) + run_completed_successfully_notifications << notification_block + end + + # + # 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 when_run_fails(¬ification_block) + run_failed_notifications << notification_block + end + + # + # Clears all listeners for client run status events. + # + # Primarily for testing purposes. + # + # @api private + # + def clear_notifications + @run_start_notifications = nil + @run_completed_successfully_notifications = nil + @run_failed_notifications = nil + end + + # + # TODO These seem protected to me. + # + + # + # Listeners to be run when the client run starts. + # + # @return [Array<Proc>] + # + # @api private + # + def run_start_notifications + @run_start_notifications ||= [] + end + + # + # Listeners to be run when the client run completes successfully. + # + # @return [Array<Proc>] + # + # @api private + # + def run_completed_successfully_notifications + @run_completed_successfully_notifications ||= [] + end + + # + # Listeners to be run when the client run fails. + # + # @return [Array<Proc>] + # + # @api private + # + def run_failed_notifications + @run_failed_notifications ||= [] + end + end + end +end |