summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2007-03-06 19:03:05 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2007-03-06 19:03:05 +0000
commit46d344b9daa24047b60183cc94509d306b6b562a (patch)
tree3c11eaad696ba3d6e6dd40bd7b9e7d1a4a71af85 /lib
parent68233706a9ff05be8fa8ab3ab7198cd0918517d6 (diff)
downloadpuppet-46d344b9daa24047b60183cc94509d306b6b562a.tar.gz
puppet-46d344b9daa24047b60183cc94509d306b6b562a.tar.xz
puppet-46d344b9daa24047b60183cc94509d306b6b562a.zip
Merging the webserver_portability branch from version 2182 to version 2258.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2259 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib')
-rw-r--r--lib/puppet.rb19
-rw-r--r--lib/puppet/configuration.rb144
-rwxr-xr-xlib/puppet/daemon.rb323
-rw-r--r--lib/puppet/dsl.rb2
-rw-r--r--lib/puppet/network/authconfig.rb28
-rw-r--r--lib/puppet/network/authorization.rb84
-rw-r--r--lib/puppet/network/client.rb132
-rw-r--r--lib/puppet/network/client/ca.rb59
-rw-r--r--lib/puppet/network/client/dipper.rb19
-rw-r--r--lib/puppet/network/client/file.rb17
-rw-r--r--lib/puppet/network/client/log.rb17
-rw-r--r--lib/puppet/network/client/logger.rb6
-rw-r--r--lib/puppet/network/client/master.rb102
-rw-r--r--lib/puppet/network/client/proxy.rb2
-rw-r--r--lib/puppet/network/client/report.rb (renamed from lib/puppet/network/client/reporter.rb)9
-rw-r--r--lib/puppet/network/client/resource.rb17
-rw-r--r--lib/puppet/network/client/runner.rb6
-rw-r--r--lib/puppet/network/client/status.rb4
-rw-r--r--lib/puppet/network/client_request.rb32
-rw-r--r--lib/puppet/network/handler.rb33
-rw-r--r--lib/puppet/network/handler/ca.rb (renamed from lib/puppet/network/server/ca.rb)2
-rwxr-xr-xlib/puppet/network/handler/filebucket.rb (renamed from lib/puppet/network/server/filebucket.rb)21
-rwxr-xr-xlib/puppet/network/handler/fileserver.rb (renamed from lib/puppet/network/server/fileserver.rb)13
-rwxr-xr-xlib/puppet/network/handler/logger.rb (renamed from lib/puppet/network/server/logger.rb)2
-rw-r--r--lib/puppet/network/handler/master.rb (renamed from lib/puppet/network/server/master.rb)6
-rwxr-xr-xlib/puppet/network/handler/report.rb (renamed from lib/puppet/network/server/report.rb)41
-rwxr-xr-xlib/puppet/network/handler/resource.rb (renamed from lib/puppet/network/server/resource.rb)6
-rwxr-xr-xlib/puppet/network/handler/runner.rb (renamed from lib/puppet/network/server/runner.rb)4
-rw-r--r--lib/puppet/network/handler/status.rb13
-rw-r--r--lib/puppet/network/networkclient.rb167
-rw-r--r--lib/puppet/network/server.rb207
-rw-r--r--lib/puppet/network/server/mongrel.rb138
-rw-r--r--lib/puppet/network/server/servlet.rb277
-rw-r--r--lib/puppet/network/server/webrick.rb153
-rw-r--r--lib/puppet/network/xmlrpc/client.rb129
-rw-r--r--lib/puppet/network/xmlrpc/processor.rb91
-rw-r--r--lib/puppet/network/xmlrpc/server.rb20
-rw-r--r--lib/puppet/network/xmlrpc/webrick_servlet.rb121
-rw-r--r--lib/puppet/parser/ast/caseopt.rb3
-rw-r--r--lib/puppet/parser/ast/casestatement.rb39
-rw-r--r--lib/puppet/parser/scope.rb2
-rw-r--r--lib/puppet/rails.rb40
-rw-r--r--lib/puppet/reports/log.rb2
-rw-r--r--lib/puppet/reports/rrdgraph.rb2
-rw-r--r--lib/puppet/reports/store.rb2
-rw-r--r--lib/puppet/reports/tagmail.rb13
-rwxr-xr-xlib/puppet/sslcertificates.rb18
-rw-r--r--lib/puppet/sslcertificates/ca.rb4
-rw-r--r--lib/puppet/sslcertificates/support.rb128
-rw-r--r--lib/puppet/type/pfile.rb16
-rwxr-xr-xlib/puppet/type/pfile/source.rb15
-rwxr-xr-xlib/puppet/type/pfilebucket.rb6
-rw-r--r--lib/puppet/util/config.rb18
-rw-r--r--lib/puppet/util/log.rb2
-rw-r--r--lib/puppet/util/subclass_loader.rb83
55 files changed, 1494 insertions, 1365 deletions
diff --git a/lib/puppet.rb b/lib/puppet.rb
index 6498d74a8..b0c6ee9f8 100644
--- a/lib/puppet.rb
+++ b/lib/puppet.rb
@@ -35,15 +35,7 @@ module Puppet
# lazy.
attr_accessor :args
attr_reader :features
- end
-
-
- def Puppet.execname
- unless defined? @name
- @name = $0.gsub(/.+#{File::SEPARATOR}/,'').sub(/\.rb$/, '')
- end
-
- return @name
+ attr_writer :name
end
# the hash that determines how our system behaves
@@ -78,9 +70,6 @@ module Puppet
@@config.setdefaults(section, hash)
end
- # Load all of the configuration parameters.
- require 'puppet/configuration'
-
# configuration parameter access and stuff
def self.[](param)
case param
@@ -116,6 +105,9 @@ module Puppet
@@config
end
+ # Load all of the configuration parameters.
+ require 'puppet/configuration'
+
def self.genconfig
if Puppet[:configprint] != ""
val = Puppet[:configprint]
@@ -268,6 +260,7 @@ module Puppet
# Stop our services
defined? @services and @services.each do |svc|
+ next unless svc.respond_to?(:shutdown)
begin
timeout(20) do
svc.shutdown
@@ -389,9 +382,9 @@ module Puppet
end
end
-require 'puppet/network/server'
require 'puppet/type'
require 'puppet/util/storage'
+require 'puppet/parser/interpreter'
if Puppet[:storeconfigs]
require 'puppet/rails'
end
diff --git a/lib/puppet/configuration.rb b/lib/puppet/configuration.rb
index 5b2d90af8..5764a2139 100644
--- a/lib/puppet/configuration.rb
+++ b/lib/puppet/configuration.rb
@@ -4,7 +4,8 @@ module Puppet
# use basedirs that are in the user's home directory.
conf = nil
var = nil
- if self.name != "puppetmasterd" and Puppet::Util::SUIDManager.uid != 0
+ name = $0.gsub(/.+#{File::SEPARATOR}/,'').sub(/\.rb$/, '')
+ if name != "puppetmasterd" and Puppet::Util::SUIDManager.uid != 0
conf = File.expand_path("~/.puppet")
var = File.expand_path("~/.puppet/var")
else
@@ -15,10 +16,12 @@ module Puppet
self.setdefaults(:puppet,
:confdir => [conf, "The main Puppet configuration directory."],
- :vardir => [var, "Where Puppet stores dynamic and growing data."]
+ :vardir => [var, "Where Puppet stores dynamic and growing data."],
+ :name => [name, "The name of the service, if we are running as one. The
+ default is essentially $0 without the path or '.rb'."]
)
- if self.name == "puppetmasterd"
+ if name == "puppetmasterd"
logopts = {:default => "$vardir/log",
:mode => 0750,
:owner => "$user",
@@ -114,7 +117,14 @@ module Puppet
# Define the config default.
self.setdefaults(self.name,
:config => ["$confdir/#{self.name}.conf",
- "The configuration file for #{self.name}."]
+ "The configuration file for #{self.name}."],
+ :pidfile => ["", "The pid file"],
+ :bindaddress => ["", "The address to bind to. Mongrel servers
+ default to 127.0.0.1 and WEBrick defaults to 0.0.0.0."],
+ :servertype => ["webrick", "The type of server to use. Currently supported
+ options are webrick and mongrel. If you use mongrel, you will need
+ a proxy in front of the process or processes, since Mongrel cannot
+ speak SSL."]
)
self.setdefaults("puppetmasterd",
@@ -145,7 +155,15 @@ module Puppet
for determining which 'node' statement applies to the client.
Possible values are 'cert' (use the subject's CN in the client's
certificate) and 'facter' (use the hostname that the client
- reported in its facts)"]
+ reported in its facts)"],
+ :bucketdir => {
+ :default => "$vardir/bucket",
+ :mode => 0750,
+ :owner => "$user",
+ :group => "$group",
+ :desc => "Where FileBucket files are stored."
+ },
+ :ca => [true, "Wether the master should function as a certificate authority."]
)
self.setdefaults("puppetd",
@@ -179,7 +197,121 @@ module Puppet
:puppetport => [8139, "Which port puppetd listens on."],
:noop => [false, "Whether puppetd should be run in noop mode."],
:runinterval => [1800, # 30 minutes
- "How often puppetd applies the client configuration; in seconds"]
+ "How often puppetd applies the client configuration; in seconds"],
+ :listen => [false, "Whether puppetd should listen for
+ connections. If this is true, then by default only the
+ ``runner`` server is started, which allows remote authorized
+ and authenticated nodes to connect and trigger ``puppetd``
+ runs."],
+ :ca_server => ["$server", "The server to use for certificate
+ authority requests. It's a separate server because it cannot
+ and does not need to horizontally scale."],
+ :ca_port => ["$master_port", "The port to use for the certificate authority."]
+ )
+
+ self.setdefaults("filebucket",
+ :clientbucketdir => {
+ :default => "$vardir/clientbucket",
+ :mode => 0750,
+ :desc => "Where FileBucket files are stored locally."
+ }
+ )
+ self.setdefaults("fileserver",
+ :fileserverconfig => ["$confdir/fileserver.conf",
+ "Where the fileserver configuration is stored."]
+ )
+ self.setdefaults(:reporting,
+ :reports => ["store",
+ "The list of reports to generate. All reports are looked for
+ in puppet/reports/<name>.rb, and multiple report names should be
+ comma-separated (whitespace is okay)."
+ ],
+ :reportdir => {:default => "$vardir/reports",
+ :mode => 0750,
+ :owner => "$user",
+ :group => "$group",
+ :desc => "The directory in which to store reports
+ received from the client. Each client gets a separate
+ subdirectory."}
+ )
+ self.setdefaults("puppetd",
+ :puppetdlockfile => [ "$statedir/puppetdlock",
+ "A lock file to temporarily stop puppetd from doing anything."],
+ :usecacheonfailure => [true,
+ "Whether to use the cached configuration when the remote
+ configuration will not compile. This option is useful for testing
+ new configurations, where you want to fix the broken configuration
+ rather than reverting to a known-good one."
+ ],
+ :ignorecache => [false,
+ "Ignore cache and always recompile the configuration. This is
+ useful for testing new configurations, where the local cache may in
+ fact be stale even if the timestamps are up to date - if the facts
+ change or if the server changes."
+ ],
+ :downcasefacts => [false,
+ "Whether facts should be made all lowercase when sent to the server."]
+ )
+
+ self.setdefaults(:puppetd,
+ :configtimeout => [120,
+ "How long the client should wait for the configuration to be retrieved
+ before considering it a failure. This can help reduce flapping if too
+ many clients contact the server at one time."
+ ],
+ :reportserver => ["$server",
+ "The server to which to send transaction reports."
+ ],
+ :report => [false,
+ "Whether to send reports after every transaction."
+ ]
+ )
+
+ # Plugin information.
+ self.setdefaults("puppet",
+ :pluginpath => ["$vardir/plugins",
+ "Where Puppet should look for plugins. Multiple directories should
+ be colon-separated, like normal PATH variables."],
+ :plugindest => ["$vardir/plugins",
+ "Where Puppet should store plugins that it pulls down from the central
+ server."],
+ :pluginsource => ["puppet://$server/plugins",
+ "From where to retrieve plugins. The standard Puppet ``file`` type
+ is used for retrieval, so anything that is a valid file source can
+ be used here."],
+ :pluginsync => [false,
+ "Whether plugins should be synced with the central server."],
+ :pluginsignore => [".svn CVS",
+ "What files to ignore when pulling down plugins."]
+ )
+
+ # Central fact information.
+ self.setdefaults("puppet",
+ :factpath => ["$vardir/facts",
+ "Where Puppet should look for facts. Multiple directories should
+ be colon-separated, like normal PATH variables."],
+ :factdest => ["$vardir/facts",
+ "Where Puppet should store facts that it pulls down from the central
+ server."],
+ :factsource => ["puppet://$server/facts",
+ "From where to retrieve facts. The standard Puppet ``file`` type
+ is used for retrieval, so anything that is a valid file source can
+ be used here."],
+ :factsync => [false,
+ "Whether facts should be synced with the central server."],
+ :factsignore => [".svn CVS",
+ "What files to ignore when pulling down facts."]
+ )
+
+ self.setdefaults(:reporting,
+ :tagmap => ["$confdir/tagmail.conf",
+ "The mapping between reporting tags and email addresses."],
+ :sendmail => [%x{which sendmail 2>/dev/null}.chomp,
+ "Where to find the sendmail binary with which to send email."],
+ :reportfrom => ["report@" + [Facter["hostname"].value, Facter["domain"].value].join("."),
+ "The 'from' email address for the reports."],
+ :smtpserver => ["none",
+ "The server through which to send email reports."]
)
end
diff --git a/lib/puppet/daemon.rb b/lib/puppet/daemon.rb
index 13caf4541..52197f562 100755
--- a/lib/puppet/daemon.rb
+++ b/lib/puppet/daemon.rb
@@ -1,284 +1,83 @@
-# helper functions for daemons
-
require 'puppet'
require 'puppet/util/pidlock'
-module Puppet
- # A module that handles operations common to all daemons. This is included
- # into the Server and Client base classes.
- module Daemon
- include Puppet::Util
-
- Puppet.config.setdefaults(:puppet, :setpidfile => [true,
- "Whether to store a PID file for the daemon."])
- def daemonname
- #$0.sub(/.+#{File::SEPARATOR}/,'')
- Puppet.execname
- end
-
- # The path to the pid file for this server
- def pidfile
- File.join(Puppet[:rundir], daemonname() + ".pid")
- end
-
- # Put the daemon into the background.
- def daemonize
- if pid = fork()
- Process.detach(pid)
- exit(0)
- end
+# 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
- # Get rid of console logging
- Puppet::Util::Log.close(:console)
+ def daemonname
+ Puppet[:name]
+ end
- Process.setsid
- Dir.chdir("/")
- begin
- $stdin.reopen "/dev/null"
- $stdout.reopen "/dev/null", "a"
- $stderr.reopen $stdout
- Puppet::Util::Log.reopen
- rescue => detail
- File.open("/tmp/daemonout", "w") { |f|
- f.puts "Could not start %s: %s" % [Puppet.execname, detail]
- }
- Puppet.err "Could not start %s: %s" % [Puppet.execname, detail]
- exit(12)
- end
+ # Put the daemon into the background.
+ def daemonize
+ if pid = fork()
+ Process.detach(pid)
+ exit(0)
end
-
- def fqdn
- unless defined? @fqdn and @fqdn
- hostname = Facter.value("hostname")
- domain = Facter.value("domain")
- if !domain || domain.empty? then
- @fqdn = hostname
- else
- @fqdn = [hostname, domain].join(".")
- end
- end
- return @fqdn
+
+ setpidfile()
+
+ # Get rid of console logging
+ Puppet::Util::Log.close(:console)
+
+ Process.setsid
+ Dir.chdir("/")
+ begin
+ $stdin.reopen "/dev/null"
+ $stdout.reopen "/dev/null", "a"
+ $stderr.reopen $stdout
+ Puppet::Util::Log.reopen
+ rescue => detail
+ File.open("/tmp/daemonout", "w") { |f|
+ f.puts "Could not start %s: %s" % [Puppet[:name], detail]
+ }
+ Puppet.err "Could not start %s: %s" % [Puppet[:name], detail]
+ exit(12)
end
+ end
- def httplog
- args = []
-
- # yuck; separate http logs
- file = nil
- Puppet.config.use(:puppet, :certificates, Puppet.execname)
- if Puppet.execname == "puppetmasterd"
- file = Puppet[:masterhttplog]
- else
- file = Puppet[:httplog]
- end
-#
-# unless FileTest.exists?(File.dirname(file))
-# Puppet.recmkdir(File.dirname(file))
-# end
-
- args << file
- if Puppet[:debug]
- args << WEBrick::Log::DEBUG
- end
-
- log = WEBrick::Log.new(*args)
-
-
- return log
- end
-
- # Read in an existing certificate.
- def readcert
- return unless @secureinit
- Puppet.config.use(:puppet, :certificates)
- # verify we've got all of the certs set up and such
-
- if defined? @cert and defined? @key and @cert and @key
- return true
- end
-
- unless defined? @fqdn
- self.fqdn
- end
-
- # we are not going to encrypt our key, but we need at a minimum
- # a keyfile and a certfile
- #@certfile = File.join(Puppet[:certdir], [@fqdn, "pem"].join("."))
- #@cacertfile = File.join(Puppet[:certdir], ["ca", "pem"].join("."))
- #@keyfile = File.join(Puppet[:privatekeydir], [@fqdn, "pem"].join("."))
- #@publickeyfile = File.join(Puppet[:publickeydir], [@fqdn, "pem"].join("."))
- @certfile = Puppet[:hostcert]
- @cacertfile = Puppet[:localcacert]
- @keyfile = Puppet[:hostprivkey]
- @publickeyfile = Puppet[:hostpubkey]
-
- if File.exists?(@keyfile)
- # load the key
- @key = OpenSSL::PKey::RSA.new(File.read(@keyfile))
- else
- return false
- end
-
- if File.exists?(@certfile)
- if File.exists?(@cacertfile)
- @cacert = OpenSSL::X509::Certificate.new(File.read(@cacertfile))
- else
- raise Puppet::Error, "Found cert file with no ca cert file"
- end
- @cert = OpenSSL::X509::Certificate.new(File.read(@certfile))
- else
- return false
- end
- return true
- end
-
- # Request a certificate from the remote system. This does all of the work
- # of creating the cert request, contacting the remote system, and
- # storing the cert locally.
- def requestcert
- unless @secureinit
- raise Puppet::DevError,
- "Tried to request cert without initialized security"
- end
- retrieved = false
- Puppet.config.use(:puppet, :certificates)
- # create the directories involved
- # FIXME it's a stupid hack that i have to do this
-# [Puppet[:certdir], Puppet[:privatekeydir], Puppet[:csrdir],
-# Puppet[:publickeydir]].each { |dir|
-# unless FileTest.exists?(dir)
-# Puppet.recmkdir(dir, 0770)
-# end
-# }
-
- if self.readcert
- Puppet.info "Certificate already exists; not requesting"
- return true
- end
-
- unless defined? @key and @key
- # create a new one and store it
- Puppet.info "Creating a new SSL key at %s" % @keyfile
- @key = OpenSSL::PKey::RSA.new(Puppet[:keylength])
- Puppet.config.write(:hostprivkey) do |f| f.print @key.to_pem end
- Puppet.config.write(:hostpubkey) do |f|
- f.print @key.public_key.to_pem
- end
- #File.open(@keyfile, "w", 0660) { |f| f.print @key.to_pem }
- #File.open(@publickeyfile, "w", 0660) { |f|
- # f.print @key.public_key.to_pem
- #}
- end
-
-
- unless defined? @driver
- Puppet.err "Cannot request a certificate without a defined target"
- return false
- end
-
- unless defined? @csr
- Puppet.info "Creating a new certificate request for %s" % @fqdn
- name = OpenSSL::X509::Name.new([["CN", @fqdn]])
-
- @csr = OpenSSL::X509::Request.new
- @csr.version = 0
- @csr.subject = name
- @csr.public_key = @key.public_key
- @csr.sign(@key, OpenSSL::Digest::MD5.new)
- end
-
- Puppet.info "Requesting certificate"
-
- # We can only request a client with a CA client, so we need
- # to create one if we don't already have one (or if we're not a CA
- # server).
- caclient = nil
- if @driver.is_a? Puppet::Network::Client::CA or @driver.is_a? Puppet::Network::Server::CA
- caclient = @driver
- else
- # Create a CA client with which to request the cert.
- if @driver.local?
- raise Puppet::DevError,
- "Incorrect setup for a local CA request"
- end
- caclient = Puppet::Network::Client::CA.new(
- :Port => @driver.puppet_port,
- :Server => @driver.puppet_server
- )
- end
-
- begin
- cert, cacert = caclient.getcert(@csr.to_pem)
- rescue => detail
- if Puppet[:trace]
- puts detail.backtrace
- end
- raise Puppet::Error.new("Certificate retrieval failed: %s" %
- detail)
- end
-
- if cert.nil? or cert == ""
- return nil
- end
- Puppet.config.write(:hostcert) do |f| f.print cert end
- Puppet.config.write(:localcacert) do |f| f.print cacert end
- #File.open(@certfile, "w", 0644) { |f| f.print cert }
- #File.open(@cacertfile, "w", 0644) { |f| f.print cacert }
- begin
- @cert = OpenSSL::X509::Certificate.new(cert)
- @cacert = OpenSSL::X509::Certificate.new(cacert)
- retrieved = true
- rescue => detail
- raise Puppet::Error.new(
- "Invalid certificate: %s" % detail
- )
- end
-
- unless @cert.check_private_key(@key)
- raise Puppet::DevError, "Received invalid certificate"
- end
- return retrieved
+ # The path to the pid file for this server
+ def pidfile
+ if Puppet[:pidfile] != ""
+ Puppet[:pidfile]
+ else
+ File.join(Puppet[:rundir], daemonname() + ".pid")
end
+ end
- # Remove the pid file
- def rmpidfile
- threadlock(:pidfile) do
- locker = Puppet::Util::Pidlock.new(pidfile)
- if locker.locked?
- locker.unlock or Puppet.err "Could not remove PID file %s" % [pidfile]
- end
+ # Remove the pid file
+ def rmpidfile
+ threadlock(:pidfile) do
+ locker = Puppet::Util::Pidlock.new(pidfile)
+ if locker.locked?
+ locker.unlock or Puppet.err "Could not remove PID file %s" % [pidfile]
end
end
+ end
- # Create the pid file.
- def setpidfile
- return unless Puppet[:setpidfile]
-
- threadlock(:pidfile) do
- unless Puppet::Util::Pidlock.new(pidfile).lock
- Puppet.err("Could not create PID file: %s" % [pidfile])
- exit(74)
- 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
end
+ end
- # Shut down our server
- def shutdown
- # Remove our pid file
- rmpidfile()
-
- # And close all logs except the console.
- Puppet::Util::Log.destinations.reject { |d| d == :console }.each do |dest|
- Puppet::Util::Log.close(dest)
- end
+ # Shut down our server
+ def shutdown
+ # Remove our pid file
+ rmpidfile()
- super
+ # And close all logs except the console.
+ Puppet::Util::Log.destinations.reject { |d| d == :console }.each do |dest|
+ Puppet::Util::Log.close(dest)
end
- def start
- setpidfile()
- super
- end
+ super
end
end
diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb
index 09a4e2b61..9c652f082 100644
--- a/lib/puppet/dsl.rb
+++ b/lib/puppet/dsl.rb
@@ -68,7 +68,7 @@ module Puppet
def apply
bucket = export()
objects = bucket.to_type
- master = Puppet::Network::Client::MasterClient.new :Master => "whatever"
+ master = Puppet::Network::Client.master.new :Master => "whatever"
master.objects = objects
master.apply
diff --git a/lib/puppet/network/authconfig.rb b/lib/puppet/network/authconfig.rb
index bf21fb5ce..19fbc6cf3 100644
--- a/lib/puppet/network/authconfig.rb
+++ b/lib/puppet/network/authconfig.rb
@@ -12,6 +12,13 @@ module Puppet
]
)
+ def self.main
+ unless defined? @main
+ @main = self.new()
+ end
+ @main
+ end
+
# Just proxy the setting methods to our rights stuff
[:allow, :deny].each do |method|
define_method(method) do |*args|
@@ -19,24 +26,19 @@ module Puppet
end
end
- # Here we add a little bit of semantics. They can set auth on a whole namespace
- # or on just a single method in the namespace.
- def allowed?(name, host, ip)
- namespace, method = name.to_s.split(".")
- unless namespace and method
- raise ArgumentError, "Invalid method name %s" % name
- end
-
- name = name.intern if name.is_a? String
- namespace = namespace.intern
- method = method.intern
+ # Here we add a little bit of semantics. They can set auth on a whole
+ # namespace or on just a single method in the namespace.
+ def allowed?(request)
+ name = request.call.intern
+ namespace = request.handler.intern
+ method = request.method.intern
read()
if @rights.include?(name)
- return @rights[name].allowed?(host, ip)
+ return @rights[name].allowed?(request.name, request.ip)
elsif @rights.include?(namespace)
- return @rights[namespace].allowed?(host, ip)
+ return @rights[namespace].allowed?(request.name, request.ip)
else
return false
end
diff --git a/lib/puppet/network/authorization.rb b/lib/puppet/network/authorization.rb
new file mode 100644
index 000000000..a3c1d50e1
--- /dev/null
+++ b/lib/puppet/network/authorization.rb
@@ -0,0 +1,84 @@
+require 'puppet/network/client_request'
+require 'puppet/network/authconfig'
+
+module Puppet::Network
+ # Most of our subclassing is just so that we can get
+ # access to information from the request object, like
+ # the client name and IP address.
+ class InvalidClientRequest < Puppet::Error; end
+ module Authorization
+ # Create our config object if necessary. This works even if
+ # there's no configuration file.
+ def authconfig
+ unless defined? @authconfig
+ @authconfig = Puppet::Network::AuthConfig.main()
+ end
+
+ @authconfig
+ end
+
+ # Verify that our client has access. We allow untrusted access to
+ # puppetca methods but no others.
+ def authorized?(request)
+ msg = "%s client %s access to %s" %
+ [request.authenticated? ? "authenticated" : "unauthenticated",
+ request, request.call]
+
+ if request.authenticated?
+ if authconfig.exists?
+ if authconfig.allowed?(request)
+ Puppet.debug "Allowing " + msg
+ return true
+ else
+ Puppet.notice "Denying " + msg
+ return false
+ end
+ else
+ # This is a hack way of seeing if we're a config master.
+ if Puppet[:name] == "puppetmasterd"
+ Puppet.debug "Allowing " + msg
+ return true
+ else
+ Puppet.notice "Denying " + msg
+ return false
+ end
+ end
+ else
+ if request.handler == "puppetca"
+ Puppet.notice "Allowing " + msg
+ else
+ Puppet.notice "Denying " + msg
+ return false
+ end
+ end
+ end
+
+ # Is this functionality available?
+ def available?(request)
+ if handler_loaded?(request.handler)
+ return true
+ else
+ Puppet.warning "Client %s requested unavailable functionality %s" %
+ [request, request.handler]
+ return false
+ end
+ end
+
+ # Make sure that this method is available and authorized.
+ def verify(request)
+ unless available?(request)
+ raise InvalidClientRequest.new(
+ "Functionality %s not available" % request.handler
+ )
+ end
+ unless authorized?(request)
+ raise InvalidClientRequest.new(
+ "Host %s not authorized to call %s" %
+ [request, request.call]
+ )
+ end
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/client.rb b/lib/puppet/network/client.rb
index 604912025..c08acabb1 100644
--- a/lib/puppet/network/client.rb
+++ b/lib/puppet/network/client.rb
@@ -1,60 +1,73 @@
# the available clients
require 'puppet'
-require 'puppet/network/networkclient'
+require 'puppet/daemon'
+require 'puppet/network/xmlrpc/client'
+require 'puppet/util/subclass_loader'
+require 'puppet/util/methodhelper'
+require 'puppet/sslcertificates/support'
+
+require 'net/http'
+
+# Some versions of ruby don't have this method defined, which basically causes
+# us to never use ssl. Yay.
+class Net::HTTP
+ def use_ssl?
+ if defined? @use_ssl
+ @use_ssl
+ else
+ false
+ end
+ end
+end
-# FIXME this still isn't a good design, because none of the handlers overlap
-# so i could just as easily include them all in the main module
-# but at least it's better organized for now
+# The base class for all of the clients. Many clients just directly
+# call methods, but some of them need to do some extra work or
+# provide a different interface.
class Puppet::Network::Client
+ Client = self
include Puppet::Daemon
include Puppet::Util
+ extend Puppet::Util::SubclassLoader
+ include Puppet::Util::MethodHelper
+
+ # This handles reading in the key and such-like.
+ include Puppet::SSLCertificates::Support
- # FIXME The cert stuff should only come up with networking, so it
- # should be in the network client, not the normal client. But if i do
- # that, it's hard to tell whether the certs have been initialized.
- include Puppet::Daemon
- attr_reader :secureinit
attr_accessor :schedule, :lastrun, :local, :stopping
- class << self
- attr_reader :drivername, :handler
- attr_accessor :netclient
- end
+ # Set up subclass loading
+ handle_subclasses :client, "puppet/network/client"
- def initcerts
- unless self.readcert
- #if self.is_a? Puppet::Network::Client::CA
- unless self.requestcert
- return nil
- end
- #else
- # return nil
- #end
- #unless self.requestcert
- #end
+ # Determine what clients look for when being passed an object for local
+ # client/server stuff. E.g., you could call Client::CA.new(:CA => ca).
+ def self.drivername
+ unless defined? @drivername
+ @drivername = self.name
end
+ @drivername
+ end
- # unless we have a driver, we're a local client and we can't add
- # certs anyway, so it doesn't matter
- unless @driver
- return true
+ # Figure out the handler for our client.
+ def self.handler
+ unless defined? @handler
+ @handler = Puppet::Network::Handler.handler(self.name)
end
+ @handler
+ end
- self.setcerts
+ # The class that handles xmlrpc interaction for us.
+ def self.xmlrpc_client
+ unless defined? @xmlrpc_client
+ @xmlrpc_client = Puppet::Network::XMLRPCClient.handler_class(self.handler)
+ end
+ @xmlrpc_client
end
+ # Create our client.
def initialize(hash)
# to whom do we connect?
@server = nil
- @nil = nil
- @secureinit = hash[:NoSecureInit] || true
-
- if hash.include?(:FQDN)
- @fqdn = hash[:FQDN]
- else
- self.fqdn
- end
if hash.include?(:Cache)
@cache = hash[:Cache]
@@ -64,37 +77,24 @@ class Puppet::Network::Client
driverparam = self.class.drivername
if hash.include?(:Server)
- if $noclientnetworking
- raise NetworkClientError.new("Networking not available: %s" %
- $nonetworking)
- end
-
args = {:Server => hash[:Server]}
args[:Port] = hash[:Port] || Puppet[:masterport]
- if self.readcert
- args[:Certificate] = @cert
- args[:Key] = @key
- args[:CAFile] = @cacertfile
- end
+ @driver = self.class.xmlrpc_client.new(args)
- netclient = nil
- unless netclient = self.class.netclient
- unless handler = self.class.handler
- raise Puppet::DevError,
- "Class %s has no handler defined" % self.class
- end
- namespace = self.class.handler.interface.prefix
- netclient = Puppet::Network::NetworkClient.netclient(namespace)
- self.class.netclient = netclient
+ if self.read_cert
+ @driver.cert_setup(self)
end
- @driver = netclient.new(args)
+
@local = false
elsif hash.include?(driverparam)
@driver = hash[driverparam]
+ if @driver == true
+ @driver = self.class.handler.new
+ end
@local = true
else
- raise ClientError, "%s must be passed a Server or %s" %
+ raise Puppet::Network::ClientError, "%s must be passed a Server or %s" %
[self.class, driverparam]
end
end
@@ -138,12 +138,6 @@ class Puppet::Network::Client
end
end
- def setcerts
- @driver.cert = @cert
- @driver.key = @key
- @driver.ca_file = @cacertfile
- end
-
def shutdown
if self.stopping
Puppet.notice "Already in shutdown"
@@ -159,7 +153,6 @@ class Puppet::Network::Client
# Start listening for events. We're pretty much just listening for
# timer events here.
def start
- setpidfile()
# Create our timer. Puppet will handle observing it and such.
timer = Puppet.newtimer(
:interval => Puppet[:runinterval],
@@ -176,15 +169,6 @@ class Puppet::Network::Client
end
require 'puppet/network/client/proxy'
- require 'puppet/network/client/ca'
- require 'puppet/network/client/dipper'
- require 'puppet/network/client/file'
- require 'puppet/network/client/log'
- require 'puppet/network/client/master'
- require 'puppet/network/client/runner'
- require 'puppet/network/client/status'
- require 'puppet/network/client/reporter'
- require 'puppet/network/client/resource'
end
# $Id$
diff --git a/lib/puppet/network/client/ca.rb b/lib/puppet/network/client/ca.rb
index 9a99c1145..fe3cb060a 100644
--- a/lib/puppet/network/client/ca.rb
+++ b/lib/puppet/network/client/ca.rb
@@ -1,22 +1,55 @@
-require 'puppet/network/client/proxy'
+require 'puppet/network/client'
-class Puppet::Network::Client::CA < Puppet::Network::Client::ProxyClient
- @drivername = :CA
+# Request a certificate from the remote system.
+class Puppet::Network::Client::CA < Puppet::Network::Client
+ class InvalidCertificate < Puppet::Error; end
- # set up the appropriate interface methods
- @handler = Puppet::Network::Server::CA
- self.mkmethods
+ def initialize(options = {})
+ options = symbolize_options(options)
+ unless options.include?(:Server) or options.include?(:CA)
+ options[:Server] = Puppet[:ca_server]
+ options[:Port] = Puppet[:ca_port]
+ end
+ super(options)
+ end
+
+ # This client is really only able to request certificates for the
+ # current host. It uses the Puppet.config settings to figure everything out.
+ def request_cert
+ Puppet.config.use(:puppet, :certificates)
+
+ if cert = read_cert
+ return cert
+ end
- def initialize(hash = {})
- if hash.include?(:CA)
- if hash[:CA].is_a? Hash
- hash[:CA] = Puppet::Network::Server::CA.new(hash[:CA])
- else
- hash[:CA] = Puppet::Network::Server::CA.new()
+ begin
+ cert, cacert = @driver.getcert(csr.to_pem)
+ rescue => detail
+ if Puppet[:trace]
+ puts detail.backtrace
end
+ raise Puppet::Error.new("Certificate retrieval failed: %s" % detail)
end
- super(hash)
+ if cert.nil? or cert == ""
+ return nil
+ end
+ Puppet.config.write(:hostcert) do |f| f.print cert end
+ Puppet.config.write(:localcacert) do |f| f.print cacert end
+
+ begin
+ @cert = OpenSSL::X509::Certificate.new(cert)
+ @cacert = OpenSSL::X509::Certificate.new(cacert)
+ rescue => detail
+ raise InvalidCertificate.new(
+ "Invalid certificate: %s" % detail
+ )
+ end
+
+ unless @cert.check_private_key(key)
+ raise InvalidCertificate, "Certificate does not match private key"
+ end
+ return @cert
end
end
diff --git a/lib/puppet/network/client/dipper.rb b/lib/puppet/network/client/dipper.rb
index 8eaffc1a0..2084e09f6 100644
--- a/lib/puppet/network/client/dipper.rb
+++ b/lib/puppet/network/client/dipper.rb
@@ -1,17 +1,14 @@
# The client class for filebuckets.
class Puppet::Network::Client::Dipper < Puppet::Network::Client
+ @handler = Puppet::Network::Handler.handler(:filebucket)
@drivername = :Bucket
-
- @handler = Puppet::Network::Server::FileBucket
attr_accessor :name
# Create our bucket client
def initialize(hash = {})
if hash.include?(:Path)
- bucket = Puppet::Network::Server::FileBucket.new(
- :Path => hash[:Path]
- )
+ bucket = self.class.handler.new(:Path => hash[:Path])
hash.delete(:Path)
hash[:Bucket] = bucket
end
@@ -24,7 +21,7 @@ class Puppet::Network::Client::Dipper < Puppet::Network::Client
unless FileTest.exists?(file)
raise(BucketError, "File %s does not exist" % file)
end
- contents = File.read(file)
+ contents = ::File.read(file)
unless local?
contents = Base64.encode64(contents)
end
@@ -35,7 +32,7 @@ class Puppet::Network::Client::Dipper < Puppet::Network::Client
def restore(file,sum)
restore = true
if FileTest.exists?(file)
- cursum = Digest::MD5.hexdigest(File.read(file))
+ cursum = Digest::MD5.hexdigest(::File.read(file))
# if the checksum has changed...
# this might be extra effort
@@ -53,14 +50,14 @@ class Puppet::Network::Client::Dipper < Puppet::Network::Client
newsum = Digest::MD5.hexdigest(newcontents)
changed = nil
unless FileTest.writable?(file)
- changed = File.stat(file).mode
- File.chmod(changed | 0200, file)
+ changed = ::File.stat(file).mode
+ ::File.chmod(changed | 0200, file)
end
- File.open(file,File::WRONLY|File::TRUNC) { |of|
+ ::File.open(file,::File::WRONLY|::File::TRUNC) { |of|
of.print(newcontents)
}
if changed
- File.chmod(changed, file)
+ ::File.chmod(changed, file)
end
else
Puppet.err "Could not find file with checksum %s" % sum
diff --git a/lib/puppet/network/client/file.rb b/lib/puppet/network/client/file.rb
index 7596aec1f..381a10cdb 100644
--- a/lib/puppet/network/client/file.rb
+++ b/lib/puppet/network/client/file.rb
@@ -1,20 +1,7 @@
-class Puppet::Network::Client::FileClient < Puppet::Network::Client::ProxyClient
+class Puppet::Network::Client::File < Puppet::Network::Client::ProxyClient
+ @handler = Puppet::Network::Handler.handler(:fileserver)
@drivername = :FileServer
-
- # set up the appropriate interface methods
- @handler = Puppet::Network::Server::FileServer
-
self.mkmethods
-
- def initialize(hash = {})
- if hash.include?(:FileServer)
- unless hash[:FileServer].is_a?(Puppet::Network::Server::FileServer)
- raise Puppet::DevError, "Must pass an actual FS object"
- end
- end
-
- super(hash)
- end
end
# $Id$
diff --git a/lib/puppet/network/client/log.rb b/lib/puppet/network/client/log.rb
deleted file mode 100644
index eddb8e0ca..000000000
--- a/lib/puppet/network/client/log.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class Puppet::Network::Client::LogClient < Puppet::Network::Client::ProxyClient
- @drivername = :Logger
-
- # set up the appropriate interface methods
- @handler = Puppet::Network::Server::Logger
- self.mkmethods
-
- def initialize(hash = {})
- if hash.include?(:Logger)
- hash[:Logger] = Puppet::Network::Server::Logger.new()
- end
-
- super(hash)
- end
-end
-
-# $Id$
diff --git a/lib/puppet/network/client/logger.rb b/lib/puppet/network/client/logger.rb
new file mode 100644
index 000000000..9c18efcd9
--- /dev/null
+++ b/lib/puppet/network/client/logger.rb
@@ -0,0 +1,6 @@
+class Puppet::Network::Client::Logger < Puppet::Network::Client::ProxyClient
+ @handler = Puppet::Network::Handler.handler(:logger)
+ self.mkmethods
+end
+
+# $Id$
diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb
index ebeddd281..dd8a883bd 100644
--- a/lib/puppet/network/client/master.rb
+++ b/lib/puppet/network/client/master.rb
@@ -2,84 +2,11 @@
require 'sync'
require 'timeout'
-class Puppet::Network::Client::MasterClient < Puppet::Network::Client
+class Puppet::Network::Client::Master < Puppet::Network::Client
unless defined? @@sync
@@sync = Sync.new
end
- @handler = Puppet::Network::Server::Master
-
- Puppet.setdefaults("puppetd",
- :puppetdlockfile => [ "$statedir/puppetdlock",
- "A lock file to temporarily stop puppetd from doing anything."],
- :usecacheonfailure => [true,
- "Whether to use the cached configuration when the remote
- configuration will not compile. This option is useful for testing
- new configurations, where you want to fix the broken configuration
- rather than reverting to a known-good one."
- ],
- :ignorecache => [false,
- "Ignore cache and always recompile the configuration. This is
- useful for testing new configurations, where the local cache may in
- fact be stale even if the timestamps are up to date - if the facts
- change or if the server changes."
- ],
- :downcasefacts => [false,
- "Whether facts should be made all lowercase when sent to the server."]
- )
-
- Puppet.setdefaults(:puppetd,
- :configtimeout => [30,
- "How long the client should wait for the configuration to be retrieved
- before considering it a failure. This can help reduce flapping if too
- many clients contact the server at one time."
- ],
- :reportserver => ["$server",
- "The server to which to send transaction reports."
- ],
- :report => [false,
- "Whether to send reports after every transaction."
- ]
- )
-
- # Plugin information.
- Puppet.setdefaults("puppet",
- :pluginpath => ["$vardir/plugins",
- "Where Puppet should look for plugins. Multiple directories should
- be colon-separated, like normal PATH variables."],
- :plugindest => ["$vardir/plugins",
- "Where Puppet should store plugins that it pulls down from the central
- server."],
- :pluginsource => ["puppet://$server/plugins",
- "From where to retrieve plugins. The standard Puppet ``file`` type
- is used for retrieval, so anything that is a valid file source can
- be used here."],
- :pluginsync => [false,
- "Whether plugins should be synced with the central server."],
- :pluginsignore => [".svn CVS",
- "What files to ignore when pulling down plugins."]
- )
-
- # Central fact information.
- Puppet.setdefaults("puppet",
- :factpath => ["$vardir/facts",
- "Where Puppet should look for facts. Multiple directories should
- be colon-separated, like normal PATH variables."],
- :factdest => ["$vardir/facts",
- "Where Puppet should store facts that it pulls down from the central
- server."],
- :factsource => ["puppet://$server/facts",
- "From where to retrieve facts. The standard Puppet ``file`` type
- is used for retrieval, so anything that is a valid file source can
- be used here."],
- :factsync => [false,
- "Whether facts should be synced with the central server."],
- :factsignore => [".svn CVS",
- "What files to ignore when pulling down facts."]
- )
-
- @drivername = :Master
-
attr_accessor :objects
attr_reader :compile_time
@@ -160,11 +87,11 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
# Cache the config
def cache(text)
Puppet.info "Caching configuration at %s" % self.cachefile
- confdir = File.dirname(Puppet[:localconfig])
- File.open(self.cachefile + ".tmp", "w", 0660) { |f|
+ confdir = ::File.dirname(Puppet[:localconfig])
+ ::File.open(self.cachefile + ".tmp", "w", 0660) { |f|
f.print text
}
- File.rename(self.cachefile + ".tmp", self.cachefile)
+ ::File.rename(self.cachefile + ".tmp", self.cachefile)
end
def cachefile
@@ -191,7 +118,7 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
end
Puppet.err "Corrupt state file %s: %s" % [Puppet[:statefile], detail]
begin
- File.unlink(Puppet[:statefile])
+ ::File.unlink(Puppet[:statefile])
retry
rescue => detail
raise Puppet::Error.new("Cannot remove %s: %s" %
@@ -336,7 +263,7 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
# Retrieve the cached config
def retrievecache
if FileTest.exists?(self.cachefile)
- return File.read(self.cachefile)
+ return ::File.read(self.cachefile)
else
return ""
end
@@ -395,7 +322,7 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
return
end
begin
- File.open(Puppet[:classfile], "w") { |f|
+ ::File.open(Puppet[:classfile], "w") { |f|
f.puts ary.join("\n")
}
rescue => detail
@@ -467,7 +394,7 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
download(:dest => Puppet[:factdest], :source => Puppet[:factsource],
:ignore => Puppet[:factsignore], :name => "fact") do |object|
- next unless path.include?(File.dirname(object[:path]))
+ next unless path.include?(::File.dirname(object[:path]))
files << object[:path]
@@ -495,11 +422,11 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
download(:dest => Puppet[:plugindest], :source => Puppet[:pluginsource],
:ignore => Puppet[:pluginsignore], :name => "plugin") do |object|
- next unless path.include?(File.dirname(object[:path]))
+ next unless path.include?(::File.dirname(object[:path]))
begin
Puppet.info "Reloading plugin %s" %
- File.basename(File.basename(object[:path])).sub(".rb",'')
+ ::File.basename(::File.basename(object[:path])).sub(".rb",'')
load object[:path]
rescue => detail
Puppet.warning "Could not reload plugin %s: %s" %
@@ -512,9 +439,9 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
return unless FileTest.directory?(dir)
Dir.entries(dir).find_all { |e| e =~ /\.rb$/ }.each do |file|
- fqfile = File.join(dir, file)
+ fqfile = ::File.join(dir, file)
begin
- Puppet.info "Loading #{type} %s" % File.basename(file.sub(".rb",''))
+ Puppet.info "Loading #{type} %s" % ::File.basename(file.sub(".rb",''))
Timeout::timeout(self.timeout) do
load fqfile
end
@@ -560,7 +487,7 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
def reportclient
unless defined? @reportclient
- @reportclient = Puppet::Network::Client::Reporter.new(
+ @reportclient = Puppet::Network::Client.report.new(
:Server => Puppet[:reportserver]
)
end
@@ -572,7 +499,8 @@ class Puppet::Network::Client::MasterClient < Puppet::Network::Client
private
- # Actually retrieve the configuration, either from the server or from a local master.
+ # Actually retrieve the configuration, either from the server or from a
+ # local master.
def get_actual_config(facts)
if @local
return get_local_config(facts)
diff --git a/lib/puppet/network/client/proxy.rb b/lib/puppet/network/client/proxy.rb
index e1295a96f..4cd37bb5d 100644
--- a/lib/puppet/network/client/proxy.rb
+++ b/lib/puppet/network/client/proxy.rb
@@ -3,7 +3,7 @@
# and that's about it
class Puppet::Network::Client::ProxyClient < Puppet::Network::Client
def self.mkmethods
- interface = @handler.interface
+ interface = self.handler.interface
namespace = interface.prefix
diff --git a/lib/puppet/network/client/reporter.rb b/lib/puppet/network/client/report.rb
index dd340da02..cb4711afe 100644
--- a/lib/puppet/network/client/reporter.rb
+++ b/lib/puppet/network/client/report.rb
@@ -1,12 +1,9 @@
-class Puppet::Network::Client::Reporter < Puppet::Network::Client
- @drivername = :Report
-
- # set up the appropriate interface methods
- @handler = Puppet::Network::Server::Report
+class Puppet::Network::Client::Report < Puppet::Network::Client
+ @handler = Puppet::Network::Handler.handler(:report)
def initialize(hash = {})
if hash.include?(:Report)
- hash[:Report] = Puppet::Network::Server::Report.new()
+ hash[:Report] = self.class.handler.new
end
super(hash)
diff --git a/lib/puppet/network/client/resource.rb b/lib/puppet/network/client/resource.rb
index 71a19bf91..bf026dff0 100644
--- a/lib/puppet/network/client/resource.rb
+++ b/lib/puppet/network/client/resource.rb
@@ -1,10 +1,7 @@
+# The client for interacting with remote Puppet agents to query and modify
+# remote system state.
class Puppet::Network::Client::Resource < Puppet::Network::Client
- @drivername = :ResourceServer
-
- @handler = Puppet::Network::Server::Resource
-
def apply(bucket)
-
case bucket
when Puppet::TransObject
tmp = Puppet::TransBucket.new
@@ -41,16 +38,6 @@ class Puppet::Network::Client::Resource < Puppet::Network::Client
return object
end
- def initialize(hash = {})
- if hash.include?(:ResourceServer)
- unless hash[:ResourceServer].is_a?(Puppet::Network::Server::Resource)
- raise Puppet::DevError, "Must pass an actual PElement server object"
- end
- end
-
- super(hash)
- end
-
def list(type, ignore = false, base = false)
bucket = @driver.list(type, ignore, base, "yaml")
diff --git a/lib/puppet/network/client/runner.rb b/lib/puppet/network/client/runner.rb
index 40d13ac86..12097c52f 100644
--- a/lib/puppet/network/client/runner.rb
+++ b/lib/puppet/network/client/runner.rb
@@ -1,13 +1,9 @@
class Puppet::Network::Client::Runner < Puppet::Network::Client::ProxyClient
- @drivername = :Runner
-
- # set up the appropriate interface methods
- @handler = Puppet::Network::Server::Runner
self.mkmethods
def initialize(hash = {})
if hash.include?(:Runner)
- hash[:Runner] = Puppet::Network::Server::Runner.new()
+ hash[:Runner] = self.class.handler.new()
end
super(hash)
diff --git a/lib/puppet/network/client/status.rb b/lib/puppet/network/client/status.rb
index 6c1a96e85..d35e44c1a 100644
--- a/lib/puppet/network/client/status.rb
+++ b/lib/puppet/network/client/status.rb
@@ -1,6 +1,4 @@
-class Puppet::Network::Client::StatusClient < Puppet::Network::Client::ProxyClient
- # set up the appropriate interface methods
- @handler = Puppet::Network::Server::ServerStatus
+class Puppet::Network::Client::Status < Puppet::Network::Client::ProxyClient
self.mkmethods
end
diff --git a/lib/puppet/network/client_request.rb b/lib/puppet/network/client_request.rb
new file mode 100644
index 000000000..16a9d9993
--- /dev/null
+++ b/lib/puppet/network/client_request.rb
@@ -0,0 +1,32 @@
+module Puppet::Network # :nodoc:
+ # A struct-like class for passing around a client request. It's mostly
+ # just used for validation and authorization.
+ class ClientRequest
+ attr_accessor :name, :ip, :authenticated, :handler, :method
+
+ def authenticated?
+ self.authenticated
+ end
+
+ # A common way of talking about the full call. Individual servers
+ # are responsible for setting the values correctly, but this common
+ # format makes it possible to check rights.
+ def call
+ unless handler and method
+ raise ArgumentError, "Request is not set up; cannot build call"
+ end
+
+ [handler, method].join(".")
+ end
+
+ def initialize(name, ip, authenticated)
+ @name, @ip, @authenticated = name, ip, authenticated
+ end
+
+ def to_s
+ "%s(%s)" % [self.name, self.ip]
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/handler.rb b/lib/puppet/network/handler.rb
new file mode 100644
index 000000000..080997e98
--- /dev/null
+++ b/lib/puppet/network/handler.rb
@@ -0,0 +1,33 @@
+require 'puppet/util/subclass_loader'
+
+module Puppet::Network
+ # The base class for the different handlers. The handlers are each responsible
+ # for separate xmlrpc namespaces.
+ class Handler
+ # This is so that the handlers can subclass just 'Handler', rather
+ # then having to specify the full class path.
+ Handler = self
+ attr_accessor :server
+
+ extend Puppet::Util::SubclassLoader
+ extend Puppet::Util
+
+ handle_subclasses :handler, "puppet/network/handler"
+
+ # Return the xmlrpc interface.
+ def self.interface
+ if defined? @interface
+ return @interface
+ else
+ raise Puppet::DevError, "Handler %s has no defined interface" %
+ self
+ end
+ end
+
+ # Create an empty init method with the same signature.
+ def initialize(hash = {})
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/server/ca.rb b/lib/puppet/network/handler/ca.rb
index 8a61399ba..06e0486bf 100644
--- a/lib/puppet/network/server/ca.rb
+++ b/lib/puppet/network/handler/ca.rb
@@ -6,7 +6,7 @@ require 'xmlrpc/server'
# Much of this was taken from QuickCert:
# http://segment7.net/projects/ruby/QuickCert/
-class Puppet::Network::Server
+class Puppet::Network::Handler
class CA < Handler
attr_reader :ca
diff --git a/lib/puppet/network/server/filebucket.rb b/lib/puppet/network/handler/filebucket.rb
index 77dbbde5e..653d566b4 100755
--- a/lib/puppet/network/server/filebucket.rb
+++ b/lib/puppet/network/handler/filebucket.rb
@@ -9,26 +9,9 @@ require 'facter'
require 'digest/md5'
require 'puppet/external/base64'
-class Puppet::Network::Server
+class Puppet::Network::Handler
class BucketError < RuntimeError; end
class FileBucket < Handler
- Puppet.config.setdefaults("puppetmasterd",
- :bucketdir => {
- :default => "$vardir/bucket",
- :mode => 0750,
- :owner => "$user",
- :group => "$group",
- :desc => "Where FileBucket files are stored."
- }
- )
-
- Puppet.config.setdefaults("filebucket",
- :clientbucketdir => {
- :default => "$vardir/clientbucket",
- :mode => 0750,
- :desc => "Where FileBucket files are stored locally."
- }
- )
@interface = XMLRPC::Service::Interface.new("puppetbucket") { |iface|
iface.add_method("string addfile(string, string)")
iface.add_method("string getfile(string)")
@@ -38,7 +21,7 @@ class Puppet::Network::Server
attr_reader :name, :path
# this doesn't work for relative paths
- def FileBucket.paths(base,md5)
+ def self.paths(base,md5)
return [
File.join(base, md5),
File.join(base, md5, "contents"),
diff --git a/lib/puppet/network/server/fileserver.rb b/lib/puppet/network/handler/fileserver.rb
index 904d497ca..6def09837 100755
--- a/lib/puppet/network/server/fileserver.rb
+++ b/lib/puppet/network/handler/fileserver.rb
@@ -1,17 +1,14 @@
require 'puppet'
+require 'puppet/network/authstore'
require 'webrick/httpstatus'
require 'cgi'
require 'delegate'
-class Puppet::Network::Server
+class Puppet::Network::Handler
class FileServerError < Puppet::Error; end
class FileServer < Handler
attr_accessor :local
- Puppet.setdefaults("fileserver",
- :fileserverconfig => ["$confdir/fileserver.conf",
- "Where the fileserver configuration is stored."])
-
CHECKPARAMS = [:mode, :type, :owner, :group, :checksum]
@interface = XMLRPC::Service::Interface.new("fileserver") { |iface|
@@ -20,13 +17,17 @@ class Puppet::Network::Server
iface.add_method("string retrieve(string, string)")
}
+ def self.params
+ CHECKPARAMS.dup
+ end
+
# Describe a given file. This returns all of the manageable aspects
# of that file.
def describe(url, links = :ignore, client = nil, clientip = nil)
links = links.intern if links.is_a? String
if links == :manage
- raise Puppet::Network::Server::FileServerError, "Cannot currently copy links"
+ raise Puppet::Network::Handler::FileServerError, "Cannot currently copy links"
end
mount, path = convert(url, client, clientip)
diff --git a/lib/puppet/network/server/logger.rb b/lib/puppet/network/handler/logger.rb
index f6bf9ba88..f01b48325 100755
--- a/lib/puppet/network/server/logger.rb
+++ b/lib/puppet/network/handler/logger.rb
@@ -1,6 +1,6 @@
require 'yaml'
-class Puppet::Network::Server
+class Puppet::Network::Handler
class LoggerError < RuntimeError; end
# Receive logs from remote hosts.
diff --git a/lib/puppet/network/server/master.rb b/lib/puppet/network/handler/master.rb
index b9b796d96..2b0a215d0 100644
--- a/lib/puppet/network/server/master.rb
+++ b/lib/puppet/network/handler/master.rb
@@ -5,7 +5,7 @@ require 'puppet/sslcertificates'
require 'xmlrpc/server'
require 'yaml'
-class Puppet::Network::Server
+class Puppet::Network::Handler
class MasterError < Puppet::Error; end
class Master < Handler
include Puppet::Util
@@ -24,7 +24,7 @@ class Puppet::Network::Server
facts["serverversion"] = Puppet.version.to_s
# And then add the server name and IP
- {"servername" => "fqdn",
+ {"servername" => "hostname",
"serverip" => "ipaddress"
}.each do |var, fact|
if obj = Facter[fact]
@@ -55,6 +55,8 @@ class Puppet::Network::Server
# Tell a client whether there's a fresh config for it
def freshness(client = nil, clientip = nil)
if Puppet.features.rails? and Puppet[:storeconfigs]
+ Puppet::Rails.connect
+
host = Puppet::Rails::Host.find_or_create_by_name(client)
host.last_freshcheck = Time.now
if clientip and (! host.ip or host.ip == "")
diff --git a/lib/puppet/network/server/report.rb b/lib/puppet/network/handler/report.rb
index 781a392ed..77e31f04a 100755
--- a/lib/puppet/network/server/report.rb
+++ b/lib/puppet/network/handler/report.rb
@@ -1,9 +1,7 @@
# A simple server for triggering a new run on a Puppet client.
-class Puppet::Network::Server
+class Puppet::Network::Handler
class Report < Handler
- class << self
- include Puppet::Util::ClassGen
- end
+ extend Puppet::Util::ClassGen
module ReportBase
include Puppet::Util::Docs
@@ -22,21 +20,6 @@ class Puppet::Network::Server
iface.add_method("string report(array)")
}
- Puppet.setdefaults(:reporting,
- :reports => ["store",
- "The list of reports to generate. All reports are looked for
- in puppet/reports/<name>.rb, and multiple report names should be
- comma-separated (whitespace is okay)."
- ],
- :reportdir => {:default => "$vardir/reports",
- :mode => 0750,
- :owner => "$user",
- :group => "$group",
- :desc => "The directory in which to store reports
- received from the client. Each client gets a separate
- subdirectory."}
- )
-
@reports = {}
@reportloader = Puppet::Util::Autoload.new(self, "puppet/reports")
@@ -78,6 +61,7 @@ class Puppet::Network::Server
@reports[symbolize(name)]
end
+ # Collect the docs for all of our reports.
def self.reportdocs
docs = ""
@@ -92,6 +76,7 @@ class Puppet::Network::Server
docs
end
+ # List each of the reports.
def self.reports
@reportloader.loadall
@reports.keys
@@ -110,16 +95,15 @@ class Puppet::Network::Server
report = CGI.unescape(report)
end
+ Puppet.info "Processing reports %s for %s" % [reports().join(", "), client]
begin
process(report)
rescue => detail
- Puppet.err "Could not process report %s: %s" % [$1, detail]
+ Puppet.err "Could not process report for %s: %s" % [client, detail]
if Puppet[:trace]
puts detail.backtrace
end
end
-
- update_timestamp(client)
end
private
@@ -141,8 +125,6 @@ class Puppet::Network::Server
reports().each do |name|
if mod = self.class.report(name)
- Puppet.info "Processing report %s for %s" % [name, client]
-
# We have to use a dup because we're including a module in the
# report.
newrep = report.dup
@@ -170,17 +152,6 @@ class Puppet::Network::Server
def reports
Puppet[:reports].gsub(/(^\s+)|(\s+$)/, '').split(/\s*,\s*/)
end
-
- def update_timestamp(client)
- return unless Puppet[:storeconfigs]
-
- if host = Puppet::Rails::Host.find_by_name(client)
- host.last_report = Time.now
- host.save
- else
- Puppet.warning "Could not find Rails host for %s" % client
- end
- end
end
end
diff --git a/lib/puppet/network/server/resource.rb b/lib/puppet/network/handler/resource.rb
index 37e331a13..92533dd2a 100755
--- a/lib/puppet/network/server/resource.rb
+++ b/lib/puppet/network/handler/resource.rb
@@ -1,8 +1,8 @@
require 'puppet'
-require 'puppet/network/server'
+require 'puppet/network/handler'
# Serve Puppet elements. Useful for querying, copying, and, um, other stuff.
-class Puppet::Network::Server
+class Puppet::Network::Handler
class Resource < Handler
attr_accessor :local
@@ -31,7 +31,7 @@ class Puppet::Network::Server
# Create a client, but specify the remote machine as the server
# because the class requires it, even though it's unused
- client = Puppet::Network::Client::MasterClient.new(:Server => client||"localhost")
+ client = Puppet::Network::Client.client(:Master).new(:Server => client||"localhost")
# Set the objects
client.objects = component
diff --git a/lib/puppet/network/server/runner.rb b/lib/puppet/network/handler/runner.rb
index c0ec8fb9d..79084f847 100755
--- a/lib/puppet/network/server/runner.rb
+++ b/lib/puppet/network/handler/runner.rb
@@ -1,4 +1,4 @@
-class Puppet::Network::Server
+class Puppet::Network::Handler
class MissingMasterError < RuntimeError; end # Cannot find the master client
# A simple server for triggering a new run on a Puppet client.
class Runner < Handler
@@ -10,7 +10,7 @@ class Puppet::Network::Server
# tags and whether to ignore schedules
def run(tags = nil, ignoreschedules = false, fg = true, client = nil, clientip = nil)
# We need to retrieve the client
- master = Puppet::Network::Client::MasterClient.instance
+ master = Puppet::Network::Client.client(:Master).instance
unless master
raise MissingMasterError, "Could not find the master client"
diff --git a/lib/puppet/network/handler/status.rb b/lib/puppet/network/handler/status.rb
new file mode 100644
index 000000000..774c49f6d
--- /dev/null
+++ b/lib/puppet/network/handler/status.rb
@@ -0,0 +1,13 @@
+class Puppet::Network::Handler
+ class Status < Handler
+ @interface = XMLRPC::Service::Interface.new("status") { |iface|
+ iface.add_method("int status()")
+ }
+
+ def status(client = nil, clientip = nil)
+ return 1
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/networkclient.rb b/lib/puppet/network/networkclient.rb
deleted file mode 100644
index 62d8906e0..000000000
--- a/lib/puppet/network/networkclient.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-require 'puppet/sslcertificates'
-require 'openssl'
-require 'puppet/daemon'
-require 'puppet/network/server'
-require 'puppet/external/base64'
-
-require 'webrick'
-require 'cgi'
-require 'xmlrpc/client'
-require 'xmlrpc/server'
-require 'yaml'
-
-module Puppet
- module Network
- class ClientError < Puppet::Error; end
- class NetworkClientError < Puppet::Error; end
- class NetworkClient < XMLRPC::Client
- attr_accessor :puppet_server, :puppet_port
- @clients = {}
-
- class << self
- include Puppet::Util
- include Puppet::Util::ClassGen
- end
-
- # Create a netclient for each handler
- def self.mkclients
- # add the methods associated with each namespace
- Puppet::Network::Server::Handler.each { |handler|
- interface = handler.interface
- namespace = interface.prefix
-
- # Create a subclass for every client type. This is
- # so that all of the methods are on their own class,
- # so that they namespaces can define the same methods if
- # they want.
- constant = handler.to_s.sub(/^.+::/, '')
- name = namespace.downcase
- newclient = genclass(name, :hash => @clients,
- :constant => constant)
-
- interface.methods.each { |ary|
- method = ary[0]
- if public_method_defined?(method)
- raise Puppet::DevError, "Method %s is already defined" %
- method
- end
- newclient.send(:define_method,method) { |*args|
- Puppet.debug "Calling %s.%s" % [namespace, method]
- #Puppet.info "peer cert is %s" % @http.peer_cert
- #Puppet.info "cert is %s" % @http.cert
- begin
- call("%s.%s" % [namespace, method.to_s],*args)
- rescue OpenSSL::SSL::SSLError => detail
- raise NetworkClientError,
- "Certificates were not trusted: %s" % detail
- rescue XMLRPC::FaultException => detail
- #Puppet.err "Could not call %s.%s: %s" %
- # [namespace, method, detail.faultString]
- #raise NetworkClientError,
- # "XMLRPC Error: %s" % detail.faultString
- raise NetworkClientError, detail.faultString
- rescue Errno::ECONNREFUSED => detail
- msg = "Could not connect to %s on port %s" %
- [@host, @port]
- raise NetworkClientError, msg
- rescue SocketError => detail
- error = NetworkClientError.new(
- "Could not find server %s" % @puppetserver
- )
- error.set_backtrace detail.backtrace
- raise error
- rescue => detail
- Puppet.err "Could not call %s.%s: %s" %
- [namespace, method, detail.inspect]
- error = NetworkClientError.new(detail.to_s)
- error.set_backtrace detail.backtrace
- raise error
- end
- }
- }
- }
- end
-
- def self.netclient(namespace)
- if @clients.empty?
- self.mkclients()
- end
-
- namespace = symbolize(namespace)
-
- @clients[namespace]
- end
-
- def ca_file=(cafile)
- @http.ca_file = cafile
- store = OpenSSL::X509::Store.new
- store.add_file(cafile)
- store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
- @http.cert_store = store
- end
-
- def cert=(cert)
- #Puppet.debug "Adding certificate"
- @http.cert = cert
- @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
- end
-
- def key=(key)
- @http.key = key
- end
-
- def initialize(hash)
- hash[:Path] ||= "/RPC2"
- hash[:Server] ||= "localhost"
- hash[:Port] ||= Puppet[:masterport]
-
- @puppet_server = hash[:Server]
- @puppet_port = hash[:Port]
-
- @puppetserver = hash[:Server]
-
- super(
- hash[:Server],
- hash[:Path],
- hash[:Port],
- nil, # proxy_host
- nil, # proxy_port
- nil, # user
- nil, # password
- true, # use_ssl
- 120 # a two minute timeout, instead of 30 seconds
- )
-
- if hash[:Certificate]
- self.cert = hash[:Certificate]
- else
- unless defined? $nocertwarned
- Puppet.err "No certificate; running with reduced functionality."
- $nocertwarned = true
- end
- end
-
- if hash[:Key]
- self.key = hash[:Key]
- end
-
- if hash[:CAFile]
- self.ca_file = hash[:CAFile]
- end
-
- # from here, i need to add the key, cert, and ca cert
- # and reorgize how i start the client
- end
-
- def local
- false
- end
-
- def local?
- false
- end
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb
index 3f19a88ff..1bf3f5f63 100644
--- a/lib/puppet/network/server.rb
+++ b/lib/puppet/network/server.rb
@@ -1,208 +1,5 @@
-# the server
-#
-# allow things to connect to us and communicate, and stuff
-
-require 'puppet'
-require 'puppet/daemon'
-require 'webrick'
-require 'webrick/https'
-require 'cgi'
-require 'xmlrpc/server'
-require 'xmlrpc/client'
-
-module Puppet
- class ServerError < RuntimeError; end
- module Network
- class Server < WEBrick::HTTPServer
- include Puppet::Daemon
-
- Puppet.config.setdefaults(:puppetd,
- :listen => [false, "Whether puppetd should listen for
- connections. If this is true, then by default only the
- ``runner`` server is started, which allows remote authorized
- and authenticated nodes to connect and trigger ``puppetd``
- runs."]
- )
-
- # Create our config object if necessary. This works even if
- # there's no configuration file.
- def authconfig
- unless defined? @authconfig
- @authconfig = Puppet::Network::AuthConfig.new()
- end
-
- @authconfig
- end
-
- # Read the CA cert and CRL and populate an OpenSSL::X509::Store
- # with them, with flags appropriate for checking client
- # certificates for revocation
- def x509store
- if Puppet[:cacrl] == 'none'
- # No CRL, no store needed
- return nil
- end
- unless File.exist?(Puppet[:cacrl])
- raise Puppet::Error, "Could not find CRL"
- end
- crl = OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl]))
- store = OpenSSL::X509::Store.new
- store.purpose = OpenSSL::X509::PURPOSE_ANY
- store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK
- store.add_file(@cacertfile)
- store.add_crl(crl)
- return store
- end
-
- def initialize(hash = {})
- Puppet.info "Starting server for Puppet version %s" % Puppet.version
- daemonize = nil
- if hash.include?(:Daemonize)
- daemonize = hash[:Daemonize]
- end
-
- # FIXME we should have some kind of access control here, using
- # :RequestHandler
- hash[:Port] ||= Puppet[:masterport]
- hash[:Logger] ||= self.httplog
- hash[:AccessLog] ||= [
- [ self.httplog, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
- [ self.httplog, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
- ]
-
- if hash.include?(:Handlers)
- unless hash[:Handlers].is_a?(Hash)
- raise ServerError, "Handlers must have arguments"
- end
-
- @handlers = hash[:Handlers].collect { |handler, args|
- hclass = nil
- unless hclass = Handler.handler(handler)
- raise ServerError, "Invalid handler %s" % handler
- end
- hclass.new(args)
- }
- else
- raise ServerError, "A server must have handlers"
- end
-
- # okay, i need to retrieve my cert and set it up, somehow
- # the default case will be that i'm also the ca
- if ca = @handlers.find { |handler| handler.is_a?(Puppet::Network::Server::CA) }
- @driver = ca
- @secureinit = true
- self.fqdn
- else
- if hash.include?(:NoSecureInit)
- @secureinit = false
- else
- @secureinit = true
- end
- end
-
- unless self.readcert
- unless self.requestcert
- raise Puppet::Error, "Cannot start without certificates"
- end
- end
-
- hash[:SSLCertificateStore] = x509store
- hash[:SSLCertificate] = @cert
- hash[:SSLPrivateKey] = @key
- hash[:SSLStartImmediately] = true
- hash[:SSLEnable] = true
- hash[:SSLCACertificateFile] = @cacertfile
- hash[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER
- hash[:SSLCertName] = nil
-
- super(hash)
-
- Puppet.info "Listening on port %s" % hash[:Port]
-
- # this creates a new servlet for every connection,
- # but all servlets have the same list of handlers
- # thus, the servlets can have their own state -- passing
- # around the requests and such -- but the handlers
- # have a global state
-
- # mount has to be called after the server is initialized
- self.mount("/RPC2", Puppet::Network::Server::Servlet, @handlers)
- end
-
- # the base class for the different handlers
- class Handler
- attr_accessor :server
- class << self
- include Puppet::Util
- end
-
- @subclasses = []
-
- def self.each
- @subclasses.each { |c| yield c }
- end
-
- def self.handler(name)
- name = name.to_s.downcase
- @subclasses.find { |h|
- h.name.to_s.downcase == name
- }
- end
-
- def self.inherited(sub)
- @subclasses << sub
- end
-
- def self.interface
- if defined? @interface
- return @interface
- else
- raise Puppet::DevError, "Handler %s has no defined interface" %
- self
- end
- end
-
- def self.name
- unless defined? @name
- @name = self.to_s.sub(/.+::/, '').intern
- end
-
- return @name
- end
-
- def initialize(hash = {})
- end
- end
-
-
- class ServerStatus < Handler
-
- @interface = XMLRPC::Service::Interface.new("status") { |iface|
- iface.add_method("int status()")
- }
-
- @name = :Status
-
- def status(status = nil, client = nil, clientip = nil)
- return 1
- end
- end
- end
- end
+# Just a stub, so we can correctly scope other classes.
+module Puppet::Network::Server # :nodoc:
end
-require 'puppet/network/authstore'
-require 'puppet/network/authconfig'
-require 'puppet/network/rights'
-require 'puppet/network/server/servlet'
-require 'puppet/network/server/master'
-require 'puppet/network/server/ca'
-require 'puppet/network/server/fileserver'
-require 'puppet/network/server/filebucket'
-require 'puppet/network/server/resource'
-require 'puppet/network/server/runner'
-require 'puppet/network/server/logger'
-require 'puppet/network/server/report'
-require 'puppet/network/client'
-
# $Id$
diff --git a/lib/puppet/network/server/mongrel.rb b/lib/puppet/network/server/mongrel.rb
new file mode 100644
index 000000000..a85ed1314
--- /dev/null
+++ b/lib/puppet/network/server/mongrel.rb
@@ -0,0 +1,138 @@
+#!/usr/bin/env ruby
+# File: 06-11-14-mongrel_xmlrpc.rb
+# Author: Manuel Holtgrewe <purestorm at ggnore.net>
+#
+# Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# This file is based heavily on a file retrieved from
+# http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/
+
+require 'rubygems'
+require 'mongrel'
+require 'xmlrpc/server'
+require 'puppet/network/server'
+require 'puppet/network/xmlrpc/server'
+require 'puppet/network/client_request'
+
+require 'resolv'
+
+# This handler can be hooked into Mongrel to accept HTTP requests. After
+# checking whether the request itself is sane, the handler forwards it
+# to an internal instance of XMLRPC::BasicServer to process it.
+#
+# You can access the server by calling the Handler's "xmlrpc_server"
+# attribute accessor method and add XMLRPC handlers there. For example:
+#
+# <pre>
+# handler = XmlRpcHandler.new
+# handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i }
+# </pre>
+class Puppet::Network::Server
+ class MongrelHandler < Mongrel::HttpHandler
+ attr_reader :xmlrpc_server
+
+ def initialize(handlers)
+ # Create a new instance of BasicServer. We are supposed to subclass it
+ # but that does not make sense since we would not introduce any new
+ # behaviour and we have to subclass Mongrel::HttpHandler so our handler
+ # works for Mongrel.
+ @xmlrpc_server = Puppet::Network::XMLRPCServer.new
+ handlers.each do |name, args|
+ unless handler = Puppet::Network::Handler.handler(name)
+ raise ArgumentError, "Invalid handler %s" % name
+ end
+ @xmlrpc_server.add_handler(handler.interface, handler.new(args))
+ end
+ end
+
+ # This method produces the same results as XMLRPC::CGIServer.serve
+ # from Ruby's stdlib XMLRPC implementation.
+ def process(request, response)
+ # Make sure this has been a POST as required for XMLRPC.
+ request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET
+ if request_method != "POST" then
+ response.start(405) { |head, out| out.write("Method Not Allowed") }
+ return
+ end
+
+ # Make sure the user has sent text/xml data.
+ request_mime = request.params["CONTENT_TYPE"] || "text/plain"
+ if parse_content_type(request_mime).first != "text/xml" then
+ response.start(400) { |head, out| out.write("Bad Request") }
+ return
+ end
+
+ # Make sure there is data in the body at all.
+ length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i
+ if length <= 0 then
+ response.start(411) { |head, out| out.write("Length Required") }
+ return
+ end
+
+ # Check the body to be valid.
+ if request.body.nil? or request.body.size != length then
+ response.start(400) { |head, out| out.write("Bad Request") }
+ return
+ end
+
+ info = client_info(request)
+
+ # All checks above passed through
+ response.start(200) do |head, out|
+ head["Content-Type"] = "text/xml; charset=utf-8"
+ begin
+ out.write(@xmlrpc_server.process(request.body, info))
+ rescue => detail
+ puts detail.backtrace
+ raise
+ end
+ end
+ end
+
+ private
+
+ def client_info(request)
+ params = request.params
+ ip = params["REMOTE_ADDR"]
+ if dn = params["HTTP_X_CLIENT_DN"]
+ client = dn.sub("/CN=", '')
+ valid = true
+ else
+ client = Resolv.getname(ip)
+ valid = false
+ end
+
+ info = Puppet::Network::ClientRequest.new(client, ip, valid)
+
+ return info
+ end
+
+ # Taken from XMLRPC::ParseContentType
+ def parse_content_type(str)
+ a, *b = str.split(";")
+ return a.strip, *b
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/server/servlet.rb b/lib/puppet/network/server/servlet.rb
deleted file mode 100644
index 325956d8c..000000000
--- a/lib/puppet/network/server/servlet.rb
+++ /dev/null
@@ -1,277 +0,0 @@
-require 'xmlrpc/server'
-
-class Puppet::Network::Server
- class ServletError < RuntimeError; end
- class Servlet < XMLRPC::WEBrickServlet
- ERR_UNAUTHORIZED = 30
-
- attr_accessor :request
-
- # this is just a duplicate of the normal method; it's here for
- # debugging when i need it
- def self.get_instance(server, *options)
- self.new(server, *options)
- end
-
- # This is a hackish way to avoid an auth message every time we have a
- # normal operation
- def self.log(msg)
- unless defined? @logs
- @logs = {}
- end
- if @logs.include?(msg)
- @logs[msg] += 1
- else
- Puppet.info msg
- @logs[msg] = 1
- end
- end
-
- def add_handler(interface, handler)
- @loadedhandlers << interface.prefix
- super
- end
-
- # Verify that our client has access. We allow untrusted access to
- # puppetca methods but no others.
- def authorize(request, method)
- namespace = method.sub(/\..+/, '')
- client = request.peeraddr[2]
- if defined? @client and @client
- client = @client
- end
- ip = request.peeraddr[3]
- if request.client_cert
- if @puppetserver.authconfig.exists?
- allowed = @puppetserver.authconfig.allowed?(method, client, ip)
-
- if allowed
- Puppet.debug "Allowing %s(%s) trusted access to %s" %
- [client, ip, method]
- return true
- else
- Puppet.debug "Denying %s(%s) trusted access to %s" %
- [client, ip, method]
- return false
- end
- else
- # This is pretty hackish, but...
- # This means we can't actually test this method at this point.
- # The next release of Puppet will almost definitely require
- # this file to exist or will default to denying all access.
- if Puppet.execname == "puppetmasterd" or defined? Test::Unit::TestCase
- Puppet.debug "Allowing %s(%s) trusted access to %s" %
- [client, ip, method]
- return true
- else
- Puppet.debug "Denying %s(%s) trusted access to %s on %s" %
- [client, ip, method, Puppet.execname]
- return false
- end
- end
- else
- if method =~ /^puppetca\./
- Puppet.notice "Allowing %s(%s) untrusted access to CA methods" %
- [client, ip]
- else
- Puppet.err "Unauthenticated client %s(%s) cannot call %s" %
- [client, ip, method]
- return false
- end
- end
- end
-
- def available?(method)
- namespace = method.sub(/\..+/, '')
- client = request.peeraddr[2]
- ip = request.peeraddr[3]
- if @loadedhandlers.include?(namespace)
- return true
- else
- Puppet.warning "Client %s(%s) requested unavailable functionality %s" %
- [client, ip, namespace]
- return false
- end
- end
-
- def initialize(server, handlers)
- @puppetserver = server
- @notified = {}
- # the servlet base class does not consume any arguments
- # and its BasicServer base class only accepts a 'class_delim'
- # option which won't change in Puppet at all
- # thus, we don't need to pass any args to our base class,
- # and we can consume them all ourselves
- super()
-
- @loadedhandlers = []
- handlers.each { |handler|
- #Puppet.debug "adding handler for %s" % handler.class
- self.add_handler(handler.class.interface, handler)
- }
-
- # Initialize these to nil, but they will get set to values
- # by the 'service' method. These have to instance variables
- # because I don't have a clear line from the service method to
- # the service hook.
- @request = nil
- @client = nil
- @clientip = nil
-
- self.set_service_hook { |obj, *args|
- if @client and @clientip
- args.push(@client, @clientip)
- end
- begin
- obj.call(*args)
- rescue XMLRPC::FaultException
- raise
- rescue Puppet::AuthorizationError => detail
- #Puppet.warning obj.inspect
- #Puppet.warning args.inspect
- Puppet.err "Permission denied: %s" % detail.to_s
- raise XMLRPC::FaultException.new(
- 1, detail.to_s
- )
- rescue Puppet::Error => detail
- #Puppet.warning obj.inspect
- #Puppet.warning args.inspect
- if Puppet[:trace]
- puts detail.backtrace
- end
- Puppet.err detail.to_s
- error = XMLRPC::FaultException.new(
- 1, detail.to_s
- )
- error.set_backtrace detail.backtrace
- raise error
- rescue => detail
- #Puppet.warning obj.inspect
- #Puppet.warning args.inspect
- if Puppet[:trace]
- puts detail.backtrace
- end
- Puppet.err "Could not call: %s" % detail.to_s
- error = XMLRPC::FaultException.new(1, detail.to_s)
- error.set_backtrace detail.backtrace
- raise error
- end
- }
- end
-
- # Handle the actual request. This does some basic collection of
- # data, and then just calls the parent method.
- def service(request, response)
- @request = request
-
- # The only way that @client can be nil is if the request is local.
- if peer = request.peeraddr
- @client = peer[2]
- @clientip = peer[3]
- else
- raise XMLRPC::FaultException.new(
- ERR_UNCAUGHT_EXCEPTION,
- "Could not retrieve client information"
- )
- end
-
- # If they have a certificate (which will almost always be true)
- # then we get the hostname from the cert, instead of via IP
- # info
- if cert = request.client_cert
- nameary = cert.subject.to_a.find { |ary|
- ary[0] == "CN"
- }
-
- if nameary.nil?
- Puppet.warning "Could not retrieve server name from cert"
- else
- unless @client == nameary[1]
- Puppet.debug "Overriding %s with cert name %s" %
- [@client, nameary[1]]
- @client = nameary[1]
- end
- end
- end
- begin
- super
- rescue => detail
- Puppet.err "Could not service request: %s: %s" %
- [detail.class, detail]
- end
- @client = nil
- @clientip = nil
- @request = nil
- end
-
- private
-
- # this is pretty much just a copy of the original method but with more
- # feedback
- # here's where we have our authorization hooks
- def dispatch(methodname, *args)
-
- if defined? @request and @request
- unless self.available?(methodname)
- raise XMLRPC::FaultException.new(
- ERR_UNAUTHORIZED,
- "Functionality %s not available" %
- methodname.sub(/\..+/, '')
- )
- end
- unless self.authorize(@request, methodname)
- raise XMLRPC::FaultException.new(
- ERR_UNAUTHORIZED,
- "Host %s not authorized to call %s" %
- [@request.host, methodname]
- )
- end
- else
- raise Puppet::DevError, "Did not get request in dispatch"
- end
-
- #Puppet.warning "dispatch on %s called with %s" %
- # [methodname, args.inspect]
- for name, obj in @handler
- if obj.kind_of? Proc
- unless methodname == name
- #Puppet.debug "obj is proc but %s != %s" %
- # [methodname, name]
- next
- end
- else
- unless methodname =~ /^#{name}(.+)$/
- #Puppet.debug "methodname did not match"
- next
- end
- unless obj.respond_to? $1
- #Puppet.debug "methodname does not respond to %s" % $1
- next
- end
- obj = obj.method($1)
- end
-
- if check_arity(obj, args.size)
- if @service_hook.nil?
- return obj.call(*args)
- else
- return @service_hook.call(obj, *args)
- end
- else
- Puppet.debug "arity is incorrect"
- end
- end
-
- if @default_handler.nil?
- raise XMLRPC::FaultException.new(
- ERR_METHOD_MISSING,
- "Method #{methodname} missing or wrong number of parameters!"
- )
- else
- @default_handler.call(methodname, *args)
- end
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/network/server/webrick.rb b/lib/puppet/network/server/webrick.rb
new file mode 100644
index 000000000..7b9f0f0c6
--- /dev/null
+++ b/lib/puppet/network/server/webrick.rb
@@ -0,0 +1,153 @@
+require 'puppet'
+require 'puppet/daemon'
+require 'webrick'
+require 'webrick/https'
+
+require 'puppet/sslcertificates/support'
+require 'puppet/network/xmlrpc/webrick_servlet'
+require 'puppet/network/server'
+require 'puppet/network/client'
+
+module Puppet
+ class ServerError < RuntimeError; end
+ module Network
+ # The old-school, pure ruby webrick server, which is the default serving
+ # mechanism.
+ class Server::WEBrick < WEBrick::HTTPServer
+ include Puppet::Daemon
+ include Puppet::SSLCertificates::Support
+
+ # Read the CA cert and CRL and populate an OpenSSL::X509::Store
+ # with them, with flags appropriate for checking client
+ # certificates for revocation
+ def x509store
+ if Puppet[:cacrl] == 'none'
+ # No CRL, no store needed
+ return nil
+ end
+ unless File.exist?(Puppet[:cacrl])
+ raise Puppet::Error, "Could not find CRL"
+ end
+ crl = OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl]))
+ store = OpenSSL::X509::Store.new
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
+ store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK
+ unless self.ca_cert
+ raise Puppet::Error, "No CA certificate"
+ end
+
+ store.add_file(Puppet[:localcacert])
+ store.add_crl(crl)
+ return store
+ end
+
+ # Set up the http log.
+ def httplog
+ args = []
+
+ # yuck; separate http logs
+ file = nil
+ Puppet.config.use(:puppet, :certificates, Puppet.name)
+ if Puppet[:name] == "puppetmasterd"
+ file = Puppet[:masterhttplog]
+ else
+ file = Puppet[:httplog]
+ end
+
+ args << file
+ if Puppet[:debug]
+ args << WEBrick::Log::DEBUG
+ end
+
+ log = WEBrick::Log.new(*args)
+
+
+ return log
+ end
+
+ # Create our server, yo.
+ def initialize(hash = {})
+ Puppet.info "Starting server for Puppet version %s" % Puppet.version
+
+ if handlers = hash[:Handlers]
+ handler_instances = setup_handlers(handlers)
+ else
+ raise ServerError, "A server must have handlers"
+ end
+
+ unless self.read_cert
+ if ca = handler_instances.find { |handler| handler.is_a?(Puppet::Network::Handler.ca) }
+ request_cert(ca)
+ else
+ raise Puppet::Error, "No certificate and no CA; cannot get cert"
+ end
+ end
+
+ setup_webrick(hash)
+
+ super(hash)
+
+ Puppet.info "Listening on port %s" % hash[:Port]
+
+ # this creates a new servlet for every connection,
+ # but all servlets have the same list of handlers
+ # thus, the servlets can have their own state -- passing
+ # around the requests and such -- but the handlers
+ # have a global state
+
+ # mount has to be called after the server is initialized
+ servlet = Puppet::Network::XMLRPC::WEBrickServlet.new(
+ handler_instances)
+ self.mount("/RPC2", servlet)
+ end
+
+ # Create a ca client to set up our cert for us.
+ def request_cert(ca)
+ client = Puppet::Network::Client.ca.new(:CA => ca)
+ unless client.request_cert
+ raise Puppet::Error, "Could get certificate"
+ end
+ end
+
+ # Create all of our handler instances.
+ def setup_handlers(handlers)
+ unless handlers.is_a?(Hash)
+ raise ServerError, "Handlers must have arguments"
+ end
+
+ handlers.collect { |handler, args|
+ hclass = nil
+ unless hclass = Handler.handler(handler)
+ raise ServerError, "Invalid handler %s" % handler
+ end
+ hclass.new(args)
+ }
+ end
+
+ # Handle all of the many webrick arguments.
+ def setup_webrick(hash)
+ hash[:Port] ||= Puppet[:masterport]
+ hash[:Logger] ||= self.httplog
+ hash[:AccessLog] ||= [
+ [ self.httplog, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
+ [ self.httplog, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
+ ]
+
+ hash[:SSLCertificateStore] = x509store
+ hash[:SSLCertificate] = self.cert
+ hash[:SSLPrivateKey] = self.key
+ hash[:SSLStartImmediately] = true
+ hash[:SSLEnable] = true
+ hash[:SSLCACertificateFile] = Puppet[:localcacert]
+ hash[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER
+ hash[:SSLCertName] = nil
+
+ if addr = Puppet[:bindaddress] and addr != ""
+ hash[:BindAddress] = addr
+ end
+ end
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/xmlrpc/client.rb b/lib/puppet/network/xmlrpc/client.rb
new file mode 100644
index 000000000..38f40a827
--- /dev/null
+++ b/lib/puppet/network/xmlrpc/client.rb
@@ -0,0 +1,129 @@
+require 'puppet/sslcertificates'
+require 'openssl'
+require 'puppet/external/base64'
+
+require 'xmlrpc/client'
+require 'yaml'
+
+module Puppet::Network
+ class ClientError < Puppet::Error; end
+ class XMLRPCClientError < Puppet::Error; end
+ class XMLRPCClient < ::XMLRPC::Client
+ attr_accessor :puppet_server, :puppet_port
+ @clients = {}
+
+ class << self
+ include Puppet::Util
+ include Puppet::Util::ClassGen
+ end
+
+ # Create a netclient for each handler
+ def self.mkclient(handler)
+ interface = handler.interface
+ namespace = interface.prefix
+
+ # Create a subclass for every client type. This is
+ # so that all of the methods are on their own class,
+ # so that they namespaces can define the same methods if
+ # they want.
+ constant = handler.name.to_s.capitalize
+ name = namespace.downcase
+ newclient = genclass(name, :hash => @clients,
+ :constant => constant)
+
+ interface.methods.each { |ary|
+ method = ary[0]
+ if public_method_defined?(method)
+ raise Puppet::DevError, "Method %s is already defined" %
+ method
+ end
+ newclient.send(:define_method,method) { |*args|
+ Puppet.debug "Calling %s.%s" % [namespace, method]
+ begin
+ call("%s.%s" % [namespace, method.to_s],*args)
+ rescue OpenSSL::SSL::SSLError => detail
+ raise XMLRPCClientError,
+ "Certificates were not trusted: %s" % detail
+ rescue ::XMLRPC::FaultException => detail
+ #Puppet.err "Could not call %s.%s: %s" %
+ # [namespace, method, detail.faultString]
+ #raise XMLRPCClientError,
+ # "XMLRPC Error: %s" % detail.faultString
+ raise XMLRPCClientError, detail.faultString
+ rescue Errno::ECONNREFUSED => detail
+ msg = "Could not connect to %s on port %s" %
+ [@host, @port]
+ raise XMLRPCClientError, msg
+ rescue SocketError => detail
+ Puppet.err "Could not find server %s: %s" %
+ [@puppet_server, detail.to_s]
+ error = XMLRPCClientError.new(
+ "Could not find server %s" % @puppet_server
+ )
+ error.set_backtrace detail.backtrace
+ raise error
+ rescue => detail
+ Puppet.err "Could not call %s.%s: %s" %
+ [namespace, method, detail.inspect]
+ error = XMLRPCClientError.new(detail.to_s)
+ error.set_backtrace detail.backtrace
+ raise error
+ end
+ }
+ }
+
+ return newclient
+ end
+
+ def self.handler_class(handler)
+ @clients[handler] || self.mkclient(handler)
+ end
+
+ # Use cert information from a Puppet client to set up the http object.
+ def cert_setup(client)
+ unless FileTest.exists?(Puppet[:localcacert])
+ raise Puppet::SSLCertificates::Support::MissingCertificate,
+ "Could not find ca certificate %s" % Puppet[:localcacert]
+ end
+ @http.ca_file = Puppet[:localcacert]
+ store = OpenSSL::X509::Store.new
+ store.add_file Puppet[:localcacert]
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ @http.cert_store = store
+ @http.cert = client.cert
+ @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ @http.key = client.key
+ end
+
+ def initialize(hash = {})
+ hash[:Path] ||= "/RPC2"
+ hash[:Server] ||= Puppet[:server]
+ hash[:Port] ||= Puppet[:masterport]
+
+ @puppet_server = hash[:Server]
+ @puppet_port = hash[:Port]
+
+ super(
+ hash[:Server],
+ hash[:Path],
+ hash[:Port],
+ nil, # proxy_host
+ nil, # proxy_port
+ nil, # user
+ nil, # password
+ true, # use_ssl
+ 120 # a two minute timeout, instead of 30 seconds
+ )
+ end
+
+ def local
+ false
+ end
+
+ def local?
+ false
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/xmlrpc/processor.rb b/lib/puppet/network/xmlrpc/processor.rb
new file mode 100644
index 000000000..248354696
--- /dev/null
+++ b/lib/puppet/network/xmlrpc/processor.rb
@@ -0,0 +1,91 @@
+require 'puppet/network/authorization'
+require 'xmlrpc/server'
+
+# Just silly.
+class ::XMLRPC::FaultException
+ def to_s
+ self.message
+ end
+end
+
+module Puppet::Network
+ # Most of our subclassing is just so that we can get
+ # access to information from the request object, like
+ # the client name and IP address.
+ module XMLRPCProcessor
+ include Puppet::Network::Authorization
+
+ ERR_UNAUTHORIZED = 30
+
+ def add_handler(interface, handler)
+ @loadedhandlers << interface.prefix
+ super(interface, handler)
+ end
+
+ def handler_loaded?(handler)
+ @loadedhandlers.include?(handler.to_s)
+ end
+
+ # Convert our data and client request into xmlrpc calls, and verify
+ # they're authorized and such-like. This method differs from the
+ # default in that it expects a ClientRequest object in addition to the
+ # data.
+ def process(data, request)
+ call, params = parser().parseMethodCall(data)
+ params << request.name << request.ip
+ handler, method = call.split(".")
+ request.handler = handler
+ request.method = method
+ begin
+ verify(request)
+ rescue InvalidClientRequest => detail
+ raise ::XMLRPC::FaultException.new(ERR_UNAUTHORIZED, detail.to_s)
+ end
+ handle(request.call, *params)
+ end
+
+ private
+
+ # Provide error handling for method calls.
+ def protect_service(obj, *args)
+ begin
+ obj.call(*args)
+ rescue ::XMLRPC::FaultException
+ raise
+ rescue Puppet::AuthorizationError => detail
+ Puppet.err "Permission denied: %s" % detail.to_s
+ raise ::XMLRPC::FaultException.new(
+ 1, detail.to_s
+ )
+ rescue Puppet::Error => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ Puppet.err detail.to_s
+ error = ::XMLRPC::FaultException.new(
+ 1, detail.to_s
+ )
+ error.set_backtrace detail.backtrace
+ raise error
+ rescue => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ Puppet.err "Could not call: %s" % detail.to_s
+ error = ::XMLRPC::FaultException.new(1, detail.to_s)
+ error.set_backtrace detail.backtrace
+ raise error
+ end
+ end
+
+ # Set up our service hook and init our handler list.
+ def setup_processor
+ @loadedhandlers = []
+ self.set_service_hook do |obj, *args|
+ protect_service(obj, *args)
+ end
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/xmlrpc/server.rb b/lib/puppet/network/xmlrpc/server.rb
new file mode 100644
index 000000000..bd05703ba
--- /dev/null
+++ b/lib/puppet/network/xmlrpc/server.rb
@@ -0,0 +1,20 @@
+require 'xmlrpc/server'
+require 'puppet/network/authorization'
+require 'puppet/network/xmlrpc/processor'
+
+module Puppet::Network
+ # Most of our subclassing is just so that we can get
+ # access to information from the request object, like
+ # the client name and IP address.
+ class XMLRPCServer < ::XMLRPC::BasicServer
+ include Puppet::Util
+ include Puppet::Network::XMLRPCProcessor
+
+ def initialize
+ super()
+ setup_processor()
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/network/xmlrpc/webrick_servlet.rb b/lib/puppet/network/xmlrpc/webrick_servlet.rb
new file mode 100644
index 000000000..0ddb056dc
--- /dev/null
+++ b/lib/puppet/network/xmlrpc/webrick_servlet.rb
@@ -0,0 +1,121 @@
+require 'xmlrpc/server'
+require 'puppet/network/authorization'
+require 'puppet/network/xmlrpc/processor'
+
+module Puppet::Network::XMLRPC
+ class ServletError < RuntimeError; end
+ class WEBrickServlet < ::XMLRPC::WEBrickServlet
+ include Puppet::Network::XMLRPCProcessor
+
+ # This is a hackish way to avoid an auth message every time we have a
+ # normal operation
+ def self.log(msg)
+ unless defined? @logs
+ @logs = {}
+ end
+ if @logs.include?(msg)
+ @logs[msg] += 1
+ else
+ Puppet.info msg
+ @logs[msg] = 1
+ end
+ end
+
+ # Accept a list of handlers and register them all.
+ def initialize(handlers)
+ # the servlet base class does not consume any arguments
+ # and its BasicServer base class only accepts a 'class_delim'
+ # option which won't change in Puppet at all
+ # thus, we don't need to pass any args to our base class,
+ # and we can consume them all ourselves
+ super()
+
+ setup_processor()
+
+ # Set up each of the passed handlers.
+ handlers.each do |handler|
+ add_handler(handler.class.interface, handler)
+ end
+ end
+
+ # Handle the actual request. We can't use the super() method, because
+ # we need to pass a ClientRequest object to process() so we can do
+ # authorization. It's the only way to stay thread-safe.
+ def service(request, response)
+ if @valid_ip
+ raise WEBrick::HTTPStatus::Forbidden unless @valid_ip.any? { |ip| request.peeraddr[3] =~ ip }
+ end
+
+ if request.request_method != "POST"
+ raise WEBrick::HTTPStatus::MethodNotAllowed,
+ "unsupported method `#{request.request_method}'."
+ end
+
+ if parse_content_type(request['Content-type']).first != "text/xml"
+ raise WEBrick::HTTPStatus::BadRequest
+ end
+
+ length = (request['Content-length'] || 0).to_i
+
+ raise WEBrick::HTTPStatus::LengthRequired unless length > 0
+
+ data = request.body
+
+ if data.nil? or data.size != length
+ raise WEBrick::HTTPStatus::BadRequest
+ end
+
+ resp = process(data, client_request(request))
+ if resp.nil? or resp.size <= 0
+ raise WEBrick::HTTPStatus::InternalServerError
+ end
+
+ response.status = 200
+ response['Content-Length'] = resp.size
+ response['Content-Type'] = "text/xml; charset=utf-8"
+ response.body = resp
+ end
+
+ private
+
+ # Generate a ClientRequest object for later validation.
+ def client_request(request)
+ if peer = request.peeraddr
+ client = peer[2]
+ clientip = peer[3]
+ else
+ raise ::XMLRPC::FaultException.new(
+ ERR_UNCAUGHT_EXCEPTION,
+ "Could not retrieve client information"
+ )
+ end
+
+ # If they have a certificate (which will almost always be true)
+ # then we get the hostname from the cert, instead of via IP
+ # info
+ valid = false
+ if cert = request.client_cert
+ nameary = cert.subject.to_a.find { |ary|
+ ary[0] == "CN"
+ }
+
+ if nameary.nil?
+ Puppet.warning "Could not retrieve server name from cert"
+ else
+ unless client == nameary[1]
+ Puppet.debug "Overriding %s with cert name %s" %
+ [client, nameary[1]]
+ client = nameary[1]
+ end
+ valid = true
+ end
+ end
+
+ info = Puppet::Network::ClientRequest.new(client, clientip, valid)
+
+ return info
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/caseopt.rb b/lib/puppet/parser/ast/caseopt.rb
index 8d578a10b..11483d082 100644
--- a/lib/puppet/parser/ast/caseopt.rb
+++ b/lib/puppet/parser/ast/caseopt.rb
@@ -54,8 +54,7 @@ class Puppet::Parser::AST
# Evaluate the actual statements; this only gets called if
# our option matched.
def evaluate(hash)
- scope = hash[:scope]
- return @statements.safeevaluate(:scope => scope)
+ return @statements.safeevaluate(hash)
end
def tree(indent = 0)
diff --git a/lib/puppet/parser/ast/casestatement.rb b/lib/puppet/parser/ast/casestatement.rb
index 760a1095c..c56e33bc9 100644
--- a/lib/puppet/parser/ast/casestatement.rb
+++ b/lib/puppet/parser/ast/casestatement.rb
@@ -18,6 +18,7 @@ class Puppet::Parser::AST
found = false
# Iterate across the options looking for a match.
+ default = nil
@options.each { |option|
option.eachvalue(scope) { |opval|
opval = opval.downcase if ! sensitive and opval.respond_to?(:downcase)
@@ -32,12 +33,16 @@ class Puppet::Parser::AST
retvalue = option.safeevaluate(:scope => scope)
break
end
+
+ if option.default?
+ default = option
+ end
}
# Unless we found something, look for the default.
unless found
- if defined? @default
- retvalue = @default.safeevaluate(:scope => scope)
+ if default
+ retvalue = default.safeevaluate(:scope => scope)
else
Puppet.debug "No true answers and no default"
retvalue = nil
@@ -46,33 +51,6 @@ class Puppet::Parser::AST
return retvalue
end
- # Do some input validation on our options.
- def initialize(hash)
- values = {}
-
- super
-
- # This won't work if we move away from only allowing
- # constants here, but for now, it's fine and useful.
- @options.each { |option|
- unless option.is_a?(CaseOpt)
- raise Puppet::DevError, "Option is not a CaseOpt"
- end
- if option.default?
- @default = option
- end
- option.eachvalue(nil) { |val|
- if values.include?(val)
- raise Puppet::ParseError,
- "Value %s appears twice in case statement" %
- val
- else
- values[val] = true
- end
- }
- }
- end
-
def tree(indent = 0)
rettree = [
@test.tree(indent + 1),
@@ -87,5 +65,6 @@ class Puppet::Parser::AST
[@test,@options].each { |child| yield child }
end
end
-
end
+
+# $Id$
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index e81862dad..e5d7df7ca 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -552,7 +552,7 @@ class Puppet::Parser::Scope
if ss.matched == '\\$'
out << '$'
else # look the variable up
- out << lookupvar(ss[1] || ss[2]) || ""
+ out << lookupvar(ss[1] || ss[2]).to_s || ""
end
elsif ss.scan(/^\\(.)/)
# Puppet.debug("Got escape: pos:%d; m:%s" % [ss.pos, ss.matched])
diff --git a/lib/puppet/rails.rb b/lib/puppet/rails.rb
index bb4781c0e..274861b8b 100644
--- a/lib/puppet/rails.rb
+++ b/lib/puppet/rails.rb
@@ -29,6 +29,27 @@ module Puppet::Rails
}
)
+ def self.connect
+ # This global init does not work for testing, because we remove
+ # the state dir on every test.
+ unless ActiveRecord::Base.connected?
+ Puppet.config.use(:puppet)
+
+ ActiveRecord::Base.logger = Logger.new(Puppet[:railslog])
+ ActiveRecord::Base.allow_concurrency = true
+ ActiveRecord::Base.verify_active_connections!
+
+ begin
+ ActiveRecord::Base.establish_connection(database_arguments())
+ rescue => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ raise Puppet::Error, "Could not connect to database: %s" % detail
+ end
+ end
+ end
+
# The arguments for initializing the database connection.
def self.database_arguments
args = {:adapter => Puppet[:dbadapter]}
@@ -54,24 +75,7 @@ module Puppet::Rails
raise Puppet::DevError, "No activerecord, cannot init Puppet::Rails"
end
- # This global init does not work for testing, because we remove
- # the state dir on every test.
- unless ActiveRecord::Base.connected?
- Puppet.config.use(:puppet)
-
- ActiveRecord::Base.logger = Logger.new(Puppet[:railslog])
- ActiveRecord::Base.allow_concurrency = true
- ActiveRecord::Base.verify_active_connections!
-
- begin
- ActiveRecord::Base.establish_connection(database_arguments())
- rescue => detail
- if Puppet[:trace]
- puts detail.backtrace
- end
- raise Puppet::Error, "Could not connect to database: %s" % detail
- end
- end
+ connect()
unless ActiveRecord::Base.connection.tables.include?("resources")
require 'puppet/rails/database/schema'
diff --git a/lib/puppet/reports/log.rb b/lib/puppet/reports/log.rb
index c33bf0a67..e6ae7e3d1 100644
--- a/lib/puppet/reports/log.rb
+++ b/lib/puppet/reports/log.rb
@@ -1,6 +1,6 @@
require 'puppet'
-Puppet::Network::Server::Report.newreport(:log) do
+Puppet::Network::Handler.report.newreport(:log) do
desc "Send all received logs to the local log destinations."
def process
diff --git a/lib/puppet/reports/rrdgraph.rb b/lib/puppet/reports/rrdgraph.rb
index 0fbe6e5ca..34162ac25 100644
--- a/lib/puppet/reports/rrdgraph.rb
+++ b/lib/puppet/reports/rrdgraph.rb
@@ -1,6 +1,6 @@
require 'puppet'
-Puppet::Network::Server::Report.newreport(:rrdgraph) do
+Puppet::Network::Handler.report.newreport(:rrdgraph) do
desc "Graph all available data about hosts using the RRD library. You
must have the RRD binary library installed to use this report, which
you can get from [Tobias Oetiker's site](http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/pub/contrib/).
diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb
index ed4f08a9e..8d6e11379 100644
--- a/lib/puppet/reports/store.rb
+++ b/lib/puppet/reports/store.rb
@@ -1,6 +1,6 @@
require 'puppet'
-Puppet::Network::Server::Report.newreport(:store, :useyaml => true) do
+Puppet::Network::Handler.report.newreport(:store, :useyaml => true) do
Puppet.config.use(:reporting)
desc "Store the yaml report on disk. Each host sends its report as a YAML dump
diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb
index 93e494d49..ba327bc85 100644
--- a/lib/puppet/reports/tagmail.rb
+++ b/lib/puppet/reports/tagmail.rb
@@ -1,20 +1,9 @@
require 'puppet'
require 'pp'
-Puppet.config.setdefaults(:reporting,
- :tagmap => ["$confdir/tagmail.conf",
- "The mapping between reporting tags and email addresses."],
- :sendmail => [%x{which sendmail 2>/dev/null}.chomp,
- "Where to find the sendmail binary with which to send email."],
- :reportfrom => ["report@" + [Facter["hostname"].value, Facter["domain"].value].join("."),
- "The 'from' email address for the reports."],
- :smtpserver => ["none",
- "The server through which to send email reports."]
-)
-
require 'net/smtp'
-Puppet::Network::Server::Report.newreport(:tagmail) do
+Puppet::Network::Handler.report.newreport(:tagmail) do
desc "This report sends specific log messages to specific email addresses
based on the tags in the log messages. See the
[tag documentation](/trac/puppet/wiki/UsingTags) for more information
diff --git a/lib/puppet/sslcertificates.rb b/lib/puppet/sslcertificates.rb
index eaee75d6c..503de7ae9 100755
--- a/lib/puppet/sslcertificates.rb
+++ b/lib/puppet/sslcertificates.rb
@@ -11,13 +11,15 @@ end
module Puppet::SSLCertificates
hostname = Facter["hostname"].value
domain = Facter["domain"].value
- if !domain || domain.empty? then
- fqdn = hostname
- else
+ if domain and domain != ""
fqdn = [hostname, domain].join(".")
+ else
+ fqdn = hostname
end
Puppet.setdefaults("certificates",
+ :certname => [fqdn, "The name to use when handling certificates. Defaults
+ to the fully qualified domain name."],
:certdir => ["$ssldir/certs", "The certificate directory."],
:publickeydir => ["$ssldir/public_keys", "The public key directory."],
:privatekeydir => { :default => "$ssldir/private_keys",
@@ -33,15 +35,19 @@ module Puppet::SSLCertificates
:desc => "Where puppetd stores the password for its private key.
Generally unused."
},
- :hostcert => { :default => "$certdir/#{fqdn}.pem",
+ :hostcsr => { :default => "$ssldir/csr_$certname.pem",
+ :mode => 0644,
+ :desc => "Where individual hosts store and look for their certificates."
+ },
+ :hostcert => { :default => "$certdir/$certname.pem",
:mode => 0644,
:desc => "Where individual hosts store and look for their certificates."
},
- :hostprivkey => { :default => "$privatekeydir/#{fqdn}.pem",
+ :hostprivkey => { :default => "$privatekeydir/$certname.pem",
:mode => 0600,
:desc => "Where individual hosts store and look for their private key."
},
- :hostpubkey => { :default => "$publickeydir/#{fqdn}.pem",
+ :hostpubkey => { :default => "$publickeydir/$certname.pem",
:mode => 0644,
:desc => "Where individual hosts store and look for their public key."
},
diff --git a/lib/puppet/sslcertificates/ca.rb b/lib/puppet/sslcertificates/ca.rb
index 42ad57e48..723a0444b 100644
--- a/lib/puppet/sslcertificates/ca.rb
+++ b/lib/puppet/sslcertificates/ca.rb
@@ -5,8 +5,6 @@ class Puppet::SSLCertificates::CA
attr_accessor :keyfile, :file, :config, :dir, :cert, :crl
Puppet.setdefaults(:ca,
- :ca => [true,
- "Whether a CA should be started in puppetmasterd."],
:cadir => { :default => "$ssldir/ca",
:owner => "$user",
:group => "$group",
@@ -97,7 +95,7 @@ class Puppet::SSLCertificates::CA
if FileTest.exists?(file)
begin
- if Puppet.execname == "puppetca"
+ if Puppet[:name] == "puppetca"
puts "Removing %s" % file
else
Puppet.info "Removing %s" % file
diff --git a/lib/puppet/sslcertificates/support.rb b/lib/puppet/sslcertificates/support.rb
new file mode 100644
index 000000000..e37a52038
--- /dev/null
+++ b/lib/puppet/sslcertificates/support.rb
@@ -0,0 +1,128 @@
+require 'puppet/sslcertificates'
+
+# A module to handle reading of certificates.
+module Puppet::SSLCertificates::Support
+ class MissingCertificate < Puppet::Error; end
+ class InvalidCertificate < Puppet::Error; end
+
+ attr_reader :cacert
+
+ # Some metaprogramming to create methods for retrieving and creating keys.
+ # This probably isn't fewer lines than defining each separately...
+ def self.keytype(name, options, &block)
+ var = "@%s" % name
+
+ maker = "mk_%s" % name
+ reader = "read_%s" % name
+
+ unless param = options[:param]
+ raise ArgumentError, "You must specify the parameter for the key"
+ end
+
+ unless klass = options[:class]
+ raise ArgumentError, "You must specify the class for the key"
+ end
+
+ # Define the method that creates it.
+ define_method(maker, &block)
+
+ # Define the reading method.
+ define_method(reader) do
+ return nil unless FileTest.exists?(Puppet[param])
+ begin
+ instance_variable_set(var,
+ klass.new(File.read(Puppet[param])))
+ rescue => detail
+ raise InvalidCertificate, "Could not read %s: %s" %
+ [param, detail]
+ end
+ end
+
+ # Define the overall method, which just calls the reader and maker
+ # as appropriate.
+ define_method(name) do
+ unless instance_variable_get(var)
+ unless cert = send(reader)
+ cert = send(maker)
+ Puppet.config.write(param) { |f| f.puts cert.to_pem }
+ end
+ instance_variable_set(var, cert)
+ end
+ instance_variable_get(var)
+ end
+ end
+
+ # The key pair.
+ keytype :key, :param => :hostprivkey, :class => OpenSSL::PKey::RSA do
+ Puppet.info "Creating a new SSL key at %s" % Puppet[:hostprivkey]
+ key = OpenSSL::PKey::RSA.new(Puppet[:keylength])
+
+ # Our key meta programming can only handle one file, so we have
+ # to separately write out the public key.
+ Puppet.config.write(:hostpubkey) do |f|
+ f.print key.public_key.to_pem
+ end
+ return key
+ end
+
+ # Our certificate request
+ keytype :csr, :param => :hostcsr, :class => OpenSSL::X509::Request do
+ Puppet.info "Creating a new certificate request for %s" %
+ Puppet[:certname]
+
+ csr = OpenSSL::X509::Request.new
+ csr.version = 0
+ csr.subject = OpenSSL::X509::Name.new([["CN", Puppet[:certname]]])
+ csr.public_key = key.public_key
+ csr.sign(key, OpenSSL::Digest::MD5.new)
+
+ return csr
+ end
+
+ keytype :cert, :param => :hostcert, :class => OpenSSL::X509::Certificate do
+ raise MissingCertificate, "No host certificate"
+ end
+
+ keytype :ca_cert, :param => :localcacert, :class => OpenSSL::X509::Certificate do
+ raise MissingCertificate, "No CA certificate"
+ end
+
+ # Request a certificate from the remote system. This does all of the work
+ # of creating the cert request, contacting the remote system, and
+ # storing the cert locally.
+ def requestcert
+ begin
+ cert, cacert = caclient.getcert(@csr.to_pem)
+ rescue => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ raise Puppet::Error.new("Certificate retrieval failed: %s" %
+ detail)
+ end
+
+ if cert.nil? or cert == ""
+ return nil
+ end
+ Puppet.config.write(:hostcert) do |f| f.print cert end
+ Puppet.config.write(:localcacert) do |f| f.print cacert end
+ #File.open(@certfile, "w", 0644) { |f| f.print cert }
+ #File.open(@cacertfile, "w", 0644) { |f| f.print cacert }
+ begin
+ @cert = OpenSSL::X509::Certificate.new(cert)
+ @cacert = OpenSSL::X509::Certificate.new(cacert)
+ retrieved = true
+ rescue => detail
+ raise Puppet::Error.new(
+ "Invalid certificate: %s" % detail
+ )
+ end
+
+ unless @cert.check_private_key(@key)
+ raise Puppet::DevError, "Received invalid certificate"
+ end
+ return retrieved
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb
index 22e7073c8..ffeba9f0e 100644
--- a/lib/puppet/type/pfile.rb
+++ b/lib/puppet/type/pfile.rb
@@ -4,7 +4,7 @@ require 'etc'
require 'uri'
require 'fileutils'
require 'puppet/type/property'
-require 'puppet/network/server/fileserver'
+require 'puppet/network/handler'
module Puppet
newtype(:file) do
@@ -104,7 +104,7 @@ module Puppet
@parent.bucket = value
value
end
- when Puppet::Network::Client::Dipper:
+ when Puppet::Network::Client.client(:Dipper):
@parent.bucket = value
value.name
else
@@ -311,7 +311,7 @@ module Puppet
# This sets the @value on :backup, too
self.bucket = obj
elsif bucket == "puppet"
- obj = Puppet::Network::Client::Dipper.new(
+ obj = Puppet::Network::Client.client(:Dipper).new(
:Path => Puppet[:clientbucketdir]
)
self.bucket = obj
@@ -322,7 +322,7 @@ module Puppet
else
self.fail "Could not find filebucket %s" % bucket
end
- when Puppet::Network::Client::Dipper: # things are hunky-dorey
+ when Puppet::Network::Client.client(:Dipper): # things are hunky-dorey
else
self.fail "Invalid bucket type %s" % bucket.class
end
@@ -357,7 +357,7 @@ module Puppet
else
backup = self.bucket || self[:backup]
case backup
- when Puppet::Network::Client::Dipper:
+ when Puppet::Network::Client.client(:Dipper):
notice "Recursively backing up to filebucket"
require 'find'
Find.find(self[:path]) do |f|
@@ -396,7 +396,7 @@ module Puppet
when "file":
backup = self.bucket || self[:backup]
case backup
- when Puppet::Network::Client::Dipper:
+ when Puppet::Network::Client.client(:Dipper):
sum = backup.backup(file)
self.info "Filebucketed to %s with sum %s" %
[backup.name, sum]
@@ -975,7 +975,7 @@ module Puppet
case uri.scheme
when "file":
unless defined? @@localfileserver
- @@localfileserver = Puppet::Network::Server::FileServer.new(
+ @@localfileserver = Puppet::Network::Handler.handler(:fileserver).new(
:Local => true,
:Mount => { "/" => "localhost" },
:Config => false
@@ -992,7 +992,7 @@ module Puppet
# FIXME We should cache a copy of this server
#sourceobj.server = Puppet::Network::NetworkClient.new(args)
unless @clients.include?(source)
- @clients[source] = Puppet::Network::Client::FileClient.new(args)
+ @clients[source] = Puppet::Network::Client.file.new(args)
end
sourceobj.server = @clients[source]
diff --git a/lib/puppet/type/pfile/source.rb b/lib/puppet/type/pfile/source.rb
index 4622e966d..2d569f2ed 100755
--- a/lib/puppet/type/pfile/source.rb
+++ b/lib/puppet/type/pfile/source.rb
@@ -1,5 +1,3 @@
-require 'puppet/network/server/fileserver'
-
module Puppet
# Copy files from a local or remote source. This state *only* does any work
# when the remote file is an actual file; in that case, this state copies
@@ -7,7 +5,6 @@ module Puppet
# this state, during retrieval, modifies the appropriate other states
# so that things get taken care of appropriately.
Puppet.type(:file).newproperty(:source) do
- PINPARAMS = Puppet::Network::Server::FileServer::CHECKPARAMS
attr_accessor :source, :local
desc "Copy a file over the current file. Uses ``checksum`` to
@@ -86,14 +83,14 @@ module Puppet
begin
desc = server.describe(path, @parent[:links])
- rescue Puppet::Network::NetworkClientError => detail
+ rescue Puppet::Network::XMLRPCClientError => detail
self.err "Could not describe %s: %s" %
[path, detail]
return nil
end
args = {}
- PINPARAMS.zip(
+ pinparams.zip(
desc.split("\t")
).each { |param, value|
if value =~ /^[0-9]+$/
@@ -144,6 +141,10 @@ module Puppet
# Now, we just check to see if the checksums are the same
return @parent.is(:checksum) == @stats[:checksum]
end
+
+ def pinparams
+ Puppet::Network::Handler.handler(:fileserver).params
+ end
# This basically calls describe() on our file, and then sets all
# of the local states appropriately. If the remote file is a normal
@@ -209,7 +210,7 @@ module Puppet
def should=(value)
super
- checks = (PINPARAMS + [:ensure])
+ checks = (pinparams + [:ensure])
checks.delete(:checksum)
@parent[:check] = checks
@@ -231,7 +232,7 @@ module Puppet
begin
contents = sourceobj.server.retrieve(path, @parent[:links])
- rescue Puppet::Network::NetworkClientError => detail
+ rescue Puppet::Network::XMLRPCClientError => detail
self.err "Could not retrieve %s: %s" %
[path, detail]
return nil
diff --git a/lib/puppet/type/pfilebucket.rb b/lib/puppet/type/pfilebucket.rb
index 5ec7e790f..9ed2bdb59 100755
--- a/lib/puppet/type/pfilebucket.rb
+++ b/lib/puppet/type/pfilebucket.rb
@@ -1,5 +1,3 @@
-require 'puppet/network/server/filebucket'
-
module Puppet
newtype(:filebucket) do
@doc = "A repository for backing up files. If no filebucket is
@@ -86,7 +84,7 @@ module Puppet
def mkbucket
if self[:server]
begin
- @bucket = Puppet::Network::Client::Dipper.new(
+ @bucket = Puppet::Network::Client.client(:Dipper).new(
:Server => self[:server],
:Port => self[:port]
)
@@ -97,7 +95,7 @@ module Puppet
end
else
begin
- @bucket = Puppet::Network::Client::Dipper.new(
+ @bucket = Puppet::Network::Client.client(:Dipper).new(
:Path => self[:path]
)
rescue => detail
diff --git a/lib/puppet/util/config.rb b/lib/puppet/util/config.rb
index 117f87f92..e139d2217 100644
--- a/lib/puppet/util/config.rb
+++ b/lib/puppet/util/config.rb
@@ -301,7 +301,7 @@ class Puppet::Util::Config
# the group can be set in the config file. The problem
# is that we're using the word 'group' twice, which is
# confusing.
- if var == :group and section == Puppet.execname and @config.include?(:group)
+ if var == :group and section == Puppet[:name] and @config.include?(:group)
@config[:group].value = value
end
next
@@ -519,7 +519,7 @@ class Puppet::Util::Config
# Convert our list of objects into a configuration file.
def to_config
- str = %{The configuration file for #{Puppet.execname}. Note that this file
+ str = %{The configuration file for #{Puppet[:name]}. Note that this file
is likely to have unused configuration parameters in it; any parameter that's
valid anywhere in Puppet can be in any config file, even if it's not used.
@@ -719,18 +719,16 @@ Generated on #{Time.now}.
def convert(value)
return value unless value
return value unless value.is_a? String
- if value =~ /\$(\w+)/
- parent = $1
- if pval = @parent[parent]
- newval = value.to_s.sub(/\$#{parent.to_s}/, pval.to_s)
- #return File.join(newval.split("/"))
- return newval
+ newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value|
+ varname = $2 || $1
+ if pval = @parent[varname]
+ pval
else
raise Puppet::DevError, "Could not find value for %s" % parent
end
- else
- return value
end
+
+ return newval
end
def desc=(value)
diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb
index 38f9d8de1..b427f7b61 100644
--- a/lib/puppet/util/log.rb
+++ b/lib/puppet/util/log.rb
@@ -167,7 +167,7 @@ class Puppet::Util::Log
if Syslog.opened?
Syslog.close
end
- name = Puppet.execname
+ name = Puppet[:name]
name = "puppet-#{name}" unless name =~ /puppet/
options = Syslog::LOG_PID | Syslog::LOG_NDELAY
diff --git a/lib/puppet/util/subclass_loader.rb b/lib/puppet/util/subclass_loader.rb
new file mode 100644
index 000000000..4f24d5544
--- /dev/null
+++ b/lib/puppet/util/subclass_loader.rb
@@ -0,0 +1,83 @@
+# A module for loading subclasses into an array and retrieving
+# them by name. Also sets up a method for each class so
+# that you can just do Klass.subclass, rather than Klass.subclass(:subclass).
+module Puppet::Util::SubclassLoader
+ attr_accessor :loader, :classloader
+
+ # Iterate over each of the subclasses.
+ def each
+ @subclasses ||= []
+ @subclasses.each { |c| yield c }
+ end
+
+ # The hook method that sets up subclass loading. We need the name
+ # of the method to create and the path in which to look for them.
+ def handle_subclasses(name, path)
+ unless self.is_a?(Class)
+ raise ArgumentError, "Must be a class to use SubclassLoader"
+ end
+ @subclasses = []
+ @loader = Puppet::Util::Autoload.new(self,
+ path, :wrap => false
+ )
+
+ @subclassname = name
+
+ @classloader = self
+
+ # Now create a method for retrieving these subclasses by name. Note
+ # that we're defining a class method here, not an instance.
+ meta_def(name) do |subname|
+ subname = subname.to_s.downcase
+
+ unless c = @subclasses.find { |c| c.name.to_s.downcase == subname }
+ loader.load(subname)
+ c = @subclasses.find { |c| c.name.to_s.downcase == subname }
+
+ # Now make the method that returns this subclass. This way we
+ # normally avoid the method_missing method.
+ if c and ! respond_to?(subname)
+ define_method(subname) { c }
+ end
+ end
+ return c
+ end
+ end
+
+ # Add a new class to our list. Note that this has to handle subclasses of
+ # subclasses, thus the reason we're keeping track of the @@classloader.
+ def inherited(sub)
+ @subclasses ||= []
+ @subclasses << sub
+ sub.classloader = self.classloader
+ if self.classloader == self
+ @subclasses << sub
+ else
+ @classloader.inherited(sub)
+ end
+ end
+
+ # See if we can load a class.
+ def method_missing(method, *args)
+ unless self == self.classloader
+ super
+ end
+ return nil unless defined? @subclassname
+ if c = self.send(@subclassname, method)
+ return c
+ else
+ return nil
+ end
+ end
+
+ # Retrieve or calculate a name.
+ def name
+ unless defined? @name
+ @name = self.to_s.sub(/.+::/, '').intern
+ end
+
+ return @name
+ end
+end
+
+# $Id$