summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
authorMichael V. O'Brien <michael@reductivelabs.com>2007-09-25 12:00:07 -0500
committerMichael V. O'Brien <michael@reductivelabs.com>2007-09-25 12:00:07 -0500
commitff2828f5dbe68ff1cb06a3503590a3e4bd1b59e3 (patch)
tree8c8960cac1d7b3e8b48e44163062be3b3f4c201f /lib/puppet
parentf8ab62b212788a4591276c95b5f67217f7517e4e (diff)
parentffaa8ce07979f4db860950fa9be08ca37964206f (diff)
downloadpuppet-ff2828f5dbe68ff1cb06a3503590a3e4bd1b59e3.tar.gz
puppet-ff2828f5dbe68ff1cb06a3503590a3e4bd1b59e3.tar.xz
puppet-ff2828f5dbe68ff1cb06a3503590a3e4bd1b59e3.zip
Merge branch 'master' of git://reductivelabs.com/puppet
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/checksum.rb64
-rw-r--r--lib/puppet/config_stores/rest.rb2
-rw-r--r--lib/puppet/defaults.rb20
-rw-r--r--lib/puppet/dsl.rb10
-rw-r--r--lib/puppet/fact_stores/yaml.rb2
-rw-r--r--lib/puppet/indirector.rb91
-rw-r--r--lib/puppet/indirector/code.rb6
-rw-r--r--lib/puppet/indirector/code/configuration.rb171
-rw-r--r--lib/puppet/indirector/exec.rb57
-rw-r--r--lib/puppet/indirector/exec/node.rb50
-rw-r--r--lib/puppet/indirector/file.rb54
-rw-r--r--lib/puppet/indirector/file/checksum.rb33
-rw-r--r--lib/puppet/indirector/indirection.rb91
-rw-r--r--lib/puppet/indirector/ldap.rb90
-rw-r--r--lib/puppet/indirector/ldap/node.rb115
-rw-r--r--lib/puppet/indirector/memory.rb21
-rw-r--r--lib/puppet/indirector/memory/node.rb8
-rw-r--r--lib/puppet/indirector/null.rb9
-rw-r--r--lib/puppet/indirector/null/node.rb14
-rw-r--r--lib/puppet/indirector/terminus.rb117
-rw-r--r--lib/puppet/indirector/yaml.rb45
-rw-r--r--lib/puppet/indirector/yaml/facts.rb5
-rw-r--r--lib/puppet/metatype/closure.rb13
-rw-r--r--lib/puppet/metatype/container.rb8
-rw-r--r--lib/puppet/metatype/instances.rb12
-rw-r--r--lib/puppet/module.rb4
-rw-r--r--lib/puppet/network/client/ca.rb8
-rw-r--r--lib/puppet/network/client/master.rb180
-rw-r--r--lib/puppet/network/handler/ca.rb2
-rw-r--r--lib/puppet/network/handler/configuration.rb14
-rwxr-xr-xlib/puppet/network/handler/facts.rb68
-rwxr-xr-xlib/puppet/network/handler/filebucket.rb2
-rwxr-xr-xlib/puppet/network/handler/fileserver.rb15
-rw-r--r--lib/puppet/network/handler/master.rb9
-rw-r--r--lib/puppet/network/handler/node.rb242
-rwxr-xr-xlib/puppet/network/handler/report.rb4
-rwxr-xr-xlib/puppet/network/handler/resource.rb13
-rwxr-xr-xlib/puppet/network/handler/runner.rb4
-rw-r--r--lib/puppet/network/server/webrick.rb2
-rw-r--r--lib/puppet/node.rb34
-rw-r--r--lib/puppet/node/configuration.rb266
-rwxr-xr-xlib/puppet/node/facts.rb36
-rw-r--r--lib/puppet/node/searching.rb106
-rw-r--r--lib/puppet/parser/ast/component.rb224
-rw-r--r--lib/puppet/parser/interpreter.rb2
-rw-r--r--lib/puppet/pgraph.rb6
-rwxr-xr-xlib/puppet/provider/maillist/mailman.rb4
-rw-r--r--lib/puppet/provider/mount.rb2
-rw-r--r--lib/puppet/rails.rb4
-rw-r--r--lib/puppet/reference/configuration.rb2
-rw-r--r--lib/puppet/reports/rrdgraph.rb2
-rw-r--r--lib/puppet/reports/store.rb6
-rw-r--r--lib/puppet/reports/tagmail.rb2
-rw-r--r--lib/puppet/sslcertificates/ca.rb18
-rw-r--r--lib/puppet/sslcertificates/inventory.rb2
-rw-r--r--lib/puppet/sslcertificates/support.rb8
-rw-r--r--lib/puppet/transaction.rb171
-rw-r--r--lib/puppet/transportable.rb94
-rw-r--r--lib/puppet/type.rb23
-rw-r--r--lib/puppet/type/component.rb67
-rw-r--r--lib/puppet/type/pfile.rb53
-rw-r--r--lib/puppet/util/feature.rb53
-rwxr-xr-xlib/puppet/util/instance_loader.rb10
-rw-r--r--lib/puppet/util/metric.rb2
-rw-r--r--lib/puppet/util/settings.rb (renamed from lib/puppet/util/config.rb)221
-rw-r--r--lib/puppet/util/storage.rb4
66 files changed, 1876 insertions, 1221 deletions
diff --git a/lib/puppet/checksum.rb b/lib/puppet/checksum.rb
new file mode 100644
index 000000000..c607953c1
--- /dev/null
+++ b/lib/puppet/checksum.rb
@@ -0,0 +1,64 @@
+#
+# Created by Luke Kanies on 2007-9-22.
+# Copyright (c) 2007. All rights reserved.
+
+require 'puppet'
+require 'puppet/indirector'
+
+# A checksum class to model translating checksums to file paths. This
+# is the new filebucket.
+class Puppet::Checksum
+ extend Puppet::Indirector
+
+ indirects :checksum
+
+ attr_reader :algorithm, :content
+
+ def algorithm=(value)
+ unless respond_to?(value)
+ raise ArgumentError, "Checksum algorithm %s is not supported" % value
+ end
+ value = value.intern if value.is_a?(String)
+ @algorithm = value
+ # Reset the checksum so it's forced to be recalculated.
+ @checksum = nil
+ end
+
+ # Calculate (if necessary) and return the checksum
+ def checksum
+ unless @checksum
+ @checksum = send(algorithm)
+ end
+ @checksum
+ end
+
+ def initialize(content, algorithm = nil)
+ raise ArgumentError.new("You must specify the content") unless content
+
+ @content = content
+ self.algorithm = algorithm || "md5"
+
+ # Init to avoid warnings.
+ @checksum = nil
+ end
+
+ # This can't be private, else respond_to? returns false.
+ def md5
+ require 'digest/md5'
+ Digest::MD5.hexdigest(content)
+ end
+
+ # This is here so the Indirector::File terminus works correctly.
+ def name
+ checksum
+ end
+
+ def sha1
+ require 'digest/sha1'
+ Digest::SHA1.hexdigest(content)
+ end
+
+ def to_s
+ "Checksum<{%s}%s>" % [algorithm, checksum]
+ end
+end
diff --git a/lib/puppet/config_stores/rest.rb b/lib/puppet/config_stores/rest.rb
index 980968bd8..bb3d937ac 100644
--- a/lib/puppet/config_stores/rest.rb
+++ b/lib/puppet/config_stores/rest.rb
@@ -1,4 +1,4 @@
-Puppet::Util::ConfigStore.newstore(:rest) do
+Puppet::Util::SettingsStore.newstore(:rest) do
desc "Store client configurations via a REST web service."
require 'net/http'
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index 78364e786..f76ae9b84 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -267,7 +267,7 @@ module Puppet
)
# Define the config default.
- self.setdefaults(self.config[:name],
+ self.setdefaults(self.settings[:name],
:config => ["$confdir/puppet.conf",
"The configuration file for #{Puppet[:name]}."],
:pidfile => ["", "The pid file"],
@@ -494,14 +494,18 @@ module Puppet
"The server through which to send email reports."]
)
- self.setdefaults(:facts,
- :factstore => ["yaml",
- "The backend store to use for client facts."]
+ # This needs to be in main because it's used too early in the system, such that
+ # we get an infinite loop otherwise.
+ self.setdefaults(:main,
+ :facts_terminus => ["yaml",
+ "The backend store to use for client facts."],
+ :checksum_terminus => ["file",
+ "The backend store to use for storing files by checksum (i.e., filebuckets)."]
)
- self.setdefaults(:yamlfacts,
- :yamlfactdir => ["$vardir/facts",
- "The directory in which client facts are stored when the yaml fact store is used."]
+ self.setdefaults(:yaml,
+ :yamldir => ["$vardir/yaml",
+ "The directory in which YAML data is stored, usually in a subdirectory."]
)
self.setdefaults(:rails,
@@ -554,7 +558,7 @@ module Puppet
setdefaults(:parser,
:typecheck => [true, "Whether to validate types during parsing."],
:paramcheck => [true, "Whether to validate parameters during parsing."],
- :node_source => ["none", "Where to look for node configuration information.
+ :node_terminus => ["null", "Where to look for node configuration information.
The default node source, ``none``, just returns a node with its facts
filled in, which is required for normal functionality.
See the `NodeSourceReference`:trac: for more information."]
diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb
index 793578bca..3696cd9ee 100644
--- a/lib/puppet/dsl.rb
+++ b/lib/puppet/dsl.rb
@@ -67,11 +67,8 @@ module Puppet
def apply
bucket = export()
- objects = bucket.to_type
- master = Puppet::Network::Client.master.new :Master => "whatever"
- master.objects = objects
-
- master.apply
+ configuration = bucket.to_configuration
+ configuration.apply
end
def export
@@ -255,8 +252,7 @@ module Puppet
def scope
unless defined?(@scope)
@interp = Puppet::Parser::Interpreter.new :Code => ""
- # Load the class, so the node object class is available.
- require 'puppet/network/handler/node'
+ require 'puppet/node'
@node = Puppet::Node.new(Facter.value(:hostname))
@node.parameters = Facter.to_hash
@interp = Puppet::Parser::Interpreter.new :Code => ""
diff --git a/lib/puppet/fact_stores/yaml.rb b/lib/puppet/fact_stores/yaml.rb
index a4b12a2e5..b33e162ba 100644
--- a/lib/puppet/fact_stores/yaml.rb
+++ b/lib/puppet/fact_stores/yaml.rb
@@ -16,7 +16,7 @@ Puppet::Util::FactStore.newstore(:yaml) do
end
def initialize
- Puppet.config.use(:yamlfacts)
+ Puppet.settings.use(:yamlfacts)
end
# Store the facts to disk.
diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb
index 0ba538355..6ff2de1b4 100644
--- a/lib/puppet/indirector.rb
+++ b/lib/puppet/indirector.rb
@@ -1,76 +1,51 @@
# Manage indirections to termini. They are organized in terms of indirections -
# - e.g., configuration, node, file, certificate -- and each indirection has one
-# or more terminus types defined. The indirection must have its preferred terminus
-# configured via a 'default' in the form of '<indirection>_terminus'; e.g.,
-# 'node_terminus = ldap'.
+# or more terminus types defined. The indirection is configured via the
+# +indirects+ method, which will be called by the class extending itself
+# with this module.
module Puppet::Indirector
- # This manages reading in all of our files for us and then retrieving
- # loaded instances. We still have to define the 'newX' method, but this
- # does all of the rest -- loading, storing, and retrieving by name.
- require 'puppet/util/instance_loader'
- include Puppet::Util::InstanceLoader
+ # LAK:FIXME We need to figure out how to handle documentation for the
+ # different indirection types.
- # Define a new indirection terminus. This method is used by the individual
- # termini in their separate files. Again, the autoloader takes care of
- # actually loading these files.
- def register_terminus(name, options = {}, &block)
- genclass(name, :hash => instance_hash(indirection.name), :attributes => options, :block => block)
- end
-
- # Retrieve a terminus class by indirection and name.
- def terminus(name)
- loaded_instance(name)
- end
+ require 'puppet/indirector/indirection'
+ require 'puppet/indirector/terminus'
# Declare that the including class indirects its methods to
# this terminus. The terminus name must be the name of a Puppet
# default, not the value -- if it's the value, then it gets
# evaluated at parse time, which is before the user has had a chance
# to override it.
- def indirects(indirection, options)
- @indirection = indirection
- @indirect_terminus = options[:to]
-
- # Set up autoloading of the appropriate termini.
- autoload "puppet/indirector/%s" % indirection
+ def indirects(indirection)
+ raise(ArgumentError, "Already handling indirection for %s; cannot also handle %s" % [@indirection.name, indirection]) if defined?(@indirection) and @indirection
+ # populate this class with the various new methods
+ extend ClassMethods
+ include InstanceMethods
+
+ # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node)
+ # & hook the instantiated Terminus into this class (Node: @indirection = terminus)
+ @indirection = Puppet::Indirector::Indirection.new(self, indirection)
end
- # Define methods for each of the HTTP methods. These just point to the
- # termini, with consistent error-handling. Each method is called with
- # the first argument being the indirection type and the rest of the
- # arguments passed directly on to the indirection terminus. There is
- # currently no attempt to standardize around what the rest of the arguments
- # should allow or include or whatever.
- # There is also no attempt to pre-validate that a given indirection supports
- # the method in question. We should probably require that indirections
- # declare supported methods, and then verify that termini implement all of
- # those methods.
- [:get, :post, :put, :delete].each do |method_name|
- define_method(method_name) do |*args|
- begin
- terminus.send(method_name, *args)
- rescue NoMethodError
- raise ArgumentError, "Indirection category %s does not respond to REST method %s" % [indirection, method_name]
- end
- end
- end
+ module ClassMethods
+ attr_reader :indirection
+
+ def find(*args)
+ indirection.find(*args)
+ end
- private
+ def destroy(*args)
+ indirection.destroy(*args)
+ end
- # Create a new terminus instance.
- def make_terminus(indirection)
- # Load our terminus class.
- unless klass = self.class.terminus(indirection, indirection.default)
- raise ArgumentError, "Could not find terminus %s for indirection %s" % [indirection.default, indirection]
- end
- return klass.new
+ def search(*args)
+ indirection.search(*args)
+ end
end
- # Return the singleton terminus for this indirection.
- def terminus
- unless terminus = @termini[indirection.name]
- terminus = @termini[indirection.name] = make_terminus(indirection)
- end
- terminus
+ module InstanceMethods
+ # these become instance methods
+ def save(*args)
+ self.class.indirection.save(self, *args)
+ end
end
end
diff --git a/lib/puppet/indirector/code.rb b/lib/puppet/indirector/code.rb
new file mode 100644
index 000000000..0c0ee146b
--- /dev/null
+++ b/lib/puppet/indirector/code.rb
@@ -0,0 +1,6 @@
+require 'puppet/indirector/terminus'
+
+# Do nothing, requiring that the back-end terminus do all
+# of the work.
+class Puppet::Indirector::Code < Puppet::Indirector::Terminus
+end
diff --git a/lib/puppet/indirector/code/configuration.rb b/lib/puppet/indirector/code/configuration.rb
new file mode 100644
index 000000000..6d0317204
--- /dev/null
+++ b/lib/puppet/indirector/code/configuration.rb
@@ -0,0 +1,171 @@
+require 'puppet/node'
+require 'puppet/node/configuration'
+require 'puppet/indirector/code'
+require 'puppet/parser/interpreter'
+require 'yaml'
+
+class Puppet::Indirector::Code::Configuration < Puppet::Indirector::Code
+ desc "Puppet's configuration compilation interface. Passed a node name
+ or other key, retrieves information about the node (using the ``node_source``)
+ and returns a compiled configuration."
+
+ include Puppet::Util
+
+ attr_accessor :code
+
+ # Compile a node's configuration.
+ def find(key, client = nil, clientip = nil)
+ # If we want to use the cert name as our key
+ if Puppet[:node_name] == 'cert' and client
+ key = client
+ end
+
+ # Note that this is reasonable, because either their node source should actually
+ # know about the node, or they should be using the ``none`` node source, which
+ # will always return data.
+ unless node = Puppet::Node.search(key)
+ raise Puppet::Error, "Could not find node '%s'" % key
+ end
+
+ # Add any external data to the node.
+ add_node_data(node)
+
+ configuration = compile(node)
+
+ return configuration
+ end
+
+ def initialize
+ set_server_facts
+ end
+
+ # Create/return our interpreter.
+ def interpreter
+ unless defined?(@interpreter) and @interpreter
+ @interpreter = create_interpreter
+ end
+ @interpreter
+ end
+
+ # Return the configuration version.
+ def version(client = nil, clientip = nil)
+ if client and node = Puppet::Node.search(client)
+ update_node_check(node)
+ return interpreter.configuration_version(node)
+ else
+ # Just return something that will always result in a recompile, because
+ # this is local.
+ return (Time.now + 1000).to_i
+ end
+ end
+
+ private
+
+ # Add any extra data necessary to the node.
+ def add_node_data(node)
+ # Merge in our server-side facts, so they can be used during compilation.
+ node.merge(@server_facts)
+ end
+
+ # Compile the actual configuration.
+ def compile(node)
+ # Ask the interpreter to compile the configuration.
+ str = "Compiled configuration for %s" % node.name
+ if node.environment
+ str += " in environment %s" % node.environment
+ end
+ config = nil
+
+ # LAK:FIXME This should log at :none when our client is
+ # local, since we don't want 'puppet' (vs. puppetmasterd) to
+ # log compile times.
+ benchmark(:notice, "Compiled configuration for %s" % node.name) do
+ begin
+ config = interpreter.compile(node)
+ rescue Puppet::Error => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ unless local?
+ Puppet.err detail.to_s
+ end
+ raise XMLRPC::FaultException.new(
+ 1, detail.to_s
+ )
+ end
+ end
+
+ return config
+ end
+
+ # Create our interpreter object.
+ def create_interpreter
+ args = {}
+
+ # Allow specification of a code snippet or of a file
+ if self.code
+ args[:Code] = self.code
+ end
+
+ # LAK:FIXME This needs to be handled somehow.
+ #if options.include?(:UseNodes)
+ # args[:UseNodes] = options[:UseNodes]
+ #elsif @local
+ # args[:UseNodes] = false
+ #end
+
+ return Puppet::Parser::Interpreter.new(args)
+ end
+
+ # Initialize our server fact hash; we add these to each client, and they
+ # won't change while we're running, so it's safe to cache the values.
+ def set_server_facts
+ @server_facts = {}
+
+ # Add our server version to the fact list
+ @server_facts["serverversion"] = Puppet.version.to_s
+
+ # And then add the server name and IP
+ {"servername" => "fqdn",
+ "serverip" => "ipaddress"
+ }.each do |var, fact|
+ if value = Facter.value(fact)
+ @server_facts[var] = value
+ else
+ Puppet.warning "Could not retrieve fact %s" % fact
+ end
+ end
+
+ if @server_facts["servername"].nil?
+ host = Facter.value(:hostname)
+ if domain = Facter.value(:domain)
+ @server_facts["servername"] = [host, domain].join(".")
+ else
+ @server_facts["servername"] = host
+ end
+ end
+ end
+
+ # Translate our configuration appropriately for sending back to a client.
+ # LAK:FIXME This method should probably be part of the protocol, but it
+ # shouldn't be here.
+ def translate(config)
+ if local?
+ config
+ else
+ CGI.escape(config.to_yaml(:UseBlock => true))
+ end
+ end
+
+ # Mark that the node has checked in. LAK:FIXME this needs to be moved into
+ # the Node class, or somewhere that's got abstract backends.
+ def update_node_check(node)
+ if Puppet.features.rails? and Puppet[:storeconfigs]
+ Puppet::Rails.connect
+
+ host = Puppet::Rails::Host.find_or_create_by_name(node.name)
+ host.last_freshcheck = Time.now
+ host.save
+ end
+ end
+end
diff --git a/lib/puppet/indirector/exec.rb b/lib/puppet/indirector/exec.rb
new file mode 100644
index 000000000..7e4ac8d18
--- /dev/null
+++ b/lib/puppet/indirector/exec.rb
@@ -0,0 +1,57 @@
+require 'puppet/indirector/terminus'
+require 'puppet/util'
+
+class Puppet::Indirector::Exec < Puppet::Indirector::Terminus
+ # Look for external node definitions.
+ def find(name)
+ # Run the command.
+ unless output = query(name)
+ return nil
+ end
+
+ # Translate the output to ruby.
+ return output
+ end
+
+ private
+
+ # Proxy the execution, so it's easier to test.
+ def execute(command)
+ Puppet::Util.execute(command)
+ end
+
+ # Call the external command and see if it returns our output.
+ def query(name)
+ external_command = command
+
+ # Make sure it's an arry
+ unless external_command.is_a?(Array)
+ raise Puppet::DevError, "Exec commands must be an array"
+ end
+
+ # Make sure it's fully qualified.
+ unless external_command[0][0] == File::SEPARATOR[0]
+ raise ArgumentError, "You must set the exec parameter to a fully qualified command"
+ end
+
+ # Add our name to it.
+ external_command << name
+ begin
+ output = execute(external_command)
+ rescue Puppet::ExecutionFailure => detail
+ if $?.exitstatus == 1
+ return nil
+ else
+ Puppet.err "Could not retrieve external node information for %s: %s" % [name, detail]
+ end
+ return nil
+ end
+
+ if output =~ /\A\s*\Z/ # all whitespace
+ Puppet.debug "Empty response for %s from exec %s terminus" % [name, self.name]
+ return nil
+ else
+ return output
+ end
+ end
+end
diff --git a/lib/puppet/indirector/exec/node.rb b/lib/puppet/indirector/exec/node.rb
new file mode 100644
index 000000000..033afe3f0
--- /dev/null
+++ b/lib/puppet/indirector/exec/node.rb
@@ -0,0 +1,50 @@
+require 'puppet/indirector/exec'
+
+class Puppet::Indirector::Exec::Node < Puppet::Indirector::Exec
+ desc "Call an external program to get node information."
+ include Puppet::Util
+
+ def command
+ command = Puppet[:external_nodes]
+ unless command != "none"
+ raise ArgumentError, "You must set the 'external_nodes' parameter to use the external node terminus"
+ end
+ command.split
+ end
+
+ # Look for external node definitions.
+ def find(name)
+ output = super or return nil
+
+ # Translate the output to ruby.
+ result = translate(name, output)
+
+ return create_node(name, result)
+ end
+
+ private
+
+ # Turn our outputted objects into a Puppet::Node instance.
+ def create_node(name, result)
+ node = Puppet::Node.new(name)
+ set = false
+ [:parameters, :classes].each do |param|
+ if value = result[param]
+ node.send(param.to_s + "=", value)
+ set = true
+ end
+ end
+
+ node.fact_merge
+ return node
+ end
+
+ # Translate the yaml string into Ruby objects.
+ def translate(name, output)
+ begin
+ YAML.load(output).inject({}) { |hash, data| hash[symbolize(data[0])] = data[1]; hash }
+ rescue => detail
+ raise Puppet::Error, "Could not load external node results for %s: %s" % [name, detail]
+ end
+ end
+end
diff --git a/lib/puppet/indirector/file.rb b/lib/puppet/indirector/file.rb
new file mode 100644
index 000000000..c2d36c46b
--- /dev/null
+++ b/lib/puppet/indirector/file.rb
@@ -0,0 +1,54 @@
+require 'puppet/indirector/terminus'
+
+# An empty terminus type, meant to just return empty objects.
+class Puppet::Indirector::File < Puppet::Indirector::Terminus
+ def destroy(file)
+ if respond_to?(:path)
+ path = path(file.name)
+ else
+ path = file.path
+ end
+ raise Puppet::Error.new("File %s does not exist; cannot destroy" % [file]) unless File.exist?(path)
+
+ begin
+ File.unlink(path)
+ rescue => detail
+ raise Puppet::Error, "Could not remove %s: %s" % [file, detail]
+ end
+ end
+
+ def find(name)
+ if respond_to?(:path)
+ path = path(name)
+ else
+ path = name
+ end
+
+ return nil unless File.exist?(path)
+
+ begin
+ content = File.read(path)
+ rescue => detail
+ raise Puppet::Error, "Could not retrieve path %s: %s" % [path, detail]
+ end
+
+ return model.new(content)
+ end
+
+ def save(file)
+ if respond_to?(:path)
+ path = path(file.name)
+ else
+ path = file.path
+ end
+ dir = File.dirname(path)
+
+ raise Puppet::Error.new("Cannot save %s; parent directory %s does not exist" % [file, dir]) unless File.directory?(dir)
+
+ begin
+ File.open(path, "w") { |f| f.print file.content }
+ rescue => detail
+ raise Puppet::Error, "Could not write %s: %s" % [file, detail]
+ end
+ end
+end
diff --git a/lib/puppet/indirector/file/checksum.rb b/lib/puppet/indirector/file/checksum.rb
new file mode 100644
index 000000000..2f0974ced
--- /dev/null
+++ b/lib/puppet/indirector/file/checksum.rb
@@ -0,0 +1,33 @@
+require 'puppet/checksum'
+require 'puppet/indirector/file'
+
+class Puppet::Indirector::File::Checksum < Puppet::Indirector::File
+ desc "Store files in a directory set based on their checksums."
+
+ def initialize
+ Puppet.settings.use(:filebucket)
+ end
+
+ def path(checksum)
+ path = []
+ path << Puppet[:bucketdir] # Start with the base directory
+ path << checksum[0..7].split("").join(File::SEPARATOR) # Add sets of directories based on the checksum
+ path << checksum # And the full checksum name itself
+ path << "contents" # And the actual file name
+
+ path.join(File::SEPARATOR)
+ end
+
+ def save(file)
+ path = File.dirname(path(file.name))
+
+ # Make the directories if necessary.
+ unless FileTest.directory?(path)
+ Puppet::Util.withumask(0007) do
+ FileUtils.mkdir_p(path)
+ end
+ end
+
+ super
+ end
+end
diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb
new file mode 100644
index 000000000..8afe0012d
--- /dev/null
+++ b/lib/puppet/indirector/indirection.rb
@@ -0,0 +1,91 @@
+# An actual indirection.
+class Puppet::Indirector::Indirection
+ @@indirections = []
+
+ # Clear all cached termini from all indirections.
+ def self.clear_cache
+ @@indirections.each { |ind| ind.clear_cache }
+ end
+
+ # Find an indirection by name. This is provided so that Terminus classes
+ # can specifically hook up with the indirections they are associated with.
+ def self.instance(name)
+ @@indirections.find { |i| i.name == name }
+ end
+
+ attr_accessor :name, :model
+
+ # Clear our cached list of termini.
+ # This is only used for testing.
+ def clear_cache
+ @termini.clear
+ end
+
+ # This is only used for testing.
+ def delete
+ @@indirections.delete(self) if @@indirections.include?(self)
+ end
+
+ def initialize(model, name, options = {})
+ @model = model
+ @name = name
+ options.each do |name, value|
+ begin
+ send(name.to_s + "=", value)
+ rescue NoMethodError
+ raise ArgumentError, "%s is not a valid Indirection parameter" % name
+ end
+ end
+ @termini = {}
+ @terminus_types = {}
+ raise(ArgumentError, "Indirection %s is already defined" % @name) if @@indirections.find { |i| i.name == @name }
+ @@indirections << self
+ end
+
+ # Return the singleton terminus for this indirection.
+ def terminus(terminus_name = nil)
+ # Get the name of the terminus.
+ unless terminus_name
+ param_name = "%s_terminus" % self.name
+ if Puppet.settings.valid?(param_name)
+ terminus_name = Puppet.settings[param_name]
+ else
+ terminus_name = Puppet[:default_terminus]
+ end
+ unless terminus_name and terminus_name.to_s != ""
+ raise ArgumentError, "Invalid terminus name %s" % terminus_name.inspect
+ end
+ terminus_name = terminus_name.intern if terminus_name.is_a?(String)
+ end
+
+ return @termini[terminus_name] ||= make_terminus(terminus_name)
+ end
+
+ def find(*args)
+ terminus.find(*args)
+ end
+
+ def destroy(*args)
+ terminus.destroy(*args)
+ end
+
+ def search(*args)
+ terminus.search(*args)
+ end
+
+ # these become instance methods
+ def save(*args)
+ terminus.save(*args)
+ end
+
+ private
+
+ # Create a new terminus instance.
+ def make_terminus(name)
+ # Load our terminus class.
+ unless klass = Puppet::Indirector::Terminus.terminus_class(name, self.name)
+ raise ArgumentError, "Could not find terminus %s for indirection %s" % [name, self.name]
+ end
+ return klass.new
+ end
+end
diff --git a/lib/puppet/indirector/ldap.rb b/lib/puppet/indirector/ldap.rb
new file mode 100644
index 000000000..fb883def6
--- /dev/null
+++ b/lib/puppet/indirector/ldap.rb
@@ -0,0 +1,90 @@
+require 'puppet/indirector/terminus'
+
+class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus
+ # Perform our ldap search and process the result.
+ def find(name)
+ # We have to use 'yield' here because the LDAP::Entry objects
+ # get destroyed outside the scope of the search, strangely.
+ ldapsearch(name) { |entry| return process(name, entry) }
+
+ # Return nil if we haven't found something.
+ return nil
+ end
+
+ # Process the found entry. We assume that we don't just want the
+ # ldap object.
+ def process(name, entry)
+ raise Puppet::DevError, "The 'process' method has not been overridden for the LDAP terminus for %s" % self.name
+ end
+
+ # Default to all attributes.
+ def search_attributes
+ nil
+ end
+
+ def search_base
+ Puppet[:ldapbase]
+ end
+
+ # The ldap search filter to use.
+ def search_filter(name)
+ raise Puppet::DevError, "No search string set for LDAP terminus for %s" % self.name
+ end
+
+ # Find the ldap node, return the class list and parent node specially,
+ # and everything else in a parameter hash.
+ def ldapsearch(node)
+ raise ArgumentError.new("You must pass a block to ldapsearch") unless block_given?
+
+ found = false
+ count = 0
+
+ begin
+ connection.search(search_base, 2, search_filter(node), search_attributes) do |entry|
+ found = true
+ yield entry
+ end
+ rescue => detail
+ if count == 0
+ # Try reconnecting to ldap if we get an exception and we haven't yet retried.
+ count += 1
+ @connection = nil
+ Puppet.warning "Retrying LDAP connection"
+ retry
+ else
+ raise Puppet::Error, "LDAP Search failed: %s" % detail
+ end
+ end
+
+ return found
+ end
+
+ private
+
+ # Create an ldap connection.
+ def connection
+ unless defined? @connection and @connection
+ unless Puppet.features.ldap?
+ raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries"
+ end
+ begin
+ if Puppet[:ldapssl]
+ @connection = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ elsif Puppet[:ldaptls]
+ @connection = LDAP::SSLConn.new(
+ Puppet[:ldapserver], Puppet[:ldapport], true
+ )
+ else
+ @connection = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ end
+ @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
+ @connection.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
+ rescue => detail
+ raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+ end
+ end
+
+ return @connection
+ end
+end
diff --git a/lib/puppet/indirector/ldap/node.rb b/lib/puppet/indirector/ldap/node.rb
new file mode 100644
index 000000000..6f35b575c
--- /dev/null
+++ b/lib/puppet/indirector/ldap/node.rb
@@ -0,0 +1,115 @@
+require 'puppet/indirector/ldap'
+
+class Puppet::Indirector::Ldap::Node < Puppet::Indirector::Ldap
+ desc "Search in LDAP for node configuration information."
+
+ # The attributes that Puppet class information is stored in.
+ def class_attributes
+ Puppet[:ldapclassattrs].split(/\s*,\s*/)
+ end
+
+ # Look for our node in ldap.
+ def find(name)
+ return nil unless information = super
+ node = Puppet::Node.new(name)
+
+ parent_info = nil
+ parent = information[:parent]
+ parents = [name]
+ while parent
+ if parents.include?(parent)
+ raise ArgumentError, "Found loop in LDAP node parents; %s appears twice" % parent
+ end
+ parents << parent
+ ldapsearch(parent) do |entry|
+ parent_info = process(parent, entry)
+ end
+ information[:classes] += parent_info[:classes]
+ parent_info[:parameters].each do |param, value|
+ # Specifically test for whether it's set, so false values are handled
+ # correctly.
+ information[:parameters][param] = value unless information[:parameters].include?(param)
+ end
+
+ parent = parent_info[:parent]
+ end
+
+ node.classes = information[:classes].uniq unless information[:classes].empty?
+ node.parameters = information[:parameters] unless information[:parameters].empty?
+ node.fact_merge
+
+ return node
+ end
+
+ # The parent attribute, if we have one.
+ def parent_attribute
+ if pattr = Puppet[:ldapparentattr] and ! pattr.empty?
+ pattr
+ else
+ nil
+ end
+ end
+
+ # Process the found entry. We assume that we don't just want the
+ # ldap object.
+ def process(name, entry)
+ result = {}
+ if pattr = parent_attribute
+ if values = entry.vals(pattr)
+ if values.length > 1
+ raise Puppet::Error,
+ "Node %s has more than one parent: %s" % [name, values.inspect]
+ end
+ unless values.empty?
+ result[:parent] = values.shift
+ end
+ end
+ end
+
+ result[:classes] = []
+ class_attributes.each { |attr|
+ if values = entry.vals(attr)
+ values.each do |v| result[:classes] << v end
+ end
+ }
+
+ result[:parameters] = entry.to_hash.inject({}) do |hash, ary|
+ if ary[1].length == 1
+ hash[ary[0]] = ary[1].shift
+ else
+ hash[ary[0]] = ary[1]
+ end
+ hash
+ end
+
+ return result
+ end
+
+ # Default to all attributes.
+ def search_attributes
+ ldapattrs = Puppet[:ldapattrs]
+
+ # results in everything getting returned
+ return nil if ldapattrs == "all"
+
+ search_attrs = class_attributes + ldapattrs.split(/\s*,\s*/)
+
+ if pattr = parent_attribute
+ search_attrs << pattr
+ end
+
+ search_attrs
+ end
+
+ # The ldap search filter to use.
+ def search_filter(name)
+ filter = Puppet[:ldapstring]
+
+ if filter.include? "%s"
+ # Don't replace the string in-line, since that would hard-code our node
+ # info.
+ filter = filter.gsub('%s', name)
+ end
+ filter
+ end
+end
diff --git a/lib/puppet/indirector/memory.rb b/lib/puppet/indirector/memory.rb
new file mode 100644
index 000000000..5bfcec95d
--- /dev/null
+++ b/lib/puppet/indirector/memory.rb
@@ -0,0 +1,21 @@
+require 'puppet/indirector/terminus'
+
+# Manage a memory-cached list of instances.
+class Puppet::Indirector::Memory < Puppet::Indirector::Terminus
+ def initialize
+ @instances = {}
+ end
+
+ def destroy(instance)
+ raise ArgumentError.new("Could not find %s to destroy" % instance) unless @instances.include?(instance.name)
+ @instances.delete(instance.name)
+ end
+
+ def find(name)
+ @instances[name]
+ end
+
+ def save(instance)
+ @instances[instance.name] = instance
+ end
+end
diff --git a/lib/puppet/indirector/memory/node.rb b/lib/puppet/indirector/memory/node.rb
new file mode 100644
index 000000000..c5000b879
--- /dev/null
+++ b/lib/puppet/indirector/memory/node.rb
@@ -0,0 +1,8 @@
+require 'puppet/indirector/memory'
+
+class Puppet::Indirector::Memory::Node < Puppet::Indirector::Memory
+ desc "Keep track of nodes 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/null.rb b/lib/puppet/indirector/null.rb
new file mode 100644
index 000000000..db2b1db1c
--- /dev/null
+++ b/lib/puppet/indirector/null.rb
@@ -0,0 +1,9 @@
+require 'puppet/indirector/terminus'
+
+# An empty terminus type, meant to just return empty objects.
+class Puppet::Indirector::Null < Puppet::Indirector::Terminus
+ # Just return nothing.
+ def find(name)
+ indirection.model.new(name)
+ end
+end
diff --git a/lib/puppet/indirector/null/node.rb b/lib/puppet/indirector/null/node.rb
new file mode 100644
index 000000000..eb08f5697
--- /dev/null
+++ b/lib/puppet/indirector/null/node.rb
@@ -0,0 +1,14 @@
+require 'puppet/indirector/null'
+
+class Puppet::Indirector::Null::Node < Puppet::Indirector::Null
+ desc "Always return an empty node object. This is the node source you should
+ use when you don't have some other, functional source you want to use,
+ as the compiler will not work without this node information."
+
+ # Just return an empty node.
+ def find(name)
+ node = super
+ node.fact_merge
+ node
+ end
+end
diff --git a/lib/puppet/indirector/terminus.rb b/lib/puppet/indirector/terminus.rb
new file mode 100644
index 000000000..bcff08d79
--- /dev/null
+++ b/lib/puppet/indirector/terminus.rb
@@ -0,0 +1,117 @@
+require 'puppet/indirector'
+require 'puppet/indirector/indirection'
+require 'puppet/util/instance_loader'
+
+# A simple class that can function as the base class for indirected types.
+class Puppet::Indirector::Terminus
+ require 'puppet/util/docs'
+ extend Puppet::Util::Docs
+
+ class << self
+ include Puppet::Util::InstanceLoader
+
+ attr_accessor :name, :terminus_type
+ attr_reader :abstract_terminus, :indirection
+
+ # Are we an abstract terminus type, rather than an instance with an
+ # associated indirection?
+ def abstract_terminus?
+ abstract_terminus
+ end
+
+ # Look up the indirection if we were only provided a name.
+ def indirection=(name)
+ if name.is_a?(Puppet::Indirector::Indirection)
+ @indirection = name
+ elsif ind = Puppet::Indirector::Indirection.instance(name)
+ @indirection = ind
+ else
+ raise ArgumentError, "Could not find indirection instance %s" % name
+ end
+ end
+
+ # Register our subclass with the appropriate indirection.
+ # This follows the convention that our terminus is named after the
+ # indirection.
+ def inherited(subclass)
+ longname = subclass.to_s
+ if longname =~ /#<Class/
+ raise ArgumentError, "Terminus subclasses must have associated constants"
+ end
+ names = longname.split("::")
+ name = names.pop.downcase.intern
+
+ subclass.name = name
+
+ # Short-circuit the abstract types, which are those that directly subclass
+ # the Terminus class.
+ if self == Puppet::Indirector::Terminus
+ subclass.mark_as_abstract_terminus
+ return
+ end
+
+ # Set the terminus type to be the name of the abstract terminus type.
+ # Yay, class/instance confusion.
+ subclass.terminus_type = self.name
+
+ # This will throw an exception if the indirection instance cannot be found.
+ # Do this last, because it also registers the terminus type with the indirection,
+ # which needs the above information.
+ subclass.indirection = name
+
+ # And add this instance to the instance hash.
+ Puppet::Indirector::Terminus.register_terminus_class(subclass)
+ end
+
+ # Mark that this instance is abstract.
+ def mark_as_abstract_terminus
+ @abstract_terminus = true
+ end
+
+ def model
+ indirection.model
+ end
+
+ # Register a class, probably autoloaded.
+ def register_terminus_class(klass)
+ setup_instance_loading klass.terminus_type
+ instance_hash(klass.terminus_type)[klass.name] = klass
+ end
+
+ # Return a terminus by name, using the autoloader.
+ def terminus_class(type, name)
+ setup_instance_loading type
+ loaded_instance(type, name)
+ end
+
+ private
+
+ def setup_instance_loading(type)
+ unless instance_loading?(type)
+ instance_load type, "puppet/indirector/%s" % type
+ end
+ end
+ end
+
+ def initialize
+ if self.class.abstract_terminus?
+ raise Puppet::DevError, "Cannot create instances of abstract terminus types"
+ end
+ end
+
+ def terminus_type
+ self.class.terminus_type
+ end
+
+ def name
+ self.class.name
+ end
+
+ def model
+ self.class.model
+ end
+
+ def indirection
+ self.class.indirection
+ end
+end
diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb
new file mode 100644
index 000000000..b9ea54f39
--- /dev/null
+++ b/lib/puppet/indirector/yaml.rb
@@ -0,0 +1,45 @@
+require 'puppet/indirector/terminus'
+
+# The base class for YAML indirection termini.
+class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus
+ def initialize
+ # Make sure our base directory exists.
+ Puppet.settings.use(:yaml)
+ end
+
+ # Read a given name's file in and convert it from YAML.
+ def find(name)
+ raise ArgumentError.new("You must specify the name of the object to retrieve") unless name
+ file = path(name)
+ return nil unless FileTest.exist?(file)
+
+ begin
+ return YAML.load(File.read(file))
+ rescue => detail
+ raise Puppet::Error, "Could not read YAML data for %s(%s): %s" % [indirection.name, name, detail]
+ end
+ end
+
+ # Convert our object to YAML and store it to the disk.
+ def save(object)
+ raise ArgumentError.new("You can only save objects that respond to :name") unless object.respond_to?(:name)
+
+ file = path(object.name)
+
+ basedir = File.dirname(file)
+
+ # This is quite likely a bad idea, since we're not managing ownership or modes.
+ unless FileTest.exist?(basedir)
+ Dir.mkdir(basedir)
+ end
+
+ File.open(file, "w", 0660) { |f| f.print YAML.dump(object) }
+ end
+
+ private
+
+ # Return the path to a given node's file.
+ def path(name)
+ File.join(Puppet[:yamldir], self.name.to_s, name.to_s + ".yaml")
+ end
+end
diff --git a/lib/puppet/indirector/yaml/facts.rb b/lib/puppet/indirector/yaml/facts.rb
new file mode 100644
index 000000000..754b0d5a6
--- /dev/null
+++ b/lib/puppet/indirector/yaml/facts.rb
@@ -0,0 +1,5 @@
+require 'puppet/indirector/yaml'
+
+class Puppet::Indirector::Yaml::Facts < Puppet::Indirector::Yaml
+ desc "Store client facts as flat files, serialized using YAML."
+end
diff --git a/lib/puppet/metatype/closure.rb b/lib/puppet/metatype/closure.rb
index efb4712c6..727bc6884 100644
--- a/lib/puppet/metatype/closure.rb
+++ b/lib/puppet/metatype/closure.rb
@@ -1,19 +1,6 @@
class Puppet::Type
attr_writer :implicit
- def self.implicitcreate(hash)
- unless hash.include?(:implicit)
- hash[:implicit] = true
- end
- if obj = self.create(hash)
- obj.implicit = true
-
- return obj
- else
- return nil
- end
- end
-
# Is this type's name isomorphic with the object? That is, if the
# name conflicts, does it necessarily mean that the objects conflict?
# Defaults to true.
diff --git a/lib/puppet/metatype/container.rb b/lib/puppet/metatype/container.rb
index 7c44a7def..7bbccf8a0 100644
--- a/lib/puppet/metatype/container.rb
+++ b/lib/puppet/metatype/container.rb
@@ -14,14 +14,6 @@ class Puppet::Type
self.class.depthfirst?
end
- def parent=(parent)
- if self.parentof?(parent)
- devfail "%s[%s] is already the parent of %s[%s]" %
- [self.class.name, self.title, parent.class.name, parent.title]
- end
- @parent = parent
- end
-
# Add a hook for testing for recursion.
def parentof?(child)
if (self == child)
diff --git a/lib/puppet/metatype/instances.rb b/lib/puppet/metatype/instances.rb
index f6c2fdd34..4af230b28 100644
--- a/lib/puppet/metatype/instances.rb
+++ b/lib/puppet/metatype/instances.rb
@@ -79,8 +79,7 @@ class Puppet::Type
end
# Force users to call this, so that we can merge objects if
- # necessary. FIXME This method should be responsible for most of the
- # error handling.
+ # necessary.
def self.create(args)
# Don't modify the original hash; instead, create a duplicate and modify it.
# We have to dup and use the ! so that it stays a TransObject if it is
@@ -138,9 +137,8 @@ class Puppet::Type
# now pass through and create the new object
elsif implicit
- Puppet.notice "Ignoring implicit %s" % title
-
- return retobj
+ Puppet.debug "Ignoring implicit %s[%s]" % [self.name, title]
+ return nil
else
# If only one of the objects is being managed, then merge them
if retobj.managed?
@@ -308,8 +306,8 @@ class Puppet::Type
# Create the path for logging and such.
def pathbuilder
- if defined? @parent and @parent
- [@parent.pathbuilder, self.ref].flatten
+ if p = parent
+ [p.pathbuilder, self.ref].flatten
else
[self.ref]
end
diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb
index 924958bbe..dc30d8167 100644
--- a/lib/puppet/module.rb
+++ b/lib/puppet/module.rb
@@ -9,7 +9,7 @@ class Puppet::Module
# parameter. Only consider paths that are absolute and existing
# directories
def self.modulepath(environment = nil)
- dirs = Puppet.config.value(:modulepath, environment).split(":")
+ dirs = Puppet.settings.value(:modulepath, environment).split(":")
if ENV["PUPPETLIB"]
dirs = ENV["PUPPETLIB"].split(":") + dirs
else
@@ -61,7 +61,7 @@ class Puppet::Module
if mod
return mod.template(file)
else
- return File.join(Puppet.config.value(:templatedir, environment), template)
+ return File.join(Puppet.settings.value(:templatedir, environment), template)
end
end
diff --git a/lib/puppet/network/client/ca.rb b/lib/puppet/network/client/ca.rb
index 412c9c59f..46fb9f51f 100644
--- a/lib/puppet/network/client/ca.rb
+++ b/lib/puppet/network/client/ca.rb
@@ -14,9 +14,9 @@ class Puppet::Network::Client::CA < Puppet::Network::Client
end
# This client is really only able to request certificates for the
- # current host. It uses the Puppet.config settings to figure everything out.
+ # current host. It uses the Puppet.settings settings to figure everything out.
def request_cert
- Puppet.config.use(:main, :ssl)
+ Puppet.settings.use(:main, :ssl)
if cert = read_cert
return cert
@@ -49,8 +49,8 @@ class Puppet::Network::Client::CA < Puppet::Network::Client
end
# Only write the cert out if it passes validating.
- Puppet.config.write(:hostcert) do |f| f.print cert end
- Puppet.config.write(:localcacert) do |f| f.print cacert end
+ Puppet.settings.write(:hostcert) do |f| f.print cert end
+ Puppet.settings.write(:localcacert) do |f| f.print cacert end
return @cert
end
diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb
index c6d7cd75d..f950a6059 100644
--- a/lib/puppet/network/client/master.rb
+++ b/lib/puppet/network/client/master.rb
@@ -7,7 +7,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
@@sync = Sync.new
end
- attr_accessor :objects
+ attr_accessor :configuration
attr_reader :compile_time
class << self
@@ -46,51 +46,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
# Return the list of dynamic facts as an array of symbols
def self.dynamic_facts
- Puppet.config[:dynamicfacts].split(/\s*,\s*/).collect { |fact| fact.downcase }
- end
-
- # This method actually applies the configuration.
- def apply(tags = nil, ignoreschedules = false)
- unless defined? @objects
- raise Puppet::Error, "Cannot apply; objects not defined"
- end
-
- transaction = @objects.evaluate
-
- if tags
- transaction.tags = tags
- end
-
- if ignoreschedules
- transaction.ignoreschedules = true
- end
-
- transaction.addtimes :config_retrieval => @configtime
-
- begin
- transaction.evaluate
- rescue Puppet::Error => detail
- Puppet.err "Could not apply complete configuration: %s" %
- detail
- rescue => detail
- Puppet.err "Got an uncaught exception of type %s: %s" %
- [detail.class, detail]
- if Puppet[:trace]
- puts detail.backtrace
- end
- ensure
- Puppet::Util::Storage.store
- end
-
- if Puppet[:report] or Puppet[:summarize]
- report(transaction)
- end
-
- return transaction
- ensure
- if defined? transaction and transaction
- transaction.cleanup
- end
+ Puppet.settings[:dynamicfacts].split(/\s*,\s*/).collect { |fact| fact.downcase }
end
# Cache the config
@@ -111,10 +67,10 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
end
def clear
- @objects.remove(true) if @objects
+ @configuration.clear(true) if @configuration
Puppet::Type.allclear
mkdefault_objects
- @objects = nil
+ @configuration = nil
end
# Initialize and load storage
@@ -183,15 +139,15 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
facts = self.class.facts
end
- if self.objects or FileTest.exists?(self.cachefile)
+ if self.configuration or FileTest.exists?(self.cachefile)
if self.fresh?(facts)
Puppet.info "Config is up to date"
- if self.objects
+ if self.configuration
return
end
if oldtext = self.retrievecache
begin
- @objects = YAML.load(oldtext).to_type
+ @configuration = YAML.load(oldtext).to_configuration
rescue => detail
Puppet.warning "Could not load cached configuration: %s" % detail
end
@@ -213,7 +169,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
end
unless objects = get_actual_config(facts)
- @objects = nil
+ @configuration = nil
return
end
@@ -225,21 +181,21 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
self.setclasses(objects.classes)
# Clear all existing objects, so we can recreate our stack.
- if self.objects
+ if self.configuration
clear()
end
- # Now convert the objects to real Puppet objects
- @objects = objects.to_type
+ # Now convert the objects to a puppet configuration graph.
+ @configuration = objects.to_configuration
- if @objects.nil?
+ if @configuration.nil?
raise Puppet::Error, "Configuration could not be processed"
end
- # and perform any necessary final actions before we evaluate.
- @objects.finalize
+ # Keep the state database up to date.
+ @configuration.host_config = true
- return @objects
+ return @configuration
end
# A simple proxy method, so it's easy to test.
@@ -249,12 +205,9 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
# Just so we can specify that we are "the" instance.
def initialize(*args)
- Puppet.config.use(:main, :ssl, :puppetd)
+ Puppet.settings.use(:main, :ssl, :puppetd)
super
- # This might be nil
- @configtime = 0
-
self.class.instance = self
@running = false
@@ -297,7 +250,9 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
end
# The code that actually runs the configuration.
- def run(tags = nil, ignoreschedules = false)
+ # This just passes any options on to the configuration,
+ # which accepts :tags and :ignoreschedules.
+ def run(options = {})
got_lock = false
splay
Puppet::Util.sync(:puppetrun).synchronize(Sync::EX) do
@@ -307,19 +262,20 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
else
got_lock = true
begin
- @configtime = thinmark do
+ duration = thinmark do
self.getconfig
end
rescue => detail
Puppet.err "Could not retrieve configuration: %s" % detail
end
- if defined? @objects and @objects
+ if defined? @configuration and @configuration
+ @configuration.retrieval_duration = duration
unless @local
Puppet.notice "Starting configuration run"
end
benchmark(:notice, "Finished configuration run") do
- self.apply(tags, ignoreschedules)
+ @configuration.apply(options)
end
end
end
@@ -366,9 +322,6 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
# Download files from the remote server, returning a list of all
# changed files.
def self.download(args)
- objects = Puppet::Type.type(:component).create(
- :name => "#{args[:name]}_collector"
- )
hash = {
:path => args[:dest],
:recurse => true,
@@ -383,18 +336,23 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
if args[:ignore]
hash[:ignore] = args[:ignore].split(/\s+/)
end
- objects.push Puppet::Type.type(:file).create(hash)
+ downconfig = Puppet::Node::Configuration.new("downloading")
+ downconfig.add_resource Puppet::Type.type(:file).create(hash)
Puppet.info "Retrieving #{args[:name]}s"
noop = Puppet[:noop]
Puppet[:noop] = false
+ files = []
begin
- trans = objects.evaluate
- trans.ignoretags = true
Timeout::timeout(self.timeout) do
- trans.evaluate
+ downconfig.apply do |trans|
+ trans.changed?.find_all do |resource|
+ yield resource if block_given?
+ files << resource[:path]
+ end
+ end
end
rescue Puppet::Error, Timeout::Error => detail
if Puppet[:debug]
@@ -403,18 +361,10 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
Puppet.err "Could not retrieve #{args[:name]}s: %s" % detail
end
- # Now source all of the changed objects, but only source those
- # that are top-level.
- files = []
- trans.changed?.find_all do |object|
- yield object if block_given?
- files << object[:path]
- end
- trans.cleanup
-
# Now clean up after ourselves
- objects.remove
- files
+ downconfig.clear
+
+ return files
ensure
# I can't imagine why this is necessary, but apparently at last one person has had problems with noop
# being nil here.
@@ -427,21 +377,21 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
# Retrieve facts from the central server.
def self.getfacts
- # Clear all existing definitions.
- Facter.clear
# Download the new facts
path = Puppet[:factpath].split(":")
files = []
download(:dest => Puppet[:factdest], :source => Puppet[:factsource],
- :ignore => Puppet[:factsignore], :name => "fact") do |object|
-
- next unless path.include?(::File.dirname(object[:path]))
+ :ignore => Puppet[:factsignore], :name => "fact") do |resource|
- files << object[:path]
+ next unless path.include?(::File.dirname(resource[:path]))
+ files << resource[:path]
end
ensure
+ # Clear all existing definitions.
+ Facter.clear
+
# Reload everything.
if Facter.respond_to? :loadfacts
Facter.loadfacts
@@ -461,20 +411,20 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
# changed plugins, because Puppet::Type loads plugins on demand.
def self.getplugins
download(:dest => Puppet[:plugindest], :source => Puppet[:pluginsource],
- :ignore => Puppet[:pluginsignore], :name => "plugin") do |object|
+ :ignore => Puppet[:pluginsignore], :name => "plugin") do |resource|
- next if FileTest.directory?(object[:path])
- path = object[:path].sub(Puppet[:plugindest], '').sub(/^\/+/, '')
+ next if FileTest.directory?(resource[:path])
+ path = resource[:path].sub(Puppet[:plugindest], '').sub(/^\/+/, '')
unless Puppet::Util::Autoload.loaded?(path)
next
end
begin
Puppet.info "Reloading downloaded file %s" % path
- load object[:path]
+ load resource[:path]
rescue => detail
Puppet.warning "Could not reload downloaded file %s: %s" %
- [object[:path], detail]
+ [resource[:path], detail]
end
end
end
@@ -517,42 +467,6 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
return timeout
end
-
- # Send off the transaction report.
- def report(transaction)
- begin
- report = transaction.generate_report()
- rescue => detail
- Puppet.err "Could not generate report: %s" % detail
- return
- end
-
- if Puppet[:rrdgraph] == true
- report.graph()
- end
-
- if Puppet[:summarize]
- puts report.summary
- end
-
- if Puppet[:report]
- begin
- reportclient().report(report)
- rescue => detail
- Puppet.err "Reporting failed: %s" % detail
- end
- end
- end
-
- def reportclient
- unless defined? @reportclient
- @reportclient = Puppet::Network::Client.report.new(
- :Server => Puppet[:reportserver]
- )
- end
-
- @reportclient
- end
loadfacts()
@@ -633,7 +547,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
Puppet.err "Could not retrieve configuration: %s" % detail
unless Puppet[:usecacheonfailure]
- @objects = nil
+ @configuration = nil
Puppet.warning "Not using cache on failed configuration"
return
end
diff --git a/lib/puppet/network/handler/ca.rb b/lib/puppet/network/handler/ca.rb
index 422b21ae1..052eb5c19 100644
--- a/lib/puppet/network/handler/ca.rb
+++ b/lib/puppet/network/handler/ca.rb
@@ -60,7 +60,7 @@ class Puppet::Network::Handler
end
def initialize(hash = {})
- Puppet.config.use(:main, :ssl, :ca)
+ Puppet.settings.use(:main, :ssl, :ca)
if hash.include? :autosign
@autosign = hash[:autosign]
end
diff --git a/lib/puppet/network/handler/configuration.rb b/lib/puppet/network/handler/configuration.rb
index 2c72d3d2b..2df1b3ab4 100644
--- a/lib/puppet/network/handler/configuration.rb
+++ b/lib/puppet/network/handler/configuration.rb
@@ -30,7 +30,7 @@ class Puppet::Network::Handler
# Note that this is reasonable, because either their node source should actually
# know about the node, or they should be using the ``none`` node source, which
# will always return data.
- unless node = node_handler.details(key)
+ unless node = Puppet::Node.search(key)
raise Puppet::Error, "Could not find node '%s'" % key
end
@@ -64,7 +64,7 @@ class Puppet::Network::Handler
# Return the configuration version.
def version(client = nil, clientip = nil)
- if client and node = node_handler.details(client)
+ if client and node = Puppet::Node.search(client)
update_node_check(node)
return interpreter.configuration_version(node)
else
@@ -79,7 +79,7 @@ class Puppet::Network::Handler
# Add any extra data necessary to the node.
def add_node_data(node)
# Merge in our server-side facts, so they can be used during compilation.
- node.fact_merge(@server_facts)
+ node.merge(@server_facts)
# Add any specified classes to the node's class list.
if classes = @options[:Classes]
@@ -159,14 +159,6 @@ class Puppet::Network::Handler
@interpreter
end
- # Create a node handler instance for looking up our nodes.
- def node_handler
- unless defined?(@node_handler)
- @node_handler = Puppet::Network::Handler.handler(:node).create
- end
- @node_handler
- end
-
# Initialize our server fact hash; we add these to each client, and they
# won't change while we're running, so it's safe to cache the values.
def set_server_facts
diff --git a/lib/puppet/network/handler/facts.rb b/lib/puppet/network/handler/facts.rb
deleted file mode 100755
index 4767e8be4..000000000
--- a/lib/puppet/network/handler/facts.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'yaml'
-require 'puppet/util/fact_store'
-
-class Puppet::Network::Handler
- # Receive logs from remote hosts.
- class Facts < Handler
- desc "An interface for storing and retrieving client facts. Currently only
- used internally by Puppet."
-
- @interface = XMLRPC::Service::Interface.new("facts") { |iface|
- iface.add_method("void set(string, string)")
- iface.add_method("string get(string)")
- iface.add_method("integer store_date(string)")
- }
-
- def initialize(hash = {})
- super
-
- backend = Puppet[:factstore]
-
- unless klass = Puppet::Util::FactStore.store(backend)
- raise Puppet::Error, "Could not find fact store %s" % backend
- end
-
- @backend = klass.new
- end
-
- # Get the facts from our back end.
- def get(node)
- if facts = @backend.get(node)
- return strip_internal(facts)
- else
- return nil
- end
- end
-
- # Set the facts in the backend.
- def set(node, facts)
- @backend.set(node, add_internal(facts))
- nil
- end
-
- # Retrieve a client's storage date.
- def store_date(node)
- if facts = get(node)
- facts[:_puppet_timestamp].to_i
- else
- nil
- end
- end
-
- private
-
- # Add internal data to the facts for storage.
- def add_internal(facts)
- facts = facts.dup
- facts[:_puppet_timestamp] = Time.now
- facts
- end
-
- # Strip out that internal data.
- def strip_internal(facts)
- facts = facts.dup
- facts.find_all { |name, value| name.to_s =~ /^_puppet_/ }.each { |name, value| facts.delete(name) }
- facts
- end
- end
-end
diff --git a/lib/puppet/network/handler/filebucket.rb b/lib/puppet/network/handler/filebucket.rb
index bb6a0e6d3..1bf8da854 100755
--- a/lib/puppet/network/handler/filebucket.rb
+++ b/lib/puppet/network/handler/filebucket.rb
@@ -64,7 +64,7 @@ class Puppet::Network::Handler # :nodoc:
end
end
- Puppet.config.use(:filebucket)
+ Puppet.settings.use(:filebucket)
@name = "Filebucket[#{@path}]"
end
diff --git a/lib/puppet/network/handler/fileserver.rb b/lib/puppet/network/handler/fileserver.rb
index a429412d2..ae0e6553d 100755
--- a/lib/puppet/network/handler/fileserver.rb
+++ b/lib/puppet/network/handler/fileserver.rb
@@ -243,7 +243,10 @@ class Puppet::Network::Handler
# the modules.
def modules_mount(module_name, client)
# Find our environment, if we have one.
- if node = node_handler.details(client || Facter.value("hostname"))
+ unless hostname = (client || Facter.value("hostname"))
+ raise ArgumentError, "Could not find hostname"
+ end
+ if node = Puppet::Node.find(hostname)
env = node.environment
else
env = nil
@@ -258,14 +261,6 @@ class Puppet::Network::Handler
end
end
- # Create a node handler instance for looking up our nodes.
- def node_handler
- unless defined?(@node_handler)
- @node_handler = Puppet::Network::Handler.handler(:node).create
- end
- @node_handler
- end
-
# Read the configuration file.
def readconfig(check = true)
return if @noreadconfig
@@ -455,7 +450,7 @@ class Puppet::Network::Handler
def getfileobject(dir, links)
unless FileTest.exists?(dir)
- self.notice "File source %s does not exist" % dir
+ self.debug "File source %s does not exist" % dir
return nil
end
diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb
index c8db277ba..030950c61 100644
--- a/lib/puppet/network/handler/master.rb
+++ b/lib/puppet/network/handler/master.rb
@@ -74,7 +74,7 @@ class Puppet::Network::Handler
client, clientip = clientname(client, clientip, facts)
# Pass the facts to the fact handler
- fact_handler.set(client, facts)
+ Puppet::Node::Facts.new(client, facts).save
# And get the configuration from the config handler
begin
@@ -134,13 +134,6 @@ class Puppet::Network::Handler
return facts
end
- def fact_handler
- unless defined? @fact_handler
- @fact_handler = Puppet::Network::Handler.handler(:facts).new :local => local?
- end
- @fact_handler
- end
-
# Translate our configuration appropriately for sending back to a client.
def translate(config)
if local?
diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb
deleted file mode 100644
index c6ccc2eb6..000000000
--- a/lib/puppet/network/handler/node.rb
+++ /dev/null
@@ -1,242 +0,0 @@
-# Created by Luke A. Kanies on 2007-08-13.
-# Copyright (c) 2007. All rights reserved.
-
-require 'puppet/util'
-require 'puppet/node'
-require 'puppet/util/classgen'
-require 'puppet/util/instance_loader'
-
-# Look up a node, along with all the details about it.
-class Puppet::Network::Handler::Node < Puppet::Network::Handler
- desc "Retrieve information about nodes."
-
- # Create a singleton node handler
- def self.create
- unless @handler
- @handler = new
- end
- @handler
- end
-
- # Add a new node source.
- def self.newnode_source(name, options = {}, &block)
- name = symbolize(name)
-
- fact_merge = options[:fact_merge]
- mod = genmodule(name, :extend => SourceBase, :hash => instance_hash(:node_source), :block => block)
- mod.send(:define_method, :fact_merge?) do
- fact_merge
- end
- mod
- end
-
- # Collect the docs for all of our node sources.
- def self.node_source_docs
- docs = ""
-
- # Use this method so they all get loaded
- instance_loader(:node_source).loadall
- loaded_instances(:node_source).sort { |a,b| a.to_s <=> b.to_s }.each do |name|
- mod = self.node_source(name)
- docs += "%s\n%s\n" % [name, "-" * name.to_s.length]
-
- docs += Puppet::Util::Docs.scrub(mod.doc) + "\n\n"
- end
-
- docs
- end
-
- # List each of the node sources.
- def self.node_sources
- instance_loader(:node_source).loadall
- loaded_instances(:node_source)
- end
-
- # Remove a defined node source; basically only used for testing.
- def self.rm_node_source(name)
- rmclass(name, :hash => instance_hash(:node_source))
- end
-
- extend Puppet::Util::ClassGen
- extend Puppet::Util::InstanceLoader
-
- # A simple base module we can use for modifying how our node sources work.
- module SourceBase
- include Puppet::Util::Docs
- end
-
- @interface = XMLRPC::Service::Interface.new("nodes") { |iface|
- iface.add_method("string details(key)")
- iface.add_method("string parameters(key)")
- iface.add_method("string environment(key)")
- iface.add_method("string classes(key)")
- }
-
- # Set up autoloading and retrieving of reports.
- instance_load :node_source, 'puppet/node_source'
-
- attr_reader :source
-
- # Return a given node's classes.
- def classes(key)
- if node = details(key)
- node.classes
- else
- nil
- end
- end
-
- # Return an entire node configuration. This uses the 'nodesearch' method
- # defined in the node_source to look for the node.
- def details(key, client = nil, clientip = nil)
- return nil unless key
- if node = cached?(key)
- return node
- end
- facts = node_facts(key)
- node = nil
- names = node_names(key, facts)
- names.each do |name|
- name = name.to_s if name.is_a?(Symbol)
- if node = nodesearch(name) and @source != "none"
- Puppet.info "Found %s in %s" % [name, @source]
- break
- end
- end
-
- # If they made it this far, we haven't found anything, so look for a
- # default node.
- unless node or names.include?("default")
- if node = nodesearch("default")
- Puppet.notice "Using default node for %s" % key
- end
- end
-
- if node
- node.source = @source
- node.names = names
-
- # Merge the facts into the parameters.
- if fact_merge?
- node.fact_merge(facts)
- end
-
- cache(node)
-
- return node
- else
- return nil
- end
- end
-
- # Return a given node's environment.
- def environment(key, client = nil, clientip = nil)
- if node = details(key)
- node.environment
- else
- nil
- end
- end
-
- # Create our node lookup tool.
- def initialize(hash = {})
- @source = hash[:Source] || Puppet[:node_source]
-
- unless mod = self.class.node_source(@source)
- raise ArgumentError, "Unknown node source '%s'" % @source
- end
-
- extend(mod)
-
- super
-
- # We cache node info for speed
- @node_cache = {}
- end
-
- # Try to retrieve a given node's parameters.
- def parameters(key, client = nil, clientip = nil)
- if node = details(key)
- node.parameters
- else
- nil
- end
- end
-
- private
-
- # Store the node to make things a bit faster.
- def cache(node)
- @node_cache[node.name] = node
- end
-
- # If the node is cached, return it.
- def cached?(name)
- # Don't use cache when the filetimeout is set to 0
- return false if [0, "0"].include?(Puppet[:filetimeout])
-
- if node = @node_cache[name] and Time.now - node.time < Puppet[:filetimeout]
- return node
- else
- return false
- end
- end
-
- # Create/cache a fact handler.
- def fact_handler
- unless defined?(@fact_handler)
- @fact_handler = Puppet::Network::Handler.handler(:facts).new
- end
- @fact_handler
- end
-
- # Short-hand for creating a new node, so the node sources don't need to
- # specify the constant.
- def newnode(options)
- Puppet::Node.new(options)
- end
-
- # Look up the node facts from our fact handler.
- def node_facts(key)
- if facts = fact_handler.get(key)
- facts
- else
- {}
- end
- end
-
- # Calculate the list of node names we should use for looking
- # up our node.
- def node_names(key, facts = nil)
- facts ||= node_facts(key)
- names = []
-
- if hostname = facts["hostname"]
- unless hostname == key
- names << hostname
- end
- else
- hostname = key
- end
-
- if fqdn = facts["fqdn"]
- hostname = fqdn
- names << fqdn
- end
-
- # Make sure both the fqdn and the short name of the
- # host can be used in the manifest
- if hostname =~ /\./
- names << hostname.sub(/\..+/,'')
- elsif domain = facts['domain']
- names << hostname + "." + domain
- end
-
- # Sort the names inversely by name length.
- names.sort! { |a,b| b.length <=> a.length }
-
- # And make sure the key is first, since that's the most
- # likely usage.
- ([key] + names).uniq
- end
-end
diff --git a/lib/puppet/network/handler/report.rb b/lib/puppet/network/handler/report.rb
index 81aee6a3c..62e9cfdec 100755
--- a/lib/puppet/network/handler/report.rb
+++ b/lib/puppet/network/handler/report.rb
@@ -71,8 +71,8 @@ class Puppet::Network::Handler
def initialize(*args)
super
- Puppet.config.use(:reporting)
- Puppet.config.use(:metrics)
+ Puppet.settings.use(:reporting)
+ Puppet.settings.use(:metrics)
end
# Accept a report from a client.
diff --git a/lib/puppet/network/handler/resource.rb b/lib/puppet/network/handler/resource.rb
index ac29dce53..7709b85fe 100755
--- a/lib/puppet/network/handler/resource.rb
+++ b/lib/puppet/network/handler/resource.rb
@@ -39,21 +39,14 @@ class Puppet::Network::Handler
end
end
- component = bucket.to_type
-
- # Create a client, but specify the remote machine as the server
- # because the class requires it, even though it's unused
- client = Puppet::Network::Client.client(:Master).new(:Master => client||"localhost")
-
- # Set the objects
- client.objects = component
+ config = bucket.to_configuration
# And then apply the configuration. This way we're reusing all
# the code in there. It should probably just be separated out, though.
- transaction = client.apply
+ transaction = config.apply
# And then clean up
- component.remove
+ config.clear(true)
# It'd be nice to return some kind of report, but... at this point
# we have no such facility.
diff --git a/lib/puppet/network/handler/runner.rb b/lib/puppet/network/handler/runner.rb
index c41e83608..4b9ccab75 100755
--- a/lib/puppet/network/handler/runner.rb
+++ b/lib/puppet/network/handler/runner.rb
@@ -50,10 +50,10 @@ class Puppet::Network::Handler
# And then we need to tell it to run, with this extra info.
if fg
- master.run(tags, ignoreschedules)
+ master.run(:tags => tags, :ignoreschedules => ignoreschedules)
else
Puppet.newthread do
- master.run(tags, ignoreschedules)
+ master.run(:tags => tags, :ignoreschedules => ignoreschedules)
end
end
diff --git a/lib/puppet/network/server/webrick.rb b/lib/puppet/network/server/webrick.rb
index 3af0cfd3f..f24411ab3 100644
--- a/lib/puppet/network/server/webrick.rb
+++ b/lib/puppet/network/server/webrick.rb
@@ -48,7 +48,7 @@ module Puppet
# yuck; separate http logs
file = nil
- Puppet.config.use(:main, :ssl, Puppet[:name])
+ Puppet.settings.use(:main, :ssl, Puppet[:name])
if Puppet[:name] == "puppetmasterd"
file = Puppet[:masterhttplog]
else
diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb
index 2d3ac712e..d71bd507e 100644
--- a/lib/puppet/node.rb
+++ b/lib/puppet/node.rb
@@ -1,10 +1,27 @@
+require 'puppet/indirector'
+
# A simplistic class for managing the node information itself.
class Puppet::Node
+ require 'puppet/node/facts'
+
+ # Set up indirection, so that nodes can be looked for in
+ # the node sources.
+ extend Puppet::Indirector
+
+ # Use the node source as the indirection terminus.
+ indirects :node
+
+ # Add the node-searching methods. This is what people will actually
+ # interact with that will find the node with the list of names or
+ # will search for a default node.
+ require 'puppet/node/searching'
+ extend Puppet::Node::Searching
+
attr_accessor :name, :classes, :parameters, :source, :ipaddress, :names
attr_reader :time
attr_writer :environment
- # Do not return environments tha are empty string, and use
+ # Do not return environments that are the empty string, and use
# explicitly set environments, then facts, then a central env
# value.
def environment
@@ -21,6 +38,9 @@ class Puppet::Node
end
def initialize(name, options = {})
+ unless name
+ raise ArgumentError, "Node names cannot be nil"
+ end
@name = name
# Provide a default value.
@@ -52,9 +72,15 @@ class Puppet::Node
end
# Merge the node facts with parameters from the node source.
- # This is only called if the node source has 'fact_merge' set to true.
- def fact_merge(facts)
- facts.each do |name, value|
+ def fact_merge
+ if facts = Puppet::Node::Facts.find(name)
+ merge(facts.values)
+ end
+ end
+
+ # Merge any random parameters into our parameter list.
+ def merge(params)
+ params.each do |name, value|
@parameters[name] = value unless @parameters.include?(name)
end
end
diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb
index 4f93fdbe5..53f63d003 100644
--- a/lib/puppet/node/configuration.rb
+++ b/lib/puppet/node/configuration.rb
@@ -1,13 +1,34 @@
+require 'puppet/indirector'
require 'puppet/external/gratr/digraph'
# This class models a node configuration. It is the thing
# meant to be passed from server to client, and it contains all
# of the information in the configuration, including the resources
# and the relationships between them.
-class Puppet::Node::Configuration < GRATR::Digraph
- attr_accessor :name, :version
+class Puppet::Node::Configuration < Puppet::PGraph
+ extend Puppet::Indirector
+ indirects :configuration
+
+ # The host name this is a configuration for.
+ attr_accessor :name
+
+ # The configuration version. Used for testing whether a configuration
+ # is up to date.
+ attr_accessor :version
+
+ # How long this configuration took to retrieve. Used for reporting stats.
+ attr_accessor :retrieval_duration
+
+ # How we should extract the configuration for sending to the client.
attr_reader :extraction_format
+ # Whether this is a host configuration, which behaves very differently.
+ # In particular, reports are sent, graphs are made, and state is
+ # stored in the state database. If this is set incorrectly, then you often
+ # end up in infinite loops, because configurations are used to make things
+ # that the host configuration needs.
+ attr_accessor :host_config
+
# Add classes to our class list.
def add_class(*classes)
classes.each do |klass|
@@ -18,10 +39,133 @@ class Puppet::Node::Configuration < GRATR::Digraph
tag(*classes)
end
+ # Add one or more resources to our graph and to our resource table.
+ def add_resource(*resources)
+ resources.each do |resource|
+ unless resource.respond_to?(:ref)
+ raise ArgumentError, "Can only add objects that respond to :ref"
+ end
+
+ ref = resource.ref
+ if @resource_table.include?(ref)
+ raise ArgumentError, "Resource %s is already defined" % ref
+ else
+ @resource_table[ref] = resource
+ end
+ resource.configuration = self
+ add_vertex!(resource)
+ end
+ end
+
+ # Apply our configuration to the local host. Valid options
+ # are:
+ # :tags - set the tags that restrict what resources run
+ # during the transaction
+ # :ignoreschedules - tell the transaction to ignore schedules
+ # when determining the resources to run
+ def apply(options = {})
+ @applying = true
+
+ Puppet::Util::Storage.load if host_config?
+ transaction = Puppet::Transaction.new(self)
+
+ transaction.tags = options[:tags] if options[:tags]
+ transaction.ignoreschedules = true if options[:ignoreschedules]
+
+ transaction.addtimes :config_retrieval => @retrieval_duration
+
+ begin
+ transaction.evaluate
+ rescue Puppet::Error => detail
+ Puppet.err "Could not apply complete configuration: %s" % detail
+ rescue => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ Puppet.err "Got an uncaught exception of type %s: %s" % [detail.class, detail]
+ ensure
+ # Don't try to store state unless we're a host config
+ # too recursive.
+ Puppet::Util::Storage.store if host_config?
+ end
+
+ if block_given?
+ yield transaction
+ end
+
+ if host_config and (Puppet[:report] or Puppet[:summarize])
+ transaction.send_report
+ end
+
+ return transaction
+ ensure
+ @applying = false
+ cleanup()
+ if defined? transaction and transaction
+ transaction.cleanup
+ end
+ end
+
+ # Are we in the middle of applying the configuration?
+ def applying?
+ @applying
+ end
+
+ def clear(remove_resources = true)
+ super()
+ # We have to do this so that the resources clean themselves up.
+ @resource_table.values.each { |resource| resource.remove } if remove_resources
+ @resource_table.clear
+
+ if defined?(@relationship_graph) and @relationship_graph
+ @relationship_graph.clear(false)
+ @relationship_graph = nil
+ end
+ end
+
def classes
@classes.dup
end
+ # Create an implicit resource, meaning that it will lose out
+ # to any explicitly defined resources. This method often returns
+ # nil.
+ # The quirk of this method is that it's not possible to create
+ # an implicit resource before an explicit resource of the same name,
+ # because all explicit resources are created before any generate()
+ # methods are called on the individual resources. Thus, this
+ # method can safely just check if an explicit resource already exists
+ # and toss this implicit resource if so.
+ def create_implicit_resource(type, options)
+ unless options.include?(:implicit)
+ options[:implicit] = true
+ end
+
+ # This will return nil if an equivalent explicit resource already exists.
+ # When resource classes no longer retain references to resource instances,
+ # this will need to be modified to catch that conflict and discard
+ # implicit resources.
+ if resource = create_resource(type, options)
+ resource.implicit = true
+
+ return resource
+ else
+ return nil
+ end
+ end
+
+ # Create a new resource and register it in the configuration.
+ def create_resource(type, options)
+ unless klass = Puppet::Type.type(type)
+ raise ArgumentError, "Unknown resource type %s" % type
+ end
+ return unless resource = klass.create(options)
+
+ @transient_resources << resource if applying?
+ add_resource(resource)
+ resource
+ end
+
# Make sure we support the requested extraction format.
def extraction_format=(value)
unless respond_to?("extract_to_%s" % value)
@@ -94,12 +238,101 @@ class Puppet::Node::Configuration < GRATR::Digraph
return result
end
- def initialize(name)
+ # Make sure all of our resources are "finished".
+ def finalize
+ @resource_table.values.each { |resource| resource.finish }
+
+ write_graph(:resources)
+ end
+
+ def host_config?
+ host_config || false
+ end
+
+ def initialize(name = nil)
super()
- @name = name
+ @name = name if name
@extraction_format ||= :transportable
@tags = []
@classes = []
+ @resource_table = {}
+ @transient_resources = []
+ @applying = false
+ @relationship_graph = nil
+
+ if block_given?
+ yield(self)
+ finalize()
+ end
+ end
+
+ # Create a graph of all of the relationships in our configuration.
+ def relationship_graph
+ unless defined? @relationship_graph and @relationship_graph
+ # It's important that we assign the graph immediately, because
+ # the debug messages below use the relationships in the
+ # relationship graph to determine the path to the resources
+ # spitting out the messages. If this is not set,
+ # then we get into an infinite loop.
+ @relationship_graph = Puppet::Node::Configuration.new
+ @relationship_graph.host_config = host_config?
+
+ # First create the dependency graph
+ self.vertices.each do |vertex|
+ @relationship_graph.add_vertex! vertex
+ vertex.builddepends.each do |edge|
+ @relationship_graph.add_edge!(edge)
+ end
+ end
+
+ # Lastly, add in any autorequires
+ @relationship_graph.vertices.each do |vertex|
+ vertex.autorequire.each do |edge|
+ unless @relationship_graph.edge?(edge)
+ unless @relationship_graph.edge?(edge.target, edge.source)
+ vertex.debug "Autorequiring %s" % [edge.source]
+ @relationship_graph.add_edge!(edge)
+ else
+ vertex.debug "Skipping automatic relationship with %s" % (edge.source == vertex ? edge.target : edge.source)
+ end
+ end
+ end
+ end
+
+ @relationship_graph.write_graph(:relationships)
+
+ # Then splice in the container information
+ @relationship_graph.splice!(self, Puppet::Type::Component)
+
+ @relationship_graph.write_graph(:expanded_relationships)
+ end
+ @relationship_graph
+ end
+
+ # Remove the resource from our configuration. Notice that we also call
+ # 'remove' on the resource, at least until resource classes no longer maintain
+ # references to the resource instances.
+ def remove_resource(*resources)
+ resources.each do |resource|
+ @resource_table.delete(resource.ref) if @resource_table.include?(resource.ref)
+ remove_vertex!(resource) if vertex?(resource)
+ @relationship_graph.remove_vertex!(resource) if @relationship_graph and @relationship_graph.vertex?(resource)
+ resource.remove
+ end
+ end
+
+ # Look a resource up by its reference (e.g., File[/etc/passwd]).
+ def resource(type, title = nil)
+ if title
+ ref = "%s[%s]" % [type.to_s.capitalize, title]
+ else
+ ref = type
+ end
+ if resource = @resource_table[ref]
+ return resource
+ elsif defined?(@relationship_graph) and @relationship_graph
+ @relationship_graph.resource(ref)
+ end
end
# Add a tag.
@@ -120,4 +353,29 @@ class Puppet::Node::Configuration < GRATR::Digraph
def tags
@tags.dup
end
+
+ # Produce the graph files if requested.
+ def write_graph(name)
+ # We only want to graph the main host configuration.
+ return unless host_config?
+
+ return unless Puppet[:graph]
+
+ Puppet.settings.use(:graphing)
+
+ file = File.join(Puppet[:graphdir], "%s.dot" % name.to_s)
+ File.open(file, "w") { |f|
+ f.puts to_dot("name" => name.to_s.capitalize)
+ }
+ end
+
+ private
+
+ def cleanup
+ unless @transient_resources.empty?
+ remove_resource(*@transient_resources)
+ @transient_resources.clear
+ @relationship_graph = nil
+ end
+ end
end
diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb
new file mode 100755
index 000000000..a2e6d9c04
--- /dev/null
+++ b/lib/puppet/node/facts.rb
@@ -0,0 +1,36 @@
+require 'puppet/node'
+require 'puppet/indirector'
+
+# Manage a given node's facts. This either accepts facts and stores them, or
+# returns facts for a given node.
+class Puppet::Node::Facts
+ # Set up indirection, so that nodes can be looked for in
+ # the node sources.
+ extend Puppet::Indirector
+
+ # Use the node source as the indirection terminus.
+ indirects :facts
+
+ attr_accessor :name, :values
+
+ def initialize(name, values = {})
+ @name = name
+ @values = values
+
+ add_internal
+ end
+
+ private
+
+ # Add internal data to the facts for storage.
+ def add_internal
+ self.values[:_timestamp] = Time.now
+ end
+
+ # Strip out that internal data.
+ def strip_internal
+ newvals = values.dup
+ newvals.find_all { |name, value| name.to_s =~ /^_/ }.each { |name, value| newvals.delete(name) }
+ newvals
+ end
+end
diff --git a/lib/puppet/node/searching.rb b/lib/puppet/node/searching.rb
new file mode 100644
index 000000000..10dd588ab
--- /dev/null
+++ b/lib/puppet/node/searching.rb
@@ -0,0 +1,106 @@
+# The module that handles actually searching for nodes. This is only included
+# in the Node class, but it's completely stand-alone functionality, so it's
+# worth making it a separate module to simplify testing.
+module Puppet::Node::Searching
+ # Retrieve a node from the node source, with some additional munging
+ # thrown in for kicks.
+ def search(key)
+ return nil unless key
+ if node = cached?(key)
+ return node
+ end
+ facts = node_facts(key)
+ node = nil
+ names = node_names(key, facts)
+ names.each do |name|
+ name = name.to_s if name.is_a?(Symbol)
+ if node = find(name)
+ #Puppet.info "Found %s in %s" % [name, @source]
+ break
+ end
+ end
+
+ # If they made it this far, we haven't found anything, so look for a
+ # default node.
+ unless node or names.include?("default")
+ if node = find("default")
+ Puppet.notice "Using default node for %s" % key
+ end
+ end
+
+ if node
+ node.names = names
+
+ cache(node)
+
+ return node
+ else
+ return nil
+ end
+ end
+
+ private
+
+ # Store the node to make things a bit faster.
+ def cache(node)
+ @node_cache ||= {}
+ @node_cache[node.name] = node
+ end
+
+ # If the node is cached, return it.
+ def cached?(name)
+ # Don't use cache when the filetimeout is set to 0
+ return false if [0, "0"].include?(Puppet[:filetimeout])
+ @node_cache ||= {}
+
+ if node = @node_cache[name] and Time.now - node.time < Puppet[:filetimeout]
+ return node
+ else
+ return false
+ end
+ end
+
+ # Look up the node facts from our fact handler.
+ def node_facts(key)
+ if facts = Puppet::Node::Facts.find(key)
+ facts.values
+ else
+ {}
+ end
+ end
+
+ # Calculate the list of node names we should use for looking
+ # up our node.
+ def node_names(key, facts = nil)
+ facts ||= node_facts(key)
+ names = []
+
+ if hostname = facts["hostname"]
+ unless hostname == key
+ names << hostname
+ end
+ else
+ hostname = key
+ end
+
+ if fqdn = facts["fqdn"]
+ hostname = fqdn
+ names << fqdn
+ end
+
+ # Make sure both the fqdn and the short name of the
+ # host can be used in the manifest
+ if hostname =~ /\./
+ names << hostname.sub(/\..+/,'')
+ elsif domain = facts['domain']
+ names << hostname + "." + domain
+ end
+
+ # Sort the names inversely by name length.
+ names.sort! { |a,b| b.length <=> a.length }
+
+ # And make sure the key is first, since that's the most
+ # likely usage.
+ ([key] + names).uniq
+ end
+end
diff --git a/lib/puppet/parser/ast/component.rb b/lib/puppet/parser/ast/component.rb
deleted file mode 100644
index 1d7fe9cdd..000000000
--- a/lib/puppet/parser/ast/component.rb
+++ /dev/null
@@ -1,224 +0,0 @@
-require 'puppet/parser/ast/branch'
-
-class Puppet::Parser::AST
- # Evaluate the stored parse tree for a given component. This will
- # receive the arguments passed to the component and also the type and
- # name of the component.
- class Component < AST::Branch
- include Puppet::Util
- include Puppet::Util::Warnings
- include Puppet::Util::MethodHelper
- class << self
- attr_accessor :name
- end
-
- # The class name
- @name = :definition
-
- attr_accessor :classname, :arguments, :code, :scope, :keyword
- attr_accessor :exported, :namespace, :parser, :virtual
-
- # These are retrieved when looking up the superclass
- attr_accessor :name
-
- attr_reader :parentclass
-
- def child_of?(klass)
- false
- end
-
- def evaluate_resource(hash)
- origscope = hash[:scope]
- title = hash[:title]
- args = symbolize_options(hash[:arguments] || {})
-
- name = args[:name] || title
-
- exported = hash[:exported]
- virtual = hash[:virtual]
-
- pscope = origscope
- scope = subscope(pscope, title)
-
- if virtual or origscope.virtual?
- scope.virtual = true
- end
-
- if exported or origscope.exported?
- scope.exported = true
- end
-
- # Additionally, add a tag for whatever kind of class
- # we are
- if @classname != "" and ! @classname.nil?
- @classname.split(/::/).each { |tag| scope.tag(tag) }
- end
-
- [name, title].each do |str|
- unless str.nil? or str =~ /[^\w]/ or str == ""
- scope.tag(str)
- end
- end
-
- # define all of the arguments in our local scope
- if self.arguments
- # Verify that all required arguments are either present or
- # have been provided with defaults.
- self.arguments.each { |arg, default|
- arg = symbolize(arg)
- unless args.include?(arg)
- if defined? default and ! default.nil?
- default = default.safeevaluate :scope => scope
- args[arg] = default
- #Puppet.debug "Got default %s for %s in %s" %
- # [default.inspect, arg.inspect, @name.inspect]
- else
- parsefail "Must pass %s to %s of type %s" %
- [arg,title,@classname]
- end
- end
- }
- end
-
- # Set each of the provided arguments as variables in the
- # component's scope.
- args.each { |arg,value|
- unless validattr?(arg)
- parsefail "%s does not accept attribute %s" % [@classname, arg]
- end
-
- exceptwrap do
- scope.setvar(arg.to_s,args[arg])
- end
- }
-
- unless args.include? :title
- scope.setvar("title",title)
- end
-
- unless args.include? :name
- scope.setvar("name",name)
- end
-
- if self.code
- return self.code.safeevaluate(:scope => scope)
- else
- return nil
- end
- end
-
- def initialize(hash = {})
- @arguments = nil
- @parentclass = nil
- super
-
- # Convert the arguments to a hash for ease of later use.
- if @arguments
- unless @arguments.is_a? Array
- @arguments = [@arguments]
- end
- oldargs = @arguments
- @arguments = {}
- oldargs.each do |arg, val|
- @arguments[arg] = val
- end
- else
- @arguments = {}
- end
-
- # Deal with metaparams in the argument list.
- @arguments.each do |arg, defvalue|
- next unless Puppet::Type.metaparamclass(arg)
- if defvalue
- warnonce "%s is a metaparam; this value will inherit to all contained elements" % arg
- else
- raise Puppet::ParseError,
- "%s is a metaparameter; please choose another name" %
- name
- end
- end
- end
-
- def find_parentclass
- @parser.findclass(namespace, parentclass)
- end
-
- # Set our parent class, with a little check to avoid some potential
- # weirdness.
- def parentclass=(name)
- if name == self.classname
- parsefail "Parent classes must have dissimilar names"
- end
-
- @parentclass = name
- end
-
- # Hunt down our class object.
- def parentobj
- if @parentclass
- # Cache our result, since it should never change.
- unless defined?(@parentobj)
- unless tmp = find_parentclass
- parsefail "Could not find %s %s" % [self.class.name, @parentclass]
- end
-
- if tmp == self
- parsefail "Parent classes must have dissimilar names"
- end
-
- @parentobj = tmp
- end
- @parentobj
- else
- nil
- end
- end
-
- # Create a new subscope in which to evaluate our code.
- def subscope(scope, name = nil)
- args = {
- :type => self.classname,
- :keyword => self.keyword,
- :namespace => self.namespace
- }
-
- args[:name] = name if name
- scope = scope.newscope(args)
- scope.source = self
-
- return scope
- end
-
- def to_s
- classname
- end
-
- # Check whether a given argument is valid. Searches up through
- # any parent classes that might exist.
- def validattr?(param)
- param = param.to_s
-
- if @arguments.include?(param)
- # It's a valid arg for us
- return true
- elsif param == "name"
- return true
-# elsif defined? @parentclass and @parentclass
-# # Else, check any existing parent
-# if parent = @scope.lookuptype(@parentclass) and parent != []
-# return parent.validarg?(param)
-# elsif builtin = Puppet::Type.type(@parentclass)
-# return builtin.validattr?(param)
-# else
-# raise Puppet::Error, "Could not find parent class %s" %
-# @parentclass
-# end
- elsif Puppet::Type.metaparam?(param)
- return true
- else
- # Or just return false
- return false
- end
- end
- end
-end
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index a4ea26572..81867c84b 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -68,7 +68,7 @@ class Puppet::Parser::Interpreter
elsif self.file
parser.file = self.file
else
- file = Puppet.config.value(:manifest, environment)
+ file = Puppet.settings.value(:manifest, environment)
parser.file = file
end
parser.parse
diff --git a/lib/puppet/pgraph.rb b/lib/puppet/pgraph.rb
index 07bdff4bb..2399d1651 100644
--- a/lib/puppet/pgraph.rb
+++ b/lib/puppet/pgraph.rb
@@ -84,7 +84,7 @@ class Puppet::PGraph < GRATR::Digraph
def matching_edges(events, base = nil)
events.collect do |event|
source = base || event.source
-
+
unless vertex?(source)
Puppet.warning "Got an event from invalid vertex %s" % source.ref
next
@@ -95,7 +95,7 @@ class Puppet::PGraph < GRATR::Digraph
adjacent(source, :direction => :out, :type => :edges).find_all do |edge|
edge.match?(event.event)
end
- end.flatten
+ end.compact.flatten
end
# Take container information from another graph and use it
@@ -209,5 +209,3 @@ class Puppet::PGraph < GRATR::Digraph
end
end
end
-
-# $Id$
diff --git a/lib/puppet/provider/maillist/mailman.rb b/lib/puppet/provider/maillist/mailman.rb
index b556eecd7..903689178 100755
--- a/lib/puppet/provider/maillist/mailman.rb
+++ b/lib/puppet/provider/maillist/mailman.rb
@@ -101,9 +101,9 @@ Puppet::Type.type(:maillist).provide(:mailman) do
# Pull the current state of the list from the full list. We're
# getting some double entendre here....
def query
- provider.class.instances.each do |list|
+ self.class.instances.each do |list|
if list.name == self.name or list.name.downcase == self.name
- return list.property_hash
+ return list.properties
end
end
nil
diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb
index 446ed641c..6a05d9826 100644
--- a/lib/puppet/provider/mount.rb
+++ b/lib/puppet/provider/mount.rb
@@ -50,5 +50,3 @@ module Puppet::Provider::Mount
end
end
end
-
-# $Id$
diff --git a/lib/puppet/rails.rb b/lib/puppet/rails.rb
index 670c622eb..53dafce79 100644
--- a/lib/puppet/rails.rb
+++ b/lib/puppet/rails.rb
@@ -9,7 +9,7 @@ module Puppet::Rails
# This global init does not work for testing, because we remove
# the state dir on every test.
unless ActiveRecord::Base.connected?
- Puppet.config.use(:main, :rails, :puppetmasterd)
+ Puppet.settings.use(:main, :rails, :puppetmasterd)
ActiveRecord::Base.logger = Logger.new(Puppet[:railslog])
begin
@@ -103,7 +103,7 @@ module Puppet::Rails
raise Puppet::DevError, "No activerecord, cannot init Puppet::Rails"
end
- Puppet.config.use(:puppetmasterd, :rails)
+ Puppet.settings.use(:puppetmasterd, :rails)
begin
ActiveRecord::Base.establish_connection(database_arguments())
diff --git a/lib/puppet/reference/configuration.rb b/lib/puppet/reference/configuration.rb
index 65fdbeb7d..4b09002b8 100644
--- a/lib/puppet/reference/configuration.rb
+++ b/lib/puppet/reference/configuration.rb
@@ -1,6 +1,6 @@
config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc => "A reference for all configuration parameters") do
docs = {}
- Puppet.config.each do |name, object|
+ Puppet.settings.each do |name, object|
docs[name] = object
end
diff --git a/lib/puppet/reports/rrdgraph.rb b/lib/puppet/reports/rrdgraph.rb
index 7209a8401..1516d331a 100644
--- a/lib/puppet/reports/rrdgraph.rb
+++ b/lib/puppet/reports/rrdgraph.rb
@@ -102,7 +102,7 @@ Puppet::Network::Handler.report.newreport(:rrdgraph) do
unless File.directory?(hostdir) and FileTest.writable?(hostdir)
# Some hackishness to create the dir with all of the right modes and ownership
- config = Puppet::Util::Config.new
+ config = Puppet::Util::Settings.new
config.setdefaults(:reports, :hostdir => {:default => hostdir, :owner => Puppet[:user], :mode => 0755, :group => Puppet[:group], :desc => "eh"})
# This creates the dir.
diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb
index 8d6e11379..389de4e6e 100644
--- a/lib/puppet/reports/store.rb
+++ b/lib/puppet/reports/store.rb
@@ -1,7 +1,7 @@
require 'puppet'
Puppet::Network::Handler.report.newreport(:store, :useyaml => true) do
- Puppet.config.use(:reporting)
+ Puppet.settings.use(: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.
@@ -11,9 +11,9 @@ Puppet::Network::Handler.report.newreport(:store, :useyaml => true) do
default report)."
def mkclientdir(client, dir)
- config = Puppet::Util::Config.new
+ config = Puppet::Util::Settings.new
config.setdefaults("reportclient-#{client}",
- "clientdir-#{client}" => { :default => dir,
+ "client-#{client}-dir" => { :default => dir,
:mode => 0750,
:desc => "Client dir for %s" % client,
:owner => Puppet[:user],
diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb
index b62a6d2d3..8c65524ee 100644
--- a/lib/puppet/reports/tagmail.rb
+++ b/lib/puppet/reports/tagmail.rb
@@ -31,7 +31,7 @@ Puppet::Network::Handler.report.newreport(:tagmail) do
"
- Puppet.config.use(:tagmail)
+ Puppet.settings.use(:tagmail)
# Find all matching messages.
def match(taglists)
diff --git a/lib/puppet/sslcertificates/ca.rb b/lib/puppet/sslcertificates/ca.rb
index 5e355f7fe..fd416dd5e 100644
--- a/lib/puppet/sslcertificates/ca.rb
+++ b/lib/puppet/sslcertificates/ca.rb
@@ -53,7 +53,7 @@ class Puppet::SSLCertificates::CA
end
def initialize(hash = {})
- Puppet.config.use(:main, :ca, :ssl)
+ Puppet.settings.use(:main, :ca, :ssl)
self.setconfig(hash)
if Puppet[:capass]
@@ -73,7 +73,7 @@ class Puppet::SSLCertificates::CA
self.getcert
init_crl
unless FileTest.exists?(@config[:serial])
- Puppet.config.write(:serial) do |f|
+ Puppet.settings.write(:serial) do |f|
f << "%04X" % 1
end
end
@@ -85,7 +85,7 @@ class Puppet::SSLCertificates::CA
20.times { pass += (rand(74) + 48).chr }
begin
- Puppet.config.write(:capass) { |f| f.print pass }
+ Puppet.settings.write(:capass) { |f| f.print pass }
rescue Errno::EACCES => detail
raise Puppet::Error, detail.to_s
end
@@ -163,10 +163,10 @@ class Puppet::SSLCertificates::CA
Puppet::Util::SUIDManager.asuser(Puppet[:user], Puppet[:group]) do
@cert = cert.mkselfsigned
end
- Puppet.config.write(:cacert) do |f|
+ Puppet.settings.write(:cacert) do |f|
f.puts @cert.to_pem
end
- Puppet.config.write(:capub) do |f|
+ Puppet.settings.write(:capub) do |f|
f.puts @cert.public_key
end
return cert
@@ -201,7 +201,7 @@ class Puppet::SSLCertificates::CA
# Take the Puppet config and store it locally.
def setconfig(hash)
@config = {}
- Puppet.config.params("ca").each { |param|
+ Puppet.settings.params("ca").each { |param|
param = param.intern if param.is_a? String
if hash.include?(param)
@config[param] = hash[param]
@@ -303,7 +303,7 @@ class Puppet::SSLCertificates::CA
raise Puppet::Error, "Certificate request for %s already exists" % host
end
- Puppet.config.writesub(:csrdir, csrfile) do |f|
+ Puppet.settings.writesub(:csrdir, csrfile) do |f|
f.print csr.to_pem
end
end
@@ -319,7 +319,7 @@ class Puppet::SSLCertificates::CA
end
Puppet::SSLCertificates::Inventory::add(cert)
- Puppet.config.writesub(:signeddir, certfile) do |f|
+ Puppet.settings.writesub(:signeddir, certfile) do |f|
f.print cert.to_pem
end
end
@@ -389,7 +389,7 @@ class Puppet::SSLCertificates::CA
@crl.next_update = now + 5 * 365*24*60*60
sign_with_key(@crl)
- Puppet.config.write(:cacrl) do |f|
+ Puppet.settings.write(:cacrl) do |f|
f.puts @crl.to_pem
end
end
diff --git a/lib/puppet/sslcertificates/inventory.rb b/lib/puppet/sslcertificates/inventory.rb
index d475c1c86..14d2155a8 100644
--- a/lib/puppet/sslcertificates/inventory.rb
+++ b/lib/puppet/sslcertificates/inventory.rb
@@ -11,7 +11,7 @@ module Puppet::SSLCertificates
inited = false
end
- Puppet.config.write(:cert_inventory, "a") do |f|
+ Puppet.settings.write(:cert_inventory, "a") do |f|
f.puts((inited ? nil : self.init).to_s + format(cert))
end
end
diff --git a/lib/puppet/sslcertificates/support.rb b/lib/puppet/sslcertificates/support.rb
index e37a52038..c8d2e846b 100644
--- a/lib/puppet/sslcertificates/support.rb
+++ b/lib/puppet/sslcertificates/support.rb
@@ -44,7 +44,7 @@ module Puppet::SSLCertificates::Support
unless instance_variable_get(var)
unless cert = send(reader)
cert = send(maker)
- Puppet.config.write(param) { |f| f.puts cert.to_pem }
+ Puppet.settings.write(param) { |f| f.puts cert.to_pem }
end
instance_variable_set(var, cert)
end
@@ -59,7 +59,7 @@ module Puppet::SSLCertificates::Support
# Our key meta programming can only handle one file, so we have
# to separately write out the public key.
- Puppet.config.write(:hostpubkey) do |f|
+ Puppet.settings.write(:hostpubkey) do |f|
f.print key.public_key.to_pem
end
return key
@@ -104,8 +104,8 @@ module Puppet::SSLCertificates::Support
if cert.nil? or cert == ""
return nil
end
- Puppet.config.write(:hostcert) do |f| f.print cert end
- Puppet.config.write(:localcacert) do |f| f.print cacert end
+ Puppet.settings.write(:hostcert) do |f| f.print cert end
+ Puppet.settings.write(:localcacert) do |f| f.print cacert end
#File.open(@certfile, "w", 0644) { |f| f.print cert }
#File.open(@cacertfile, "w", 0644) { |f| f.print cacert }
begin
diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb
index 46bac3058..4e2ab702f 100644
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@ -6,10 +6,14 @@ require 'puppet/propertychange'
module Puppet
class Transaction
- attr_accessor :component, :resources, :ignoreschedules, :ignoretags
- attr_accessor :relgraph, :sorted_resources, :configurator
+ attr_accessor :component, :configuration, :ignoreschedules
+ attr_accessor :sorted_resources, :configurator
+ # The report, once generated.
attr_reader :report
+
+ # The list of events generated in this transaction.
+ attr_reader :events
attr_writer :tags
@@ -22,13 +26,14 @@ class Transaction
end
end
- # Check to see if we should actually allow deleition.
+ # Check to see if we should actually allow processing, but this really only
+ # matters when a resource is getting deleted.
def allow_processing?(resource, changes)
# If a resource is going to be deleted but it still has
# dependencies, then don't delete it unless it's implicit or the
# dependency is itself being deleted.
if resource.purging? and resource.deleting?
- if deps = @relgraph.dependents(resource) and ! deps.empty? and deps.detect { |d| ! d.deleting? }
+ if deps = relationship_graph.dependents(resource) and ! deps.empty? and deps.detect { |d| ! d.deleting? }
resource.warning "%s still depend%s on me -- not purging" %
[deps.collect { |r| r.ref }.join(","), deps.length > 1 ? "":"s"]
return false
@@ -129,6 +134,9 @@ class Transaction
# Find all of the changed resources.
def changed?
@changes.find_all { |change| change.changed }.collect { |change|
+ unless change.property.resource
+ raise "No resource for %s" % change.inspect
+ end
change.property.resource
}.uniq
end
@@ -137,14 +145,8 @@ class Transaction
# contained resources might never get cleaned up.
def cleanup
if defined? @generated
- @generated.each do |resource|
- resource.remove
- end
+ relationship_graph.remove_resource(*@generated)
end
- if defined? @relgraph
- @relgraph.clear
- end
- @resources.clear
end
# Copy an important relationships from the parent to the newly-generated
@@ -158,10 +160,11 @@ class Transaction
else
edge = [resource, gen_child]
end
- unless @relgraph.edge?(edge[1], edge[0])
- @relgraph.add_edge!(*edge)
+ relationship_graph.add_resource(gen_child) unless relationship_graph.resource(gen_child.ref)
+
+ unless relationship_graph.edge?(edge[1], edge[0])
+ relationship_graph.add_edge!(*edge)
else
- @relgraph.add_vertex!(gen_child)
resource.debug "Skipping automatic relationship to %s" % gen_child
end
end
@@ -192,7 +195,8 @@ class Transaction
children.each do |child|
child.finish
# Make sure that the vertex is in the relationship graph.
- @relgraph.add_vertex!(child)
+ relationship_graph.add_resource(child) unless relationship_graph.resource(child.ref)
+ child.configuration = relationship_graph
end
@generated += children
return children
@@ -265,9 +269,12 @@ class Transaction
# Collect the targets of any subscriptions to those events. We pass
# the parent resource in so it will override the source in the events,
# since eval_generated children can't have direct relationships.
- @relgraph.matching_edges(events, resource).each do |edge|
- edge = edge.dup
- label = edge.label
+ relationship_graph.matching_edges(events, resource).each do |orig_edge|
+ # We have to dup the label here, else we modify the original edge label,
+ # which affects whether a given event will match on the next run, which is,
+ # of course, bad.
+ edge = orig_edge.class.new(orig_edge.source, orig_edge.target)
+ label = orig_edge.label.dup
label[:event] = events.collect { |e| e.event }
edge.label = label
set_trigger(edge)
@@ -283,8 +290,6 @@ class Transaction
def evaluate
@count = 0
- graph(@resources, :resources)
-
# Start logging.
Puppet::Util::Log.newdestination(@report)
@@ -314,6 +319,7 @@ class Transaction
Puppet.debug "Finishing transaction %s with %s changes" %
[self.object_id, @count]
+ @events = allevents
allevents
end
@@ -333,7 +339,7 @@ class Transaction
# enough to check the immediate dependencies, which is why we use
# a tree from the reversed graph.
skip = false
- deps = @relgraph.dependencies(resource)
+ deps = relationship_graph.dependencies(resource)
deps.each do |dep|
if fails = failed?(dep)
resource.notice "Dependency %s[%s] has %s failures" %
@@ -347,7 +353,7 @@ class Transaction
# Collect any dynamically generated resources.
def generate
- list = @resources.vertices
+ list = @configuration.vertices
# Store a list of all generated resources, so that we can clean them up
# after the transaction closes.
@@ -369,7 +375,8 @@ class Transaction
end
made.uniq!
made.each do |res|
- @resources.add_vertex!(res)
+ @configuration.add_resource(res)
+ res.configuration = configuration
newlist << res
@generated << res
res.finish
@@ -410,32 +417,24 @@ class Transaction
return @report
end
- # Produce the graph files if requested.
- def graph(gr, name)
- # We don't want to graph the configuration process.
- return if self.configurator
-
- return unless Puppet[:graph]
-
- Puppet.config.use(:graphing)
-
- file = File.join(Puppet[:graphdir], "%s.dot" % name.to_s)
- File.open(file, "w") { |f|
- f.puts gr.to_dot("name" => name.to_s.capitalize)
- }
+ # Should we ignore tags?
+ def ignore_tags?
+ ! @configuration.host_config?
end
# this should only be called by a Puppet::Type::Component resource now
# and it should only receive an array
def initialize(resources)
- if resources.is_a?(Puppet::PGraph)
- @resources = resources
+ if resources.is_a?(Puppet::Node::Configuration)
+ @configuration = resources
+ elsif resources.is_a?(Puppet::PGraph)
+ raise "Transactions should get configurations now, not PGraph"
else
- @resources = resources.to_graph
+ raise "Transactions require configurations"
end
@resourcemetrics = {
- :total => @resources.vertices.length,
+ :total => @configuration.vertices.length,
:out_of_sync => 0, # The number of resources that had changes
:applied => 0, # The number of resources fixed
:skipped => 0, # The number of resources skipped
@@ -474,7 +473,7 @@ class Transaction
# types, just providers.
def prefetch
prefetchers = {}
- @resources.each do |resource|
+ @configuration.each do |resource|
if provider = resource.provider and provider.class.respond_to?(:prefetch)
prefetchers[provider.class] ||= {}
prefetchers[provider.class][resource.title] = resource
@@ -501,48 +500,49 @@ class Transaction
# Now add any dynamically generated resources
generate()
-
- # Create a relationship graph from our resource graph
- @relgraph = relationship_graph
# This will throw an error if there are cycles in the graph.
- @sorted_resources = @relgraph.topsort
+ @sorted_resources = relationship_graph.topsort
end
-
- # Create a graph of all of the relationships in our resource graph.
+
def relationship_graph
- graph = Puppet::PGraph.new
-
- # First create the dependency graph
- @resources.vertices.each do |vertex|
- graph.add_vertex!(vertex)
- vertex.builddepends.each do |edge|
- graph.add_edge!(edge)
- end
+ configuration.relationship_graph
+ end
+
+ # Send off the transaction report.
+ def send_report
+ begin
+ report = generate_report()
+ rescue => detail
+ Puppet.err "Could not generate report: %s" % detail
+ return
+ end
+
+ if Puppet[:rrdgraph] == true
+ report.graph()
+ end
+
+ if Puppet[:summarize]
+ puts report.summary
end
- # Lastly, add in any autorequires
- graph.vertices.each do |vertex|
- vertex.autorequire.each do |edge|
- unless graph.edge?(edge)
- unless graph.edge?(edge.target, edge.source)
- vertex.debug "Autorequiring %s" % [edge.source]
- graph.add_edge!(edge)
- else
- vertex.debug "Skipping automatic relationship with %s" % (edge.source == vertex ? edge.target : edge.source)
- end
- end
+ if Puppet[:report]
+ begin
+ reportclient().report(report)
+ rescue => detail
+ Puppet.err "Reporting failed: %s" % detail
end
end
-
- graph(graph, :relationships)
-
- # Then splice in the container information
- graph.splice!(@resources, Puppet::Type::Component)
+ end
- graph(graph, :expanded_relationships)
-
- return graph
+ def reportclient
+ unless defined? @reportclient
+ @reportclient = Puppet::Network::Client.report.new(
+ :Server => Puppet[:reportserver]
+ )
+ end
+
+ @reportclient
end
# Roll all completed changes back.
@@ -572,7 +572,7 @@ class Transaction
end
# FIXME This won't work right now.
- @relgraph.matching_edges(events).each do |edge|
+ relationship_graph.matching_edges(events).each do |edge|
@targets[edge.target] << edge
end
@@ -606,7 +606,7 @@ class Transaction
# Should this resource be skipped?
def skip?(resource)
skip = false
- if ! tagged?(resource)
+ if missing_tags?(resource)
resource.debug "Not tagged with %s" % tags.join(", ")
elsif ! scheduled?(resource)
resource.debug "Not scheduled"
@@ -620,29 +620,22 @@ class Transaction
# The tags we should be checking.
def tags
- # Allow the tags to be overridden
unless defined? @tags
- @tags = Puppet[:tags]
- end
-
- unless defined? @processed_tags
- if @tags.nil? or @tags == ""
+ tags = Puppet[:tags]
+ if tags.nil? or tags == ""
@tags = []
else
- @tags = [@tags] unless @tags.is_a? Array
- @tags = @tags.collect do |tag|
- tag.split(/\s*,\s*/)
- end.flatten
+ @tags = tags.split(/\s*,\s*/)
end
- @processed_tags = true
end
@tags
end
# Is this resource tagged appropriately?
- def tagged?(resource)
- self.ignoretags or tags.empty? or resource.tagged?(tags)
+ def missing_tags?(resource)
+ return false if self.ignore_tags? or tags.empty?
+ return true unless resource.tagged?(tags)
end
# Are there any edges that target this resource?
diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb
index aa7eb92f7..ecd179ed7 100644
--- a/lib/puppet/transportable.rb
+++ b/lib/puppet/transportable.rb
@@ -60,7 +60,18 @@ module Puppet
instance_variables
end
- def to_type(parent = nil)
+ def to_ref
+ unless defined? @ref
+ if self.type and self.name
+ @ref = "%s[%s]" % [self.type, self.name]
+ else
+ @ref = nil
+ end
+ end
+ @ref
+ end
+
+ def to_type
retobj = nil
if typeklass = Puppet::Type.type(self.type)
# FIXME This should really be done differently, but...
@@ -77,10 +88,6 @@ module Puppet
raise Puppet::Error.new("Could not find object type %s" % self.type)
end
- if parent
- parent.push retobj
- end
-
return retobj
end
end
@@ -167,12 +174,7 @@ module Puppet
end
end
- str = nil
- if self.top
- str = "%s"
- else
- str = "#{@keyword} #{@type} {\n%s\n}"
- end
+ str = "#{@keyword} #{@name} {\n%s\n}"
str % @children.collect { |child|
child.to_manifest
}.collect { |str|
@@ -188,7 +190,44 @@ module Puppet
instance_variables
end
- def to_type(parent = nil)
+ # Create a resource graph from our structure.
+ def to_configuration
+ configuration = Puppet::Node::Configuration.new(Facter.value("hostname")) do |config|
+ delver = proc do |obj|
+ unless container = config.resource(obj.to_ref)
+ container = obj.to_type
+ config.add_resource container
+ end
+ obj.each do |child|
+ unless resource = config.resource(child.to_ref)
+ next unless resource = child.to_type
+ config.add_resource resource
+ end
+ config.add_edge!(container, resource)
+ if child.is_a?(self.class)
+ delver.call(child)
+ end
+ end
+ end
+
+ delver.call(self)
+ end
+
+ return configuration
+ end
+
+ def to_ref
+ unless defined? @ref
+ if self.type and self.name
+ @ref = "%s[%s]" % [self.type, self.name]
+ else
+ @ref = nil
+ end
+ end
+ @ref
+ end
+
+ def to_type
# this container will contain the equivalent of all objects at
# this level
#container = Puppet::Component.new(:name => @name, :type => @type)
@@ -235,45 +274,16 @@ module Puppet
#Puppet.debug "%s[%s] has no parameters" % [@type, @name]
end
- #if parent
- # hash[:parent] = parent
- #end
container = Puppet::Type::Component.create(hash)
end
#Puppet.info container.inspect
- if parent
- parent.push container
- end
-
# unless we successfully created the container, return an error
unless container
Puppet.warning "Got no container back"
return nil
end
- self.each { |child|
- # the fact that we descend here means that we are
- # always going to execute depth-first
- # which is _probably_ a good thing, but one never knows...
- unless child.is_a?(Puppet::TransBucket) or
- child.is_a?(Puppet::TransObject)
- raise Puppet::DevError,
- "TransBucket#to_type cannot handle objects of type %s" %
- child.class
- end
-
- # Now just call to_type on them with the container as a parent
- begin
- child.to_type(container)
- rescue => detail
- if Puppet[:trace] and ! detail.is_a?(Puppet::Error)
- puts detail.backtrace
- end
- Puppet.err detail.to_s
- end
- }
-
# at this point, no objects at are level are still Transportable
# objects
return container
@@ -287,7 +297,5 @@ module Puppet
end
end
- #------------------------------------------------------------
end
-# $Id$
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index b4b6e3b18..19f676ff4 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -44,7 +44,9 @@ class Type
# that it is clear whether it operates on all attributes (thus has 'attr' in
# the method name, or whether it operates on a specific type of attributes.
attr_accessor :file, :line
- attr_reader :parent
+
+ # The configuration that this resource is stored in.
+ attr_accessor :configuration
attr_writer :title
attr_writer :noop
@@ -310,6 +312,25 @@ class Type
return self[:name]
end
+ # Look up our parent in the configuration, if we have one.
+ def parent
+ return nil unless configuration
+
+ # This is kinda weird.
+ if implicit?
+ parents = configuration.relationship_graph.adjacent(self, :direction => :in)
+ else
+ parents = configuration.adjacent(self, :direction => :in)
+ end
+ if parents
+ # We should never have more than one parent, so let's just ignore
+ # it if we happen to.
+ return parents.shift
+ else
+ return nil
+ end
+ end
+
# Return the "type[name]" style reference.
def ref
"%s[%s]" % [self.class.name.to_s.capitalize, self.title]
diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb
index 5905d85ab..c5842e0e7 100644
--- a/lib/puppet/type/component.rb
+++ b/lib/puppet/type/component.rb
@@ -52,6 +52,7 @@ Puppet::Type.newtype(:component) do
# this is only called on one component over the whole system
# this also won't work with scheduling, but eh
def evaluate
+ raise "Component#evaluate is deprecated"
self.finalize unless self.finalized?
transaction = Puppet::Transaction.new(self)
transaction.component = self
@@ -127,27 +128,6 @@ Puppet::Type.newtype(:component) do
return false
end
end
-
- def push(*childs)
- unless defined? @children
- @children = []
- end
- childs.each { |child|
- # Make sure we don't have any loops here.
- if parentof?(child)
- devfail "Already the parent of %s[%s]" % [child.class.name, child.title]
- end
- unless child.is_a?(Puppet::Type)
- self.debug "Got object of type %s" % child.class
- self.devfail(
- "Containers can only contain Puppet resources, not %s" %
- child.class
- )
- end
- @children.push(child)
- child.parent = self
- }
- end
# Component paths are special because they function as containers.
def pathbuilder
@@ -162,30 +142,15 @@ Puppet::Type.newtype(:component) do
else
myname = self.title
end
- if self.parent
- return [@parent.pathbuilder, myname]
+ if p = self.parent
+ return [p.pathbuilder, myname]
else
return [myname]
end
end
- # Remove an object. The argument determines whether the object's
- # subscriptions get eliminated, too.
- def remove(rmdeps = true)
- # Our children remove themselves from our @children array (else the object
- # we called this on at the top would not be removed), so we duplicate the
- # array and iterate over that. If we don't do this, only half of the
- # objects get removed.
- @children.dup.each { |child|
- child.remove(rmdeps)
- }
-
- @children.clear
-
- # Get rid of params and provider, too.
- super
-
- @parent = nil
+ def ref
+ title
end
# We have a different way of setting the title
@@ -203,30 +168,12 @@ Puppet::Type.newtype(:component) do
end
def refresh
- @children.collect { |child|
+ configuration.adjacent(self).each do |child|
if child.respond_to?(:refresh)
child.refresh
child.log "triggering %s" % :refresh
end
- }
- end
-
- # Convert to a graph object with all of the container info.
- def to_graph
- graph = Puppet::PGraph.new
-
- delver = proc do |obj|
- obj.each do |child|
- graph.add_edge!(obj, child)
- if child.is_a?(self.class)
- delver.call(child)
- end
- end
end
-
- delver.call(self)
-
- return graph
end
def to_s
@@ -237,5 +184,3 @@ Puppet::Type.newtype(:component) do
end
end
end
-
-# $Id$
diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb
index 99b5a7435..5bf9fef78 100644
--- a/lib/puppet/type/pfile.rb
+++ b/lib/puppet/type/pfile.rb
@@ -574,6 +574,9 @@ module Puppet
# Create a new file or directory object as a child to the current
# object.
def newchild(path, local, hash = {})
+ unless configuration
+ raise Puppet::DevError, "File recursion cannot happen without a configuration"
+ end
# make local copy of arguments
args = symbolize_options(@arghash)
@@ -608,14 +611,14 @@ module Puppet
}
child = nil
- klass = self.class
-
+
# The child might already exist because 'localrecurse' runs
# before 'sourcerecurse'. I could push the override stuff into
# a separate method or something, but the work is the same other
# than this last bit, so it doesn't really make sense.
- if child = klass[path]
+ if child = configuration.resource(:file, path)
unless child.parent.object_id == self.object_id
+ puts("Parent is %s, I am %s" % [child.parent.ref, self.ref]) if child.parent
self.debug "Not managing more explicit file %s" %
path
return nil
@@ -640,28 +643,22 @@ module Puppet
#notice "Creating new file with args %s" % args.inspect
args[:parent] = self
begin
- child = klass.implicitcreate(args)
-
- # implicit creation can return nil
- if child.nil?
- return nil
- end
- rescue Puppet::Error => detail
- self.notice(
- "Cannot manage: %s" %
- [detail.message]
- )
- self.debug args.inspect
- child = nil
+ # This method is used by subclasses of :file, so use the class name rather than hard-coding
+ # :file.
+ return nil unless child = configuration.create_implicit_resource(self.class.name, args)
rescue => detail
- self.notice(
- "Cannot manage: %s" %
- [detail]
- )
- self.debug args.inspect
- child = nil
+ puts detail.backtrace
+ self.notice "Cannot manage: %s" % [detail]
+ return nil
end
end
+
+ # LAK:FIXME This shouldn't be necessary, but as long as we're
+ # modeling the relationship graph specifically, it is.
+ configuration.relationship_graph.add_edge! self, child
+ unless child.parent
+ raise "Did not set parent of %s" % child.ref
+ end
return child
end
@@ -669,12 +666,14 @@ module Puppet
# path names, rather than including the full parent's title each
# time.
def pathbuilder
- if defined? @parent
+ # We specifically need to call the method here, so it looks
+ # up our parent in the configuration graph.
+ if parent = parent()
# We only need to behave specially when our parent is also
# a file
- if @parent.is_a?(self.class)
+ if parent.is_a?(self.class)
# Remove the parent file name
- list = @parent.pathbuilder
+ list = parent.pathbuilder
list.pop # remove the parent's path info
return list << self.ref
else
@@ -695,9 +694,7 @@ module Puppet
# files.
def recurse
# are we at the end of the recursion?
- unless self.recurse?
- return
- end
+ return unless self.recurse?
recurse = self[:recurse]
# we might have a string, rather than a number
diff --git a/lib/puppet/util/feature.rb b/lib/puppet/util/feature.rb
index 30c38e286..2669d1ab1 100644
--- a/lib/puppet/util/feature.rb
+++ b/lib/puppet/util/feature.rb
@@ -16,8 +16,7 @@ class Puppet::Util::Feature
if self.class.respond_to?(method)
raise ArgumentError, "Feature %s is already defined" % name
end
-
- result = true
+
if block_given?
begin
result = yield
@@ -25,33 +24,21 @@ class Puppet::Util::Feature
warn "Failed to load feature test for %s: %s" % [name, detail]
result = false
end
- end
-
- if ary = options[:libs]
- ary = [ary] unless ary.is_a?(Array)
-
- ary.each do |lib|
- unless lib.is_a?(String)
- raise ArgumentError, "Libraries must be passed as strings not %s" % lib.class
- end
-
- begin
- require lib
- rescue Exception
- Puppet.debug "Failed to load library '%s' for feature '%s'" % [lib, name]
- result = false
- end
- end
+ @results[name] = result
end
meta_def(method) do
- result
+ unless @results.include?(name)
+ @results[name] = test(name, options)
+ end
+ @results[name]
end
end
# Create a new feature collection.
def initialize(path)
@path = path
+ @results = {}
@loader = Puppet::Util::Autoload.new(self, @path)
end
@@ -71,6 +58,28 @@ class Puppet::Util::Feature
return false
end
end
-end
-# $Id$
+ # Actually test whether the feature is present. We only want to test when
+ # someone asks for the feature, so we don't unnecessarily load
+ # files.
+ def test(name, options)
+ result = true
+ if ary = options[:libs]
+ ary = [ary] unless ary.is_a?(Array)
+
+ ary.each do |lib|
+ unless lib.is_a?(String)
+ raise ArgumentError, "Libraries must be passed as strings not %s" % lib.class
+ end
+
+ begin
+ require lib
+ rescue Exception
+ Puppet.debug "Failed to load library '%s' for feature '%s'" % [lib, name]
+ result = false
+ end
+ end
+ end
+ result
+ end
+end
diff --git a/lib/puppet/util/instance_loader.rb b/lib/puppet/util/instance_loader.rb
index 1a64c9c69..f280014eb 100755
--- a/lib/puppet/util/instance_loader.rb
+++ b/lib/puppet/util/instance_loader.rb
@@ -5,6 +5,12 @@ require 'puppet/util'
# of Puppet::Util::Autoload
module Puppet::Util::InstanceLoader
include Puppet::Util
+
+ # Are we instance-loading this type?
+ def instance_loading?(type)
+ defined?(@autoloaders) and @autoloaders.include?(symbolize(type))
+ end
+
# Define a new type of autoloading.
def instance_load(type, path, options = {})
@autoloaders ||= {}
@@ -54,7 +60,7 @@ module Puppet::Util::InstanceLoader
# Retrieve an alread-loaded instance, or attempt to load our instance.
def loaded_instance(type, name)
name = symbolize(name)
- instances = instance_hash(type)
+ return nil unless instances = instance_hash(type)
unless instances.include? name
if instance_loader(type).load(name)
unless instances.include? name
@@ -70,5 +76,3 @@ module Puppet::Util::InstanceLoader
instances[name]
end
end
-
-# $Id$
diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb
index 13bbc2af1..133aa9c2a 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.config.use(:metrics)
+ Puppet.settings.use(:metrics)
start ||= Time.now.to_i - 5
diff --git a/lib/puppet/util/config.rb b/lib/puppet/util/settings.rb
index 9cdb4cfe3..1478cd8a5 100644
--- a/lib/puppet/util/config.rb
+++ b/lib/puppet/util/settings.rb
@@ -5,7 +5,7 @@ require 'getoptlong'
# The class for handling configuration files.
-class Puppet::Util::Config
+class Puppet::Util::Settings
include Enumerable
include Puppet::Util
@@ -45,15 +45,17 @@ class Puppet::Util::Config
end
# A simplified equality operator.
- def ==(other)
- self.each { |myname, myobj|
- unless other[myname] == value(myname)
- return false
- end
- }
-
- return true
- end
+ # LAK: For some reason, this causes mocha to not be able to mock
+ # the 'value' method, and it's not used anywhere.
+# def ==(other)
+# self.each { |myname, myobj|
+# unless other[myname] == value(myname)
+# return false
+# end
+# }
+#
+# return true
+# end
# Generate the list of valid arguments, in a format that GetoptLong can
# understand, and add them to the passed option list.
@@ -69,14 +71,14 @@ class Puppet::Util::Config
return options
end
- # Turn the config into a transaction and apply it
+ # Turn the config into a Puppet configuration and apply it
def apply
trans = self.to_transportable
begin
- comp = trans.to_type
- trans = comp.evaluate
- trans.evaluate
- comp.remove
+ config = trans.to_configuration
+ config.store_state = false
+ config.apply
+ config.clear
rescue => detail
if Puppet[:trace]
puts detail.backtrace
@@ -476,57 +478,15 @@ class Puppet::Util::Config
end
# Convert a single section into transportable objects.
- def section_to_transportable(section, done = nil, includefiles = true)
+ def section_to_transportable(section, done = nil)
done ||= Hash.new { |hash, key| hash[key] = {} }
objects = []
persection(section) do |obj|
if @config[:mkusers] and value(:mkusers)
- [:owner, :group].each do |attr|
- type = nil
- if attr == :owner
- type = :user
- else
- type = attr
- end
- # If a user and/or group is set, then make sure we're
- # managing that object
- if obj.respond_to? attr and name = obj.send(attr)
- # Skip root or wheel
- next if %w{root wheel}.include?(name.to_s)
-
- # Skip owners and groups we've already done, but tag
- # them with our section if necessary
- if done[type].include?(name)
- tags = done[type][name].tags
- unless tags.include?(section)
- done[type][name].tags = tags << section
- end
- elsif newobj = Puppet::Type.type(type)[name]
- unless newobj.property(:ensure)
- newobj[:ensure] = "present"
- end
- newobj.tag(section)
- if type == :user
- newobj[:comment] ||= "%s user" % name
- end
- else
- newobj = Puppet::TransObject.new(name, type.to_s)
- newobj.tags = ["puppet", "configuration", section]
- newobj[:ensure] = "present"
- if type == :user
- newobj[:comment] ||= "%s user" % name
- end
- # Set the group appropriately for the user
- if type == :user
- newobj[:gid] = Puppet[:group]
- end
- done[type][name] = newobj
- objects << newobj
- end
- end
- end
+ objects += add_user_resources(section, obj, done)
end
+ # Only files are convertable to transportable resources.
if obj.respond_to? :to_transportable
next if value(obj.name) =~ /^\/dev/
transobjects = obj.to_transportable
@@ -544,7 +504,8 @@ class Puppet::Util::Config
end
bucket = Puppet::TransBucket.new
- bucket.type = section
+ bucket.type = "Settings"
+ bucket.name = section
bucket.push(*objects)
bucket.keyword = "class"
@@ -596,9 +557,9 @@ class Puppet::Util::Config
end
# Convert our list of objects into a component that can be applied.
- def to_component
+ def to_configuration
transport = self.to_transportable
- return transport.to_type
+ return transport.to_configuration
end
# Convert our list of objects into a configuration file.
@@ -634,7 +595,7 @@ Generated on #{Time.now}.
end
# Convert our configuration into a list of transportable objects.
- def to_transportable
+ def to_transportable(*sections)
done = Hash.new { |hash, key|
hash[key] = {}
}
@@ -643,14 +604,20 @@ Generated on #{Time.now}.
if defined? @file.file and @file.file
topbucket.name = @file.file
else
- topbucket.name = "configtop"
+ topbucket.name = "top"
end
- topbucket.type = "puppetconfig"
+ topbucket.type = "Settings"
topbucket.top = true
# Now iterate over each section
- eachsection do |section|
- topbucket.push section_to_transportable(section, done)
+ if sections.empty?
+ eachsection do |section|
+ sections << section
+ end
+ end
+ sections.each do |section|
+ obj = section_to_transportable(section, done)
+ topbucket.push obj
end
topbucket
@@ -683,37 +650,31 @@ Generated on #{Time.now}.
}
return if runners.empty?
- bucket = Puppet::TransBucket.new
- bucket.type = "puppetconfig"
- bucket.top = true
-
- # Create a hash to keep track of what we've done so far.
- @done = Hash.new { |hash, key| hash[key] = {} }
- runners.each do |section|
- bucket.push section_to_transportable(section, @done, false)
- end
-
- objects = bucket.to_type
-
- objects.finalize
- tags = nil
- if Puppet[:tags]
- tags = Puppet[:tags]
- Puppet[:tags] = ""
- end
- trans = objects.evaluate
- trans.ignoretags = true
- trans.configurator = true
- trans.evaluate
- if tags
- Puppet[:tags] = tags
- end
-
- # Remove is a recursive process, so it's sufficient to just call
- # it on the component.
- objects.remove(true)
-
- objects = nil
+ bucket = to_transportable(*sections)
+
+ config = bucket.to_configuration
+ config.host_config = false
+ config.apply
+ config.clear
+
+# tags = nil
+# if Puppet[:tags]
+# tags = Puppet[:tags]
+# Puppet[:tags] = ""
+# end
+# trans = objects.evaluate
+# trans.ignoretags = true
+# trans.configurator = true
+# trans.evaluate
+# if tags
+# Puppet[:tags] = tags
+# end
+#
+# # Remove is a recursive process, so it's sufficient to just call
+# # it on the component.
+# objects.remove(true)
+#
+# objects = nil
runners.each { |s| @used << s }
end
@@ -845,6 +806,48 @@ Generated on #{Time.now}.
private
+ # Create the transportable objects for users and groups.
+ def add_user_resources(section, obj, done)
+ resources = []
+ [:owner, :group].each do |attr|
+ type = nil
+ if attr == :owner
+ type = :user
+ else
+ type = attr
+ end
+ # If a user and/or group is set, then make sure we're
+ # managing that object
+ if obj.respond_to? attr and name = obj.send(attr)
+ # Skip root or wheel
+ next if %w{root wheel}.include?(name.to_s)
+
+ # Skip owners and groups we've already done, but tag
+ # them with our section if necessary
+ if done[type].include?(name)
+ tags = done[type][name].tags
+ unless tags.include?(section)
+ done[type][name].tags = tags << section
+ end
+ else
+ newobj = Puppet::TransObject.new(name, type.to_s)
+ newobj.tags = ["puppet", "configuration", section]
+ newobj[:ensure] = :present
+ if type == :user
+ newobj[:comment] ||= "%s user" % name
+ end
+ # Set the group appropriately for the user
+ if type == :user
+ newobj[:gid] = Puppet[:group]
+ end
+ done[type][name] = newobj
+ resources << newobj
+ end
+ end
+ end
+ resources
+ end
+
# Extract extra setting information for files.
def extract_fileinfo(string)
result = {}
@@ -1105,6 +1108,11 @@ Generated on #{Time.now}.
# Set the type appropriately. Yep, a hack. This supports either naming
# the variable 'dir', or adding a slash at the end.
def munge(value)
+ # If it's not a fully qualified path...
+ if value.is_a?(String) and value !~ /^\$/ and value !~ /^\//
+ # Make it one
+ value = File.join(Dir.getwd, value)
+ end
if value.to_s =~ /\/$/
@type = :directory
return value.sub(/\/$/, '')
@@ -1127,9 +1135,6 @@ Generated on #{Time.now}.
end
# Convert the object to a TransObject instance.
- # FIXME There's no dependency system in place right now; if you use
- # a section that requires another section, there's nothing done to
- # correct that for you, at the moment.
def to_transportable
type = self.type
return nil unless type
@@ -1138,9 +1143,9 @@ Generated on #{Time.now}.
objects = []
path = self.value
- unless path =~ /^#{File::SEPARATOR}/
- path = File.join(Dir.getwd, path)
- end
+
+ # Skip plain files that don't exist, since we won't be managing them anyway.
+ return nil unless self.name.to_s =~ /dir$/ or File.exist?(path) or self.create
obj = Puppet::TransObject.new(path, "file")
# Only create directories, or files that are specifically marked to
@@ -1157,7 +1162,7 @@ Generated on #{Time.now}.
}
# Only chown or chgrp when root
- if Puppet::Util::SUIDManager.uid == 0
+ if Puppet.features.root?
[:group, :owner].each { |var|
if value = self.send(var)
obj[var] = value
@@ -1187,7 +1192,7 @@ Generated on #{Time.now}.
name = $1
unless @parent.include?(name)
raise ArgumentError,
- "Configuration parameter '%s' is undefined" %
+ "Settings parameter '%s' is undefined" %
name
end
}
@@ -1218,5 +1223,3 @@ Generated on #{Time.now}.
end
end
end
-
-# $Id$
diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb
index a10183615..9e99057d9 100644
--- a/lib/puppet/util/storage.rb
+++ b/lib/puppet/util/storage.rb
@@ -46,7 +46,7 @@ class Puppet::Util::Storage
self.init
def self.load
- Puppet.config.use(:main)
+ Puppet.settings.use(:main)
unless File.exists?(Puppet[:statefile])
unless defined? @@state and ! @@state.nil?
@@ -99,5 +99,3 @@ class Puppet::Util::Storage
end
end
end
-
-# $Id$