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 56 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 134 def initialize(json_attribs=nil, args={}) @json_attribs = json_attribs @node = nil @run_status = nil @runner = nil @ohai = Ohai::System.new # If we want why-run output and user hasn't explicitly specified a format # we need to use a formatter that will render whyrun output. if Chef::Config.why_run if Chef::Config.formatter == "null" Chef::Log.warn("Forcing formatter of 'doc' to capture whyrun output.") Chef::Config[:formatter] = 'doc' end end formatter = Chef::Formatters.new(Chef::Config.formatter, STDOUT, STDERR) @events = EventDispatch::Dispatcher.new(formatter) @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 69 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 74 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 63 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 88 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 95 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 81 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 241 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
Converges the node.
true |
Always returns true |
# File lib/chef/client.rb, line 374 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 => e # TODO: should this be a separate #converge_failed(exception) method? @events.converge_complete raise end
# File lib/chef/client.rb, line 303 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
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 287 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 218 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 318 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::REST.new(config[:client_url], config[:validation_client_name], config[:validation_key]).register(client_name, config[:client_key]) @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 162 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 107 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 115 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 214 def run_ohai ohai.all_plugins end
Callback to fire notifications that the Chef run is starting
# File lib/chef/client.rb, line 100 def run_started self.class.run_start_notifications.each do |notification| notification.call(run_status) end end
# File lib/chef/client.rb, line 203 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[: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 185 def setup_run_context if Chef::Config[:solo] Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, Chef::Config[:cookbook_path]) } cookbook_collection = Chef::CookbookCollection.new(Chef::CookbookLoader.new(Chef::Config[:cookbook_path])) 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 346 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.