diff options
Diffstat (limited to 'lib/puppet/agent.rb')
-rw-r--r-- | lib/puppet/agent.rb | 249 |
1 files changed, 81 insertions, 168 deletions
diff --git a/lib/puppet/agent.rb b/lib/puppet/agent.rb index e2b4ebdc7..c91552b16 100644 --- a/lib/puppet/agent.rb +++ b/lib/puppet/agent.rb @@ -1,201 +1,74 @@ -# The client for interacting with the puppetmaster config server. require 'sync' -require 'timeout' -require 'puppet/network/http_pool' -require 'puppet/util' +require 'puppet/daemon' +# A general class for triggering a run of another +# class. class Puppet::Agent - require 'puppet/agent/fact_handler' - require 'puppet/agent/plugin_handler' - require 'puppet/agent/locker' + include Puppet::Daemon - include Puppet::Agent::FactHandler - include Puppet::Agent::PluginHandler + require 'puppet/agent/locker' include Puppet::Agent::Locker - # For benchmarking - include Puppet::Util - - unless defined? @@sync - @@sync = Sync.new - end - - attr_accessor :catalog - attr_reader :compile_time - - class << self - # Puppetd should only have one instance running, and we need a way - # to retrieve it. - attr_accessor :instance - include Puppet::Util - end - - def clear - @catalog.clear(true) if @catalog - @catalog = nil - end + attr_reader :client_class, :client + attr_accessor :stopping - # Initialize and load storage - def dostorage - begin - Puppet::Util::Storage.load - @compile_time ||= Puppet::Util::Storage.cache(:configuration)[:compile_time] - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err "Corrupt state file %s: %s" % [Puppet[:statefile], detail] - begin - ::File.unlink(Puppet[:statefile]) - retry - rescue => detail - raise Puppet::Error.new("Cannot remove %s: %s" % - [Puppet[:statefile], detail]) - end - end - end - # Just so we can specify that we are "the" instance. - def initialize - Puppet.settings.use(:main, :ssl, :puppetd) - - self.class.instance = self - @running = false + def initialize(client_class) @splayed = false - end - - # Prepare for catalog retrieval. Downloads everything necessary, etc. - def prepare - dostorage() - - download_plugins() - download_fact_plugins() - - upload_facts() + @client_class = client_class end - # Mark that we should restart. The Puppet module checks whether we're running, - # so this only gets called if we're in the middle of a run. - def restart - # If we're currently running, then just mark for later - Puppet.notice "Received signal to restart; waiting until run is complete" - @restart = true + def lockfile_path + client_class.lockfile_path end - # Should we restart? - def restart? - if defined? @restart - @restart - else - false + # Perform a run with our client. + def run + if client + Puppet.notice "Run of %s already in progress; skipping" % client_class + return end - end - - # Get the remote catalog, yo. Returns nil if no catalog can be found. - def retrieve_catalog - name = Facter.value("hostname") - catalog_class = Puppet::Resource::Catalog - - # First try it with no cache, then with the cache. - result = nil - begin - duration = thinmark do - result = catalog_class.find(name, :use_cache => false) - end - rescue => detail - puts detail.backtrace if Puppet[:trace] - Puppet.err "Could not retrieve catalog from remote server: %s" % detail + if stopping? + Puppet.notice "In shutdown progress; skipping run" + return end - - unless result + splay + with_client do |client| begin - duration = thinmark do - result = catalog_class.find(name, :use_cache => true) - end + sync.synchronize { lock { client.run } } rescue => detail puts detail.backtrace if Puppet[:trace] - Puppet.err "Could not retrieve catalog from cache: %s" % detail + Puppet.err "Could not run %s: %s" % [client_class, detail] end end - - return nil unless result - - convert_catalog(result, duration) end - # Convert a plain resource catalog into our full host catalog. - def convert_catalog(result, duration) - catalog = result.to_ral - catalog.retrieval_duration = duration - catalog.host_config = true - catalog.write_class_file - return catalog - end - - # The code that actually runs the catalog. - # This just passes any options on to the catalog, - # which accepts :tags and :ignoreschedules. - def run(options = {}) - got_lock = false - splay - Puppet::Util.sync(:puppetrun).synchronize(Sync::EX) do - got_lock = lock do - unless catalog = retrieve_catalog - Puppet.err "Could not retrieve catalog; skipping run" - return - end - - begin - benchmark(:notice, "Finished catalog run") do - catalog.apply(options) - end - rescue => detail - puts detail.backtrace if Puppet[:trace] - Puppet.err "Failed to apply catalog: %s" % detail - end - end - - unless got_lock - Puppet.notice "Lock file %s exists; skipping catalog run" % lockfile.lockfile - return + def shutdown + if self.stopping? + Puppet.notice "Already in shutdown" + return + end + self.stopping = true + if client and client.respond_to?(:stop) + begin + client.stop + rescue + puts detail.backtrace if Puppet[:trace] + Puppet.err "Could not stop %s: %s" % [client_class, detail] end - - # Now close all of our existing http connections, since there's no - # reason to leave them lying open. - Puppet::Network::HttpPool.clear_http_instances - - lockfile.unlock - - # Did we get HUPped during the run? If so, then restart now that we're - # done with the run. - Process.kill(:HUP, $$) if self.restart? end - end - def running? - lockfile.locked? + super + ensure + self.stopping = false end - private - - def self.timeout - timeout = Puppet[:configtimeout] - case timeout - when String: - if timeout =~ /^\d+$/ - timeout = Integer(timeout) - else - raise ArgumentError, "Configuration timeout must be an integer" - end - when Integer: # nothing - else - raise ArgumentError, "Configuration timeout must be an integer" - end - - return timeout + def stopping? + stopping end + # Have we splayed already? def splayed? @splayed end @@ -210,4 +83,44 @@ class Puppet::Agent sleep(time) @splayed = true end + + # Start listening for events. We're pretty much just listening for + # timer events here. + def start + # Create our timer. Puppet will handle observing it and such. + Puppet.newtimer( + :interval => Puppet[:runinterval], + :tolerance => 1, + :start? => true + ) do + run() + end + + # Run once before we start following the timer + run() + end + + def sync + unless defined?(@sync) and @sync + @sync = Sync.new + end + @sync + end + + private + + # Create and yield a client instance, keeping a reference + # to it during the yield. + def with_client + begin + @client = client_class.new + rescue => details + puts detail.backtrace if Puppet[:trace] + Puppet.err "Could not create instance of %s: %s" % [client_class, details] + return + end + yield @client + ensure + @client = nil + end end |