diff options
Diffstat (limited to 'lib/puppet')
53 files changed, 988 insertions, 539 deletions
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index ce1411b62..9a95c3cab 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -6,6 +6,10 @@ module Puppet var = nil name = $0.gsub(/.+#{File::SEPARATOR}/,'').sub(/\.rb$/, '') + # Make File.expand_path happy + require 'etc' + ENV["HOME"] ||= Etc.getpwuid(Process.uid).dir + if name != "puppetmasterd" and Puppet::Util::SUIDManager.uid != 0 conf = File.expand_path("~/.puppet") var = File.expand_path("~/.puppet/var") @@ -123,10 +127,17 @@ module Puppet namespaces and methods. This can be used as a coarse-grained authorization system for both ``puppetd`` and ``puppetmasterd``." ], - :environment => ["", "The environment Puppet is running in. For clients (e.g., ``puppetd``) this - determines the environment itself, which is used to find modules and much more. For - servers (i.e., ``puppetmasterd``) this provides the default environment for nodes we - know nothing about."], + :environments => ["production,development", "The valid environments for Puppet clients. + This is more useful as a server-side setting than client, but any + environment chosen must be in this list. Values should be + separated by a comma."], + :environment => {:default => "development", :desc => "The environment Puppet is running in. For clients + (e.g., ``puppetd``) this determines the environment itself, which + is used to find modules and much more. For servers (i.e., + ``puppetmasterd``) this provides the default environment for nodes + we know nothing about.", + :hook => proc { |value| raise(ArgumentError, "Invalid environment %s" % value) unless Puppet::Node::Environment.valid?(value) } + }, :diff_args => ["", "Which arguments to pass to the diff command when printing differences between files."], :diff => ["diff", "Which diff command to use when printing differences between files."], :show_diff => [false, "Whether to print a contextual diff when files are being replaced. The diff @@ -137,7 +148,11 @@ module Puppet :daemonize => { :default => true, :desc => "Send the process into the background. This is the default.", :short => "D" - } + }, + :maximum_uid => [4294967290, "The maximum allowed UID. Some platforms use negative UIDs + but then ship with tools that do not know how to handle signed ints, so the UIDs show up as + huge numbers that can then not be fed back into the system. This is a hackish way to fail in a + slightly more useful way when that happens."] ) hostname = Facter["hostname"].value @@ -151,6 +166,10 @@ module Puppet Puppet.setdefaults(:ssl, :certname => [fqdn, "The name to use when handling certificates. Defaults to the fully qualified domain name."], + :certdnsnames => ['*:*.*:*.*.*:*.*.*.*:*.*.*.*.*:*.*.*.*.*.*', "The DNS + names on the Server certificate as a colon-separated list. Defaults + to wildcard match for all DNS names up to 6 dot-separated components + long."], :certdir => ["$ssldir/certs", "The certificate directory."], :publickeydir => ["$ssldir/public_keys", "The public key directory."], :privatekeydir => { :default => "$ssldir/private_keys", @@ -520,6 +539,8 @@ module Puppet used when networked databases are used."], :dbpassword => [ "puppet", "The database password for Client caching. Only used when networked databases are used."], + :dbsocket => [ "", "The database socket location. Only used when networked + databases are used. Will be ignored if the value is an empty string."], :railslog => {:default => "$logdir/rails.log", :mode => 0600, :owner => "$user", diff --git a/lib/puppet/indirector/facts/memory.rb b/lib/puppet/indirector/facts/memory.rb new file mode 100644 index 000000000..3c10d5964 --- /dev/null +++ b/lib/puppet/indirector/facts/memory.rb @@ -0,0 +1,9 @@ +require 'puppet/node/facts' +require 'puppet/indirector/memory' + +class Puppet::Node::Facts::Memory < Puppet::Indirector::Memory + desc "Keep track of facts in memory but nowhere else. This is used for + one-time compiles, such as what the stand-alone ``puppet`` does. + To use this terminus, you must load it with the data you want it + to contain." +end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 9a9b1c0bf..816b4ffc5 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -13,6 +13,14 @@ class Puppet::Indirector::Indirection @@indirections.find { |i| i.name == name } end + # Find an indirected model by name. This is provided so that Terminus classes + # can specifically hook up with the indirections they are associated with. + def self.model(name) + match = @@indirections.find { |i| i.name == name } + return nil unless match + match.model + end + attr_accessor :name, :model # Create and return our cache terminus. diff --git a/lib/puppet/indirector/module_files.rb b/lib/puppet/indirector/module_files.rb index c79fae57b..84286d8a5 100644 --- a/lib/puppet/indirector/module_files.rb +++ b/lib/puppet/indirector/module_files.rb @@ -57,10 +57,8 @@ class Puppet::Indirector::ModuleFiles < Puppet::Indirector::Terminus def environment(node_name) if node_name and node = Puppet::Node.find(node_name) node.environment - elsif env = Puppet.settings[:environment] and env != "" - env else - nil + Puppet::Node::Environment.new.name end end diff --git a/lib/puppet/indirector/report/processor.rb b/lib/puppet/indirector/report/processor.rb index d2b7c84f3..fd1bc413a 100644 --- a/lib/puppet/indirector/report/processor.rb +++ b/lib/puppet/indirector/report/processor.rb @@ -7,7 +7,7 @@ class Puppet::Transaction::Report::Processor < Puppet::Indirector::Code the report types listed in the 'reports' setting." def initialize - Puppet.settings.use(:reporting, :metrics) + Puppet.settings.use(:main, :reporting, :metrics) end def save(report) diff --git a/lib/puppet/metatype/instances.rb b/lib/puppet/metatype/instances.rb index 2f9918067..8cc648e8f 100644 --- a/lib/puppet/metatype/instances.rb +++ b/lib/puppet/metatype/instances.rb @@ -99,6 +99,8 @@ class Puppet::Type end end + # If they've specified a type and called on the base, then + # delegate to the subclass. if type if typeklass = self.type(type) return typeklass.create(hash) @@ -233,19 +235,22 @@ class Puppet::Type hash.delete :name end - unless title - raise Puppet::Error, - "You must specify a title for objects of type %s" % self.to_s + if configuration = hash[:configuration] + hash.delete(:configuration) end + raise(Puppet::Error, "You must specify a title for objects of type %s" % self.to_s) unless title + if hash.include? :type unless self.validattr? :type hash.delete :type end end + # okay, now make a transobject out of hash begin trans = Puppet::TransObject.new(title, self.name.to_s) + trans.configuration = configuration if configuration hash.each { |param, value| trans[param] = value } diff --git a/lib/puppet/metatype/metaparams.rb b/lib/puppet/metatype/metaparams.rb index 1ab3f26c1..eb158a47d 100644 --- a/lib/puppet/metatype/metaparams.rb +++ b/lib/puppet/metatype/metaparams.rb @@ -94,7 +94,7 @@ class Puppet::Type # We've got four relationship metaparameters, so this method is used # to reduce code duplication between them. - def store_relationship(param, values) + def munge_relationship(param, values) # We need to support values passed in as an array or as a # resource reference. result = [] @@ -194,20 +194,24 @@ class Puppet::Type unless aliases.is_a?(Array) aliases = [aliases] end - @resource.info "Adding aliases %s" % aliases.collect { |a| - a.inspect - }.join(", ") + + raise(ArgumentError, "Cannot add aliases without a configuration") unless @resource.configuration + + @resource.info "Adding aliases %s" % aliases.collect { |a| a.inspect }.join(", ") + aliases.each do |other| - if obj = @resource.class[other] - unless obj == @resource - self.fail( - "%s can not create alias %s: object already exists" % - [@resource.title, other] - ) + if obj = @resource.configuration.resource(@resource.class.name, other) + unless obj.object_id == @resource.object_id + self.fail("%s can not create alias %s: object already exists" % [@resource.title, other]) end next end + + # LAK:FIXME Old-school, add the alias to the class. @resource.class.alias(other, @resource) + + # Newschool, add it to the configuration. + @resource.configuration.alias(@resource, other) end end end @@ -247,7 +251,16 @@ class Puppet::Type end def munge(rels) - @resource.store_relationship(self.class.name, rels) + @resource.munge_relationship(self.class.name, rels) + end + + def validate_relationship + @value.each do |value| + unless @resource.configuration.resource(*value) + description = self.class.direction == :in ? "dependency" : "dependent" + raise Puppet::Error, "Could not find #{description} %s[%s]" % [value[0].to_s.capitalize, value[1]] + end + end end # Create edges from each of our relationships. :in diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index 5408cabe4..ea351ddc3 100644 --- a/lib/puppet/network/client/master.rb +++ b/lib/puppet/network/client/master.rb @@ -139,63 +139,57 @@ class Puppet::Network::Client::Master < Puppet::Network::Client facts = self.class.facts end - if self.configuration or FileTest.exists?(self.cachefile) - if self.fresh?(facts) - Puppet.info "Config is up to date" - if self.configuration - return - end - if oldtext = self.retrievecache - begin - @configuration = YAML.load(oldtext).to_configuration - rescue => detail - Puppet.warning "Could not load cached configuration: %s" % detail - end - return - end - end - end - Puppet.debug("getting config") + raise Puppet::Network::ClientError.new("Could not retrieve any facts") unless facts.length > 0 # Retrieve the plugins. - if Puppet[:pluginsync] - getplugins() - end + getplugins() if Puppet[:pluginsync] - unless facts.length > 0 - raise Puppet::Network::ClientError.new( - "Could not retrieve any facts" - ) + if (self.configuration or FileTest.exist?(self.cachefile)) and self.fresh?(facts) + Puppet.info "Configuration is up to date" + return if use_cached_config end - unless objects = get_actual_config(facts) - @configuration = nil + Puppet.debug("Retrieving configuration") + + # If we can't retrieve the configuration, just return, which will either + # fail, or use the in-memory configuration. + unless yaml_objects = get_actual_config(facts) + use_cached_config(true) return end - unless objects.is_a?(Puppet::TransBucket) - raise NetworkClientError, - "Invalid returned objects of type %s" % objects.class + begin + objects = YAML.load(yaml_objects) + rescue => detail + msg = "Configuration could not be translated from yaml" + msg += "; using cached configuration" if use_cached_config(true) + Puppet.warning msg + return end self.setclasses(objects.classes) # Clear all existing objects, so we can recreate our stack. - if self.configuration - clear() - end + clear() if self.configuration # Now convert the objects to a puppet configuration graph. - @configuration = objects.to_configuration + begin + @configuration = objects.to_configuration + rescue => detail + clear() + puts detail.backtrace if Puppet[:trace] + msg = "Configuration could not be instantiated: %s" % detail + msg += "; using cached configuration" if use_cached_config(true) + Puppet.warning msg + return + end - if @configuration.nil? - raise Puppet::Error, "Configuration could not be processed" + if ! @configuration.from_cache + self.cache(yaml_objects) end # Keep the state database up to date. @configuration.host_config = true - - return @configuration end # A simple proxy method, so it's easy to test. @@ -270,11 +264,9 @@ class Puppet::Network::Client::Master < Puppet::Network::Client Puppet.err "Could not retrieve configuration: %s" % detail end - if defined? @configuration and @configuration + if self.configuration @configuration.retrieval_duration = duration - unless @local - Puppet.notice "Starting configuration run" - end + Puppet.notice "Starting configuration run" unless @local benchmark(:notice, "Finished configuration run") do @configuration.apply(options) end @@ -500,34 +492,16 @@ class Puppet::Network::Client::Master < Puppet::Network::Client # 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) - else - begin - Timeout::timeout(self.class.timeout) do - return get_remote_config(facts) - end - rescue Timeout::Error - Puppet.err "Configuration retrieval timed out" - return nil + begin + Timeout::timeout(self.class.timeout) do + return get_remote_config(facts) end + rescue Timeout::Error + Puppet.err "Configuration retrieval timed out" + return nil end end - # Retrieve a configuration from a local master. - def get_local_config(facts) - # If we're local, we don't have to do any of the conversion - # stuff. - objects = @driver.getconfig(facts, "yaml") - @compile_time = Time.now - - if objects == "" - raise Puppet::Error, "Could not retrieve configuration" - end - - return objects - end - # Retrieve a config from a remote master. def get_remote_config(facts) textobjects = "" @@ -545,45 +519,18 @@ class Puppet::Network::Client::Master < Puppet::Network::Client end rescue => detail - puts detail.backtrace Puppet.err "Could not retrieve configuration: %s" % detail - - unless Puppet[:usecacheonfailure] - @configuration = nil - Puppet.warning "Not using cache on failed configuration" - return - end + return nil end end - fromcache = false - if textobjects == "" - unless textobjects = self.retrievecache - raise Puppet::Error.new( - "Cannot connect to server and there is no cached configuration" - ) - end - Puppet.warning "Could not get config; using cached copy" - fromcache = true - else - @compile_time = Time.now - Puppet::Util::Storage.cache(:configuration)[:facts] = facts - Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time - end + return nil if textobjects == "" - begin - objects = YAML.load(textobjects) - rescue => detail - raise Puppet::Error, - "Could not understand configuration: %s" % - detail.to_s - end + @compile_time = Time.now + Puppet::Util::Storage.cache(:configuration)[:facts] = facts + Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time - if @cache and ! fromcache - self.cache(textobjects) - end - - return objects + return textobjects end def lockfile @@ -609,4 +556,32 @@ class Puppet::Network::Client::Master < Puppet::Network::Client Puppet.info "Sleeping for %s seconds (splay is enabled)" % time sleep(time) end + + private + + # Use our cached config, optionally specifying whether this is + # necessary because of a failure. + def use_cached_config(because_of_failure = false) + return true if self.configuration + + if because_of_failure and ! Puppet[:usecacheonfailure] + @configuration = nil + Puppet.warning "Not using cache on failed configuration" + return false + end + + return false unless oldtext = self.retrievecache + + begin + @configuration = YAML.load(oldtext).to_configuration + @configuration.from_cache = true + @configuration.host_config = true + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.warning "Could not load cached configuration: %s" % detail + clear + return false + end + return true + end end diff --git a/lib/puppet/network/handler/report.rb b/lib/puppet/network/handler/report.rb index 12abc9b15..8ddeed9f6 100755 --- a/lib/puppet/network/handler/report.rb +++ b/lib/puppet/network/handler/report.rb @@ -18,7 +18,7 @@ class Puppet::Network::Handler def initialize(*args) super - Puppet.settings.use(:reporting, :metrics) + Puppet.settings.use(:main, :reporting, :metrics) end # Accept a report from a client. diff --git a/lib/puppet/network/handler/runner.rb b/lib/puppet/network/handler/runner.rb index a8d0da9ce..c97e4791a 100755 --- a/lib/puppet/network/handler/runner.rb +++ b/lib/puppet/network/handler/runner.rb @@ -43,7 +43,7 @@ class Puppet::Network::Handler end if ignoreschedules - msg += " without schedules" + msg += " ignoring schedules" end Puppet.notice msg diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb new file mode 100644 index 000000000..062c67c71 --- /dev/null +++ b/lib/puppet/network/http.rb @@ -0,0 +1,13 @@ +class Puppet::Network::HTTP + def self.server_class_by_type(kind) + return Puppet::Network::HTTP::WEBrick if kind.to_sym == :webrick + if kind.to_sym == :mongrel + raise ArgumentError, "Mongrel is not installed on this platform" unless Puppet.features.mongrel? + return Puppet::Network::HTTP::Mongrel + end + raise ArgumentError, "Unknown HTTP server name [#{kind}]" + end +end + +require 'puppet/network/http/webrick' +require 'puppet/network/http/mongrel' diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb new file mode 100644 index 000000000..773381c8d --- /dev/null +++ b/lib/puppet/network/http/handler.rb @@ -0,0 +1,109 @@ +class Puppet::Network::HTTP::Handler + def initialize(args = {}) + raise ArgumentError unless @server = args[:server] + raise ArgumentError unless @handler = args[:handler] + @model = find_model_for_handler(@handler) + register_handler + end + + # handle an HTTP request + def process(request, response) + return do_find(request, response) if get?(request) and singular?(request) + return do_search(request, response) if get?(request) and plural?(request) + return do_destroy(request, response) if delete?(request) and singular?(request) + return do_save(request, response) if put?(request) and singular?(request) + raise ArgumentError, "Did not understand HTTP #{http_method(request)} request for '#{path(request)}'" + rescue Exception => e + return do_exception(request, response, e) + end + + private + + def do_find(request, response) + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + args = params(request) + result = @model.find(key, args).to_yaml + encode_result(request, response, result) + end + + def do_search(request, response) + args = params(request) + result = @model.search(args).collect {|obj| obj.to_yaml } + encode_result(request, response, result) + end + + def do_destroy(request, response) + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + args = params(request) + result = @model.destroy(key, args) + encode_result(request, response, YAML.dump(result)) + end + + def do_save(request, response) + data = body(request) + raise ArgumentError, "No data to save" if !data or data.empty? + args = params(request) + obj = @model.new + result = obj.save(args.merge(:data => data)).to_yaml + encode_result(request, response, result) + end + + def do_exception(request, response, exception, status=404) + encode_result(request, response, exception.to_s, status) + end + + def find_model_for_handler(handler) + Puppet::Indirector::Indirection.model(handler) || + raise(ArgumentError, "Cannot locate indirection [#{handler}].") + end + + def get?(request) + http_method(request) == 'GET' + end + + def put?(request) + http_method(request) == 'PUT' + end + + def delete?(request) + http_method(request) == 'DELETE' + end + + def singular?(request) + %r{/#{@handler.to_s}$}.match(path(request)) + end + + def plural?(request) + %r{/#{@handler.to_s}s$}.match(path(request)) + end + + # methods specific to a given web server + + def register_handler + raise NotImplementedError + end + + def http_method(request) + raise NotImplementedError + end + + def path(request) + raise NotImplementedError + end + + def request_key(request) + raise NotImplementedError + end + + def body(request) + raise NotImplementedError + end + + def params(request) + raise NotImplementedError + end + + def encode_result(request, response, result, status = 200) + raise NotImplementedError + end +end diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb new file mode 100644 index 000000000..8ea669531 --- /dev/null +++ b/lib/puppet/network/http/mongrel.rb @@ -0,0 +1,54 @@ +require 'mongrel' if Puppet.features.mongrel? + +require 'puppet/network/http/mongrel/rest' +require 'puppet/network/http/mongrel/xmlrpc' + +class Puppet::Network::HTTP::Mongrel + def initialize(args = {}) + @listening = false + end + + def listen(args = {}) + raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].empty? + raise ArgumentError, ":protocols must be specified." if !args[:protocols] or args[:protocols].empty? + raise ArgumentError, ":address must be specified." unless args[:address] + raise ArgumentError, ":port must be specified." unless args[:port] + raise "Mongrel server is already listening" if listening? + + @protocols = args[:protocols] + @handlers = args[:handlers] + @server = Mongrel::HttpServer.new(args[:address], args[:port]) + + setup_handlers + + @server.run + @listening = true + end + + def unlisten + raise "Mongrel server is not listening" unless listening? + @server.graceful_shutdown + @listening = false + end + + def listening? + @listening + end + + private + + def setup_handlers + @protocols.each do |protocol| + @handlers.each do |handler| + class_for_protocol(protocol).new(:server => @server, :handler => handler) + end + end + end + + # TODO/FIXME: need a spec which forces delegation to the real class + def class_for_protocol(protocol) + return Puppet::Network::HTTP::MongrelREST if protocol.to_sym == :rest + return Puppet::Network::HTTP::MongrelXMLRPC if protocol.to_sym == :xmlrpc + raise ArgumentError, "Unknown protocol [#{protocol}]." + end +end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb new file mode 100644 index 000000000..db63613ab --- /dev/null +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -0,0 +1,37 @@ +require 'puppet/network/http/handler' + +class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler + + private + + def register_handler + @server.register('/' + @handler.to_s, self) + @server.register('/' + @handler.to_s + 's', self) + end + + def http_method(request) + request.params[Mongrel::Const::REQUEST_METHOD] + end + + def path(request) + '/' + request.params[Mongrel::Const::REQUEST_PATH].split('/')[1] + end + + def request_key(request) + request.params[Mongrel::Const::REQUEST_PATH].split('/')[2] + end + + def body(request) + request.body + end + + def params(request) + Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]) + end + + def encode_result(request, response, result, status = 200) + response.start(status) do |head, body| + body.write(result) + end + end +end diff --git a/lib/puppet/network/http/mongrel/xmlrpc.rb b/lib/puppet/network/http/mongrel/xmlrpc.rb new file mode 100644 index 000000000..92acd4f0e --- /dev/null +++ b/lib/puppet/network/http/mongrel/xmlrpc.rb @@ -0,0 +1,4 @@ +class Puppet::Network::HTTP::MongrelXMLRPC + def initialize(args = {}) + end +end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb new file mode 100644 index 000000000..c4b2ed3c6 --- /dev/null +++ b/lib/puppet/network/http/webrick.rb @@ -0,0 +1,51 @@ +require 'webrick' +require 'webrick/https' +require 'puppet/network/http/webrick/rest' +require 'puppet/network/http/webrick/xmlrpc' + +class Puppet::Network::HTTP::WEBrick + def initialize(args = {}) + @listening = false + end + + def listen(args = {}) + raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].empty? + raise ArgumentError, ":protocols must be specified." if !args[:protocols] or args[:protocols].empty? + raise ArgumentError, ":address must be specified." unless args[:address] + raise ArgumentError, ":port must be specified." unless args[:port] + raise "WEBrick server is already listening" if listening? + + @protocols = args[:protocols] + @handlers = args[:handlers] + @server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port]) + setup_handlers + @server.start + @listening = true + end + + def unlisten + raise "WEBrick server is not listening" unless listening? + @server.shutdown + @listening = false + end + + def listening? + @listening + end + + private + + def setup_handlers + @protocols.each do |protocol| + @handlers.each do |handler| + class_for_protocol(protocol).new(:server => @server, :handler => handler) + end + end + end + + def class_for_protocol(protocol) + return Puppet::Network::HTTP::WEBrickREST if protocol.to_sym == :rest + return Puppet::Network::HTTP::WEBrickXMLRPC if protocol.to_sym == :xmlrpc + raise ArgumentError, "Unknown protocol [#{protocol}]." + end +end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb new file mode 100644 index 000000000..dd0c84d61 --- /dev/null +++ b/lib/puppet/network/http/webrick/rest.rb @@ -0,0 +1,41 @@ +require 'puppet/network/http/handler' + +class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler + + # WEBrick uses a service() method to respond to requests. Simply delegate to the handler response() method. + def service(request, response) + process(request, response) + end + + private + + def register_handler + @server.mount('/' + @handler.to_s, self) + @server.mount('/' + @handler.to_s + 's', self) + end + + def http_method(request) + request.request_method + end + + def path(request) + '/' + request.path.split('/')[1] + end + + def request_key(request) + request.path.split('/')[2] + end + + def body(request) + request.body + end + + def params(request) + request.query + end + + def encode_result(request, response, result, status = 200) + response.status = status + response.body = result + end +end
\ No newline at end of file diff --git a/lib/puppet/network/http/webrick/xmlrpc.rb b/lib/puppet/network/http/webrick/xmlrpc.rb new file mode 100644 index 000000000..793708f8a --- /dev/null +++ b/lib/puppet/network/http/webrick/xmlrpc.rb @@ -0,0 +1,4 @@ +class Puppet::Network::HTTP::WEBrickXMLRPC + def initialize(args = {}) + end +end diff --git a/lib/puppet/network/http_server/mongrel.rb b/lib/puppet/network/http_server/mongrel.rb index ba4b048b3..ce2196eca 100644 --- a/lib/puppet/network/http_server/mongrel.rb +++ b/lib/puppet/network/http_server/mongrel.rb @@ -54,6 +54,10 @@ module Puppet::Network attr_reader :xmlrpc_server def initialize(handlers) + if Puppet[:debug] + $mongrel_debug_client = true + Puppet.debug 'Mongrel client debugging enabled. [$mongrel_debug_client = true].' + end # 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 diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb index 84a71a6b4..50e3bd686 100644 --- a/lib/puppet/network/server.rb +++ b/lib/puppet/network/server.rb @@ -1,66 +1,65 @@ class Puppet::Network::Server - attr_reader :server_type + attr_reader :server_type, :protocols, :address, :port - # which HTTP server subclass actually handles web requests of a certain type? (e.g., :rest => RESTServer) - def self.server_class_by_name(name) - klass = (name.to_s + 'Server').to_sym - const_get klass - end - - # we will actually return an instance of the Server subclass which handles the HTTP web server, instead of - # an instance of this generic Server class. A tiny bit of sleight-of-hand is necessary to make this happen. - def self.new(args = {}) - server_type = Puppet[:servertype] or raise "No servertype configuration found." - obj = self.server_class_by_name(server_type).allocate - obj.send :initialize, args.merge(:server_type => server_type) - obj - end - - def initialize(args = {}) - @routes = {} - @listening = false - @server_type = args[:server_type] - self.register(args[:handlers]) if args[:handlers] - end + def initialize(args = {}) + @server_type = Puppet[:servertype] or raise "No servertype configuration found." # e.g., WEBrick, Mongrel, etc. + http_server_class || raise(ArgumentError, "Could not determine HTTP Server class for server type [#{@server_type}]") + @address = args[:address] || Puppet[:bindaddress] || + raise(ArgumentError, "Must specify :address or configure Puppet :bindaddress.") + @port = args[:port] || Puppet[:masterport] || + raise(ArgumentError, "Must specify :port or configure Puppet :masterport") + @protocols = [] + @listening = false + @routes = {} + self.register(args[:handlers]) if args[:handlers] + end - def register(*indirections) - raise ArgumentError, "indirection names are required" if indirections.empty? - indirections.flatten.each { |i| @routes[i.to_sym] = true } - end + def register(*indirections) + raise ArgumentError, "Indirection names are required." if indirections.empty? + indirections.flatten.each { |i| @routes[i.to_sym] = true } + end - def unregister(*indirections) - indirections = @routes.keys if indirections.empty? - indirections.flatten.each do |i| - raise(ArgumentError, "indirection [%s] is not known" % i) unless @routes[i.to_sym] - @routes.delete(i.to_sym) + def unregister(*indirections) + raise "Cannot unregister indirections while server is listening." if listening? + indirections = @routes.keys if indirections.empty? + + indirections.flatten.each do |i| + raise(ArgumentError, "Indirection [%s] is unknown." % i) unless @routes[i.to_sym] + end + + indirections.flatten.each do |i| + @routes.delete(i.to_sym) + end end - end - def listening? - @listening - end + def listening? + @listening + end - def listen - raise "Cannot listen -- already listening" if listening? - start_web_server - @listening = true - end + def listen + raise "Cannot listen -- already listening." if listening? + http_server.listen(@routes.dup) + @listening = true + end - def unlisten - raise "Cannot unlisten -- not currently listening" unless listening? - stop_web_server - @listening = false - end + def unlisten + raise "Cannot unlisten -- not currently listening." unless listening? + http_server.unlisten + @listening = false + end + + def http_server_class + http_server_class_by_type(@server_type) + end private - def start_web_server - raise NotImplementedError, "this method needs to be implemented by the actual web server (sub)class" - end - - def stop_web_server - raise NotImplementedError, "this method needs to be implemented by the actual web server (sub)class" - end + def http_server + @http_server ||= http_server_class.new + end + + def http_server_class_by_type(kind) + Puppet::Network::HTTP.server_class_by_type(kind) + end end - diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index 9758c895c..2d87b6515 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -3,6 +3,7 @@ require 'puppet/indirector' # A simplistic class for managing the node information itself. class Puppet::Node require 'puppet/node/facts' + require 'puppet/node/environment' # Set up indirection, so that nodes can be looked for in # the node sources. @@ -19,19 +20,23 @@ class Puppet::Node attr_accessor :name, :classes, :parameters, :source, :ipaddress, :names attr_reader :time - attr_writer :environment + + # Set the environment, making sure that it's valid. + def environment=(value) + raise(ArgumentError, "Invalid environment %s" % value) unless Puppet::Node::Environment.valid?(value) + @environment = value + end # Do not return environments that are the empty string, and use # explicitly set environments, then facts, then a central env # value. def environment - unless @environment and @environment != "" - if env = parameters["environment"] and env != "" - @environment = env - elsif env = Puppet[:environment] and env != "" + unless @environment + if env = parameters["environment"] + raise(ArgumentError, "Invalid environment %s from parameters" % env) unless Puppet::Node::Environment.valid?(env) @environment = env else - @environment = nil + @environment = Puppet::Node::Environment.new.name.to_s end end @environment @@ -66,7 +71,7 @@ class Puppet::Node @parameters = options[:parameters] || {} - @environment = options[:environment] + self.environment = options[:environment] if options[:environment] @time = Time.now end diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb index e49090d70..804f357d1 100644 --- a/lib/puppet/node/configuration.rb +++ b/lib/puppet/node/configuration.rb @@ -38,6 +38,10 @@ class Puppet::Node::Configuration < Puppet::PGraph # relationship graph. attr_accessor :is_relationship_graph + # Whether this configuration was retrieved from the cache, which affects + # whether it is written back out again. + attr_accessor :from_cache + # Add classes to our class list. def add_class(*classes) classes.each do |klass| @@ -66,6 +70,16 @@ class Puppet::Node::Configuration < Puppet::PGraph end end + # Create an alias for a resource. + def alias(resource, name) + resource.ref =~ /^(.+)\[/ + + newref = "%s[%s]" % [$1 || resource.class.name, name] + raise(ArgumentError, "Cannot alias %s to %s; resource %s already exists" % [resource.ref, name, newref]) if @resource_table[newref] + @resource_table[newref] = resource + @aliases[resource.ref] << newref + end + # Apply our configuration to the local host. Valid options # are: # :tags - set the tags that restrict what resources run @@ -274,6 +288,8 @@ class Puppet::Node::Configuration < Puppet::PGraph @applying = false @relationship_graph = nil + @aliases = Hash.new { |hash, key| hash[key] = [] } + if block_given? yield(self) finalize() @@ -331,7 +347,9 @@ class Puppet::Node::Configuration < Puppet::PGraph # references to the resource instances. def remove_resource(*resources) resources.each do |resource| - @resource_table.delete(resource.ref) if @resource_table.include?(resource.ref) + @resource_table.delete(resource.ref) + @aliases[resource.ref].each { |res_alias| @resource_table.delete(res_alias) } + @aliases[resource.ref].clear remove_vertex!(resource) if vertex?(resource) @relationship_graph.remove_vertex!(resource) if @relationship_graph and @relationship_graph.vertex?(resource) resource.remove diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb new file mode 100644 index 000000000..2a314803f --- /dev/null +++ b/lib/puppet/node/environment.rb @@ -0,0 +1,46 @@ +# Model the environment that a node can operate in. This class just +# provides a simple wrapper for the functionality around environments. +class Puppet::Node::Environment + # Return the list of valid environments. Just looks them up in + # the settings. + def self.valid + Puppet.settings.value(:environments).split(",").collect { |e| e.to_sym } + end + + # Is the provided environment valid? + def self.valid?(name) + return false if name.to_s == "" + valid.include?(name.to_sym) + end + + @seen = {} + + # Return an existing environment instance, or create a new one, + # validating the environment name. + def self.new(name = nil) + name ||= Puppet.settings.value(:environment) + + raise ArgumentError, "Environment name must be specified" unless name + + raise(ArgumentError, "'%s' is not a valid environment" % name) unless valid?(name) + + symbol = name.to_sym + + return @seen[symbol] if @seen[symbol] + + obj = self.allocate + obj.send :initialize, symbol + @seen[symbol] = obj + end + + attr_reader :name + + # Return an environment-specific setting. + def [](param) + Puppet.settings.value(param, self.name) + end + + def initialize(name) + @name = name + end +end diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index d15c1f622..fae0587e1 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -1,6 +1,7 @@ require 'puppet/util/methodhelper' require 'puppet/util/log_paths' require 'puppet/util/logging' +require 'puppet/util/docs' class Puppet::Parameter include Puppet::Util @@ -10,6 +11,7 @@ class Puppet::Parameter include Puppet::Util::MethodHelper class << self include Puppet::Util + include Puppet::Util::Docs attr_reader :validater, :munger, :name, :default, :required_features attr_accessor :metaparam diff --git a/lib/puppet/parser/ast/astarray.rb b/lib/puppet/parser/ast/astarray.rb index 9a2dc286c..5f1e838d0 100644 --- a/lib/puppet/parser/ast/astarray.rb +++ b/lib/puppet/parser/ast/astarray.rb @@ -49,6 +49,7 @@ class Puppet::Parser::AST end end } + rets = [settors, others].flatten.collect { |child| child.safeevaluate(:scope => scope) } diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index 9c039bb49..d4904bebf 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -26,7 +26,7 @@ class Puppet::Parser::AST # Verify that we haven't already been evaluated. This is # what provides the singleton aspect. if existing_scope = scope.compile.class_scope(self) - Puppet.debug "%s class already evaluated" % @type + Puppet.debug "Class '%s' already evaluated; not evaluating again" % (classname == "" ? "main" : classname) return nil end diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb index 3eb37dfa2..e3c91bccb 100644 --- a/lib/puppet/parser/collector.rb +++ b/lib/puppet/parser/collector.rb @@ -23,7 +23,8 @@ class Puppet::Parser::Collector host = Puppet::Rails::Host.find_by_name(@scope.host) args = {:include => {:param_values => :param_name}} - args[:conditions] = "(exported = 't' AND restype = '%s')" % [@type] + args[:conditions] = "(exported = %s AND restype = '%s')" % + [ActiveRecord::Base.connection.quote(true), @type] if @equery args[:conditions] += " AND (%s)" % [@equery] end diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index 992b165e5..93ba180a7 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -14,7 +14,7 @@ require 'puppet/util/errors' class Puppet::Parser::Compile include Puppet::Util include Puppet::Util::Errors - attr_reader :topscope, :parser, :node, :facts, :collections, :configuration + attr_reader :parser, :node, :facts, :collections, :configuration, :node_scope # Add a collection to the global list. def add_collection(coll) @@ -83,12 +83,12 @@ class Puppet::Parser::Compile return @configuration end - # FIXME There are no tests for this. + # LAK:FIXME There are no tests for this. def delete_collection(coll) @collections.delete(coll) if @collections.include?(coll) end - # FIXME There are no tests for this. + # LAK:FIXME There are no tests for this. def delete_resource(resource) @resource_table.delete(resource.ref) if @resource_table.include?(resource.ref) end @@ -107,14 +107,14 @@ class Puppet::Parser::Compile # Evaluate all of the classes specified by the node. def evaluate_node_classes - evaluate_classes(@node.classes, @topscope) + evaluate_classes(@node.classes, topscope) end # Evaluate each specified class in turn. If there are any classes we can't # find, just tag the configuration and move on. This method really just # creates resource objects that point back to the classes, and then the # resources are themselves evaluated later in the process. - def evaluate_classes(classes, scope) + def evaluate_classes(classes, scope, lazy_evaluate = true) unless scope.source raise Puppet::DevError, "No source for scope passed to evaluate_classes" end @@ -126,6 +126,10 @@ class Puppet::Parser::Compile # of resources. resource = Puppet::Parser::Resource.new(:type => "class", :title => klass.classname, :scope => scope, :source => scope.source) store_resource(scope, resource) + + # If they've disabled lazy evaluation (which the :include function does), + # then evaluate our resource immediately. + resource.evaluate unless lazy_evaluate @configuration.tag(klass.classname) found << name else @@ -138,9 +142,7 @@ class Puppet::Parser::Compile # Return a resource by either its ref or its type and title. def findresource(string, name = nil) - if name - string = "%s[%s]" % [string.capitalize, name] - end + string = "%s[%s]" % [string.capitalize, name] if name @resource_table[string] end @@ -169,7 +171,7 @@ class Puppet::Parser::Compile # using the top scope. Adds an edge between the scope and # its parent to the graph. def newscope(parent, options = {}) - parent ||= @topscope + parent ||= topscope options[:compile] = self options[:parser] ||= self.parser scope = Puppet::Parser::Scope.new(options) @@ -225,6 +227,12 @@ class Puppet::Parser::Compile @configuration.add_edge!(scope.resource, resource) end + # The top scope is usually the top-level scope, but if we're using AST nodes, + # then it is instead the node's scope. + def topscope + node_scope || @topscope + end + private # If ast nodes are enabled, then see if we can find and evaluate one. @@ -237,10 +245,7 @@ class Puppet::Parser::Compile break if astnode = @parser.nodes[name.to_s.downcase] end - unless astnode - astnode = @parser.nodes["default"] - end - unless astnode + unless (astnode ||= @parser.nodes["default"]) raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ") end @@ -249,6 +254,12 @@ class Puppet::Parser::Compile resource = Puppet::Parser::Resource.new(:type => "node", :title => astnode.classname, :scope => topscope, :source => topscope.source) store_resource(topscope, resource) @configuration.tag(astnode.classname) + + resource.evaluate + + # Now set the node scope appropriately, so that :topscope can + # behave differently. + @node_scope = class_scope(astnode) end # Evaluate our collections and return true if anything returned an object. @@ -258,10 +269,11 @@ class Puppet::Parser::Compile found_something = false exceptwrap do - @collections.each do |collection| - if collection.evaluate - found_something = true - end + # We have to iterate over a dup of the array because + # collections can delete themselves from the list, which + # changes its length and causes some collections to get missed. + @collections.dup.each do |collection| + found_something = true if collection.evaluate end end @@ -314,6 +326,8 @@ class Puppet::Parser::Compile @configuration.add_vertex!(@main_resource) @resource_table["Class[main]"] = @main_resource + + @main_resource.evaluate end # Make sure the entire configuration is evaluated. @@ -404,7 +418,6 @@ class Puppet::Parser::Compile # A graph for maintaining scope relationships. @scope_graph = GRATR::Digraph.new - @scope_graph.add_vertex!(@topscope) # For maintaining the relationship between scopes and their resources. @configuration = Puppet::Node::Configuration.new(@node.name) diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 1d07122d4..a0e8da86f 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -109,7 +109,9 @@ module Functions # Include the specified classes newfunction(:include, :doc => "Evaluate one or more classes.") do |vals| vals = [vals] unless vals.is_a?(Array) - klasses = compile.evaluate_classes(vals, self) + + # The 'false' disables lazy evaluation. + klasses = compile.evaluate_classes(vals, self, false) missing = vals.find_all do |klass| ! klasses.include?(klass) diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index a8da6b054..2f7fff13f 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -15,6 +15,14 @@ class Puppet::Parser::Resource attr_writer :tags + # Determine whether the provided parameter name is a relationship parameter. + def self.relationship_parameter?(name) + unless defined?(@relationship_names) + @relationship_names = Puppet::Type.relationship_params.collect { |p| p.name } + end + @relationship_names.include?(name) + end + # Proxy a few methods to our @ref object. [:builtin?, :type, :title].each do |method| define_method(method) do @@ -337,13 +345,19 @@ class Puppet::Parser::Resource # from any parent scope, and there's currently no way to turn that off. def add_metaparams Puppet::Type.eachmetaparam do |name| - # Skip metaparams that we already have defined. - next if @params[name] - if val = scope.lookupvar(name.to_s, false) - unless val == :undefined - set_parameter(name, val) - end - end + # Skip metaparams that we already have defined, unless they're relationship metaparams. + # LAK:NOTE Relationship metaparams get treated specially -- we stack them, instead of + # overriding. + next if @params[name] and not self.class.relationship_parameter?(name) + + # Skip metaparams for which we get no value. + next unless val = scope.lookupvar(name.to_s, false) and val != :undefined + + # The default case: just set the value + return set_parameter(name, val) unless @params[name] + + # For relationship params, though, join the values (a la #446). + @params[name].value = [@params[name].value, val].flatten end end @@ -364,21 +378,10 @@ class Puppet::Parser::Resource def override_parameter(param) # This can happen if the override is defining a new parameter, rather # than replacing an existing one. - unless current = @params[param.name] - @params[param.name] = param - return - end + (@params[param.name] = param and return) unless current = @params[param.name] - # The parameter is already set. See if they're allowed to override it. - if param.source.child_of?(current.source) - if param.add - # Merge with previous value. - param.value = [ current.value, param.value ].flatten - end - - # Replace it, keeping all of its info. - @params[param.name] = param - else + # The parameter is already set. Fail if they're not allowed to override it. + unless param.source.child_of?(current.source) if Puppet[:trace] puts caller end @@ -396,6 +399,15 @@ class Puppet::Parser::Resource msg += "; cannot redefine" raise Puppet::ParseError.new(msg, param.line, param.file) end + + # If we've gotten this far, we're allowed to override. + + # Merge with previous value, if the parameter was generated with the +> syntax. + # It's important that we use the new param instance here, not the old one, + # so that the source is registered correctly for later overrides. + param.value = [current.value, param.value].flatten if param.add + + @params[param.name] = param end # Verify that all passed parameters are valid. This throws an error if diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index 53a2c5605..26c1254e3 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -284,16 +284,16 @@ class Puppet::Provider end end - def self.to_s - unless defined? @str - if self.resource_type - @str = "%s provider %s" % [resource_type.name, self.name] - else - @str = "unattached provider %s" % [self.name] - end - end - @str - end +# def self.to_s +# unless defined? @str +# if self.resource_type +# @str = "%s provider %s" % [resource_type.name, self.name] +# else +# @str = "unattached provider %s" % [self.name] +# end +# end +# @str +# end dochook(:defaults) do if @defaults.length > 0 diff --git a/lib/puppet/provider/interface/redhat.rb b/lib/puppet/provider/interface/redhat.rb index 2f9c36903..aa217620e 100644 --- a/lib/puppet/provider/interface/redhat.rb +++ b/lib/puppet/provider/interface/redhat.rb @@ -2,29 +2,44 @@ require 'puppet/provider/parsedfile' require 'erb' Puppet::Type.type(:interface).provide(:redhat) do - @interface_dir = "/etc/sysconfig/network-scripts" - confine :exists => @interface_dir + desc "Manage network interfaces on Red Hat operating systems. This provider + parses and generates configuration files in ``/etc/sysconfig/network-scripts``." + + INTERFACE_DIR = "/etc/sysconfig/network-scripts" + confine :exists => INTERFACE_DIR defaultfor :operatingsystem => [:fedora, :centos, :redhat] # Create the setter/gettor methods to match the model. mk_resource_methods - @alias_template = ERB.new <<-ALIAS + @templates = {} + + # Register a template. + def self.register_template(name, string) + @templates[name] = ERB.new(string) + end + + # Retrieve a template by name. + def self.template(name) + @templates[name] + end + + register_template :alias, <<-ALIAS DEVICE=<%= self.device %> ONBOOT=<%= self.on_boot %> -BOOTPROTO=<%= self.bootproto %> +BOOTPROTO=none IPADDR=<%= self.name %> NETMASK=<%= self.netmask %> BROADCAST= ALIAS - @loopback_template = ERB.new <<-LOOPBACKDUMMY + register_template :normal, <<-LOOPBACKDUMMY DEVICE=<%= self.device %> ONBOOT=<%= self.on_boot %> BOOTPROTO=static IPADDR=<%= self.name %> -NETMASK=255.255.255.255 +NETMASK=<%= self.netmask %> BROADCAST= LOOPBACKDUMMY @@ -34,7 +49,6 @@ LOOPBACKDUMMY # maximum number of aliases per interface @max_aliases_per_iface = 10 - @@dummies = [] @@aliases = Hash.new { |hash, key| hash[key] = [] } @@ -42,18 +56,14 @@ LOOPBACKDUMMY # use prior to needing to call self.next_dummy later on. def self.instances # parse all of the config files at once - Dir.glob("%s/ifcfg-*" % @interface_dir).collect do |file| - + Dir.glob("%s/ifcfg-*" % INTERFACE_DIR).collect do |file| record = parse(file) # store the existing dummy interfaces - if record[:interface_type] == :dummy - @@dummies << record[:ifnum] unless @@dummies.include?(record[:ifnum]) - end + @@dummies << record[:ifnum] if (record[:interface_type] == :dummy and ! @@dummies.include?(record[:ifnum])) + + @@aliases[record[:interface]] << record[:ifnum] if record[:interface_type] == :alias - if record[:interface_type] == :alias - @@aliases[record[:interface]] << record[:ifnum] - end new(record) end end @@ -85,68 +95,16 @@ LOOPBACKDUMMY # Parse the existing file. def self.parse(file) + instance = new() + return instance unless FileTest.exist?(file) - opts = {} - return opts unless FileTest.exists?(file) - - File.open(file) do |f| - f.readlines.each do |line| - if line =~ /^(\w+)=(.+)$/ - opts[$1.downcase.intern] = $2 - end - end - end - - # figure out the "real" device information - case opts[:device] - when /:/: - if opts[:device].include?(":") - opts[:interface], opts[:ifnum] = opts[:device].split(":") - end - - opts[:interface_type] = :alias - when /^dummy/: - opts[:interface_type] = :loopback - opts[:interface] = "dummy" - - # take the number of the dummy interface, as this is used - # when working out whether to call next_dummy when dynamically - # creating these - opts[:ifnum] = opts[:device].sub("dummy",'') - - @@dummies << opts[:ifnum].to_s unless @@dummies.include?(opts[:ifnum].to_s) - else - opts[:interface_type] = :normal - opts[:interface] = opts[:device] - end - - # translate whether we come up on boot to true/false - case opts[:onboot].downcase - when "yes": - opts[:onboot] = :true - when "no": - opts[:onboot] = :false - else - # this case should never happen, but just in case - opts[:onboot] = false - end - - - # Remove any attributes we don't want. These would be - # pretty easy to support. - [:bootproto, :broadcast, :netmask, :device].each do |opt| - if opts.include?(opt) - opts.delete(opt) + File.readlines(file).each do |line| + if line =~ /^(\w+)=(.+)$/ + instance.send($1.downcase + "=", $2) end end - if opts.include?(:ipaddr) - opts[:name] = opts[:ipaddr] - opts.delete(:ipaddr) - end - - return opts - + return instance end # Prefetch our interface list, yo. @@ -159,7 +117,7 @@ LOOPBACKDUMMY end def create - @resource.class.validproperties.each do |property| + self.class.resource_type.validproperties.each do |property| if value = @resource.should(property) @property_hash[property] = value end @@ -170,11 +128,11 @@ LOOPBACKDUMMY end def destroy - File.unlink(@resource[:target]) + File.unlink(file_path) end def exists? - FileTest.exists?(@resource[:target]) + FileTest.exist?(file_path) end # generate the content for the interface file, so this is dependent @@ -182,23 +140,49 @@ LOOPBACKDUMMY # address (also dummy) on linux. For linux it's quite involved, and we # will use an ERB template def generate - # choose which template to use for the interface file, based on - # the interface type - case @resource.should(:interface_type) - when :loopback - return @loopback_template.result(binding) - when :alias - return @alias_template.result(binding) - end + itype = self.interface_type == :alias ? :alias : :normal + self.class.template(itype).result(binding) end # Where should the file be written out? - # This defaults to @interface_dir/ifcfg-<namevar>, but can have a + # This defaults to INTERFACE_DIR/ifcfg-<namevar>, but can have a # more symbolic name by setting interface_desc in the type. def file_path - @resource[:interface_desc] ||= @resource[:name] - return File.join(@interface_dir, "ifcfg-" + @resource[:interface_desc]) + if resource and val = resource[:interface_desc] + desc = val + else + desc = self.name + end + + self.fail("Could not get name for interface") unless desc + + if self.interface_type == :alias + return File.join(INTERFACE_DIR, "ifcfg-" + self.interface + ":" + desc) + else + return File.join(INTERFACE_DIR, "ifcfg-" + desc) + end + end + + # Use the device value to figure out all kinds of nifty things. + def device=(value) + case value + when /:/: + @property_hash[:interface], @property_hash[:ifnum] = value.split(":") + @property_hash[:interface_type] = :alias + when /^dummy/: + @property_hash[:interface_type] = :loopback + @property_hash[:interface] = "dummy" + # take the number of the dummy interface, as this is used + # when working out whether to call next_dummy when dynamically + # creating these + @property_hash[:ifnum] = value.sub("dummy",'') + + @@dummies << @property_hash[:ifnum].to_s unless @@dummies.include?(@property_hash[:ifnum].to_s) + else + @property_hash[:interface_type] = :normal + @property_hash[:interface] = value + end end # create the device name, so this based on the IP, and interface + type @@ -213,6 +197,11 @@ LOOPBACKDUMMY end end + # Set the name to our ip address. + def ipaddr=(value) + @property_hash[:name] = value + end + # whether the device is to be brought up on boot or not. converts # the true / false of the type, into yes / no values respectively # writing out the ifcfg-* files @@ -227,10 +216,21 @@ LOOPBACKDUMMY end end + # Mark whether the interface should be started on boot. + def on_boot=(value) + # translate whether we come up on boot to true/false + case value.downcase + when "yes": + @property_hash[:onboot] = :true + else + @property_hash[:onboot] = :false + end + end + # Write the new file out. def flush # Don't flush to disk if we're removing the config. - return if @resource.should(:ensure) == :absent + return if self.ensure == :absent @property_hash.each do |name, val| if val == :absent @@ -238,13 +238,13 @@ LOOPBACKDUMMY end end - File.open(@resource[:target], "w") do |f| + File.open(file_path, "w") do |f| f.puts generate() end end def prefetch - @property_hash = self.class.parse(@resource[:target]) + @property_hash = self.class.parse(file_path) end end diff --git a/lib/puppet/provider/interface/sunos.rb b/lib/puppet/provider/interface/sunos.rb index 6c55eae8e..eda21ca3d 100644 --- a/lib/puppet/provider/interface/sunos.rb +++ b/lib/puppet/provider/interface/sunos.rb @@ -1,88 +1,22 @@ require 'puppet/provider/parsedfile' require 'erb' -Puppet::Type.type(:interface).provide(:sunos, - :default_target => "/etc/hostname.lo0", - :parent => Puppet::Provider::ParsedFile, - :filetype => :flat -) do - +Puppet::Type.type(:interface).provide(:sunos) do confine :kernel => "SunOS" - # Two types of lines: - # the first line does not start with 'addif' - # the rest do - record_line :sunos, :fields => %w{interface_type name ifopts onboot}, :rts => true, :absent => "", :block_eval => :instance do - # Parse our interface line - def process(line) - details = {:ensure => :present} - - values = line.split(/\s+/) - - # Are we the primary interface? - if values[0] == "addif" - details[:interface_type] = :alias - values.shift - else - details[:interface_type] = :normal - end - - # Should the interface be up by default? - if values[-1] == "up" - details[:onboot] = :true - values.pop - else - details[:onboot] = :false - end - - # Set the interface name. - details[:name] = values.shift - - # Handle any interface options - unless values.empty? - details[:ifopts] = values.join(" ") - end - - return details - end - - # Turn our record into a line. - def to_line(details) - ret = [] - if details[:interface_type] != :normal - ret << "addif" - end - ret << details[:name] - - if details[:ifopts] and details[:ifopts] != :absent - if details[:ifopts].is_a?(Array) - ret << details[:ifopts].join(" ") - else - ret << details[:ifopts] - end - end - - if details[:onboot] and details[:onboot] != :false - ret << "up" - end - - return ret.join(" ") - end - end - - def self.header - # over-write the default puppet behaviour of adding a header - # because on further investigation this breaks the solaris - # init scripts - %{} - end + # Add accessor/getter methods for each property/parameter; these methods + # modify @property_hash. + mk_resource_methods # Get a list of interface instances. def self.instances Dir.glob("/etc/hostname.*").collect do |file| - # We really only expect one record from each file - parse(file).shift - end.collect { |record| new(record) } + device = File.basename(file).split(".").pop + + instance = new(:interface => device) + instance.parse + instance + end end def self.match(hash) @@ -103,13 +37,97 @@ Puppet::Type.type(:interface).provide(:sunos, end end + def initialize(*args) + @property_hash = {} + super + end + + def create + self.class.resource_type.validproperties.each do |property| + if value = resource.should(property) + @property_hash[property] = value + end + end + @property_hash[:name] = resource.name + + return (@resource.class.name.to_s + "_created").intern + end + + def destroy + File.unlink(file_path) + @property_hash[:ensure] = :absent + end + + def exists? + FileTest.exist?(file_path) + end + # Where should the file be written out? Can be overridden by setting # :target in the model. def file_path - unless @resource[:interface] - raise ArgumentError, "You must provide the interface name on Solaris" + self.fail("Could not determine interface") unless interface = @property_hash[:interface] || (resource and resource[:interface]) + return File.join("/etc", "hostname." + interface) + end + + def flush + return if self.ensure == :absent + File.open(file_path, "w") { |f| f.print generate() + "\n" } + end + + # Turn our record into a line. + def generate + ret = [] + if self.interface_type == :alias + ret << "addif" + end + ret << self.name + + if self.ifopts != :absent + if @property_hash[:ifopts].is_a?(Array) + ret << @property_hash[:ifopts].join(" ") + else + ret << @property_hash[:ifopts] + end + end + + if self.onboot and ! [:absent, :false].include?(self.onboot) + ret << "up" end - return File.join("/etc", "hostname." + @resource[:interface]) + + return ret.join(" ") end -end + # Parse our interface file. + def parse + (@property_hash = {:ensure => :absent} and return) unless FileTest.exist?(file_path) + + values = File.read(file_path).chomp.split(/\s+/) + + @property_hash[:ensure] = :present + #@property_hash = {:ensure => :present} + + # Are we the primary interface? + if values[0] == "addif" + @property_hash[:interface_type] = :alias + values.shift + else + @property_hash[:interface_type] = :normal + end + + # Should the interface be up by default? + if values[-1] == "up" + @property_hash[:onboot] = :true + values.pop + else + @property_hash[:onboot] = :false + end + + # Set the interface name. + @property_hash[:name] = values.shift + + # Handle any interface options + unless values.empty? + @property_hash[:ifopts] = values.join(" ") + end + end +end diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb index 6a05d9826..a63a0402c 100644 --- a/lib/puppet/provider/mount.rb +++ b/lib/puppet/provider/mount.rb @@ -14,7 +14,7 @@ module Puppet::Provider::Mount if self.options and self.options != :absent args << "-o" << self.options end - args << @resource[:name] + args << resource[:name] if respond_to?(:flush) flush @@ -24,8 +24,8 @@ module Puppet::Provider::Mount def remount info "Remounting" - if @resource[:remounts] == :true - mountcmd "-o", "remount", @resource[:name] + if resource[:remounts] == :true + mountcmd "-o", "remount", resource[:name] else unmount() mount() @@ -34,16 +34,19 @@ module Puppet::Provider::Mount # This only works when the mount point is synced to the fstab. def unmount - umount @resource[:name] + umount resource[:name] end # Is the mount currently mounted? def mounted? - platform = Facter["operatingsystem"].value - name = @resource[:name] + platform = Facter.value("operatingsystem") + name = resource[:name] mounts = mountcmd.split("\n").find do |line| - if platform == "Darwin" + case platform + when "Darwin" line =~ / on #{name} / or line =~ %r{ on /private/var/automount#{name}} + when "Solaris" + line =~ /^#{name} on / else line =~ / on #{name} / end diff --git a/lib/puppet/rails.rb b/lib/puppet/rails.rb index e9be5a547..55d03b878 100644 --- a/lib/puppet/rails.rb +++ b/lib/puppet/rails.rb @@ -45,6 +45,7 @@ module Puppet::Rails args[:username] = Puppet[:dbuser] args[:password] = Puppet[:dbpassword] args[:database] = Puppet[:dbname] + args[:args] = Puppet[:dbsocket] unless Puppet[:dbsocket] == "" else raise ArgumentError, "Invalid db adapter %s" % Puppet[:dbadapter] end diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb index dfc992820..b7ef42a7b 100644 --- a/lib/puppet/reports/store.rb +++ b/lib/puppet/reports/store.rb @@ -1,7 +1,7 @@ require 'puppet' Puppet::Reports.register_report(:store) do - Puppet.settings.use(:reporting) + Puppet.settings.use(:main, :reporting) desc "Store the yaml report on disk. Each host sends its report as a YAML dump and this just stores the file on disk, in the ``reportdir`` directory. diff --git a/lib/puppet/sslcertificates.rb b/lib/puppet/sslcertificates.rb index 45b9e7a72..1139db048 100755 --- a/lib/puppet/sslcertificates.rb +++ b/lib/puppet/sslcertificates.rb @@ -9,7 +9,7 @@ rescue LoadError end module Puppet::SSLCertificates - #def self.mkcert(type, name, ttl, issuercert, issuername, serial, publickey) + #def self.mkcert(type, name, dnsnames, ttl, issuercert, issuername, serial, publickey) def self.mkcert(hash) [:type, :name, :ttl, :issuer, :serial, :publickey].each { |param| unless hash.include?(param) @@ -39,6 +39,7 @@ module Puppet::SSLCertificates basic_constraint = nil key_usage = nil ext_key_usage = nil + subject_alt_name = [] ef = OpenSSL::X509::ExtensionFactory.new @@ -60,16 +61,17 @@ module Puppet::SSLCertificates key_usage = %w{cRLSign keyCertSign} when :server: basic_constraint = "CA:FALSE" + hash[:dnsnames].each(':') { |d| subject_alt_name << 'DNS:' + d } if hash[:dnsnames] key_usage = %w{digitalSignature keyEncipherment} - ext_key_usage = %w{serverAuth clientAuth} + ext_key_usage = %w{serverAuth clientAuth} when :ocsp: basic_constraint = "CA:FALSE" key_usage = %w{nonRepudiation digitalSignature} - ext_key_usage = %w{serverAuth OCSPSigning} + ext_key_usage = %w{serverAuth OCSPSigning} when :client: basic_constraint = "CA:FALSE" key_usage = %w{nonRepudiation digitalSignature keyEncipherment} - ext_key_usage = %w{clientAuth emailProtection} + ext_key_usage = %w{clientAuth emailProtection} ex << ef.create_extension("nsCertType", "client,email") else raise Puppet::Error, "unknown cert type '%s'" % hash[:type] @@ -80,12 +82,9 @@ module Puppet::SSLCertificates ex << ef.create_extension("basicConstraints", basic_constraint, true) ex << ef.create_extension("subjectKeyIdentifier", "hash") - if key_usage - ex << ef.create_extension("keyUsage", key_usage.join(",")) - end - if ext_key_usage - ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(",")) - end + ex << ef.create_extension("keyUsage", key_usage.join(",")) if key_usage + ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(",")) if ext_key_usage + ex << ef.create_extension("subjectAltName", subject_alt_name.join(",")) if ! subject_alt_name.empty? #if @ca_config[:cdp_location] then # ex << ef.create_extension("crlDistributionPoints", @@ -99,10 +98,7 @@ module Puppet::SSLCertificates cert.extensions = ex # for some reason this _must_ be the last extension added - if hash[:type] == :ca - ex << ef.create_extension("authorityKeyIdentifier", - "keyid:always,issuer:always") - end + ex << ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") if hash[:type] == :ca return cert end diff --git a/lib/puppet/sslcertificates/ca.rb b/lib/puppet/sslcertificates/ca.rb index 39facdd48..e1b5f2386 100644 --- a/lib/puppet/sslcertificates/ca.rb +++ b/lib/puppet/sslcertificates/ca.rb @@ -278,6 +278,7 @@ class Puppet::SSLCertificates::CA newcert = Puppet::SSLCertificates.mkcert( :type => :server, :name => csr.subject, + :dnsnames => Puppet[:certdnsnames], :ttl => ttl, :issuer => @cert, :serial => serial, diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index c8fc2f199..ef53889cf 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -311,7 +311,7 @@ class Transaction ret = eval_resource(resource) end - if Puppet[:evaltrace] + if Puppet[:evaltrace] and @configuration.host_config? resource.info "Evaluated in %0.2f seconds" % seconds end ret diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb index 281ad00d3..6a573489c 100644 --- a/lib/puppet/transportable.rb +++ b/lib/puppet/transportable.rb @@ -59,15 +59,11 @@ module Puppet tmpname = @type end trans = TransObject.new(tmpname, :component) - if defined? @parameters - @parameters.each { |param,value| - Puppet.debug "Defining %s on %s of type %s" % - [param,@name,@type] - trans[param] = value - } - else - #Puppet.debug "%s[%s] has no parameters" % [@type, @name] - end + @params.each { |param,value| + next unless Puppet::Type::Component.validattr?(param) + Puppet.debug "Defining %s on %s of type %s" % [param,@name,@type] + trans[param] = value + } Puppet::Type::Component.create(trans) end @@ -107,16 +103,7 @@ module Puppet def to_type retobj = nil if typeklass = Puppet::Type.type(self.type) - # FIXME This should really be done differently, but... - if retobj = typeklass[self.name] - self.each do |param, val| - retobj[param] = val - end - else - unless retobj = typeklass.create(self) - return nil - end - end + return typeklass.create(self) else return to_component end @@ -135,7 +122,7 @@ module Puppet class TransBucket include Enumerable - attr_accessor :name, :type, :file, :line, :classes, :keyword, :top + attr_accessor :name, :type, :file, :line, :classes, :keyword, :top, :configuration %w{delete shift include? length empty? << []}.each { |method| define_method(method) do |*args| @@ -218,11 +205,13 @@ module Puppet def to_configuration configuration = Puppet::Node::Configuration.new(Facter.value("hostname")) do |config| delver = proc do |obj| + obj.configuration = config unless container = config.resource(obj.to_ref) container = obj.to_type config.add_resource container end obj.each do |child| + child.configuration = config unless resource = config.resource(child.to_ref) next unless resource = child.to_type config.add_resource resource @@ -252,65 +241,25 @@ module Puppet end def to_type - # this container will contain the equivalent of all objects at - # this level - #container = Puppet::Component.new(:name => @name, :type => @type) - #unless defined? @name - # raise Puppet::DevError, "TransBuckets must have names" - #end unless defined? @type Puppet.debug "TransBucket '%s' has no type" % @name end - usetrans = true - if usetrans - tmpname = nil - - # Nodes have the same name and type - if self.name - tmpname = "%s[%s]" % [@type, self.name] - else - tmpname = @type - end - trans = TransObject.new(tmpname, :component) - if defined? @parameters - @parameters.each { |param,value| - Puppet.debug "Defining %s on %s of type %s" % - [param,@name,@type] - trans[param] = value - } - else - #Puppet.debug "%s[%s] has no parameters" % [@type, @name] - end - container = Puppet::Type::Component.create(trans) + # Nodes have the same name and type + if self.name + tmpname = "%s[%s]" % [@type, self.name] else - hash = { - :name => self.name, - :type => @type - } - if defined? @parameters - @parameters.each { |param,value| - Puppet.debug "Defining %s on %s of type %s" % - [param,@name,@type] - hash[param] = value - } - else - #Puppet.debug "%s[%s] has no parameters" % [@type, @name] - end - - container = Puppet::Type::Component.create(hash) + tmpname = @type end - #Puppet.info container.inspect - - # unless we successfully created the container, return an error - unless container - Puppet.warning "Got no container back" - return nil + trans = TransObject.new(tmpname, :component) + if defined? @parameters + @parameters.each { |param,value| + Puppet.debug "Defining %s on %s of type %s" % + [param,@name,@type] + trans[param] = value + } end - - # at this point, no objects at are level are still Transportable - # objects - return container + return Puppet::Type::Component.create(trans) end def param(param,value) diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index b7ff1f664..f5dd0f8dd 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -182,7 +182,7 @@ class Type # directly from it. This is the main object instantiation mechanism. if hash.is_a?(Puppet::TransObject) #self[:name] = hash[:name] - [:file, :line, :tags].each { |getter| + [:file, :line, :tags, :configuration].each { |getter| if hash.respond_to?(getter) setter = getter.to_s + "=" if val = hash.send(getter) @@ -194,10 +194,11 @@ class Type # XXX This will need to change when transobjects change to titles. @title = hash.name hash = hash.to_hash - elsif hash[:title] - # XXX This should never happen - @title = hash[:title] - hash.delete(:title) + else + if hash[:title] + @title = hash[:title] + hash.delete(:title) + end end # Before anything else, set our parent if it was included @@ -221,7 +222,7 @@ class Type if attrs.include?(namevar) attrs.delete(namevar) else - self.devfail "My namevar isn\'t a valid attribute...?" + self.devfail "My namevar isn't a valid attribute...?" end else self.devfail "I was not passed a namevar" @@ -284,6 +285,14 @@ class Type # Scheduling has to be done when the whole config is instantiated, so # that file order doesn't matter in finding them. self.schedule + + # Make sure all of our relationships are valid. Again, must be done + # when the entire configuration is instantiated. + self.class.relationship_params.collect do |klass| + if param = @parameters[klass.name] + param.validate_relationship + end + end.flatten.reject { |r| r.nil? } end # Return a cached value diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb index 4dc542a65..7aa24a302 100644 --- a/lib/puppet/type/component.rb +++ b/lib/puppet/type/component.rb @@ -93,9 +93,9 @@ Puppet::Type.newtype(:component) do end # Initialize a new component - def initialize(args) + def initialize(*args) @children = [] - super(args) + super # If the title isn't a full resource reference, assume # we're a class and make an alias for that. diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index 3154098db..17cb1667f 100755 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -348,7 +348,7 @@ Puppet::Type.newtype(:cron) do The user defaults to whomever Puppet is running as." - defaultto { ENV["USER"] || "root" } + defaultto { Etc.getpwuid(Process.uid).name || "root" } end newproperty(:target) do diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 9aab4dbd7..5bb3158c4 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -113,15 +113,16 @@ module Puppet self.fail "Command exceeded timeout" % value.inspect end - loglevel = @resource[:loglevel] - if status.exitstatus.to_s != self.should.to_s - self.fail("%s returned %s instead of %s" % - [self.resource[:command], status.exitstatus, self.should.to_s]) - end - if log = @resource[:logoutput] - if log == :true + case log + when :true log = @resource[:loglevel] + when :on_failure + if status.exitstatus.to_s != self.should.to_s + log = @resource[:loglevel] + else + log = :false + end end unless log == :false @output.split(/\n/).each { |line| @@ -130,6 +131,11 @@ module Puppet end end + if status.exitstatus.to_s != self.should.to_s + self.fail("%s returned %s instead of %s" % + [self.resource[:command], status.exitstatus, self.should.to_s]) + end + return event end end @@ -201,10 +207,11 @@ module Puppet newparam(:logoutput) do desc "Whether to log output. Defaults to logging output at the - loglevel for the ``exec`` resource. Values are **true**, *false*, - and any legal log level." + loglevel for the ``exec`` resource. Use *on_failure* to only + log the output when the command reports an error. Values are + **true**, *false*, *on_failure*, and any legal log level." - values = [:true, :false] + values = [:true, :false, :on_failure] # And all of the log levels Puppet::Util::Log.eachlevel { |level| values << level } newvalues(*values) diff --git a/lib/puppet/type/interface.rb b/lib/puppet/type/interface.rb index 1ddf68641..2f6c28ad3 100644 --- a/lib/puppet/type/interface.rb +++ b/lib/puppet/type/interface.rb @@ -43,14 +43,18 @@ Puppet::Type.newtype(:interface) do and alias. This is use to force a given number to be used" end + newproperty(:netmask) do + desc "The netmask for the interface." + end + newproperty(:ifopts) do desc "Interface options." end newparam(:target) do + include Puppet::Util::Warnings desc "The path to the file this resource creates." - defaultto { @resource.provider.file_path } + munge { |value| warnonce "Interface targets are deprecated and no longer have any function" } end end - diff --git a/lib/puppet/type/pfile/content.rb b/lib/puppet/type/pfile/content.rb index 11458ef18..6dcda0aa6 100755 --- a/lib/puppet/type/pfile/content.rb +++ b/lib/puppet/type/pfile/content.rb @@ -33,7 +33,13 @@ module Puppet end # Override this method to provide diffs if asked for. + # Also, fix #872: when content is used, and replace is true, the file + # should be insync when it exists def insync?(is) + if ! @resource.replace? and File.exists?(@resource[:path]) + return true + end + result = super if ! result and Puppet[:show_diff] and File.exists?(@resource[:path]) string_file_diff(@resource[:path], self.should) diff --git a/lib/puppet/type/pfile/group.rb b/lib/puppet/type/pfile/group.rb index 9625b6354..5f7caf342 100755 --- a/lib/puppet/type/pfile/group.rb +++ b/lib/puppet/type/pfile/group.rb @@ -6,6 +6,10 @@ module Puppet name or group ID." @event = :file_changed + validate do |group| + raise(Puppet::Error, "Invalid group name '%s'" % group.inspect) unless group and group != "" + end + def id2name(id) if id > 70000 return nil diff --git a/lib/puppet/type/pfile/owner.rb b/lib/puppet/type/pfile/owner.rb index d3fedd44e..6f9bbd6a2 100755 --- a/lib/puppet/type/pfile/owner.rb +++ b/lib/puppet/type/pfile/owner.rb @@ -6,12 +6,9 @@ module Puppet @event = :file_changed def id2name(id) - if id.is_a?(Symbol) - return id.to_s - end - if id > 70000 - return nil - end + return id.to_s if id.is_a?(Symbol) + return nil if id > Puppet[:maximum_uid].to_i + begin user = Etc.getpwuid(id) rescue TypeError @@ -19,6 +16,7 @@ module Puppet rescue ArgumentError return nil end + if user.uid == "" return nil else @@ -113,8 +111,8 @@ module Puppet # On OS X, files that are owned by -2 get returned as really # large UIDs instead of negative ones. This isn't a Ruby bug, # it's an OS X bug, since it shows up in perl, too. - if currentvalue > 120000 - self.warning "current state is silly: %s" % currentvalue + if currentvalue > Puppet[:maximum_uid].to_i + self.warning "Apparently using negative UID (%s) on a platform that does not consistently handle them" % currentvalue currentvalue = :silly end diff --git a/lib/puppet/type/pfile/target.rb b/lib/puppet/type/pfile/target.rb index b4a6481e0..a0e5dc401 100644 --- a/lib/puppet/type/pfile/target.rb +++ b/lib/puppet/type/pfile/target.rb @@ -45,6 +45,8 @@ module Puppet end end + @resource.send(:property_fix) + :link_created end end diff --git a/lib/puppet/util/docs.rb b/lib/puppet/util/docs.rb index aabf58d89..01178e5f4 100644 --- a/lib/puppet/util/docs.rb +++ b/lib/puppet/util/docs.rb @@ -67,6 +67,11 @@ module Puppet::Util::Docs str + "\n" end + attr_reader :nodoc + def nodoc? + nodoc + end + # Pad a field with spaces def pad(value, length) value.to_s + (" " * (length - value.to_s.length)) diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb index d0719ff3b..ca23aa87f 100644 --- a/lib/puppet/util/metric.rb +++ b/lib/puppet/util/metric.rb @@ -21,7 +21,7 @@ class Puppet::Util::Metric end def create(start = nil) - Puppet.settings.use(:metrics) + Puppet.settings.use(:main, :metrics) start ||= Time.now.to_i - 5 diff --git a/lib/puppet/util/posix.rb b/lib/puppet/util/posix.rb index 84fb744a9..c518a8797 100755 --- a/lib/puppet/util/posix.rb +++ b/lib/puppet/util/posix.rb @@ -53,6 +53,13 @@ module Puppet::Util::POSIX return object.send(field) end end + + # Apparently the group/passwd methods need to get reset; if we skip + # this call, then new users aren't found. + case type + when :passwd: Etc.send(:endpwent) + when :group: Etc.send(:endgrent) + end return nil end diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index 0bd288ec2..1db396dc4 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -643,13 +643,6 @@ Generated on #{Time.now}. @used = [] end - runners = sections.collect { |s| - symbolize(s) - }.find_all { |s| - ! @used.include? s - } - return if runners.empty? - bucket = to_transportable(*sections) config = bucket.to_configuration @@ -661,7 +654,8 @@ Generated on #{Time.now}. end config.clear - runners.each { |s| @used << s } + sections.each { |s| @used << s } + @used.uniq end end |