summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2007-09-15 22:17:20 -0600
committerLuke Kanies <luke@madstop.com>2007-09-15 22:17:20 -0600
commitf17f19dae941b17a56c1fc83ed3a89712b98c427 (patch)
tree3615e9be9ed585511bbe0b85737208bbd85f00f0
parent3ccf483f77b026dde8a53bd8e9dff6a5fd0f6722 (diff)
downloadpuppet-f17f19dae941b17a56c1fc83ed3a89712b98c427.tar.gz
puppet-f17f19dae941b17a56c1fc83ed3a89712b98c427.tar.xz
puppet-f17f19dae941b17a56c1fc83ed3a89712b98c427.zip
The whole system now uses Configuration objects instead of
ever converting the Transportable objects into a tree of components and then converting that into a graph. This is a significant step, and drastically simplifies the model of how to use a configuration. The old code might have looked something like this: file = Puppet::Type.create :path => "/whatever", ... comp = Puppet::Type.create :name => :whatever comp.push file transaction = comp.evaluate transaction.evaluate The new code looks like this: file = Puppet::Type.create :path => "/whatever", ... config = Puppet::Node::Configuration.new config.add_resource file config.apply I did not really intend to do this much refactoring, but I found I could not use a Configuration object to do work without refactoring a lot of the system. The primary problem was that the Client::Master and the Config classes determined how the transactions behaved; when I moved to using a Configuration, this distinction was lost, which meant that configurations were often needing to create other configurations, which resulted in a whole lot of infinite recursion (e.g., Config objects that create directories for Puppet use Configuration objects -- yes, I'm s/Config/Settings/g soon -- and these Configuration objects would need to create directories). Not everything is fixed, but it's very close. I am clearly over the hump, though, so I wanted to get a commit in.
-rw-r--r--lib/puppet/defaults.rb9
-rw-r--r--lib/puppet/indirector/facts/yaml.rb4
-rw-r--r--lib/puppet/network/client/master.rb174
-rwxr-xr-xlib/puppet/network/handler/fileserver.rb5
-rw-r--r--lib/puppet/node.rb3
-rw-r--r--lib/puppet/node/configuration.rb81
-rw-r--r--lib/puppet/provider/mount.rb2
-rw-r--r--lib/puppet/transaction.rb98
-rw-r--r--lib/puppet/transportable.rb71
-rw-r--r--lib/puppet/type/component.rb1
-rw-r--r--lib/puppet/util/config.rb185
-rw-r--r--lib/puppet/util/storage.rb2
-rwxr-xr-xspec/unit/node/configuration.rb147
-rwxr-xr-xspec/unit/node/node.rb4
-rwxr-xr-xspec/unit/other/transbucket.rb20
-rwxr-xr-xspec/unit/util/config.rb115
-rw-r--r--test/lib/puppettest/support/assertions.rb52
-rwxr-xr-xtest/lib/puppettest/support/resources.rb34
-rw-r--r--test/lib/puppettest/support/utils.rb38
-rwxr-xr-xtest/network/client/master.rb52
-rwxr-xr-xtest/other/transactions.rb216
21 files changed, 736 insertions, 577 deletions
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index 4b442d094..cb6e67a7b 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -494,12 +494,11 @@ module Puppet
"The server through which to send email reports."]
)
- self.setdefaults(: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,
:fact_store => ["yaml",
- "The backend store to use for client facts."]
- )
-
- self.setdefaults(:yamlfacts,
+ "The backend store to use for client facts."],
:yamlfactdir => ["$vardir/facts",
"The directory in which client facts are stored when the yaml fact store is used."]
)
diff --git a/lib/puppet/indirector/facts/yaml.rb b/lib/puppet/indirector/facts/yaml.rb
index f29ea8ebc..55f0d16ee 100644
--- a/lib/puppet/indirector/facts/yaml.rb
+++ b/lib/puppet/indirector/facts/yaml.rb
@@ -16,10 +16,6 @@ Puppet::Indirector.register_terminus :facts, :yaml do
Puppet::Node::Facts.new(node, values)
end
- def initialize
- Puppet.config.use(:yamlfacts)
- end
-
# Store the facts to disk.
def post(facts)
File.open(path(facts.name), "w", 0600) do |f|
diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb
index c6d7cd75d..b5824e6bd 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
@@ -49,50 +49,6 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
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
- end
-
# Cache the config
def cache(text)
Puppet.info "Caching configuration at %s" % self.cachefile
@@ -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.
@@ -252,9 +208,6 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
Puppet.config.use(:main, :ssl, :puppetd)
super
- # This might be nil
- @configtime = 0
-
self.class.instance = self
@running = false
@@ -297,7 +250,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
end
# The code that actually runs the configuration.
- def run(tags = nil, ignoreschedules = false)
+ def run
got_lock = false
splay
Puppet::Util.sync(:puppetrun).synchronize(Sync::EX) do
@@ -307,19 +260,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
end
end
end
@@ -366,9 +320,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 +334,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 +359,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 +375,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 +409,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 +465,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 +545,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/fileserver.rb b/lib/puppet/network/handler/fileserver.rb
index 993e9d51a..022e0c1d4 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 = Puppet::Node.get(client || Facter.value("hostname"))
+ unless hostname = (client || Facter.value("hostname"))
+ raise ArgumentError, "Could not find hostname"
+ end
+ if node = Puppet::Node.get(hostname)
env = node.environment
else
env = nil
diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb
index 7ad7bc3b3..247cd0521 100644
--- a/lib/puppet/node.rb
+++ b/lib/puppet/node.rb
@@ -38,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.
diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb
index be30ddb68..8cb6ed1f9 100644
--- a/lib/puppet/node/configuration.rb
+++ b/lib/puppet/node/configuration.rb
@@ -5,9 +5,26 @@ require 'puppet/external/gratr/digraph'
# of the information in the configuration, including the resources
# and the relationships between them.
class Puppet::Node::Configuration < Puppet::PGraph
- attr_accessor :name, :version
+ # 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|
@@ -30,10 +47,50 @@ class Puppet::Node::Configuration < Puppet::PGraph
else
@resource_table[ref] = resource
end
+ add_vertex!(resource)
+ end
+
+ # Apply our configuration to the local host.
+ def apply
+ Puppet::Util::Storage.load if host_config?
+ transaction = Puppet::Transaction.new(self)
+
+ 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
+ if defined? transaction and transaction
+ transaction.cleanup
+ end
end
- def clear
- super
+ 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
end
@@ -113,13 +170,23 @@ class Puppet::Node::Configuration < Puppet::PGraph
return result
end
- def initialize(name)
+ # Make sure all of our resources are "finished".
+ def finalize
+ @resource_table.values.each { |resource| resource.finish }
+ end
+
+ def initialize(name = nil)
super()
- @name = name
+ @name = name if name
@extraction_format ||= :transportable
@tags = []
@classes = []
@resource_table = {}
+
+ if block_given?
+ yield(self)
+ finalize()
+ end
end
# Look a resource up by its reference (e.g., File[/etc/passwd]).
@@ -127,6 +194,10 @@ class Puppet::Node::Configuration < Puppet::PGraph
@resource_table[ref]
end
+ def host_config?
+ host_config || false
+ end
+
# Add a tag.
def tag(*names)
names.each do |name|
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/transaction.rb b/lib/puppet/transaction.rb
index 46bac3058..0d81b1e63 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 :component, :configuration, :ignoreschedules
attr_accessor :relgraph, :sorted_resources, :configurator
+ # The report, once generated.
attr_reader :report
+
+ # The list of events generated in this transaction.
+ attr_reader :events
attr_writer :tags
@@ -129,6 +133,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
@@ -144,7 +151,6 @@ class Transaction
if defined? @relgraph
@relgraph.clear
end
- @resources.clear
end
# Copy an important relationships from the parent to the newly-generated
@@ -283,7 +289,7 @@ class Transaction
def evaluate
@count = 0
- graph(@resources, :resources)
+ graph(@configuration, :resources)
# Start logging.
Puppet::Util::Log.newdestination(@report)
@@ -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,7 @@ class Transaction
end
made.uniq!
made.each do |res|
- @resources.add_vertex!(res)
+ @configuration.add_vertex!(res)
newlist << res
@generated << res
res.finish
@@ -412,8 +418,8 @@ class Transaction
# Produce the graph files if requested.
def graph(gr, name)
- # We don't want to graph the configuration process.
- return if self.configurator
+ # We only want to graph the main host configuration.
+ return unless @configuration.host_config?
return unless Puppet[:graph]
@@ -425,17 +431,24 @@ class Transaction
}
end
+ # 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 +487,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
@@ -514,7 +527,7 @@ class Transaction
graph = Puppet::PGraph.new
# First create the dependency graph
- @resources.vertices.each do |vertex|
+ @configuration.vertices.each do |vertex|
graph.add_vertex!(vertex)
vertex.builddepends.each do |edge|
graph.add_edge!(edge)
@@ -538,12 +551,48 @@ class Transaction
graph(graph, :relationships)
# Then splice in the container information
- graph.splice!(@resources, Puppet::Type::Component)
+ graph.splice!(@configuration, Puppet::Type::Component)
graph(graph, :expanded_relationships)
return 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
+
+ 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
# Roll all completed changes back.
def rollback
@@ -606,7 +655,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 +669,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 782ba2467..e4cde6e37 100644
--- a/lib/puppet/transportable.rb
+++ b/lib/puppet/transportable.rb
@@ -71,7 +71,7 @@ module Puppet
@ref
end
- def to_type(parent = nil)
+ def to_type
retobj = nil
if typeklass = Puppet::Type.type(self.type)
# FIXME This should really be done differently, but...
@@ -88,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
@@ -200,29 +196,29 @@ module Puppet
end
# Create a resource graph from our structure.
- def to_graph
- graph = Puppet::Node::Configuration.new(Facter.value(:hostname))
-
- delver = proc do |obj|
- obj.each do |child|
- unless container = graph.resource(obj.to_ref)
+ 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
- graph.add_resource container
- end
- unless resource = graph.resource(child.to_ref)
- resource = child.to_type
- graph.add_resource resource
+ config.add_resource container
end
- graph.add_edge!(container, resource)
- if child.is_a?(self.class)
- delver.call(child)
+ 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
- delver.call(self)
-
- return graph
+ return configuration
end
def to_ref
@@ -236,7 +232,7 @@ module Puppet
@ref
end
- def to_type(parent = nil)
+ def to_type
# this container will contain the equivalent of all objects at
# this level
#container = Puppet::Component.new(:name => @name, :type => @type)
@@ -283,45 +279,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
diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb
index 79afa95a7..1615abad8 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
diff --git a/lib/puppet/util/config.rb b/lib/puppet/util/config.rb
index 9cdb4cfe3..5875f141b 100644
--- a/lib/puppet/util/config.rb
+++ b/lib/puppet/util/config.rb
@@ -69,14 +69,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 +476,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 +502,8 @@ class Puppet::Util::Config
end
bucket = Puppet::TransBucket.new
- bucket.type = section
+ bucket.type = "Configuration"
+ bucket.name = section
bucket.push(*objects)
bucket.keyword = "class"
@@ -634,7 +593,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 +602,23 @@ 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 = "Configuration"
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)
+ #puts obj.to_manifest
+ #puts "%s: %s" % [section, obj.inspect]
+ topbucket.push obj
+ #topbucket.push section_to_transportable(section, done)
end
topbucket
@@ -683,37 +651,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 +807,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 = {}
@@ -1141,6 +1145,9 @@ Generated on #{Time.now}.
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)
obj = Puppet::TransObject.new(path, "file")
# Only create directories, or files that are specifically marked to
@@ -1157,7 +1164,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
@@ -1218,5 +1225,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..cd41aa572 100644
--- a/lib/puppet/util/storage.rb
+++ b/lib/puppet/util/storage.rb
@@ -99,5 +99,3 @@ class Puppet::Util::Storage
end
end
end
-
-# $Id$
diff --git a/spec/unit/node/configuration.rb b/spec/unit/node/configuration.rb
index 774a1550f..3c30d9b3e 100755
--- a/spec/unit/node/configuration.rb
+++ b/spec/unit/node/configuration.rb
@@ -134,25 +134,158 @@ describe Puppet::Node::Configuration, " when extracting transobjects" do
end
end
-describe Puppet::Node::Configuration, " functioning as a resource container" do
+describe Puppet::Node::Configuration, " when functioning as a resource container" do
before do
- @graph = Puppet::Node::Configuration.new("host")
+ @config = Puppet::Node::Configuration.new("host")
@one = stub 'resource1', :ref => "Me[you]"
@two = stub 'resource2', :ref => "Me[him]"
@dupe = stub 'resource3', :ref => "Me[you]"
end
it "should make all vertices available by resource reference" do
- @graph.add_resource(@one)
- @graph.resource(@one.ref).should equal(@one)
+ @config.add_resource(@one)
+ @config.resource(@one.ref).should equal(@one)
+ @config.vertices.find { |r| r.ref == @one.ref }.should equal(@one)
end
it "should not allow two resources with the same resource reference" do
- @graph.add_resource(@one)
- proc { @graph.add_resource(@dupe) }.should raise_error(ArgumentError)
+ @config.add_resource(@one)
+ proc { @config.add_resource(@dupe) }.should raise_error(ArgumentError)
end
it "should not store objects that do not respond to :ref" do
- proc { @graph.add_resource("thing") }.should raise_error(ArgumentError)
+ proc { @config.add_resource("thing") }.should raise_error(ArgumentError)
end
+
+ it "should remove all resources when asked" do
+ @config.add_resource @one
+ @config.add_resource @two
+ @one.expects :remove
+ @two.expects :remove
+ @config.clear(true)
+ end
+
+ it "should support a mechanism for finishing resources" do
+ @one.expects :finish
+ @two.expects :finish
+ @config.add_resource @one
+ @config.add_resource @two
+
+ @config.finalize
+ end
+
+ it "should optionally support an initialization block and should finalize after such blocks" do
+ @one.expects :finish
+ @two.expects :finish
+ config = Puppet::Node::Configuration.new("host") do |conf|
+ conf.add_resource @one
+ conf.add_resource @two
+ end
+ end
+end
+
+module ApplyingConfigurations
+ def setup
+ @config = Puppet::Node::Configuration.new("host")
+
+ @config.retrieval_duration = Time.now
+ @transaction = mock 'transaction'
+ Puppet::Transaction.stubs(:new).returns(@transaction)
+ @transaction.stubs(:evaluate)
+ @transaction.stubs(:cleanup)
+ @transaction.stubs(:addtimes)
+ end
+end
+
+describe Puppet::Node::Configuration, " when applying" do
+ include ApplyingConfigurations
+
+ it "should create and evaluate a transaction" do
+ @transaction.expects(:evaluate)
+ @config.apply
+ end
+
+ it "should provide the configuration time to the transaction" do
+ @transaction.expects(:addtimes).with do |arg|
+ arg[:config_retrieval].should be_instance_of(Time)
+ true
+ end
+ @config.apply
+ end
+
+ it "should clean up the transaction" do
+ @transaction.expects :cleanup
+ @config.apply
+ end
+
+ it "should return the transaction" do
+ @config.apply.should equal(@transaction)
+ end
+
+ it "should yield the transaction if a block is provided" do
+ pending "the code works but is not tested"
+ end
+
+ it "should default to not being a host configuration" do
+ @config.host_config.should be_nil
+ end
+end
+
+describe Puppet::Node::Configuration, " when applying host configurations" do
+ include ApplyingConfigurations
+
+ # super() doesn't work in the setup method for some reason
+ before do
+ @config.host_config = true
+ end
+
+ it "should send a report if reporting is enabled" do
+ Puppet[:report] = true
+ @transaction.expects :send_report
+ @config.apply
+ end
+
+ it "should send a report if report summaries are enabled" do
+ Puppet[:summarize] = true
+ @transaction.expects :send_report
+ @config.apply
+ end
+
+ it "should initialize the state database before applying a configuration" do
+ Puppet::Util::Storage.expects(:load)
+
+ # Short-circuit the apply, so we know we're loading before the transaction
+ Puppet::Transaction.expects(:new).raises ArgumentError
+ proc { @config.apply }.should raise_error(ArgumentError)
+ end
+
+ it "should sync the state database after applying" do
+ Puppet::Util::Storage.expects(:store)
+ @config.apply
+ end
+
+ after { Puppet.config.clear }
+end
+
+describe Puppet::Node::Configuration, " when applying non-host configurations" do
+ include ApplyingConfigurations
+
+ before do
+ @config.host_config = false
+ end
+
+ it "should never send reports" do
+ Puppet[:report] = true
+ Puppet[:summarize] = true
+ @transaction.expects(:send_report).never
+ @config.apply
+ end
+
+ it "should never modify the state database" do
+ Puppet::Util::Storage.expects(:load).never
+ Puppet::Util::Storage.expects(:store).never
+ @config.apply
+ end
+
+ after { Puppet.config.clear }
end
diff --git a/spec/unit/node/node.rb b/spec/unit/node/node.rb
index 9342dc5ce..2f63c253d 100755
--- a/spec/unit/node/node.rb
+++ b/spec/unit/node/node.rb
@@ -11,6 +11,10 @@ describe Puppet::Node, " when initializing" do
@node.name.should == "testnode"
end
+ it "should not allow nil node names" do
+ proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError)
+ end
+
it "should default to an empty parameter hash" do
@node.parameters.should == {}
end
diff --git a/spec/unit/other/transbucket.rb b/spec/unit/other/transbucket.rb
index c013973ee..8cb9abaa4 100755
--- a/spec/unit/other/transbucket.rb
+++ b/spec/unit/other/transbucket.rb
@@ -48,7 +48,7 @@ describe Puppet::TransBucket do
end
end
-describe Puppet::TransBucket, " when generating a resource graph" do
+describe Puppet::TransBucket, " when generating a configuration" do
before do
@bottom = Puppet::TransBucket.new
@bottom.type = "fake"
@@ -70,7 +70,7 @@ describe Puppet::TransBucket, " when generating a resource graph" do
@top.push(@topobj)
@top.push(@middle)
- @graph = @top.to_graph
+ @config = @top.to_configuration
@users = %w{top middle bottom}
@fakes = %w{fake[bottom] fake[middle] fake[top]}
@@ -78,18 +78,28 @@ describe Puppet::TransBucket, " when generating a resource graph" do
it "should convert all transportable objects to RAL resources" do
@users.each do |name|
- @graph.vertices.find { |r| r.class.name == :user and r.title == name }.should be_instance_of(Puppet::Type.type(:user))
+ @config.vertices.find { |r| r.class.name == :user and r.title == name }.should be_instance_of(Puppet::Type.type(:user))
end
end
it "should convert all transportable buckets to RAL components" do
@fakes.each do |name|
- @graph.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component))
+ @config.vertices.find { |r| r.class.name == :component and r.title == name }.should be_instance_of(Puppet::Type.type(:component))
end
end
it "should add all resources to the graph's resource table" do
- @graph.resource("fake[top]").should equal(@top)
+ @config.resource("fake[top]").should equal(@top)
+ end
+
+ it "should finalize all resources" do
+ @config.vertices.each do |vertex| vertex.should be_finalized end
+ end
+
+ it "should only call to_type on each resource once" do
+ @topobj.expects(:to_type)
+ @bottomobj.expects(:to_type)
+ @top.to_configuration
end
after do
diff --git a/spec/unit/util/config.rb b/spec/unit/util/config.rb
index 28ccb04d7..ff9e28d3b 100755
--- a/spec/unit/util/config.rb
+++ b/spec/unit/util/config.rb
@@ -299,7 +299,7 @@ describe Puppet::Util::Config, " when parsing its configuration" do
end
it "should support specifying file all metadata (owner, group, mode) in the configuration file" do
- @config.setdefaults :section, :myfile => ["/my/file", "a"]
+ @config.setdefaults :section, :myfile => ["/myfile", "a"]
text = "[main]
myfile = /other/file {owner = luke, group = luke, mode = 644}
@@ -312,7 +312,7 @@ describe Puppet::Util::Config, " when parsing its configuration" do
end
it "should support specifying file a single piece of metadata (owner, group, or mode) in the configuration file" do
- @config.setdefaults :section, :myfile => ["/my/file", "a"]
+ @config.setdefaults :section, :myfile => ["/myfile", "a"]
text = "[main]
myfile = /other/file {owner = luke}
@@ -388,16 +388,34 @@ describe Puppet::Util::Config, " when reparsing its configuration" do
end
describe Puppet::Util::Config, " when being used to manage the host machine" do
+ before do
+ @config = Puppet::Util::Config.new
+ @config.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"]
+ @config.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "luke", :group => "johnny", :mode => 0755}
+ @config.setdefaults :files, :myfile => {:default => "/myfile", :desc => "a", :mode => 0755}
+ end
+
it "should provide a method that writes files with the correct modes" do
pending "Not converted from test/unit yet"
end
it "should provide a method that creates directories with the correct modes" do
- pending "Not converted from test/unit yet"
+ Puppet::Util::SUIDManager.expects(:asuser).with("luke", "johnny").yields
+ Dir.expects(:mkdir).with("/otherdir", 0755)
+ @config.mkdir(:otherdir)
end
- it "should provide a method to declare what directories should exist" do
- pending "Not converted from test/unit yet"
+ it "should be able to create needed directories in a single section" do
+ Dir.expects(:mkdir).with("/maindir")
+ Dir.expects(:mkdir).with("/seconddir")
+ @config.use(:main)
+ end
+
+ it "should be able to create needed directories in multiple sections" do
+ Dir.expects(:mkdir).with("/maindir")
+ Dir.expects(:mkdir).with("/otherdir", 0755)
+ Dir.expects(:mkdir).with("/seconddir")
+ @config.use(:main, :other)
end
it "should provide a method to trigger enforcing of file modes on existing files and directories" do
@@ -408,13 +426,85 @@ describe Puppet::Util::Config, " when being used to manage the host machine" do
pending "Not converted from test/unit yet"
end
- it "should provide an option to create needed users and groups" do
+ it "should provide a method to convert the file mode enforcement into transportable resources" do
+ # Make it think we're root so it tries to manage user and group.
+ Puppet.features.stubs(:root?).returns(true)
+ File.stubs(:exist?).with("/myfile").returns(true)
+ trans = nil
+ trans = @config.to_transportable
+ resources = []
+ trans.delve { |obj| resources << obj if obj.is_a? Puppet::TransObject }
+ %w{/maindir /seconddir /otherdir /myfile}.each do |path|
+ obj = resources.find { |r| r.type == "file" and r.name == path }
+ if path.include?("dir")
+ obj[:ensure].should == :directory
+ else
+ # Do not create the file, just manage mode
+ obj[:ensure].should be_nil
+ end
+ obj.should be_instance_of(Puppet::TransObject)
+ case path
+ when "/otherdir":
+ obj[:owner].should == "luke"
+ obj[:group].should == "johnny"
+ obj[:mode].should == 0755
+ when "/myfile":
+ obj[:mode].should == 0755
+ end
+ end
+ end
+
+ it "should not try to manage user or group when not running as root" do
+ Puppet.features.stubs(:root?).returns(false)
+ trans = nil
+ trans = @config.to_transportable(:other)
+ trans.delve do |obj|
+ next unless obj.is_a?(Puppet::TransObject)
+ obj[:owner].should be_nil
+ obj[:group].should be_nil
+ end
+ end
+
+ it "should add needed users and groups to the manifest when asked" do
+ # This is how we enable user/group management
+ @config.setdefaults :main, :mkusers => [true, "w"]
+ Puppet.features.stubs(:root?).returns(false)
+ trans = nil
+ trans = @config.to_transportable(:other)
+ resources = []
+ trans.delve { |obj| resources << obj if obj.is_a? Puppet::TransObject and obj.type != "file" }
+
+ user = resources.find { |r| r.type == "user" }
+ user.should be_instance_of(Puppet::TransObject)
+ user.name.should == "luke"
+ user[:ensure].should == :present
+
+ # This should maybe be a separate test, but...
+ group = resources.find { |r| r.type == "group" }
+ group.should be_instance_of(Puppet::TransObject)
+ group.name.should == "johnny"
+ group[:ensure].should == :present
+ end
+
+ it "should ignore tags and schedules when creating files and directories"
+
+ it "should apply all resources in debug mode to reduce logging"
+
+ it "should not try to manage absent files" do
+ # Make it think we're root so it tries to manage user and group.
+ Puppet.features.stubs(:root?).returns(true)
+ trans = nil
+ trans = @config.to_transportable
+ file = nil
+ trans.delve { |obj| file = obj if obj.name == "/myfile" }
+ file.should be_nil
+ end
+
+ it "should be able to turn the current configuration into a parseable manifest" do
pending "Not converted from test/unit yet"
end
- it "should provide a method to print out the current configuration" do
- pending "Not converted from test/unit yet"
- end
+ it "should convert octal numbers correctly when producing a manifest"
it "should be able to provide all of its parameters in a format compatible with GetOpt::Long" do
pending "Not converted from test/unit yet"
@@ -423,4 +513,11 @@ describe Puppet::Util::Config, " when being used to manage the host machine" do
it "should not attempt to manage files within /dev" do
pending "Not converted from test/unit yet"
end
+
+ it "should not modify the stored state database when managing resources" do
+ Puppet::Util::Storage.expects(:store).never
+ Puppet::Util::Storage.expects(:load).never
+ Dir.expects(:mkdir).with("/maindir")
+ @config.use(:main)
+ end
end
diff --git a/test/lib/puppettest/support/assertions.rb b/test/lib/puppettest/support/assertions.rb
index 9369f17e7..7e3e5ca2b 100644
--- a/test/lib/puppettest/support/assertions.rb
+++ b/test/lib/puppettest/support/assertions.rb
@@ -38,7 +38,7 @@ module PuppetTest
run_events(:rollback, trans, events, msg)
end
- def assert_events(events, *items)
+ def assert_events(events, *resources)
trans = nil
comp = nil
msg = nil
@@ -46,56 +46,26 @@ module PuppetTest
unless events.is_a? Array
raise Puppet::DevError, "Incorrect call of assert_events"
end
- if items[-1].is_a? String
- msg = items.pop
+ if resources[-1].is_a? String
+ msg = resources.pop
end
- remove_comp = false
- # They either passed a comp or a list of items.
- if items[0].is_a? Puppet.type(:component)
- comp = items.shift
- else
- comp = newcomp(items[0].title, *items)
- remove_comp = true
- end
- msg ||= comp.title
- assert_nothing_raised("Component %s failed" % [msg]) {
- trans = comp.evaluate
- }
+ config = resources2config(*resources)
+ transaction = Puppet::Transaction.new(config)
- run_events(:evaluate, trans, events, msg)
+ run_events(:evaluate, transaction, events, msg)
- if remove_comp
- Puppet.type(:component).delete(comp)
- end
-
- return trans
+ return transaction
end
# A simpler method that just applies what we have.
- def assert_apply(*objects)
- if objects[0].is_a?(Puppet.type(:component))
- comp = objects.shift
- unless objects.empty?
- objects.each { |o| comp.push o }
- end
- else
- comp = newcomp(*objects)
- end
- trans = nil
-
- assert_nothing_raised("Failed to create transaction") {
- trans = comp.evaluate
- }
+ def assert_apply(*resources)
+ config = resources2config(*resources)
events = nil
- assert_nothing_raised("Failed to evaluate transaction") {
- events = trans.evaluate.collect { |e| e.event }
+ assert_nothing_raised("Failed to evaluate") {
+ events = config.apply.events
}
- trans.cleanup
- Puppet.type(:component).delete(comp)
events
end
end
-
-# $Id$
diff --git a/test/lib/puppettest/support/resources.rb b/test/lib/puppettest/support/resources.rb
index 45d89c5fb..18d7caa77 100755
--- a/test/lib/puppettest/support/resources.rb
+++ b/test/lib/puppettest/support/resources.rb
@@ -4,34 +4,34 @@
# Copyright (c) 2006. All rights reserved.
module PuppetTest::Support::Resources
- def treefile(name)
- Puppet::Type.type(:file).create :path => "/tmp/#{name}", :mode => 0755
+ def tree_resource(name)
+ Puppet::Type.type(:file).create :title => name, :path => "/tmp/#{name}", :mode => 0755
end
- def treecomp(name)
+ def tree_container(name)
Puppet::Type::Component.create :name => name, :type => "yay"
end
- def treenode(name, *children)
- comp = treecomp name
- children.each do |c|
- if c.is_a?(String)
- comp.push treefile(c)
- else
- comp.push c
+ def treenode(config, name, *resources)
+ comp = tree_container name
+ resources.each do |resource|
+ if resource.is_a?(String)
+ resource = tree_resource(resource)
end
+ config.add_edge!(comp, resource)
+ config.add_resource resource unless config.resource(resource.ref)
end
return comp
end
def mktree
- one = treenode("one", "a", "b")
- two = treenode("two", "c", "d")
- middle = treenode("middle", "e", "f", two)
- top = treenode("top", "g", "h", middle, one)
+ configuration = Puppet::Node::Configuration.new do |config|
+ one = treenode(config, "one", "a", "b")
+ two = treenode(config, "two", "c", "d")
+ middle = treenode(config, "middle", "e", "f", two)
+ top = treenode(config, "top", "g", "h", middle, one)
+ end
- return one, two, middle, top
+ return configuration
end
end
-
-# $Id$ \ No newline at end of file
diff --git a/test/lib/puppettest/support/utils.rb b/test/lib/puppettest/support/utils.rb
index c7d54d5e6..7f4260e31 100644
--- a/test/lib/puppettest/support/utils.rb
+++ b/test/lib/puppettest/support/utils.rb
@@ -19,6 +19,25 @@ module PuppetTest
}
end
+ # Turn a list of resources, or possibly a configuration and some resources,
+ # into a configuration object.
+ def resources2config(*resources)
+ if resources[0].is_a?(Puppet::Node::Configuration)
+ config = resources.shift
+ unless resources.empty?
+ resources.each { |r| config.add_resource r }
+ end
+ elsif resources[0].is_a?(Puppet.type(:component))
+ raise ArgumentError, "resource2config() no longer accpts components"
+ comp = resources.shift
+ comp.delve
+ else
+ config = Puppet::Node::Configuration.new
+ resources.each { |res| config.add_resource res }
+ end
+ return config
+ end
+
# stop any services that might be hanging around
def stopservices
if stype = Puppet::Type.type(:service)
@@ -127,20 +146,17 @@ module PuppetTest
}
end
- def newcomp(*ary)
- name = nil
- if ary[0].is_a?(String)
- name = ary.shift
+ def mk_configuration(*resources)
+ if resources[0].is_a?(String)
+ name = resources.shift
else
- name = ary[0].title
+ name = :testing
+ end
+ config = Puppet::Node::Configuration.new :testing do |conf|
+ resources.each { |resource| conf.add_resource resource }
end
- comp = Puppet.type(:component).create(:name => name)
- ary.each { |item|
- comp.push item
- }
-
- return comp
+ return config
end
def setme
diff --git a/test/network/client/master.rb b/test/network/client/master.rb
index a29254d16..2e9ed2752 100755
--- a/test/network/client/master.rb
+++ b/test/network/client/master.rb
@@ -88,51 +88,6 @@ class TestMasterClient < Test::Unit::TestCase
return master, objects
end
- def test_apply
- master, objects = mk_fake_client
-
- check = Proc.new do |hash|
- assert(objects.trans, "transaction was not created")
- trans = objects.trans
- hash[:yes].each do |m|
- assert_equal(1, trans.send(m.to_s + "?"), "did not call #{m} enough times")
- end
- hash[:no].each do |m|
- assert_equal(0, trans.send(m.to_s + "?"), "called #{m} too many times")
- end
- end
-
- # First try it with no arguments
- assert_nothing_raised do
- master.apply
- end
- check.call :yes => %w{evaluate cleanup addtimes}, :no => %w{report tags ignoreschedules}
- assert_equal(0, master.reported, "master sent report with reports disabled")
-
-
- # Now enable reporting and make sure the report method gets called
- Puppet[:report] = true
- assert_nothing_raised do
- master.apply
- end
- check.call :yes => %w{evaluate cleanup addtimes}, :no => %w{tags ignoreschedules}
- assert_equal(1, master.reported, "master did not send report")
-
- # Now try it with tags enabled
- assert_nothing_raised do
- master.apply("tags")
- end
- check.call :yes => %w{evaluate cleanup tags addtimes}, :no => %w{ignoreschedules}
- assert_equal(2, master.reported, "master did not send report")
-
- # and ignoreschedules
- assert_nothing_raised do
- master.apply("tags", true)
- end
- check.call :yes => %w{evaluate cleanup tags ignoreschedules addtimes}, :no => %w{}
- assert_equal(3, master.reported, "master did not send report")
- end
-
def test_getconfig
client = mkclient
@@ -167,9 +122,8 @@ class TestMasterClient < Test::Unit::TestCase
[:getplugins, :get_actual_config].each do |method|
assert($methodsrun.include?(method), "method %s was not run" % method)
end
-
- objects = client.objects
- assert(objects.finalized?, "objects were not finalized")
+
+ assert_instance_of(Puppet::Node::Configuration, client.configuration, "Configuration was not created")
end
def test_disable
@@ -233,7 +187,7 @@ class TestMasterClient < Test::Unit::TestCase
}
end
- # This method is supposed
+ # This method downloads files, and yields each file object if a block is given.
def test_download
source = tempfile()
dest = tempfile()
diff --git a/test/other/transactions.rb b/test/other/transactions.rb
index bf5f65084..833ead662 100755
--- a/test/other/transactions.rb
+++ b/test/other/transactions.rb
@@ -7,8 +7,6 @@ require 'puppettest'
require 'mocha'
require 'puppettest/support/resources'
-# $Id$
-
class TestTransactions < Test::Unit::TestCase
include PuppetTest::FileTesting
include PuppetTest::Support::Resources
@@ -133,7 +131,7 @@ class TestTransactions < Test::Unit::TestCase
inst = type.create :name => "yay"
# Create a transaction
- trans = Puppet::Transaction.new(newcomp(inst))
+ trans = Puppet::Transaction.new(mk_configuration(inst))
# Make sure prefetch works
assert_nothing_raised do
@@ -255,7 +253,7 @@ class TestTransactions < Test::Unit::TestCase
}
- component = newcomp("file",file)
+ component = mk_configuration("file",file)
require 'etc'
groupname = Etc.getgrgid(File.stat(file.name).gid).name
assert_nothing_raised() {
@@ -299,7 +297,7 @@ class TestTransactions < Test::Unit::TestCase
@@tmpfiles << execfile
- component = newcomp("both",file,exec)
+ component = mk_configuration("both",file,exec)
# 'subscribe' expects an array of arrays
exec[:subscribe] = [[file.class.name,file.name]]
@@ -343,24 +341,30 @@ class TestTransactions < Test::Unit::TestCase
file[:group] = @groups[0]
assert_apply(file)
- fcomp = newcomp("file",file)
- ecomp = newcomp("exec",exec)
+ config = Puppet::Node::Configuration.new
+ fcomp = Puppet::Type.type(:component).create(:name => "file")
+ config.add_resource fcomp
+ config.add_resource file
+ config.add_edge!(fcomp, file)
- component = newcomp("both",fcomp,ecomp)
+ ecomp = Puppet::Type.type(:component).create(:name => "exec")
+ config.add_resource ecomp
+ config.add_resource exec
+ config.add_edge!(ecomp, exec)
# 'subscribe' expects an array of arrays
#component[:require] = [[file.class.name,file.name]]
ecomp[:subscribe] = fcomp
exec[:refreshonly] = true
- trans = assert_events([], component)
+ trans = assert_events([], config)
assert_nothing_raised() {
file[:group] = @groups[1]
file[:mode] = "755"
}
- trans = assert_events([:file_changed, :file_changed, :triggered], component)
+ trans = assert_events([:file_changed, :file_changed, :triggered], config)
end
# Make sure that multiple subscriptions get triggered.
@@ -437,11 +441,10 @@ class TestTransactions < Test::Unit::TestCase
:subscribe => ["file", file.name]
)
- comp = newcomp(file,exec)
- comp.finalize
+ config = mk_configuration(file,exec)
# Run it once
- assert_apply(comp)
+ assert_apply(config)
assert(FileTest.exists?(fname), "File did not get created")
assert(!exec.scheduled?, "Exec is somehow scheduled")
@@ -451,7 +454,7 @@ class TestTransactions < Test::Unit::TestCase
file[:content] = "some content"
- assert_events([:file_changed, :triggered], comp)
+ assert_events([:file_changed, :triggered], config)
assert(FileTest.exists?(fname), "File did not get recreated")
# Now remove it, so it can get created again
@@ -469,7 +472,7 @@ class TestTransactions < Test::Unit::TestCase
assert(! file.insync?(file.retrieve), "Uh, file is in sync?")
- assert_events([:file_changed, :triggered], comp)
+ assert_events([:file_changed, :triggered], config)
assert(FileTest.exists?(fname), "File did not get recreated")
end
@@ -493,11 +496,9 @@ class TestTransactions < Test::Unit::TestCase
:ensure => :file
)
- comp = newcomp(exec, file1, file2)
-
- comp.finalize
+ config = mk_configuration(exec, file1, file2)
- assert_apply(comp)
+ assert_apply(config)
assert(! FileTest.exists?(file1[:path]),
"File got created even tho its dependency failed")
@@ -506,24 +507,20 @@ class TestTransactions < Test::Unit::TestCase
end
end
- def f(n)
- Puppet::Type.type(:file)["/tmp/#{n.to_s}"]
- end
-
def test_relationship_graph
- one, two, middle, top = mktree
+ config = mktree
+
+ config.meta_def(:f) do |name|
+ self.resource("File[%s]" % name)
+ end
- {one => two, "f" => "c", "h" => middle}.each do |source, target|
- if source.is_a?(String)
- source = f(source)
- end
- if target.is_a?(String)
- target = f(target)
- end
+ {"one" => "two", "File[f]" => "File[c]", "File[h]" => "middle"}.each do |source_ref, target_ref|
+ source = config.resource(source_ref) or raise "Missing %s" % source_ref
+ target = config.resource(target_ref) or raise "Missing %s" % target_ref
target[:require] = source
end
- trans = Puppet::Transaction.new(top)
+ trans = Puppet::Transaction.new(config)
graph = nil
assert_nothing_raised do
@@ -544,13 +541,13 @@ class TestTransactions < Test::Unit::TestCase
sorted = graph.topsort.reverse
# Now make sure the appropriate edges are there and are in the right order
- assert(graph.dependents(f(:f)).include?(f(:c)),
+ assert(graph.dependents(config.f(:f)).include?(config.f(:c)),
"c not marked a dep of f")
- assert(sorted.index(f(:c)) < sorted.index(f(:f)),
+ assert(sorted.index(config.f(:c)) < sorted.index(config.f(:f)),
"c is not before f")
- one.each do |o|
- two.each do |t|
+ config.resource("one").each do |o|
+ config.resource("two").each do |t|
assert(graph.dependents(o).include?(t),
"%s not marked a dep of %s" % [t.ref, o.ref])
assert(sorted.index(t) < sorted.index(o),
@@ -558,22 +555,22 @@ class TestTransactions < Test::Unit::TestCase
end
end
- trans.resources.leaves(middle).each do |child|
- assert(graph.dependents(f(:h)).include?(child),
+ trans.configuration.leaves(config.resource("middle")).each do |child|
+ assert(graph.dependents(config.f(:h)).include?(child),
"%s not marked a dep of h" % [child.ref])
- assert(sorted.index(child) < sorted.index(f(:h)),
+ assert(sorted.index(child) < sorted.index(config.f(:h)),
"%s is not before h" % child.ref)
end
# Lastly, make sure our 'g' vertex made it into the relationship
# graph, since it's not involved in any relationships.
- assert(graph.vertex?(f(:g)),
+ assert(graph.vertex?(config.f(:g)),
"Lost vertexes with no relations")
# Now make the reversal graph and make sure all of the vertices made it into that
reverse = graph.reversal
%w{a b c d e f g h}.each do |letter|
- file = f(letter)
+ file = config.f(letter)
assert(reverse.vertex?(file), "%s did not make it into reversal" % letter)
end
end
@@ -594,15 +591,15 @@ class TestTransactions < Test::Unit::TestCase
yay = Puppet::Type.newgenerator :title => "yay"
rah = Puppet::Type.newgenerator :title => "rah"
- comp = newcomp(yay, rah)
- trans = comp.evaluate
+ config = mk_configuration(yay, rah)
+ trans = Puppet::Transaction.new(config)
assert_nothing_raised do
trans.generate
end
%w{ya ra y r}.each do |name|
- assert(trans.resources.vertex?(Puppet::Type.type(:generator)[name]),
+ assert(trans.configuration.vertex?(Puppet::Type.type(:generator)[name]),
"Generated %s was not a vertex" % name)
assert($finished.include?(name), "%s was not finished" % name)
end
@@ -613,7 +610,7 @@ class TestTransactions < Test::Unit::TestCase
end
%w{ya ra y r}.each do |name|
- assert(!trans.resources.vertex?(Puppet::Type.type(:generator)[name]),
+ assert(!trans.configuration.vertex?(Puppet::Type.type(:generator)[name]),
"Generated vertex %s was not removed from graph" % name)
assert_nil(Puppet::Type.type(:generator)[name],
"Generated vertex %s was not removed from class" % name)
@@ -633,8 +630,8 @@ class TestTransactions < Test::Unit::TestCase
yay = Puppet::Type.newgenerator :title => "yay"
rah = Puppet::Type.newgenerator :title => "rah", :subscribe => yay
- comp = newcomp(yay, rah)
- trans = comp.evaluate
+ config = mk_configuration(yay, rah)
+ trans = Puppet::Transaction.new(config)
trans.prepare
@@ -702,7 +699,7 @@ class TestTransactions < Test::Unit::TestCase
end
# Now, start over and make sure that everything gets evaluated.
- trans = comp.evaluate
+ trans = Puppet::Transaction.new(config)
$evaluated.clear
assert_nothing_raised do
trans.evaluate
@@ -712,53 +709,51 @@ class TestTransactions < Test::Unit::TestCase
"Not all resources were evaluated or not in the right order")
end
+ # Make sure tags on the transaction get copied to the configuration.
def test_tags
- res = Puppet::Type.newfile :path => tempfile()
- comp = newcomp(res)
-
- # Make sure they default to none
- assert_equal([], comp.evaluate.tags)
-
- # Make sure we get the main tags
- Puppet[:tags] = %w{this is some tags}
- assert_equal(%w{this is some tags}, comp.evaluate.tags)
-
- # And make sure they get processed correctly
- Puppet[:tags] = ["one", "two,three", "four"]
- assert_equal(%w{one two three four}, comp.evaluate.tags)
-
- # lastly, make sure we can override them
- trans = comp.evaluate
- trans.tags = ["one", "two,three", "four"]
- assert_equal(%w{one two three four}, comp.evaluate.tags)
+ config = Puppet::Node::Configuration.new
+ transaction = Puppet::Transaction.new(config)
+ assert_equal([], transaction.tags, "Tags defaulted to non-empty")
+
+ Puppet[:tags] = "one,two"
+ transaction = Puppet::Transaction.new(config)
+ assert_equal(%w{one two}, transaction.tags, "Tags were not copied from the central configuration")
+ end
+
+ def test_ignore_tags?
+ config = Puppet::Node::Configuration.new
+ config.host_config = true
+ transaction = Puppet::Transaction.new(config)
+ assert(! transaction.ignore_tags?, "Ignoring tags when applying a host configuration")
+
+ config.host_config = false
+ transaction = Puppet::Transaction.new(config)
+ assert(transaction.ignore_tags?, "Not ignoring tags when applying a non-host configuration")
end
- def test_tagged?
- res = Puppet::Type.newfile :path => tempfile()
- comp = newcomp(res)
- trans = comp.evaluate
-
- assert(trans.tagged?(res), "tagged? defaulted to false")
-
- # Now set some tags
- trans.tags = %w{some tags}
-
- # And make sure it's false
- assert(! trans.tagged?(res), "matched invalid tags")
-
- # Set ignoretags and make sure it sticks
- trans.ignoretags = true
- assert(trans.tagged?(res), "tags were not ignored")
-
- # Now make sure we actually correctly match tags
- res[:tag] = "mytag"
- trans.ignoretags = false
- trans.tags = %w{notag}
-
- assert(! trans.tagged?(res), "tags incorrectly matched")
-
- trans.tags = %w{mytag yaytag}
- assert(trans.tagged?(res), "tags should have matched")
+ def test_missing_tags?
+ resource = stub 'resource', :tagged? => true
+ config = Puppet::Node::Configuration.new
+
+ # Mark it as a host config so we don't care which test is first
+ config.host_config = true
+ transaction = Puppet::Transaction.new(config)
+ assert(! transaction.missing_tags?(resource), "Considered a resource to be missing tags when none are set")
+
+ # host configurations pay attention to tags, no one else does.
+ Puppet[:tags] = "three,four"
+ config.host_config = false
+ transaction = Puppet::Transaction.new(config)
+ assert(! transaction.missing_tags?(resource), "Considered a resource to be missing tags when not running a host configuration")
+
+ #
+ config.host_config = true
+ transaction = Puppet::Transaction.new(config)
+ assert(! transaction.missing_tags?(resource), "Considered a resource to be missing tags when running a host configuration and all tags are present")
+
+ transaction = Puppet::Transaction.new(config)
+ resource.stubs :tagged? => false
+ assert(transaction.missing_tags?(resource), "Considered a resource not to be missing tags when running a host configuration and tags are missing")
end
# Make sure changes generated by eval_generated resources have proxies
@@ -772,8 +767,8 @@ class TestTransactions < Test::Unit::TestCase
end
resource = type.create :name => "test"
- comp = newcomp(resource)
- trans = comp.evaluate
+ config = mk_configuration(resource)
+ trans = Puppet::Transaction.new(config)
trans.prepare
assert_nothing_raised do
@@ -831,7 +826,7 @@ class TestTransactions < Test::Unit::TestCase
end
# Make a graph with some stuff in it.
- graph = Puppet::PGraph.new
+ graph = Puppet::Node::Configuration.new
# Add a non-triggering edge.
a = trigger.new(:a)
@@ -887,7 +882,8 @@ class TestTransactions < Test::Unit::TestCase
def test_graph
Puppet.config.use(:main)
# Make a graph
- graph = Puppet::PGraph.new
+ graph = Puppet::Node::Configuration.new
+ graph.host_config = true
graph.add_edge!("a", "b")
# Create our transaction
@@ -908,27 +904,12 @@ class TestTransactions < Test::Unit::TestCase
end
assert(FileTest.exists?(dotfile), "Did not create graph.")
end
-
- def test_created_graphs
- FileUtils.mkdir_p(Puppet[:graphdir])
- file = Puppet::Type.newfile(:path => tempfile, :content => "yay")
- exec = Puppet::Type.type(:exec).create(:command => "echo yay", :path => ENV['PATH'],
- :require => file)
-
- Puppet[:graph] = true
- assert_apply(file, exec)
-
- %w{resources relationships expanded_relationships}.each do |name|
- file = File.join(Puppet[:graphdir], "%s.dot" % name)
- assert(FileTest.exists?(file), "graph for %s was not created" % name)
- end
- end
def test_set_target
file = Puppet::Type.newfile(:path => tempfile(), :content => "yay")
exec1 = Puppet::Type.type(:exec).create :command => "/bin/echo exec1"
exec2 = Puppet::Type.type(:exec).create :command => "/bin/echo exec2"
- trans = Puppet::Transaction.new(newcomp(file, exec1, exec2))
+ trans = Puppet::Transaction.new(mk_configuration(file, exec1, exec2))
# First try it with an edge that has no callback
edge = Puppet::Relationship.new(file, exec1)
@@ -975,7 +956,8 @@ class TestTransactions < Test::Unit::TestCase
one[:require] = two
two[:require] = one
- trans = newcomp(one, two).evaluate
+ config = mk_configuration(one, two)
+ trans = Puppet::Transaction.new(config)
assert_raise(Puppet::Error) do
trans.prepare
end
@@ -1042,8 +1024,8 @@ class TestTransactions < Test::Unit::TestCase
rels[dir] = file
rels.each do |after, before|
- comp = newcomp(before, after)
- trans = comp.evaluate
+ config = mk_configuration(before, after)
+ trans = Puppet::Transaction.new(config)
str = "from %s to %s" % [before, after]
assert_nothing_raised("Failed to create graph %s" % str) do
@@ -1063,7 +1045,7 @@ class TestTransactions < Test::Unit::TestCase
one[:require] = two
one[:subscribe] = two
- comp = newcomp(one, two)
+ comp = mk_configuration(one, two)
trans = Puppet::Transaction.new(comp)
graph = trans.relationship_graph
@@ -1188,5 +1170,3 @@ class TestTransactions < Test::Unit::TestCase
assert_equal(1, $flushed, "object was flushed in noop")
end
end
-
-# $Id$