#!/usr/bin/env ruby # == Synopsis # # Retrieve the client configuration from the central puppet server and apply # it to the local host. # # Currently must be run out periodically, using cron or something similar. # # = Usage # # puppetd [-D|--daemonize|--no-daemonize] [-d|--debug] [--disable] [--enable] # [-h|--help] [--fqdn ] [-l|--logdest syslog||console] # [-o|--onetime] [--serve ] [-t|--test] [--noop] # [-V|--version] [-v|--verbose] [-w|--waitforcert ] # # = Description # # This is the main puppet client. Its job is to retrieve the local machine's # configuration from a remote server and apply it. In order to successfully # communicate with the remote server, the client must have a certificate signed # by a certificate authority that the server trusts; the recommended method # for this, at the moment, is to run a certificate authority as part of the # puppet server (which is the default). The client will connect and request # a signed certificate, and will continue connecting until it receives one. # # Once the client has a signed certificate, it will retrieve its configuration # and apply it. # # = Usage Notes # # +puppetd+ does its best to find a compromise between interactive use and # daemon use. Run with no arguments and no configuration, it will go into the # backgroun, attempt to get a signed certificate, and retrieve and apply its # configuration every 30 minutes. # # Some flags are meant specifically for interactive use -- in particular, # +test+ and +tags+ are useful. +test+ enables verbose logging, causes # the daemon to stay in the foreground, exits if the server's configuration is # invalid (this happens if, for instance, you've left a syntax error on the # server), and exits after running the configuration once (rather than hanging # around as a long-running process). # # +tags+ allows you to specify what portions of a configuration you want to apply. # Puppet elements are tagged with all of the class or definition names that # contain them, and you can use the +tags+ flag to specify one of these names, # causing only configuration elements contained within that class or definition # to be applied. This is very useful when you are testing new configurations -- # for instance, if you are just starting to manage +ntpd+, you would put all of # the new elements into an +ntpd+ class, and call puppet with +--tags ntpd+, # which would only apply that small portion of the configuration during your # testing, rather than applying the whole thing. # # = Options # # Note that any configuration parameter that's valid in the configuration file # is also a valid long argument. For example, 'server' is a valid configuration # parameter, so you can specify '--server ' as an argument. # # See the configuration file documentation at # http://reductivelabs.com/trac/puppet/wiki/ConfigurationReference for # the full list of acceptable parameters. A commented list of all # configuration options can also be generated by running puppetd with # '--genconfig'. # # daemonize:: # Send the process into the background. This is the default. # # no-daemonize:: # Do not send the process into the background. # # debug:: # Enable full debugging. # # disable:: # Disable working on the local system. This puts a lock file in place, # causing +puppetd+ not to work on the system until the lock file is removed. # This is useful if you are testing a configuration and do not want the central # configuration to override the local state until everything is tested and # committed. # # +puppetd+ uses the same lock file while it is running, so no more than one # +puppetd+ process is working at a time. # # +puppetd+ exits after executing this. # # enable:: # Enable working on the local system. This removes any lock file, causing # +puppetd+ to start managing the local system again (although it will continue # to use its normal scheduling, so it might not start for another half hour). # # +puppetd+ exits after executing this. # # fqdn:: # Set the fully-qualified domain name of the client. This is only used for # certificate purposes, but can be used to override the discovered hostname. # If you need to use this flag, it is generally an indication of a setup problem. # # help:: # Print this help message # # logdest:: # Where to send messages. Choose between syslog, the console, and a log file. # Defaults to sending messages to syslog, or the console if debugging or # verbosity is enabled. # # no-client:: # Do not create a config client. This will cause the daemon to run # without ever checking for its configuration automatically, and only # makes sense when used in conjunction with --listen. # # onetime:: # Run the configuration once, rather than as a long-running daemon. This is # useful for interactively running puppetd. # # serve:: # Start another type of server. By default, +puppetd+ will start # a service handler that allows authenticated and authorized remote nodes to # trigger the configuration to be pulled down and applied. You can specify # any handler here that does not require configuration, e.g., filebucket, ca, # or resource. The handlers are in +lib/puppet/network/handler+, and the names # must match exactly, both in the call to +serve+ and in +namespaceauth.conf+. # # test:: # Enable the most common options used for testing. These are +onetime+, # +verbose+, +ignorecache, and +no-usecacheonfailure+. # # noop:: # Use +noop+ mode where the daemon runs in a no-op or dry-run mode. This is useful # for seeing what changes Puppet will make without actually executing the changes. # # verbose:: # Turn on verbose reporting. # # version:: # Print the puppet version number and exit. # # waitforcert:: # This option only matters for daemons that do not yet have certificates # and it is enabled by default, with a value of 120 (seconds). This causes # +puppetd+ to connect to the server every 2 minutes and ask it to sign a # certificate request. This is useful for the initial setup of a puppet # client. You can turn off waiting for certificates by specifying a time # of 0. # # = Example # # puppetd --server puppet.domain.com # # = Author # # Luke Kanies # # = Copyright # # Copyright (c) 2005, 2006 Reductive Labs, LLC # Licensed under the GNU Public License # Do an initial trap, so that cancels don't get a stack trace. trap(:INT) do $stderr.puts "Cancelling startup" exit(0) end require 'puppet' require 'puppet/executables/client/certhandler' require 'puppet/network/client' require 'getoptlong' options = [ [ "--centrallogging", GetoptLong::NO_ARGUMENT ], [ "--disable", GetoptLong::NO_ARGUMENT ], [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--enable", GetoptLong::NO_ARGUMENT ], [ "--fqdn", "-f", GetoptLong::REQUIRED_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ], [ "--logdest", "-l", GetoptLong::REQUIRED_ARGUMENT ], [ "--onetime", "-o", GetoptLong::NO_ARGUMENT ], [ "--test", "-t", GetoptLong::NO_ARGUMENT ], [ "--serve", "-s", GetoptLong::REQUIRED_ARGUMENT ], [ "--no-client", GetoptLong::NO_ARGUMENT ], [ "--verbose", "-v", GetoptLong::NO_ARGUMENT ], [ "--version", "-V", GetoptLong::NO_ARGUMENT ], [ "--waitforcert", "-w", GetoptLong::REQUIRED_ARGUMENT ] ] # Add all of the config parameters as valid options. Puppet.settings.addargs(options) result = GetoptLong.new(*options) args = {} options = { :waitforcert => 120, # Default to checking for certs every 5 minutes :onetime => false, :verbose => false, :debug => false, :centrallogs => false, :setdest => false, :enable => false, :disable => false, :client => true, :fqdn => nil, :serve => {} } begin explicit_waitforcert = false result.each { |opt,arg| case opt # First check to see if the argument is a valid configuration parameter; # if so, set it. NOTE: there is a catch-all at the bottom for defaults.rb when "--disable" options[:disable] = true when "--serve" if Puppet::Network::Handler.handler(arg) options[:serve][arg.to_sym] = {} else raise "Could not find handler for %s" % arg end when "--enable" options[:enable] = true when "--test" options[:test] = true when "--centrallogging" options[:centrallogs] = true when "--help" if Puppet.features.usage? RDoc::usage && exit else puts "No help available unless you have RDoc::usage installed" exit end when "--version" puts "%s" % Puppet.version exit when "--verbose" options[:verbose] = true when "--debug" options[:debug] = true when "--fqdn" options[:fqdn] = arg when "--no-client" options[:client] = false when "--onetime" options[:onetime] = true options[:waitforcert] = 0 unless explicit_waitforcert when "--port" args[:Port] = arg when "--logdest" begin Puppet::Util::Log.newdestination(arg) options[:setdest] = true rescue => detail if Puppet[:debug] puts detail.backtrace end $stderr.puts detail.to_s end when "--waitforcert" options[:waitforcert] = arg.to_i explicit_waitforcert = true else Puppet.settings.handlearg(opt, arg) end } rescue GetoptLong::InvalidOption => detail $stderr.puts detail $stderr.puts "Try '#{$0} --help'" exit(1) end # Now parse the config Puppet.parse_config if options[:test] # Enable all of the most common test options. Puppet.settings.handlearg("--ignorecache") Puppet.settings.handlearg("--no-usecacheonfailure") Puppet.settings.handlearg("--no-splay") Puppet.settings.handlearg("--show_diff") Puppet.settings.handlearg("--no-daemonize") options[:verbose] = true options[:onetime] = true options[:waitforcert] = 0 end # Handle the logging settings. if options[:debug] or options[:verbose] Puppet::Util::Log.newdestination(:console) if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end end unless options[:setdest] Puppet::Util::Log.newdestination(:syslog) end if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end # If noop is set, then also enable diffs if Puppet[:noop] Puppet[:show_diff] = true end args[:Server] = Puppet[:server] if options[:fqdn] args[:FQDN] = options[:fqdn] Puppet[:certname] = options[:fqdn] end if options[:centrallogs] logdest = args[:Server] if args.include?(:Port) logdest += ":" + args[:Port] end Puppet::Util::Log.newdestination(logdest) end # We need tomake the client either way, we just don't start it # if --no-client is set. client = Puppet::Network::Client.master.new(args) if options[:enable] client.enable elsif options[:disable] client.disable end if options[:enable] or options[:disable] exit(0) end server = nil # It'd be nice to daemonize later, but we have to daemonize before the # waitforcert happens. if Puppet[:daemonize] client.daemonize end unless Puppet::Executables::Client::CertHandler.new(options[:waitforcert], options[:onetime]).read_retrieve client.recycle_connection end objects = [] # This has to go after the certs are dealt with. if Puppet[:listen] and ! options[:onetime] unless FileTest.exists?(Puppet[:authconfig]) Puppet.err "Will not start without authorization file %s" % Puppet[:authconfig] exit(14) end # FIXME: we should really figure out how to distribute the CRL # to clients. In the meantime, we just disable CRL checking if # the CRL file doesn't exist unless File::exist?(Puppet[:cacrl]) Puppet[:cacrl] = 'false' end handlers = nil if options[:serve].empty? handlers = {:Runner => {}} else handlers = options[:serve] end handlers.each do |name, hash| Puppet.info "Starting handler for %s" % name end args[:Handlers] = handlers args[:Port] = Puppet[:puppetport] require 'puppet/network/http_server/webrick' begin server = Puppet::Network::HTTPServer::WEBrick.new(args) rescue => detail $stderr.puts detail puts detail.backtrace exit(1) end objects << server elsif options[:onetime] and Puppet[:listen] Puppet.notice "Ignoring --listen on onetime run" end if options[:client] objects << client end # Set traps for INT and TERM Puppet.settraps # If --onetime is specified, we don't run 'start', which means we don't # create a pidfile. if options[:onetime] unless options[:client] $stderr.puts "onetime is specified but there is no client" exit(43) end # Add the service, so the traps work correctly. Puppet.newservice(client) begin client.run rescue => detail if Puppet[:trace] puts detail.backtrace end Puppet.err detail.to_s end exit(0) else if server Puppet.newservice(server) end if options[:client] Puppet.notice "Starting Puppet client version %s" % [Puppet.version] Puppet.newservice(client) end Puppet.settraps Puppet.start end