diff options
author | Luke Kanies <luke@madstop.com> | 2009-02-02 17:19:07 -0600 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2009-02-06 18:08:43 -0600 |
commit | c0fcb2137e66af8ba60a959faa221034c6832b69 (patch) | |
tree | 10f62605333979ea64445dca5d4d66d58237de97 /lib/puppet | |
parent | 700e823f7c33eb3c5b4d9e467742fd24f63bbeef (diff) | |
download | puppet-c0fcb2137e66af8ba60a959faa221034c6832b69.tar.gz puppet-c0fcb2137e66af8ba60a959faa221034c6832b69.tar.xz puppet-c0fcb2137e66af8ba60a959faa221034c6832b69.zip |
Creating and using a new Puppet::Daemon class
This replaces the short-lived EventManager class, all of
the service- and timer-related code in puppet.rb, and moves
code from agent.rb, server.rb, and other places into one
class responsible for starting, stopping, pids, and more.
The Daemon module is no longer in existence, so it's been
removed from the classes that were using it.
Signed-off-by: Luke Kanies <luke@madstop.com>
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/agent.rb | 39 | ||||
-rw-r--r-- | lib/puppet/agent/locker.rb | 1 | ||||
-rwxr-xr-x | lib/puppet/daemon.rb | 102 | ||||
-rw-r--r-- | lib/puppet/event_manager.rb | 182 | ||||
-rw-r--r-- | lib/puppet/network/http_server/mongrel.rb | 2 | ||||
-rw-r--r-- | lib/puppet/network/http_server/webrick.rb | 2 | ||||
-rw-r--r-- | lib/puppet/util/settings.rb | 19 |
7 files changed, 107 insertions, 240 deletions
diff --git a/lib/puppet/agent.rb b/lib/puppet/agent.rb index c91552b16..e7d40f68c 100644 --- a/lib/puppet/agent.rb +++ b/lib/puppet/agent.rb @@ -1,17 +1,19 @@ require 'sync' -require 'puppet/daemon' +require 'puppet/external/event-loop' # A general class for triggering a run of another # class. class Puppet::Agent - include Puppet::Daemon - require 'puppet/agent/locker' include Puppet::Agent::Locker - attr_reader :client_class, :client + attr_reader :client_class, :client, :needing_restart, :splayed attr_accessor :stopping + def configure_delayed_restart + @needing_restart = true + end + # Just so we can specify that we are "the" instance. def initialize(client_class) @splayed = false @@ -23,6 +25,16 @@ class Puppet::Agent client_class.lockfile_path end + def needing_restart? + @needing_restart + end + + def restart + configure_delayed_restart and return if running? + Process.kill(:HUP, $$) + @needing_restart = false + end + # Perform a run with our client. def run if client @@ -44,7 +56,12 @@ class Puppet::Agent end end - def shutdown + # If the client instance is set, we're mid-run. + def running? + ! client.nil? + end + + def stop if self.stopping? Puppet.notice "Already in shutdown" return @@ -58,8 +75,6 @@ class Puppet::Agent Puppet.err "Could not stop %s: %s" % [client_class, detail] end end - - super ensure self.stopping = false end @@ -70,7 +85,7 @@ class Puppet::Agent # Have we splayed already? def splayed? - @splayed + splayed end # Sleep when splay is enabled; else just return. @@ -88,16 +103,12 @@ class Puppet::Agent # 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 + timer = EventLoop::Timer.new(:interval => Puppet[:runinterval], :tolerance => 1, :start? => true) do run() end # Run once before we start following the timer - run() + timer.sound_alarm end def sync diff --git a/lib/puppet/agent/locker.rb b/lib/puppet/agent/locker.rb index c24fdad64..eaf19177a 100644 --- a/lib/puppet/agent/locker.rb +++ b/lib/puppet/agent/locker.rb @@ -30,7 +30,6 @@ module Puppet::Agent::Locker def lockfile unless defined?(@lockfile) - #@lockfile = Puppet::Util::Pidlock.new(Puppet[:puppetdlockfile]) @lockfile = Puppet::Util::Pidlock.new(lockfile_path) end diff --git a/lib/puppet/daemon.rb b/lib/puppet/daemon.rb index b0576124e..5f811d897 100755 --- a/lib/puppet/daemon.rb +++ b/lib/puppet/daemon.rb @@ -1,10 +1,11 @@ require 'puppet' require 'puppet/util/pidlock' +require 'puppet/external/event-loop' # A module that handles operations common to all daemons. This is included # into the Server and Client base classes. -module Puppet::Daemon - include Puppet::Util +class Puppet::Daemon + attr_accessor :agent, :server, :argv def daemonname Puppet[:name] @@ -17,7 +18,7 @@ module Puppet::Daemon exit(0) end - setpidfile() + create_pidfile() # Get rid of console logging Puppet::Util::Log.close(:console) @@ -38,18 +39,42 @@ module Puppet::Daemon end end - # The path to the pid file for this server + # Create a pidfile for our daemon, so we can be stopped and others + # don't try to start. + def create_pidfile + Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do + unless Puppet::Util::Pidlock.new(pidfile).lock + raise "Could not create PID file: %s" % [pidfile] + end + end + end + + # Provide the path to our pidfile. def pidfile - if Puppet[:pidfile] != "" - Puppet[:pidfile] - else - File.join(Puppet[:rundir], daemonname() + ".pid") + Puppet[:pidfile] + end + + def reexec + raise Puppet::DevError, "Cannot reexec unless ARGV arguments are set" unless argv + command = $0 + " " + argv.join(" ") + Puppet.notice "Restarting with '%s'" % command + stop(:exit => false) + exec(command) + end + + def reload + return unless agent + if agent.running? + Puppet.notice "Not triggering already-running agent" + return end + + agent.run end - # Remove the pid file - def rmpidfile - threadlock(:pidfile) do + # Remove the pid file for our daemon. + def remove_pidfile + Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do locker = Puppet::Util::Pidlock.new(pidfile) if locker.locked? locker.unlock or Puppet.err "Could not remove PID file %s" % [pidfile] @@ -57,25 +82,52 @@ module Puppet::Daemon end end - # Create the pid file. - def setpidfile - threadlock(:pidfile) do - unless Puppet::Util::Pidlock.new(pidfile).lock - Puppet.err("Could not create PID file: %s" % [pidfile]) - exit(74) - end + def restart + if agent and agent.running? + agent.configure_delayed_restart + else + reexec end end - # Shut down our server - def shutdown - # Remove our pid file - rmpidfile() + def reopen_logs + Puppet::Util::Log.reopen + end - # And close all logs except the console. - Puppet::Util::Log.destinations.reject { |d| d == :console }.each do |dest| - Puppet::Util::Log.close(dest) + # Trap a couple of the main signals. This should probably be handled + # in a way that anyone else can register callbacks for traps, but, eh. + def set_signal_traps + {:INT => :stop, :TERM => :stop, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method| + trap(signal) do + Puppet.notice "Caught #{signal}; calling #{method}" + send(method) + end end end + + # Stop everything + def stop(args = {:exit => true}) + server.stop if server + + agent.stop if agent + + remove_pidfile() + + Puppet::Util::Log.close_all + + exit if args[:exit] + end + + def start + set_signal_traps + + create_pidfile + + raise Puppet::DevError, "Daemons must have an agent, server, or both" unless agent or server + agent.start if agent + server.start if server + + EventLoop.current.run + end end diff --git a/lib/puppet/event_manager.rb b/lib/puppet/event_manager.rb deleted file mode 100644 index 50c489673..000000000 --- a/lib/puppet/event_manager.rb +++ /dev/null @@ -1,182 +0,0 @@ -require 'puppet/external/event-loop' - -# Manage events related to starting, stopping, and restarting -# contained services. -class Puppet::EventManager - include SignalObserver - - attr_reader :services, :threads, :timers - - def initialize - @services = [] - @threads = [] - @timers = [] - end - - # Create a new service that we're supposed to run - def add_service(service) - @services << service - end - - def newthread(&block) - @threads << Thread.new { yield } - end - - # Add a timer we need to pay attention to. - # This is only used by Puppet::Agent at the moment. - def newtimer(hash, &block) - timer = EventLoop::Timer.new(hash) - @timers << timer - - if block_given? - observe_signal(timer, :alarm, &block) - end - - # In case they need it for something else. - timer - end - - # Reload any services that can be reloaded. Really, this is just - # meant to trigger an Agent run. - def reload - done = 0 - services.find_all { |service| service.respond_to?(:run) }.each do |service| - if service.running? - Puppet.notice "Not triggering already-running %s" % service.class - next - end - - Puppet.notice "Triggering a run of %s" % service.class - - done += 1 - begin - service.run - rescue => detail - Puppet.err "Could not run service %s: %s" % [service.class, detail] - end - end - - unless done > 0 - Puppet.notice "No services were reloaded" - end - end - - def reopen_logs - Puppet::Util::Log.reopen - end - - # Relaunch the executable. - def restart - if client = @services.find { |s| s.is_a? Puppet::Network::Client.master } and client.running? - client.restart - else - command = $0 + " " + self.args.join(" ") - Puppet.notice "Restarting with '%s'" % command - Puppet.shutdown(false) - Puppet::Util::Log.reopen - exec(command) - end - end - - # Trap a couple of the main signals. This should probably be handled - # in a way that anyone else can register callbacks for traps, but, eh. - def set_traps - {:INT => :shutdown, :TERM => :shutdown, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method| - trap(signal) do - Puppet.notice "Caught #{signal}; calling #{method}" - send(method) - end - end - end - - # Shutdown our server process, meaning stop all services and all threads. - # Optionally, exit. - def shutdown(leave = true) - Puppet.notice "Shutting down" - stop_timers - - stop_services - - stop_threads - - if leave - exit(0) - end - end - - # Start all of our services and optionally our event loop, which blocks, - # waiting for someone, somewhere, to generate events of some kind. - def start - start_services - - start_timers - - EventLoop.current.run - end - - def start_services - # Starting everything in its own thread. Otherwise - # we might have one service stop another service from - # doing things like registering timers. - @services.dup.each do |svc| - begin - svc.start - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - @services.delete svc - Puppet.err "Could not start %s: %s" % [svc.class, detail] - end - end - - # We need to give the services a chance to register their timers before - # we try to start monitoring them. - sleep 0.5 - - unless @services.length > 0 - Puppet.notice "No remaining services; exiting" - exit(1) - end - end - - def stop_services - # Stop our services - services.each do |svc| - begin - timeout(20) do - svc.shutdown - end - rescue TimeoutError - Puppet.err "%s could not shut down within 20 seconds" % svc.class - end - end - end - - # Monitor all of the timers that have been set up. - def start_timers - timers.each do |timer| - EventLoop.current.monitor_timer timer - end - end - - def stop_timers - # Unmonitor our timers - timers.each do |timer| - EventLoop.current.ignore_timer timer - end - end - - def stop_threads - # And wait for them all to die, giving a decent amount of time - threads.each do |thr| - begin - timeout(20) do - thr.join - end - rescue TimeoutError - # Just ignore this, since we can't intelligently provide a warning - end - end - end -end diff --git a/lib/puppet/network/http_server/mongrel.rb b/lib/puppet/network/http_server/mongrel.rb index e9421c781..924c11728 100644 --- a/lib/puppet/network/http_server/mongrel.rb +++ b/lib/puppet/network/http_server/mongrel.rb @@ -34,7 +34,6 @@ require 'puppet/network/xmlrpc/server' require 'puppet/network/http_server' require 'puppet/network/client_request' require 'puppet/network/handler' -require 'puppet/daemon' require 'resolv' @@ -51,7 +50,6 @@ require 'resolv' # </pre> module Puppet::Network class HTTPServer::Mongrel < ::Mongrel::HttpHandler - include Puppet::Daemon attr_reader :xmlrpc_server def initialize(handlers) diff --git a/lib/puppet/network/http_server/webrick.rb b/lib/puppet/network/http_server/webrick.rb index 568b4e798..0e835d057 100644 --- a/lib/puppet/network/http_server/webrick.rb +++ b/lib/puppet/network/http_server/webrick.rb @@ -1,5 +1,4 @@ require 'puppet' -require 'puppet/daemon' require 'webrick' require 'webrick/https' require 'fcntl' @@ -16,7 +15,6 @@ module Puppet # The old-school, pure ruby webrick server, which is the default serving # mechanism. class HTTPServer::WEBrick < WEBrick::HTTPServer - include Puppet::Daemon include Puppet::SSLCertificates::Support # Read the CA cert and CRL and populate an OpenSSL::X509::Store diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index b9736f5ac..0af842c8d 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -3,6 +3,7 @@ require 'sync' require 'puppet/transportable' require 'getoptlong' +require 'puppet/external/event-loop' # The class for handling configuration files. class Puppet::Util::Settings @@ -46,9 +47,6 @@ class Puppet::Util::Settings # Generate the list of valid arguments, in a format that GetoptLong can # understand, and add them to the passed option list. def addargs(options) - # Hackish, but acceptable. Copy the current ARGV for restarting. - Puppet.args = ARGV.dup - # Add all of the config parameters as valid options. self.each { |name, element| element.getopt_args.each { |args| options << args } @@ -496,16 +494,9 @@ class Puppet::Util::Settings end # Create a timer to check whether the file should be reparsed. - def settimer - if Puppet[:filetimeout] > 0 - @timer = Puppet.newtimer( - :interval => Puppet[:filetimeout], - :tolerance => 1, - :start? => true - ) do - self.reparse() - end - end + def set_filetimeout_timer + return unless timeout = self[:filetimeout] and timeout > 0 + EventLoop::Timer.new(:interval => timeout, :tolerance => 1, :start? => true) { self.reparse() } end # Convert the settings we manage into a catalog full of resources that model those settings. @@ -822,7 +813,7 @@ Generated on #{Time.now}. # Create a timer so that this file will get checked automatically # and reparsed if necessary. - settimer() + set_filetimeout_timer() result = Hash.new { |names, name| names[name] = {} |