summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/defaults.rb31
-rw-r--r--lib/puppet/indirector/facts/memory.rb9
-rw-r--r--lib/puppet/indirector/indirection.rb8
-rw-r--r--lib/puppet/indirector/module_files.rb4
-rw-r--r--lib/puppet/indirector/report/processor.rb2
-rw-r--r--lib/puppet/metatype/instances.rb11
-rw-r--r--lib/puppet/metatype/metaparams.rb35
-rw-r--r--lib/puppet/network/client/master.rb171
-rwxr-xr-xlib/puppet/network/handler/report.rb2
-rwxr-xr-xlib/puppet/network/handler/runner.rb2
-rw-r--r--lib/puppet/network/http.rb13
-rw-r--r--lib/puppet/network/http/handler.rb109
-rw-r--r--lib/puppet/network/http/mongrel.rb54
-rw-r--r--lib/puppet/network/http/mongrel/rest.rb37
-rw-r--r--lib/puppet/network/http/mongrel/xmlrpc.rb4
-rw-r--r--lib/puppet/network/http/webrick.rb51
-rw-r--r--lib/puppet/network/http/webrick/rest.rb41
-rw-r--r--lib/puppet/network/http/webrick/xmlrpc.rb4
-rw-r--r--lib/puppet/network/http_server/mongrel.rb4
-rw-r--r--lib/puppet/network/server.rb105
-rw-r--r--lib/puppet/node.rb19
-rw-r--r--lib/puppet/node/configuration.rb20
-rw-r--r--lib/puppet/node/environment.rb46
-rw-r--r--lib/puppet/parameter.rb2
-rw-r--r--lib/puppet/parser/ast/astarray.rb1
-rw-r--r--lib/puppet/parser/ast/hostclass.rb2
-rw-r--r--lib/puppet/parser/collector.rb3
-rw-r--r--lib/puppet/parser/compile.rb49
-rw-r--r--lib/puppet/parser/functions.rb4
-rw-r--r--lib/puppet/parser/resource.rb54
-rw-r--r--lib/puppet/provider.rb20
-rw-r--r--lib/puppet/provider/interface/redhat.rb180
-rw-r--r--lib/puppet/provider/interface/sunos.rb178
-rw-r--r--lib/puppet/provider/mount.rb17
-rw-r--r--lib/puppet/rails.rb1
-rw-r--r--lib/puppet/reports/store.rb2
-rwxr-xr-xlib/puppet/sslcertificates.rb24
-rw-r--r--lib/puppet/sslcertificates/ca.rb1
-rw-r--r--lib/puppet/transaction.rb2
-rw-r--r--lib/puppet/transportable.rb93
-rw-r--r--lib/puppet/type.rb21
-rw-r--r--lib/puppet/type/component.rb4
-rwxr-xr-xlib/puppet/type/cron.rb2
-rwxr-xr-xlib/puppet/type/exec.rb27
-rw-r--r--lib/puppet/type/interface.rb8
-rwxr-xr-xlib/puppet/type/pfile/content.rb6
-rwxr-xr-xlib/puppet/type/pfile/group.rb4
-rwxr-xr-xlib/puppet/type/pfile/owner.rb14
-rw-r--r--lib/puppet/type/pfile/target.rb2
-rw-r--r--lib/puppet/util/docs.rb5
-rw-r--r--lib/puppet/util/metric.rb2
-rwxr-xr-xlib/puppet/util/posix.rb7
-rw-r--r--lib/puppet/util/settings.rb10
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