The main object in a Chef run. Preps a Chef::Node and Chef::RunContext, syncs cookbooks if necessary, and triggers convergence.
Clears all notifications for client run status events. Primarily for testing purposes.
# File lib/chef/client.rb, line 58 def self.clear_notifications @run_start_notifications = nil @run_completed_successfully_notifications = nil @run_failed_notifications = nil end
Creates a new Chef::Client.
# File lib/chef/client.rb, line 136 def initialize(json_attribs=nil, args={}) @json_attribs = json_attribs @node = nil @run_status = nil @runner = nil @ohai = Ohai::System.new event_handlers = configure_formatters event_handlers += Array(Chef::Config[:event_handlers]) @events = EventDispatch::Dispatcher.new(*event_handlers) @override_runlist = args.delete(:override_runlist) runlist_override_sanity_check! end
The list of notifications to be run when the client run completes successfully.
# File lib/chef/client.rb, line 71 def self.run_completed_successfully_notifications @run_completed_successfully_notifications ||= [] end
The list of notifications to be run when the client run fails.
# File lib/chef/client.rb, line 76 def self.run_failed_notifications @run_failed_notifications ||= [] end
The list of notifications to be run when the client run starts.
# File lib/chef/client.rb, line 65 def self.run_start_notifications @run_start_notifications ||= [] 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.
# File lib/chef/client.rb, line 90 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.
# File lib/chef/client.rb, line 97 def self.when_run_fails(¬ification_block) run_failed_notifications << notification_block 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.
# File lib/chef/client.rb, line 83 def self.when_run_starts(¬ification_block) run_start_notifications << notification_block end
Applies environment, external JSON attributes, and override run list to the node, Then expands the run_list.
node<Chef::Node> |
The modified @node object. @node is modified in place. |
# File lib/chef/client.rb, line 267 def build_node # Allow user to override the environment of a node by specifying # a config parameter. if Chef::Config[:environment] && !Chef::Config[:environment].chop.empty? @node.chef_environment(Chef::Config[:environment]) end # consume_external_attrs may add items to the run_list. Save the # expanded run_list, which we will pass to the server later to # determine which versions of cookbooks to use. @node.reset_defaults_and_overrides @node.consume_external_attrs(ohai.data, @json_attribs) unless(@override_runlist.empty?) @original_runlist = @node.run_list.run_list_items.dup runlist_override_sanity_check! @node.run_list(*@override_runlist) Chef::Log.warn "Run List override has been provided." Chef::Log.warn "Original Run List: [#{@original_runlist.join(', ')}]" Chef::Log.warn "Overridden Run List: [#{@node.run_list}]" end @run_list_expansion = expand_run_list # @run_list_expansion is a RunListExpansion. # # Convert @expanded_run_list, which is an # Array of Hashes of the form # {:name => NAME, :version_constraint => Chef::VersionConstraint }, # into @expanded_run_list_with_versions, an # Array of Strings of the form # "#{NAME}@#{VERSION}" @expanded_run_list_with_versions = @run_list_expansion.recipes.with_version_constraints_strings Chef::Log.info("Run List is [#{@node.run_list}]") Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]") @run_status = Chef::RunStatus.new(@node, @events) @events.node_load_completed(node, @expanded_run_list_with_versions, Chef::Config) @node end
# File lib/chef/client.rb, line 151 def configure_formatters formatters_for_run.map do |formatter_name, output_path| if output_path.nil? Chef::Formatters.new(formatter_name, STDOUT, STDERR) else io = File.open(output_path, "a+") io.sync = true Chef::Formatters.new(formatter_name, io, io) end end end
Converges the node.
true |
Always returns true |
# File lib/chef/client.rb, line 400 def converge(run_context) @events.converge_start(run_context) Chef::Log.debug("Converging node #{node_name}") @runner = Chef::Runner.new(run_context) runner.converge @events.converge_complete true rescue Exception # TODO: should this be a separate #converge_failed(exception) method? @events.converge_complete raise end
# File lib/chef/client.rb, line 171 def default_formatter if (STDOUT.tty? && !Chef::Config[:force_logger]) || Chef::Config[:force_formatter] [:doc] else [:null] end end
# File lib/chef/client.rb, line 413 def do_windows_admin_check if Chef::Platform.windows? Chef::Log.debug("Checking for administrator privileges....") if !has_admin_privileges? message = "chef-client doesn't have administrator privileges on node #{node_name}." if Chef::Config[:fatal_windows_admin_check] Chef::Log.fatal(message) Chef::Log.fatal("fatal_windows_admin_check is set to TRUE.") raise Chef::Exceptions::WindowsNotAdmin, message else Chef::Log.warn("#{message} This might cause unexpected resource failures.") end else Chef::Log.debug("chef-client has administrator privileges on node #{node_name}.") end end end
# File lib/chef/client.rb, line 329 def expand_run_list if Chef::Config[:solo] @node.expand!('disk') else @node.expand!('server') end rescue Exception => e # TODO: wrap/munge exception with useful error output. @events.run_list_expand_failed(node, e) raise end
# File lib/chef/client.rb, line 163 def formatters_for_run if Chef::Config.formatters.empty? [default_formatter] else Chef::Config.formatters end end
In client-server operation, loads the node state from the server. In chef-solo operation, builds a new node object.
# File lib/chef/client.rb, line 313 def load_node @events.node_load_start(node_name, Chef::Config) Chef::Log.debug("Building node object for #{node_name}") if Chef::Config[:solo] @node = Chef::Node.build(node_name) else @node = Chef::Node.find_or_create(node_name) end rescue Exception => e # TODO: wrap this exception so useful error info can be given to the # user. @events.node_load_failed(node_name, e, Chef::Config) raise end
# File lib/chef/client.rb, line 244 def node_name name = Chef::Config[:node_name] || ohai[:fqdn] || ohai[:hostname] Chef::Config[:node_name] = name unless name msg = "Unable to determine node name: configure node_name or configure the system's hostname and fqdn" raise Chef::Exceptions::CannotDetermineNodeName, msg end # node names > 90 bytes only work with authentication protocol >= 1.1 # see discussion in config.rb. if name.bytesize > 90 Chef::Config[:authentication_protocol_version] = "1.1" end name end
rest<Chef::REST> |
returns Chef::REST connection object |
# File lib/chef/client.rb, line 344 def register(client_name=node_name, config=Chef::Config) if File.exists?(config[:client_key]) @events.skipping_registration(client_name, config) Chef::Log.debug("Client key #{config[:client_key]} is present - skipping registration") else @events.registration_start(node_name, config) Chef::Log.info("Client key #{config[:client_key]} is not present - registering") Chef::ApiClient::Registration.new(node_name, config[:client_key]).run @events.registration_completed end # We now have the client key, and should use it from now on. @rest = Chef::REST.new(config[:chef_server_url], client_name, config[:client_key]) @resource_reporter = Chef::ResourceReporter.new(@rest) @events.register(@resource_reporter) rescue Exception => e # TODO: munge exception so a semantic failure message can be given to the # user @events.registration_failed(node_name, e, config) raise end
Do a full run for this Chef::Client. Calls:
do_run
This provides a wrapper around do_run allowing the run to be optionally forked.
boolean |
Return value from do_run. Should always returns true. |
# File lib/chef/client.rb, line 186 def run if(Chef::Config[:client_fork] && Process.respond_to?(:fork)) Chef::Log.info "Forking chef instance to converge..." pid = fork do Chef::Log.info "Forked instance now converging" do_run exit end Chef::Log.info "Fork successful. Waiting for new chef pid: #{pid}" result = Process.waitpid2(pid) raise "Forked convergence run failed" unless result.last.success? Chef::Log.info "Forked child successfully reaped (pid: #{pid})" true else do_run end end
Callback to fire notifications that the run completed successfully
# File lib/chef/client.rb, line 109 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
# File lib/chef/client.rb, line 117 def run_failed failure_handlers = self.class.run_failed_notifications failure_handlers.each do |notification| notification.call(run_status) end end
# File lib/chef/client.rb, line 240 def run_ohai ohai.all_plugins end
Callback to fire notifications that the Chef run is starting
# File lib/chef/client.rb, line 102 def run_started self.class.run_start_notifications.each do |notification| notification.call(run_status) end end
# File lib/chef/client.rb, line 229 def save_updated_node unless Chef::Config[:solo] Chef::Log.debug("Saving the current state of node #{node_name}") if(@original_runlist) @node.run_list(*@original_runlist) @node.automatic_attrs[:runlist_override_history] = {Time.now.to_i => @override_runlist.inspect} end @node.save end end
Configures the Chef::Cookbook::FileVendor class to fetch file from the server or disk as appropriate, creates the run context for this run, and sanity checks the cookbook collection.
Chef::RunContext:: the run context for this run.
# File lib/chef/client.rb, line 209 def setup_run_context if Chef::Config[:solo] Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, Chef::Config[:cookbook_path]) } cl = Chef::CookbookLoader.new(Chef::Config[:cookbook_path]) cl.load_cookbooks cookbook_collection = Chef::CookbookCollection.new(cl) run_context = Chef::RunContext.new(node, cookbook_collection, @events) else Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::RemoteFileVendor.new(manifest, rest) } cookbook_hash = sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbook_hash) run_context = Chef::RunContext.new(node, cookbook_collection, @events) end run_status.run_context = run_context run_context.load(@run_list_expansion) assert_cookbook_path_not_empty(run_context) run_context end
Sync_cookbooks eagerly loads all files except files and templates. It returns the cookbook_hash -- the return result from /environments/#{node.chef_environment}/cookbook_versions, which we will use for our run_context.
Hash |
The hash of cookbooks with download URLs as given by the server |
# File lib/chef/client.rb, line 372 def sync_cookbooks Chef::Log.debug("Synchronizing cookbooks") begin @events.cookbook_resolution_start(@expanded_run_list_with_versions) cookbook_hash = rest.post_rest("environments/#{@node.chef_environment}/cookbook_versions", {:run_list => @expanded_run_list_with_versions}) rescue Exception => e # TODO: wrap/munge exception to provide helpful error output @events.cookbook_resolution_failed(@expanded_run_list_with_versions, e) raise else @events.cookbook_resolution_complete(cookbook_hash) end synchronizer = Chef::CookbookSynchronizer.new(cookbook_hash, @events) synchronizer.sync_cookbooks # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks") cookbook_hash end
Generated with the Darkfish Rdoc Generator 2.