diff options
| author | Luke Kanies <luke@madstop.com> | 2007-09-17 15:21:44 -0700 |
|---|---|---|
| committer | Luke Kanies <luke@madstop.com> | 2007-09-17 15:21:44 -0700 |
| commit | 9fa2628a844c75b8f554f283dfece01667f20594 (patch) | |
| tree | 7c1b57c56879b2b083063895bae6423f2b94dc9c /lib | |
| parent | b3c8cdb67d9a423a1d14764f1e58f677d7ef8d41 (diff) | |
This is basically another intermediate commit. I feel like
I've gone too far down the rabbit hole to turn back now, but the
code is clearly getting more centralized around the Configuration
class, which is the goal.
Things are currently a bit muddy between recursion, dynamic resource
generation, transactions, and the configuration, and I don't expect
to be able to clear it up much until we rewrite all of the tests
for the Transaction class, since that is when we'll actually be
setting its behaviour.
At this point, Files (which are currently the only resources that
generate other resources) are responsible for adding their edges
to the relationship graph. This puts them knowing more than I would
like about how the relationship graph works, but it'll have to do for now.
There are still failing tests, but files seem to work again. Now to
go through the rest of the tests and make them work.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/puppet/dsl.rb | 7 | ||||
| -rw-r--r-- | lib/puppet/metatype/closure.rb | 13 | ||||
| -rw-r--r-- | lib/puppet/metatype/container.rb | 8 | ||||
| -rw-r--r-- | lib/puppet/metatype/instances.rb | 7 | ||||
| -rwxr-xr-x | lib/puppet/network/handler/resource.rb | 13 | ||||
| -rw-r--r-- | lib/puppet/node/configuration.rb | 164 | ||||
| -rw-r--r-- | lib/puppet/reports/store.rb | 2 | ||||
| -rw-r--r-- | lib/puppet/transaction.rb | 89 | ||||
| -rw-r--r-- | lib/puppet/transportable.rb | 7 | ||||
| -rw-r--r-- | lib/puppet/type.rb | 13 | ||||
| -rw-r--r-- | lib/puppet/type/component.rb | 24 | ||||
| -rw-r--r-- | lib/puppet/type/pfile.rb | 18 | ||||
| -rw-r--r-- | lib/puppet/util/config.rb | 18 | ||||
| -rw-r--r-- | lib/puppet/util/graph.rb | 39 |
14 files changed, 204 insertions, 218 deletions
diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb index 795358e83..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 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..4fd72e85c 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 @@ -308,8 +307,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/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/node/configuration.rb b/lib/puppet/node/configuration.rb index 9cd23926e..c882e0ee1 100644 --- a/lib/puppet/node/configuration.rb +++ b/lib/puppet/node/configuration.rb @@ -35,20 +35,22 @@ class Puppet::Node::Configuration < Puppet::PGraph tag(*classes) end - # Add a resource to our graph and to our resource table. - def add_resource(resource) - unless resource.respond_to?(:ref) - raise ArgumentError, "Can only add objects that respond to :ref" - 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 + 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 - resource.configuration = self - add_vertex!(resource) end # Apply our configuration to the local host. @@ -58,6 +60,8 @@ class Puppet::Node::Configuration < Puppet::PGraph transaction.addtimes :config_retrieval => @retrieval_duration + @applying = true + begin transaction.evaluate rescue Puppet::Error => detail @@ -83,22 +87,62 @@ class Puppet::Node::Configuration < Puppet::PGraph 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. + def create_implicit_resource(type, options) + unless options.include?(:implicit) + options[:implicit] = true + end + 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) @@ -174,6 +218,12 @@ class Puppet::Node::Configuration < Puppet::PGraph # 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) @@ -183,20 +233,76 @@ class Puppet::Node::Configuration < Puppet::PGraph @tags = [] @classes = [] @resource_table = {} + @transient_resources = [] + @applying = false 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 + relationships = self.class.new + + # First create the dependency graph + self.vertices.each do |vertex| + relationships.add_resource vertex + vertex.builddepends.each do |edge| + relationships.add_edge!(edge) + end + end + + # Lastly, add in any autorequires + relationships.vertices.each do |vertex| + vertex.autorequire.each do |edge| + unless relationships.edge?(edge) + unless relationships.edge?(edge.target, edge.source) + vertex.debug "Autorequiring %s" % [edge.source] + relationships.add_edge!(edge) + else + vertex.debug "Skipping automatic relationship with %s" % (edge.source == vertex ? edge.target : edge.source) + end + end + end + end + + relationships.write_graph(:relationships) + + # Then splice in the container information + relationships.splice!(self, Puppet::Type::Component) + + relationships.write_graph(:expanded_relationships) + @relationship_graph = relationships + end + @relationship_graph + end - # Look a resource up by its reference (e.g., File[/etc/passwd]). - def resource(ref) - @resource_table[ref] + # 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) + resource.remove + end end - def host_config? - host_config || false + # 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. @@ -217,4 +323,28 @@ class Puppet::Node::Configuration < Puppet::PGraph 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.config.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 + end + end end diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb index 8d6e11379..d51f50372 100644 --- a/lib/puppet/reports/store.rb +++ b/lib/puppet/reports/store.rb @@ -13,7 +13,7 @@ Puppet::Network::Handler.report.newreport(:store, :useyaml => true) do def mkclientdir(client, dir) config = Puppet::Util::Config.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/transaction.rb b/lib/puppet/transaction.rb index 0d81b1e63..43889927c 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -7,7 +7,7 @@ require 'puppet/propertychange' module Puppet class Transaction attr_accessor :component, :configuration, :ignoreschedules - attr_accessor :relgraph, :sorted_resources, :configurator + attr_accessor :sorted_resources, :configurator # The report, once generated. attr_reader :report @@ -32,7 +32,7 @@ class Transaction # 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 @@ -144,12 +144,7 @@ class Transaction # contained resources might never get cleaned up. def cleanup if defined? @generated - @generated.each do |resource| - resource.remove - end - end - if defined? @relgraph - @relgraph.clear + relationship_graph.remove_resource(*@generated) end end @@ -164,10 +159,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 @@ -198,7 +194,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 @@ -271,7 +268,7 @@ 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| + relationship_graph.matching_edges(events, resource).each do |edge| edge = edge.dup label = edge.label label[:event] = events.collect { |e| e.event } @@ -289,8 +286,6 @@ class Transaction def evaluate @count = 0 - graph(@configuration, :resources) - # Start logging. Puppet::Util::Log.newdestination(@report) @@ -320,6 +315,7 @@ class Transaction Puppet.debug "Finishing transaction %s with %s changes" % [self.object_id, @count] + @events = allevents allevents end @@ -339,7 +335,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" % @@ -375,7 +371,8 @@ class Transaction end made.uniq! made.each do |res| - @configuration.add_vertex!(res) + @configuration.add_resource(res) + res.configuration = configuration newlist << res @generated << res res.finish @@ -416,21 +413,6 @@ class Transaction return @report end - # Produce the graph files if requested. - def graph(gr, name) - # We only want to graph the main host configuration. - return unless @configuration.host_config? - - 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) - } - end - # Should we ignore tags? def ignore_tags? ! @configuration.host_config? @@ -514,48 +496,13 @@ 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 - @configuration.vertices.each do |vertex| - graph.add_vertex!(vertex) - vertex.builddepends.each do |edge| - graph.add_edge!(edge) - end - 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 - end - end - - graph(graph, :relationships) - - # Then splice in the container information - graph.splice!(@configuration, Puppet::Type::Component) - graph(graph, :expanded_relationships) - - return graph + def relationship_graph + configuration.relationship_graph end # Send off the transaction report. @@ -621,7 +568,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 diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb index e4cde6e37..ecd179ed7 100644 --- a/lib/puppet/transportable.rb +++ b/lib/puppet/transportable.rb @@ -174,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| diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 03f571d4a..1db435224 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -312,6 +312,19 @@ class Type return self[:name] end + # Look up our parent in the configuration, if we have one. + def parent + return nil unless configuration + + # We should never have more than one parent, so let's just ignore + # it if we happen to. + if parents = configuration.adjacent(self, :direction => :in) + 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 1615abad8..1eadeb060 100644 --- a/lib/puppet/type/component.rb +++ b/lib/puppet/type/component.rb @@ -163,8 +163,8 @@ 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 @@ -215,24 +215,6 @@ Puppet::Type.newtype(:component) do 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 if self.title =~ /\[/ @@ -242,5 +224,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..da345e5d6 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,13 +611,12 @@ 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 self.debug "Not managing more explicit file %s" % path @@ -640,12 +642,7 @@ 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 + return nil unless child = configuration.create_implicit_resource(:file, args) rescue Puppet::Error => detail self.notice( "Cannot manage: %s" % @@ -661,6 +658,7 @@ module Puppet self.debug args.inspect child = nil end + configuration.relationship_graph.add_edge! self, child end return child end @@ -695,9 +693,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/config.rb b/lib/puppet/util/config.rb index 5875f141b..3988b3fe0 100644 --- a/lib/puppet/util/config.rb +++ b/lib/puppet/util/config.rb @@ -555,9 +555,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. @@ -615,10 +615,7 @@ Generated on #{Time.now}. 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 @@ -1109,6 +1106,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(/\/$/, '') @@ -1131,9 +1133,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 @@ -1142,9 +1141,6 @@ 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) diff --git a/lib/puppet/util/graph.rb b/lib/puppet/util/graph.rb deleted file mode 100644 index e51b8e25a..000000000 --- a/lib/puppet/util/graph.rb +++ /dev/null @@ -1,39 +0,0 @@ -# Created by Luke Kanies on 2006-11-16. -# Copyright (c) 2006. All rights reserved. - -require 'puppet' -require 'puppet/pgraph' - -# A module that handles the small amount of graph stuff in Puppet. -module Puppet::Util::Graph - # Make a graph where each of our children gets converted to - # the receiving end of an edge. Call the same thing on all - # of our children, optionally using a block - def to_graph(graph = nil, &block) - # Allow our calling function to send in a graph, so that we - # can call this recursively with one graph. - graph ||= Puppet::PGraph.new - - self.each do |child| - unless block_given? and ! yield(child) - graph.add_edge!(self, child) - - if graph.cyclic? - raise Puppet::Error, "%s created a cyclic graph" % self - end - - if child.respond_to?(:to_graph) - child.to_graph(graph, &block) - end - end - end - - if graph.cyclic? - raise Puppet::Error, "%s created a cyclic graph" % self - end - - graph - end -end - -# $Id$ |
