diff options
Diffstat (limited to 'lib/puppet')
-rw-r--r-- | lib/puppet/config.rb | 222 | ||||
-rw-r--r-- | lib/puppet/log.rb | 4 | ||||
-rw-r--r-- | lib/puppet/server.rb | 3 | ||||
-rw-r--r-- | lib/puppet/server/ca.rb | 2 | ||||
-rwxr-xr-x | lib/puppet/server/fileserver.rb | 2 | ||||
-rwxr-xr-x | lib/puppet/sslcertificates.rb | 60 | ||||
-rw-r--r-- | lib/puppet/sslcertificates/ca.rb | 110 | ||||
-rw-r--r-- | lib/puppet/sslcertificates/certificate.rb | 10 | ||||
-rw-r--r-- | lib/puppet/transportable.rb | 6 | ||||
-rw-r--r-- | lib/puppet/type.rb | 13 | ||||
-rw-r--r-- | lib/puppet/util.rb | 47 |
11 files changed, 337 insertions, 142 deletions
diff --git a/lib/puppet/config.rb b/lib/puppet/config.rb index 295bf2035..010473dbf 100644 --- a/lib/puppet/config.rb +++ b/lib/puppet/config.rb @@ -5,23 +5,22 @@ class Config # Retrieve a config value def [](param) - param = param.intern unless param.is_a? Symbol + param = convert(param) if @config.include?(param) if @config[param] val = @config[param].value return val end else - nil + raise ArgumentError, "Invalid argument %s" % param end end # Set a config value. This doesn't set the defaults, it sets the value itself. def []=(param, value) - param = param.intern unless param.is_a? Symbol + param = convert(param) unless @config.include?(param) - raise Puppet::Error, "Unknown configuration parameter %s" % param - #@config[param] = newelement(param, value) + raise Puppet::Error, "Unknown configuration parameter %s" % param.inspect end unless @order.include?(param) @order << param @@ -40,6 +39,47 @@ class Config return true end + # Generate the list of valid arguments, in a format that GetoptLong can + # understand, and add them to the passed option list. + def addargs(options) + require 'getoptlong' + # Add all of the config parameters as valid options. + self.each { |param, obj| + if self.boolean?(param) + options << ["--#{param}", GetoptLong::NO_ARGUMENT] + options << ["--no-#{param}", GetoptLong::NO_ARGUMENT] + else + options << ["--#{param}", GetoptLong::REQUIRED_ARGUMENT] + end + } + + return options + end + + # Turn the config into a transaction and apply it + def apply + trans = self.to_transportable + begin + comp = trans.to_type + trans = comp.evaluate + trans.evaluate + comp.remove + rescue => detail + puts detail.backtrace + Puppet.err "Could not configure myself: %s" % detail + end + end + + # Is our parameter a boolean parameter? + def boolean?(param) + param = convert(param) + if @config.include?(param) and @config[param].kind_of? CBoolean + return true + else + return false + end + end + # Remove all set values. def clear @config.each { |name, obj| @@ -47,6 +87,15 @@ class Config } end + def convert(param) + case param + when String: return param.intern + when Symbol: return param + else + raise ArgumentError, "Invalid param type %s" % param.class + end + end + def each @order.each { |name| if @config.include?(name) @@ -75,10 +124,40 @@ class Config # Return an object by name. def element(param) - param = param.intern unless param.is_a? Symbol + param = convert(param) @config[param] end + # Handle a command-line argument. + def handlearg(opt, value = nil) + if value == "true" + value = true + end + if value == "false" + value = false + end + str = opt.sub(/^--/,'') + bool = true + newstr = str.sub(/^no-/, '') + if newstr != str + str = newstr + bool = false + end + if self.valid?(str) + if self.boolean?(str) + self[str] = bool + else + self[str] = value + end + + # Mark that this was set on the cli, so it's not overridden if the config + # gets reread. + @config[str.intern].setbycli = true + else + raise ArgumentError, "Invalid argument %s" % opt + end + end + # Create a new config object def initialize @order = [] @@ -87,6 +166,7 @@ class Config # Return all of the parameters associated with a given section. def params(section) + section = section.intern if section.is_a? String @config.find_all { |name, obj| obj.section == section }.collect { |name, obj| @@ -134,8 +214,13 @@ class Config values[section][var.to_s] = value next end - Puppet.info "%s: Setting %s to '%s'" % [section, var, value] - self[var] = value + + # Don't override set parameters, since the file is parsed + # after cli arguments are handled. + unless @config.include?(var) and @config[var].setbycli + Puppet.debug "%s: Setting %s to '%s'" % [section, var, value] + self[var] = value + end @config[var].section = section metas.each { |meta| @@ -155,6 +240,7 @@ class Config # what kind of element we're creating, but the value itself might be either # a default or a value, so we can't actually assign it. def newelement(param, desc, value) + param = convert(param) mod = nil case value when true, false, "true", "false": @@ -171,9 +257,12 @@ class Config element.extend(mod) end + @order << param + return element end + # Iterate across all of the objects in a given section. def persection(section) self.each { |name, obj| if obj.section == section @@ -221,7 +310,13 @@ class Config } if obj.respond_to? :to_transportable - objects << obj.to_transportable + unless done[:file].include? obj.value + trans = obj.to_transportable + # transportable could return nil + next unless trans + objects << trans + done[:file] << obj.value + end end } @@ -241,6 +336,7 @@ class Config section = section.intern unless section.is_a? Symbol #hash.each { |param, value| defs.each { |param, value, desc| + param = convert(param) if @config.include?(param) and @config[param].default raise Puppet::Error, "Default %s is already defined" % param end @@ -260,11 +356,27 @@ class Config # Convert our list of objects into a configuration file. def to_config - str = "" + 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. + +Every section can specify three special parameters: owner, group, and mode. +These parameters affect the required permissions of any files specified after +their specification. Puppet will sometimes use these parameters to check its +own configured state, so they can be used to make Puppet a bit more self-managing. + +Note also that the section names are entirely for human-level organizational +purposes; they don't provide separate namespaces. All parameters are in a +single namespace. + +Generated on #{Time.now}. + +}.gsub(/^/, "# ") + eachsection do |section| str += "[#{section}]\n" persection(section) do |obj| - str += obj.to_s + "\n" + str += obj.to_config + "\n" end end @@ -276,6 +388,7 @@ class Config done = { :owner => [], :group => [], + :file => [] } topbucket = Puppet::TransBucket.new @@ -299,12 +412,23 @@ class Config # Convert to a parseable manifest def to_manifest transport = self.to_transportable - return transport.to_manifest + + manifest = transport.to_manifest + "\n" + eachsection { |section| + manifest += "include #{section}\n" + } + + return manifest + end + + def valid?(param) + param = convert(param) + @config.has_key?(param) end # The base element type. class CElement - attr_accessor :name, :section, :default, :parent, :desc + attr_accessor :name, :section, :default, :parent, :desc, :setbycli # Unset any set value. def clear @@ -314,15 +438,43 @@ class Config # Create the new element. Pretty much just sets the name. def initialize(name, desc, value = nil) @name = name - @desc = desc + @desc = desc.gsub(/^\s*/, '') if value @value = value end end - def to_s - str = @desc.gsub(/^/, " # ") + - "\n %s = %s" % [@name, self.value] + def set? + if defined? @value and ! @value.nil? + return true + else + return false + end + end + + # Convert the object to a config statement. + def to_config + str = @desc.gsub(/^/, "# ") + "\n" + + # Add in a statement about the default. + if defined? @default and @default + str += "# The default value is '%s'.\n" % @default + end + + line = "%s = %s" % [@name, self.value] + + # If the value has not been overridden, then print it out commented + # and unconverted, so it's clear that that's the default and how it + # works. + if defined? @value and ! @value.nil? + line = "%s = %s" % [@name, self.value] + else + line = "# %s = %s" % [@name, @default] + end + + str += line + "\n" + + str.gsub(/^/, " ") end # Retrieves the value, or if it's not set, retrieves the default. @@ -358,12 +510,11 @@ class Config # A file. module CFile - attr_accessor :owner, :group, :mode, :type + attr_accessor :owner, :group, :mode def convert(value) - unless value - return nil - end + return value unless value + return value unless value.is_a? String if value =~ /\$(\w+)/ parent = $1 if pval = @parent[parent] @@ -380,20 +531,33 @@ class Config # Set the type appropriately. Yep, a hack. This supports either naming # the variable 'dir', or adding a slash at the end. def munge(value) - if value.to_s =~ /dir/ - @type = :directory - elsif value =~ /\/$/ + if value.to_s =~ /\/$/ @type = :directory return value.sub(/\/$/, '') - else - @type = :file end return value end + # Return the appropriate type. + def type + value = self.value + if @name.to_s =~ /dir/ + return :directory + elsif value.to_s =~ /\/$/ + return :directory + elsif value.is_a? String + return :file + else + return nil + end + end + + # Convert the object to a TransObject instance. def to_transportable + type = self.type + return nil unless type obj = Puppet::TransObject.new(self.value, "file") - obj[:ensure] = self.type + obj[:ensure] = type [:owner, :group, :mode].each { |var| if value = self.send(var) obj[var] = value @@ -407,6 +571,7 @@ class Config # Make sure any provided variables look up to something. def validate(value) + return true unless value.is_a? String value.scan(/\$(\w+)/) { |name| name = name[0] unless @parent[name] @@ -423,7 +588,8 @@ class Config when true, "true": return true when false, "false": return false else - raise Puppet::Error, "Invalid value %s for %s" % [value, @name] + raise Puppet::Error, "Invalid value '%s' for %s" % + [value.inspect, @name] end end end diff --git a/lib/puppet/log.rb b/lib/puppet/log.rb index 65043a463..672ff1d8b 100644 --- a/lib/puppet/log.rb +++ b/lib/puppet/log.rb @@ -27,14 +27,14 @@ module Puppet :crit => RESET } - @destinations = {:syslog => Syslog.open("puppet")} + #@destinations = {:syslog => Syslog.open("puppet")} + @destinations = {:console => :console} # Reset all logs to basics. Basically just closes all files and undefs # all of the other objects. def Log.close(dest = nil) if dest if @destinations.include?(dest) - Puppet.warning "Closing %s" % dest if @destinations.respond_to?(:close) @destinations[dest].close end diff --git a/lib/puppet/server.rb b/lib/puppet/server.rb index cd73ce885..4fadc3987 100644 --- a/lib/puppet/server.rb +++ b/lib/puppet/server.rb @@ -34,9 +34,6 @@ module Puppet daemonize = hash[:Daemonize] end - if daemonize - self.daemonize - end # FIXME we should have some kind of access control here, using # :RequestHandler hash[:Port] ||= Puppet[:masterport] diff --git a/lib/puppet/server/ca.rb b/lib/puppet/server/ca.rb index 7afd0c82c..a008feb70 100644 --- a/lib/puppet/server/ca.rb +++ b/lib/puppet/server/ca.rb @@ -26,7 +26,7 @@ class Server # we only otherwise know how to handle files unless @autosign =~ /^\// raise Puppet::Error, "Invalid autosign value %s" % - @autosign + @autosign.inspect end unless FileTest.exists?(@autosign) diff --git a/lib/puppet/server/fileserver.rb b/lib/puppet/server/fileserver.rb index 27e4d814a..cd8511ad6 100755 --- a/lib/puppet/server/fileserver.rb +++ b/lib/puppet/server/fileserver.rb @@ -9,7 +9,7 @@ class Server attr_accessor :local Puppet.setdefaults("fileserver", - [:fileserverconfig, "$puppetconf/fileserver.conf", + [:fileserverconfig, "$confdir/fileserver.conf", "Where the fileserver configuration is stored."]) #CHECKPARAMS = %w{checksum type mode owner group} diff --git a/lib/puppet/sslcertificates.rb b/lib/puppet/sslcertificates.rb index 0c6322bcf..fef661178 100755 --- a/lib/puppet/sslcertificates.rb +++ b/lib/puppet/sslcertificates.rb @@ -9,36 +9,36 @@ rescue LoadError end module Puppet::SSLCertificates - def self.mkdir(dir) - # this is all a bunch of stupid hackery - unless FileTest.exists?(dir) - comp = Puppet.type(:component).create( - :name => "certdir creation" - ) - path = [''] - - dir.split(File::SEPARATOR).each { |d| - path << d - if FileTest.exists?(File.join(path)) - unless FileTest.directory?(File.join(path)) - raise "%s exists but is not a directory" % File.join(path) - end - else - obj = Puppet::Type.type(:file).create( - :name => File.join(path), - :mode => "750", - :ensure => "directory" - ) - - comp.push obj - end - } - trans = comp.evaluate - trans.evaluate - end - - Puppet::Type.allclear - end +# def self.mkdir(dir) +# # this is all a bunch of stupid hackery +# unless FileTest.exists?(dir) +# comp = Puppet.type(:component).create( +# :name => "certdir creation" +# ) +# path = [''] +# +# dir.split(File::SEPARATOR).each { |d| +# path << d +# if FileTest.exists?(File.join(path)) +# unless FileTest.directory?(File.join(path)) +# raise "%s exists but is not a directory" % File.join(path) +# end +# else +# obj = Puppet::Type.type(:file).create( +# :name => File.join(path), +# :mode => "750", +# :ensure => "directory" +# ) +# +# comp.push obj +# end +# } +# trans = comp.evaluate +# trans.evaluate +# end +# +# Puppet::Type.allclear +# end #def self.mkcert(type, name, days, issuercert, issuername, serial, publickey) def self.mkcert(hash) diff --git a/lib/puppet/sslcertificates/ca.rb b/lib/puppet/sslcertificates/ca.rb index 40b34e1ee..a3cd376fc 100644 --- a/lib/puppet/sslcertificates/ca.rb +++ b/lib/puppet/sslcertificates/ca.rb @@ -2,46 +2,6 @@ class Puppet::SSLCertificates::CA Certificate = Puppet::SSLCertificates::Certificate attr_accessor :keyfile, :file, :config, :dir, :cert -# @@params = [ -# :certdir, -# :publickeydir, -# :privatekeydir, -# :cadir, -# :cakey, -# :cacert, -# :capass, -# :capub, -# :csrdir, -# :signeddir, -# :serial, -# :privatedir, -# :ca_crl_days, -# :ca_days, -# :ca_md, -# :req_bits, -# :keylength, -# :autosign -# ] -# :certdir => [:ssldir, "certs"], -# :publickeydir => [:ssldir, "public_keys"], -# :privatekeydir => [:ssldir, "private_keys"], -# :cadir => [:ssldir, "ca"], -# :cacert => [:cadir, "ca_crt.pem"], -# :cakey => [:cadir, "ca_key.pem"], -# :capub => [:cadir, "ca_pub.pem"], -# :csrdir => [:cadir, "requests"], -# :signeddir => [:cadir, "signed"], -# :capass => [:cadir, "ca.pass"], -# :serial => [:cadir, "serial"], -# :privatedir => [:ssldir, "private"], -# :passfile => [:privatedir, "password"], -# :autosign => [:puppetconf, "autosign.conf"], -# :ca_crl_days => 365, -# :ca_days => 1825, -# :ca_md => "md5", -# :req_bits => 2048, -# :keylength => 1024, - Puppet.setdefaults("ca", [:certdir, "$ssldir/certs", "The certificate directory."], [:publickeydir, "$ssldir/public_keys", "The public key directory."], @@ -51,19 +11,26 @@ class Puppet::SSLCertificates::CA [:cacert, "$cadir/ca_crt.pem", "The CA certificate."], [:cakey, "$cadir/ca_key.pem", "The CA private key."], [:capub, "$cadir/ca_pub.pem", "The CA public key."], + [:caprivatedir, "$cadir/private", + "Where the CA stores private certificate information."], [:csrdir, "$cadir/requests", "Where the CA stores certificate requests"], [:signeddir, "$cadir/signed", "Where the CA stores signed certificates."], - [:capass, "$cadir/ca.pass", - "Where the CA stores the password for the private key; usually not used."], + [:capass, "$caprivatedir/ca.pass", + "Where the CA stores the password for the private key"], [:serial, "$cadir/serial", "Where the serial number for certificates is stored."], + [:privatedir, "$ssldir/private", + "Where the client stores private certificate information."], [:passfile, "$privatedir/password", "Where puppetd stores the password for its private key. Generally unused."], - [:autosign, "$puppetconf/autosign.conf", - "Where to look for the autosigning configuration file."], + [:autosign, "$confdir/autosign.conf", + "Whether to enable autosign. Valid values are true (which autosigns + any key request, and is a very bad idea), false (which never autosigns + any key request), and the path to a file, which uses that configuration + file to determine which keys to sign."], [:ca_days, 1825, "How long a certificate should be valid."], [:ca_md, "md5", "The type of hash used in certificates."], [:req_bits, 2048, "The bit length of the certificates."], @@ -97,30 +64,51 @@ class Puppet::SSLCertificates::CA def initialize(hash = {}) self.setconfig(hash) + if Puppet[:capass] + if FileTest.exists?(Puppet[:capass]) + #puts "Reading %s" % Puppet[:capass] + #system "ls -al %s" % Puppet[:capass] + #File.read Puppet[:capass] + Puppet.info "Getting pass" + @config[:password] = self.getpass + else + # Don't create a password if the cert already exists + unless FileTest.exists?(@config[:cacert]) + Puppet.info "Genning pass" + @config[:password] = self.genpass + end + end + end + self.getcert unless FileTest.exists?(@config[:serial]) File.open(@config[:serial], "w") { |f| f << "%04X" % 1 } end - - if Puppet[:capass] and ! FileTest.exists?(Puppet[:capass]) - self.genpass - end end def genpass pass = "" 20.times { pass += (rand(74) + 48).chr } - unless @config[:capass] - raise "No passfile" + Puppet.recmkdir(File.dirname(@config[:capass])) + begin + File.open(@config[:capass], "w", 0600) { |f| f.print pass } + rescue Errno::EACCES => detail + raise Puppet::Error, detail.to_s end - Puppet::SSLCertificates.mkdir(File.dirname(@config[:capass])) - File.open(@config[:capass], "w", 0600) { |f| f.print pass } return pass end + def getpass + if @config[:capass] and File.readable?(@config[:capass]) + return File.read(@config[:capass]) + else + raise Puppet::Error, "Could not read CA passfile %s" % @config[:capass] + end + end + def getcert if FileTest.exists?(@config[:cacert]) @cert = OpenSSL::X509::Certificate.new( @@ -161,7 +149,7 @@ class Puppet::SSLCertificates::CA cert = Certificate.new( :name => "CAcert", :cert => @config[:cacert], - :encrypt => @config[:passfile], + :encrypt => @config[:capass], :key => @config[:cakey], :selfsign => true, :length => 1825, @@ -187,22 +175,13 @@ class Puppet::SSLCertificates::CA def setconfig(hash) @config = {} Puppet.config.params("ca").each { |param| + param = param.intern if param.is_a? String if hash.include?(param) - begin @config[param] = hash[param] Puppet[param] = hash[param] hash.delete(param) - rescue => detail - puts detail - exit - end else - begin @config[param] = Puppet[param] - rescue => detail - puts detail - exit - end end } @@ -217,10 +196,10 @@ class Puppet::SSLCertificates::CA [:cadir, :csrdir, :signeddir].each { |dir| unless @config[dir] - raise "%s is undefined" % dir + raise Puppet::DevError, "%s is undefined" % dir end unless FileTest.exists?(@config[dir]) - Puppet::SSLCertificates.mkdir(@config[dir]) + Puppet.recmkdir(@config[dir]) end } end @@ -249,6 +228,7 @@ class Puppet::SSLCertificates::CA File.read(@config[:cakey]), @config[:password] ) else + system("ls -al %s" % Puppet[:capass]) cakey = OpenSSL::PKey::RSA.new( File.read(@config[:cakey]) ) diff --git a/lib/puppet/sslcertificates/certificate.rb b/lib/puppet/sslcertificates/certificate.rb index 65ceb44b9..618b7473a 100644 --- a/lib/puppet/sslcertificates/certificate.rb +++ b/lib/puppet/sslcertificates/certificate.rb @@ -54,7 +54,7 @@ class Puppet::SSLCertificates::Certificate def initialize(hash) unless hash.include?(:name) - raise "You must specify the common name for the certificate" + raise Puppet::Error, "You must specify the common name for the certificate" end @name = hash[:name] @@ -72,7 +72,7 @@ class Puppet::SSLCertificates::Certificate @cacertfile ||= File.join(Puppet[:certdir], "ca.pem") unless FileTest.directory?(@dir) - Puppet::SSLCertificates.mkdir(@dir) + Puppet.recmkdir(@dir) end unless @certfile =~ /\.pem$/ @@ -82,14 +82,14 @@ class Puppet::SSLCertificates::Certificate Puppet[:privatekeydir], [@name,"pem"].join(".") ) unless FileTest.directory?(@dir) - Puppet::SSLCertificates.mkdir(@dir) + Puppet.recmkdir(@dir) end [@keyfile].each { |file| dir = File.dirname(file) unless FileTest.directory?(dir) - Puppet::SSLCertificates.mkdir(dir) + Puppet.recmkdir(dir) end } @@ -122,7 +122,7 @@ class Puppet::SSLCertificates::Certificate @password = f.read.chomp } else - raise ":encrypt must be a path to a pass phrase file" + raise Puppet::Error, ":encrypt must be a path to a pass phrase file" end else @password = nil diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb index 128a06a84..b9dedfe9e 100644 --- a/lib/puppet/transportable.rb +++ b/lib/puppet/transportable.rb @@ -62,8 +62,8 @@ module Puppet retobj = nil if type = Puppet::Type.type(self.type) unless retobj = type.create(self) - Puppet.notice "Could not create %s[%s]" % - [self.type, self.name] + #Puppet.notice "Could not create %s[%s]" % + # [self.type, self.name] return nil end #retobj.file = @file @@ -208,7 +208,7 @@ module Puppet # Now just call to_type on them with the container as a parent unless obj = child.to_type(container) # nothing; we assume the method already warned - Puppet.warning "Could not create child %s" % child.name + #Puppet.warning "Could not create child %s" % child.name end } diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 3625299f8..6655606df 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -1008,17 +1008,22 @@ class Type < Puppet::Element # Remove an object. The argument determines whether the object's # subscriptions get eliminated, too. - def remove(rmdeps) + def remove(rmdeps = true) @children.each { |child| - child.remove + child.remove(rmdeps) } - self.class.delete(self) if rmdeps Puppet::Event::Subscription.dependencies(self).each { |dep| - self.unsubscribe(dep) + begin + self.unsubscribe(dep) + rescue + # ignore failed unsubscribes + end } end + self.warning "Removing" + self.class.delete(self) if defined? @parent and @parent @parent.delete(self) diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 669e8310a..6e74ca602 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -85,6 +85,53 @@ module Util return retval end + # Change the process to a different user + def self.chuser + if group = Puppet[:group] + if group =~ /^\d+$/ + group = Integer(group) + else + begin + g = Etc.getgrnam(group) + rescue ArgumentError + $stderr.puts "Could not find group %s" % group + end + group = g.gid + end + unless Process.gid == group + begin + Process.egid = group + Process.gid = group + rescue + $stderr.puts "could not change to group %s" % group + exit(74) + end + end + end + + if user = Puppet[:user] + if user =~ /^\d+$/ + user = Integer(user) + else + begin + u = Etc.getpwnam(user) + rescue ArgumentError + $stderr.puts "Could not find user %s" % user + end + user = u.uid + end + unless Process.uid == user + begin + Process.euid = user + Process.uid = user + rescue + $stderr.puts "could not change to user %s" % user + exit(74) + end + end + end + end + # Create a lock file while something is happening def self.lock(*opts) lock = opts[0] + ".lock" |