diff options
author | Luke Kanies <luke@madstop.com> | 2007-09-04 12:09:48 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2007-09-04 12:09:48 -0500 |
commit | 3b2efd2a4b32478b6c6a71e1421061405a0bb11e (patch) | |
tree | 72e5ada4b0eeb3c83a1f20dfb524363a3fc5db81 | |
parent | 0faf76ee187c7fa7c67a7fb7e7c345897006b7d8 (diff) | |
download | puppet-3b2efd2a4b32478b6c6a71e1421061405a0bb11e.tar.gz puppet-3b2efd2a4b32478b6c6a71e1421061405a0bb11e.tar.xz puppet-3b2efd2a4b32478b6c6a71e1421061405a0bb11e.zip |
We now have a real configuration object, as a subclass of GRATR::Digraph, that has a resource graph including resources for the container objects like classes and nodes. It is apparently functional, but I have not gone through all of the other tests to fix them yet. That is next.
38 files changed, 378 insertions, 658 deletions
diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb new file mode 100644 index 000000000..e667007e9 --- /dev/null +++ b/lib/puppet/node/configuration.rb @@ -0,0 +1,123 @@ +require 'puppet/external/gratr/digraph' + +# This class models a node configuration. It is the thing +# meant to be passed from server to client, and it contains all +# of the information in the configuration, including the resources +# and the relationships between them. +class Puppet::Node::Configuration < GRATR::Digraph + attr_accessor :name + attr_reader :extraction_format + + # Add classes to our class list. + def add_class(*classes) + classes.each do |klass| + @classes << klass + end + + # Add the class names as tags, too. + tag(*classes) + end + + def classes + @classes.dup + end + + # Make sure we support the requested extraction format. + def extraction_format=(value) + unless respond_to?("extract_to_%s" % value) + raise ArgumentError, "Invalid extraction format %s" % value + end + @extraction_format = value + end + + # Turn our configuration graph into whatever the client is expecting. + def extract + send("extract_to_%s" % extraction_format) + end + + # Create the traditional TransBuckets and TransObjects from our configuration + # graph. This will hopefully be deprecated soon. + def extract_to_transportable + top = nil + current = nil + buckets = {} + + unless main = vertices.find { |res| res.type == "class" and res.title == :main } + raise Puppet::DevError, "Could not find 'main' class; cannot generate configuration" + end + + # Create a proc for examining edges, which we'll use to build our tree + # of TransBuckets and TransObjects. + bucket = nil + edges = proc do |edge| + # The sources are always non-builtins. + source, target = edge.source, edge.target + unless tmp = buckets[source.to_s] + if tmp = buckets[source.to_s] = source.to_trans + bucket = tmp + else + # This is because virtual resources return nil. If a virtual + # container resource contains realized resources, we still need to get + # to them. So, we keep a reference to the last valid bucket + # we returned and use that if the container resource is virtual. + end + end + bucket = tmp || bucket + if child = target.to_trans + unless bucket + raise "No bucket created for %s" % source + end + bucket.push child + + # It's important that we keep a reference to any TransBuckets we've created, so + # we don't create multiple buckets for children. + unless target.builtin? + buckets[target.to_s] = child + end + end + end + dfs(:start => main, :examine_edge => edges) + + unless main + raise Puppet::DevError, "Could not find 'main' class; cannot generate configuration" + end + + # Retrive the bucket for the top-level scope and set the appropriate metadata. + unless result = buckets[main.to_s] + raise Puppet::DevError, "Did not evaluate top scope" + end + + result.classes = classes + + # Clear the cache to encourage the GC + buckets.clear + return result + end + + def initialize(name) + super() + @name = name + @extraction_format ||= :transportable + @tags = [] + @classes = [] + end + + # Add a tag. + def tag(*names) + names.each do |name| + name = name.to_s + @tags << name unless @tags.include?(name) + if name.include?("::") + name.split("::").each do |sub| + @tags << sub unless @tags.include?(sub) + end + end + end + nil + end + + # Return the list of tags. + def tags + @tags.dup + end +end diff --git a/lib/puppet/parser/ast/astarray.rb b/lib/puppet/parser/ast/astarray.rb index c0212f919..2a8f4d4c1 100644 --- a/lib/puppet/parser/ast/astarray.rb +++ b/lib/puppet/parser/ast/astarray.rb @@ -65,21 +65,6 @@ class Puppet::Parser::AST return self end - - # Convert to a string. Only used for printing the parse tree. - def to_s - return "[" + @children.collect { |child| - child.to_s - }.join(", ") + "]" - end - - # Print the parse tree. - def tree(indent = 0) - #puts((AST.indent * indent) + self.pin) - self.collect { |child| - child.tree(indent) - }.join("\n" + (AST.midline * (indent+1)) + "\n") - end end # A simple container class, containing the parameters for an object. @@ -88,5 +73,3 @@ class Puppet::Parser::AST # meant completely different things. class ResourceInst < ASTArray; end end - -# $Id$ diff --git a/lib/puppet/parser/ast/branch.rb b/lib/puppet/parser/ast/branch.rb index 5a4d323f5..dcd09baa3 100644 --- a/lib/puppet/parser/ast/branch.rb +++ b/lib/puppet/parser/ast/branch.rb @@ -35,15 +35,5 @@ class Puppet::Parser::AST end } end - - # Pretty-print the parse tree. - def tree(indent = 0) - return ((@@indline * indent) + - self.typewrap(self.pin)) + "\n" + self.collect { |child| - child.tree(indent + 1) - }.join("\n") - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/caseopt.rb b/lib/puppet/parser/ast/caseopt.rb index 11483d082..d1d9d0e9c 100644 --- a/lib/puppet/parser/ast/caseopt.rb +++ b/lib/puppet/parser/ast/caseopt.rb @@ -56,16 +56,5 @@ class Puppet::Parser::AST def evaluate(hash) return @statements.safeevaluate(hash) end - - def tree(indent = 0) - rettree = [ - @value.tree(indent + 1), - ((@@indline * indent) + self.typewrap(self.pin)), - @statements.tree(indent + 1) - ] - return rettree.flatten.join("\n") - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/casestatement.rb b/lib/puppet/parser/ast/casestatement.rb index c56e33bc9..3c6f9c7e2 100644 --- a/lib/puppet/parser/ast/casestatement.rb +++ b/lib/puppet/parser/ast/casestatement.rb @@ -51,20 +51,8 @@ class Puppet::Parser::AST return retvalue end - def tree(indent = 0) - rettree = [ - @test.tree(indent + 1), - ((@@indline * indent) + self.typewrap(self.pin)), - @options.tree(indent + 1) - ] - - return rettree.flatten.join("\n") - end - def each [@test,@options].each { |child| yield child } end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb index a7564f0c9..e05977a47 100644 --- a/lib/puppet/parser/ast/collection.rb +++ b/lib/puppet/parser/ast/collection.rb @@ -26,5 +26,3 @@ class Collection < AST::Branch end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/collexpr.rb b/lib/puppet/parser/ast/collexpr.rb index 5fb11c709..4a96d9c61 100644 --- a/lib/puppet/parser/ast/collexpr.rb +++ b/lib/puppet/parser/ast/collexpr.rb @@ -77,5 +77,3 @@ class CollExpr < AST::Branch end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/component.rb b/lib/puppet/parser/ast/component.rb index 17cfa9d61..1d7fe9cdd 100644 --- a/lib/puppet/parser/ast/component.rb +++ b/lib/puppet/parser/ast/component.rb @@ -222,5 +222,3 @@ class Puppet::Parser::AST end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index c350c2cdb..9ad1f539d 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -33,8 +33,6 @@ class Puppet::Parser::AST # Create a new scope. scope = subscope(origscope, resource) - scope.virtual = true if resource.virtual or origscope.virtual? - scope.exported = true if resource.exported or origscope.exported? # Additionally, add a tag for whatever kind of class # we are @@ -126,21 +124,16 @@ class Puppet::Parser::AST # Create a new subscope in which to evaluate our code. def subscope(scope, resource = nil) - if resource - type = resource.type - else - type = self.classname + unless resource + raise ArgumentError, "Resources are required when creating subscopes" end args = { :resource => resource, - :type => type, :keyword => self.keyword, :namespace => self.namespace, :source => self } - args[:name] = resource.title if resource - oldscope = scope scope = scope.newscope(args) scope.source = self diff --git a/lib/puppet/parser/ast/else.rb b/lib/puppet/parser/ast/else.rb index ff2f233b7..e76051372 100644 --- a/lib/puppet/parser/ast/else.rb +++ b/lib/puppet/parser/ast/else.rb @@ -16,15 +16,5 @@ class Puppet::Parser::AST scope = hash[:scope] return @statements.safeevaluate(:scope => scope) end - - def tree(indent = 0) - rettree = [ - ((@@indline * indent) + self.typewrap(self.pin)), - @statements.tree(indent + 1) - ] - return rettree.flatten.join("\n") - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/function.rb b/lib/puppet/parser/ast/function.rb index 052b8a8b1..0cd1fff62 100644 --- a/lib/puppet/parser/ast/function.rb +++ b/lib/puppet/parser/ast/function.rb @@ -53,5 +53,3 @@ class Puppet::Parser::AST end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index 41ca34432..0a8e33970 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -22,6 +22,7 @@ class Puppet::Parser::AST # Evaluate the code associated with this class. def evaluate(options) scope = options[:scope] + raise(ArgumentError, "Classes require resources") unless options[:resource] # Verify that we haven't already been evaluated. This is # what provides the singleton aspect. if existing_scope = scope.compile.class_scope(self) @@ -31,13 +32,15 @@ class Puppet::Parser::AST pnames = nil if pklass = self.parentobj - pklass.safeevaluate :scope => scope + pklass.safeevaluate :scope => scope, :resource => options[:resource] scope = parent_scope(scope, pklass) pnames = scope.namespaces end - unless options[:nosubscope] + # Don't create a subscope for the top-level class, since it already + # has its own scope. + unless options[:resource].title == :main scope = subscope(scope, options[:resource]) end diff --git a/lib/puppet/parser/ast/ifstatement.rb b/lib/puppet/parser/ast/ifstatement.rb index 300f68dab..66a07b01f 100644 --- a/lib/puppet/parser/ast/ifstatement.rb +++ b/lib/puppet/parser/ast/ifstatement.rb @@ -26,18 +26,5 @@ class Puppet::Parser::AST end end end - - def tree(indent = 0) - rettree = [ - @test.tree(indent + 1), - ((@@indline * indent) + self.typewrap(self.pin)), - @statements.tree(indent + 1), - @else.tree(indent + 1) - ] - - return rettree.flatten.join("\n") - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb index c2fd0939d..225253061 100644 --- a/lib/puppet/parser/ast/leaf.rb +++ b/lib/puppet/parser/ast/leaf.rb @@ -10,11 +10,6 @@ class Puppet::Parser::AST return @value end - # Print the value in parse tree context. - def tree(indent = 0) - return ((@@indent * indent) + self.typewrap(self.value)) - end - def to_s return @value end @@ -92,7 +87,4 @@ class Puppet::Parser::AST end end end - end - -# $Id$ diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb index d46df3cff..20c03f4ce 100644 --- a/lib/puppet/parser/ast/node.rb +++ b/lib/puppet/parser/ast/node.rb @@ -7,7 +7,6 @@ class Puppet::Parser::AST @name = :node attr_accessor :name - #def evaluate(scope, facts = {}) def evaluate(options) scope = options[:scope] @@ -24,7 +23,7 @@ class Puppet::Parser::AST end scope = scope.newscope( - :type => self.name, + :resource => options[:resource], :keyword => @keyword, :source => self, :namespace => "" # nodes are always in "" diff --git a/lib/puppet/parser/ast/resourcedef.rb b/lib/puppet/parser/ast/resourcedef.rb index 7a43f6b32..02eac2b7b 100644 --- a/lib/puppet/parser/ast/resourcedef.rb +++ b/lib/puppet/parser/ast/resourcedef.rb @@ -1,88 +1,27 @@ require 'puppet/parser/ast/branch' -# Any normal puppet object declaration. Can result in a class or a -# component, in addition to builtin types. +# Any normal puppet resource declaration. Can point to a definition or a +# builtin type. class Puppet::Parser::AST class ResourceDef < AST::Branch attr_accessor :title, :type, :exported, :virtual attr_reader :params - # probably not used at all - def []=(index,obj) - @params[index] = obj - end - - # probably not used at all - def [](index) - return @params[index] - end - - # Iterate across all of our children. - def each - [@type,@title,@params].flatten.each { |param| - #Puppet.debug("yielding param %s" % param) - yield param - } - end - # Does not actually return an object; instead sets an object # in the current scope. - def evaluate(hash) - scope = hash[:scope] - @scope = scope - hash = {} - - # Get our type and name. - objtype = @type - - # Disable definition inheritance, for now. 8/27/06, luke - #if objtype == "super" - # objtype = supertype() - # @subtype = true - #else - @subtype = false - #end + def evaluate(options) + scope = options[:scope] # Evaluate all of the specified params. paramobjects = @params.collect { |param| param.safeevaluate(:scope => scope) } - # Now collect info from our parent. - parentname = nil - if @subtype - parentname = supersetup(hash) - end - - objtitles = nil - # Determine our name if we have one. - if self.title - objtitles = @title.safeevaluate(:scope => scope) - # it's easier to always use an array, even for only one name - unless objtitles.is_a?(Array) - objtitles = [objtitles] - end - else - if parentname - objtitles = [parentname] - else - # See if they specified the name as a parameter instead of - # as a normal name (i.e., before the colon). - unless object # we're a builtin - if objclass = Puppet::Type.type(objtype) - namevar = objclass.namevar + objtitles = @title.safeevaluate(:scope => scope) - tmp = hash["name"] || hash[namevar.to_s] - - if tmp - objtitles = [tmp] - end - else - # This isn't grammatically legal. - raise Puppet::ParseError, "Got a resource with no title" - end - end - end + # it's easier to always use an array, even for only one name + unless objtitles.is_a?(Array) + objtitles = [objtitles] end # This is where our implicit iteration takes place; if someone @@ -90,26 +29,26 @@ class ResourceDef < AST::Branch # many times. objtitles.collect { |objtitle| exceptwrap :type => Puppet::ParseError do - exp = self.exported || scope.exported? + exp = self.exported || scope.resource.exported? # We want virtual to be true if exported is true. We can't # just set :virtual => self.virtual in the initialization, # because sometimes the :virtual attribute is set *after* # :exported, in which case it clobbers :exported if :exported # is true. Argh, this was a very tough one to track down. - virt = self.virtual || scope.virtual? || exported + virt = self.virtual || scope.resource.virtual? || exp obj = Puppet::Parser::Resource.new( - :type => objtype, + :type => @type, :title => objtitle, :params => paramobjects, - :file => @file, - :line => @line, + :file => self.file, + :line => self.line, :exported => exp, :virtual => virt, :source => scope.source, :scope => scope ) - # And then store the resource in the scope. + # And then store the resource in the compile. # XXX At some point, we need to switch all of this to return # objects instead of storing them like this. scope.compile.store_resource(scope, obj) @@ -118,14 +57,6 @@ class ResourceDef < AST::Branch }.reject { |obj| obj.nil? } end - # Create our ResourceDef. Handles type checking for us. - def initialize(hash) - @checked = false - super - - #self.typecheck(@type.value) - end - # Set the parameters for our object. def params=(params) if params.is_a?(AST::ASTArray) @@ -138,85 +69,5 @@ class ResourceDef < AST::Branch ) end end - - def supercomp - unless defined? @supercomp - if @scope and comp = @scope.inside - @supercomp = comp - else - error = Puppet::ParseError.new( - "'super' is only valid within definitions" - ) - error.line = self.line - error.file = self.file - raise error - end - end - @supercomp - end - - # Take all of the arguments of our parent and add them into our own, - # without overriding anything. - def supersetup(hash) - comp = supercomp() - - # Now check each of the arguments from the parent. - comp.arguments.each do |name, value| - unless hash.has_key? name - hash[name] = value - end - end - - # Return the parent name, so it can be used if appropriate. - return comp.name - end - - # Retrieve our supertype. - def supertype - unless defined? @supertype - if parent = supercomp.parentclass - @supertype = parent - else - error = Puppet::ParseError.new( - "%s does not have a parent class" % comp.type - ) - error.line = self.line - error.file = self.file - raise error - end - end - @supertype - end - - # Print this object out. - def tree(indent = 0) - return [ - @type.tree(indent + 1), - @title.tree(indent + 1), - ((@@indline * indent) + self.typewrap(self.pin)), - @params.collect { |param| - begin - param.tree(indent + 1) - rescue NoMethodError => detail - Puppet.err @params.inspect - error = Puppet::DevError.new( - "failed to tree a %s" % self.class - ) - error.set_backtrace detail.backtrace - raise error - end - }.join("\n") - ].join("\n") - end - - def to_s - return "%s => { %s }" % [@title, - @params.collect { |param| - param.to_s - }.join("\n") - ] - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/resourcedefaults.rb b/lib/puppet/parser/ast/resourcedefaults.rb index df16b1b59..44ec146b0 100644 --- a/lib/puppet/parser/ast/resourcedefaults.rb +++ b/lib/puppet/parser/ast/resourcedefaults.rb @@ -6,10 +6,6 @@ class Puppet::Parser::AST class ResourceDefaults < AST::Branch attr_accessor :type, :params - def each - [@type,@params].each { |child| yield child } - end - # As opposed to ResourceDef, this stores each default for the given # object type. def evaluate(hash) @@ -21,20 +17,5 @@ class Puppet::Parser::AST scope.setdefaults(type, params) end end - - def tree(indent = 0) - return [ - @type.tree(indent + 1), - ((@@indline * 4 * indent) + self.typewrap(self.pin)), - @params.tree(indent + 1) - ].join("\n") - end - - def to_s - return "%s { %s }" % [@type,@params] - end end - end - -# $Id$ diff --git a/lib/puppet/parser/ast/resourceoverride.rb b/lib/puppet/parser/ast/resourceoverride.rb index 418c9c8e4..4232737fc 100644 --- a/lib/puppet/parser/ast/resourceoverride.rb +++ b/lib/puppet/parser/ast/resourceoverride.rb @@ -58,5 +58,3 @@ class Puppet::Parser::AST end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/resourceparam.rb b/lib/puppet/parser/ast/resourceparam.rb index d87720160..8b1e7b367 100644 --- a/lib/puppet/parser/ast/resourceparam.rb +++ b/lib/puppet/parser/ast/resourceparam.rb @@ -20,19 +20,5 @@ class Puppet::Parser::AST :add => self.add ) end - - def tree(indent = 0) - return [ - @param.tree(indent + 1), - ((@@indline * indent) + self.typewrap(self.pin)), - @value.tree(indent + 1) - ].join("\n") - end - - def to_s - return "%s => %s" % [@param,@value] - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/resourceref.rb b/lib/puppet/parser/ast/resourceref.rb index e5bb69a46..167417f13 100644 --- a/lib/puppet/parser/ast/resourceref.rb +++ b/lib/puppet/parser/ast/resourceref.rb @@ -4,6 +4,14 @@ class Puppet::Parser::AST # A reference to an object. Only valid as an rvalue. class ResourceRef < AST::Branch attr_accessor :title, :type + # Is the type a builtin type? + def builtintype?(type) + if typeklass = Puppet::Type.type(type) + return typeklass + else + return false + end + end def each [@type,@title].flatten.each { |param| @@ -21,38 +29,24 @@ class Puppet::Parser::AST objtype = @type.downcase title = @title.safeevaluate(:scope => scope) - if scope.builtintype?(objtype) - # nothing - elsif dtype = scope.finddefine(objtype) - objtype = dtype.classname - elsif objtype == "class" - # Look up the full path to the class - if classobj = scope.findclass(title) - title = classobj.classname + unless builtintype?(objtype) + if dtype = scope.finddefine(objtype) + objtype = dtype.classname + elsif objtype == "class" + # Look up the full path to the class + if classobj = scope.findclass(title) + title = classobj.classname + else + raise Puppet::ParseError, "Could not find class %s" % title + end else - raise Puppet::ParseError, "Could not find class %s" % title + raise Puppet::ParseError, "Could not find resource type %s" % objtype end - else - raise Puppet::ParseError, "Could not find resource type %s" % objtype end return Puppet::Parser::Resource::Reference.new( :type => objtype, :title => title ) end - - def tree(indent = 0) - return [ - @type.tree(indent + 1), - @title.tree(indent + 1), - ((@@indline * indent) + self.typewrap(self.pin)) - ].join("\n") - end - - def to_s - return "%s[%s]" % [@type,@title] - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/selector.rb b/lib/puppet/parser/ast/selector.rb index e5eb3b6f5..d363ab7e4 100644 --- a/lib/puppet/parser/ast/selector.rb +++ b/lib/puppet/parser/ast/selector.rb @@ -60,15 +60,5 @@ class Puppet::Parser::AST return retvalue end - - def tree(indent = 0) - return [ - @param.tree(indent + 1), - ((@@indline * indent) + self.typewrap(self.pin)), - @values.tree(indent + 1) - ].join("\n") - end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/tag.rb b/lib/puppet/parser/ast/tag.rb index 4a2015cde..e2882d2f0 100644 --- a/lib/puppet/parser/ast/tag.rb +++ b/lib/puppet/parser/ast/tag.rb @@ -24,5 +24,3 @@ class Puppet::Parser::AST end end end - -# $Id$ diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb index ecc10bd14..1e7f874bc 100644 --- a/lib/puppet/parser/ast/vardef.rb +++ b/lib/puppet/parser/ast/vardef.rb @@ -22,20 +22,6 @@ class Puppet::Parser::AST def each [@name,@value].each { |child| yield child } end - - def tree(indent = 0) - return [ - @name.tree(indent + 1), - ((@@indline * 4 * indent) + self.typewrap(self.pin)), - @value.tree(indent + 1) - ].join("\n") - end - - def to_s - return "%s => %s" % [@name,@value] - end end end - -# $Id$ diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index 0ae712e57..a8e80eb9d 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -6,6 +6,7 @@ require 'puppet/external/gratr/import' require 'puppet/external/gratr/dot' require 'puppet/node' +require 'puppet/node/configuration' require 'puppet/util/errors' # Maintain a graph of scopes, along with a bunch of data @@ -14,7 +15,6 @@ class Puppet::Parser::Compile include Puppet::Util include Puppet::Util::Errors attr_reader :topscope, :parser, :node, :facts, :collections - attr_accessor :extraction_format attr_writer :ast_nodes @@ -39,7 +39,7 @@ class Puppet::Parser::Compile end end @class_scopes[name] = scope - tag(name) + @configuration.add_class(name) unless name == "" end # Return the scope associated with a class. This is just here so @@ -57,7 +57,7 @@ class Puppet::Parser::Compile # Return a list of all of the defined classes. def classlist - return @class_scopes.keys.reject { |k| k == "" } + return @configuration.classes end # Compile our configuration. This mostly revolves around finding and evaluating classes. @@ -82,7 +82,7 @@ class Puppet::Parser::Compile store() end - return extract() + return @configuration.extract end # FIXME There are no tests for this. @@ -93,8 +93,6 @@ class Puppet::Parser::Compile # FIXME There are no tests for this. def delete_resource(resource) @resource_table.delete(resource.ref) if @resource_table.include?(resource.ref) - - @resource_graph.remove_vertex!(resource) if @resource_graph.vertex?(resource) end # Return the node's environment. @@ -122,27 +120,23 @@ class Puppet::Parser::Compile found = [] classes.each do |name| # If we can find the class, then make a resource that will evaluate it. - if klass = @parser.findclass("", name) - # This will result in class_set getting called, which - # will in turn result in tags. Yay. - klass.safeevaluate(:scope => scope) + if klass = scope.findclass(name) + # Create a resource to model this class, and then add it to the list + # of resources. + unless scope.source + raise "No source for %s" % scope.to_s + end + resource = Puppet::Parser::Resource.new(:type => "class", :title => klass.classname, :scope => scope, :source => scope.source) + store_resource(scope, resource) found << name else Puppet.info "Could not find class %s for %s" % [name, node.name] - tag(name) + @configuration.tag(name) end end found end - # Make sure we support the requested extraction format. - def extraction_format=(value) - unless respond_to?("extract_to_%s" % value) - raise ArgumentError, "Invalid extraction format %s" % value - end - @extraction_format = value - end - # Return a resource by either its ref or its type and title. def findresource(string, name = nil) if name @@ -168,9 +162,8 @@ class Puppet::Parser::Compile end end - @extraction_format ||= :transportable - initvars() + init_main() end # Create a new scope, with either a specified parent scope or @@ -230,7 +223,7 @@ class Puppet::Parser::Compile # And in the resource graph. At some point, this might supercede # the global resource table, but the table is a lot faster # so it makes sense to maintain for now. - @resource_graph.add_edge!(scope, resource) + @configuration.add_edge!(scope.resource, resource) end private @@ -310,71 +303,19 @@ class Puppet::Parser::Compile # Find and evaluate our main object, if possible. def evaluate_main - if klass = @parser.findclass("", "") - # Set the source, so objects can tell where they were defined. - topscope.source = klass - klass.safeevaluate :scope => topscope, :nosubscope => true - end - end + @main = @parser.findclass("", "") || @parser.newclass("") + @topscope.source = @main + @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) + @topscope.resource = @main_resource - # Turn our configuration graph into whatever the client is expecting. - def extract - send("extract_to_%s" % extraction_format) - end + @configuration.add_vertex!(@main_resource) - # Create the traditional TransBuckets and TransObjects from our configuration - # graph. This will hopefully be deprecated soon. - def extract_to_transportable - top = nil - current = nil - buckets = {} - - # I'm *sure* there's a simple way to do this using a breadth-first search - # or something, but I couldn't come up with, and this is both fast - # and simple, so I'm not going to worry about it too much. - @scope_graph.vertices.each do |scope| - # For each scope, we need to create a TransBucket, and then - # put all of the scope's resources into that bucket, translating - # each resource into a TransObject. - - # Unless the bucket's already been created, make it now and add - # it to the cache. - unless bucket = buckets[scope] - bucket = buckets[scope] = scope.to_trans - end - - # First add any contained scopes - @scope_graph.adjacent(scope, :direction => :out).each do |vertex| - # If there's not already a bucket, then create and cache it. - unless child_bucket = buckets[vertex] - child_bucket = buckets[vertex] = vertex.to_trans - end - bucket.push child_bucket - end - - # Then add the resources. - if @resource_graph.vertex?(scope) - @resource_graph.adjacent(scope, :direction => :out).each do |vertex| - # Some resources don't get translated, e.g., virtual resources. - if obj = vertex.to_trans - bucket.push obj - end - end - end - end - - # Retrive the bucket for the top-level scope and set the appropriate metadata. - result = buckets[topscope] - - result.copy_type_and_name(topscope) - - unless classlist.empty? - result.classes = classlist - end - - # Clear the cache to encourage the GC - buckets.clear - return result + @resource_table["Class[main]"] = @main_resource + #if klass = @parser.findclass("", "") + # # Set the source, so objects can tell where they were defined. + # topscope.source = klass + # klass.safeevaluate :scope => topscope, :nosubscope => true + #end end # Make sure the entire configuration is evaluated. @@ -431,6 +372,13 @@ class Puppet::Parser::Compile @resource_table.each { |name, resource| resource.finish if resource.respond_to?(:finish) } end + # Initialize the top-level scope, class, and resource. + def init_main + # Create our initial scope and a resource that will evaluate main. + @topscope = Puppet::Parser::Scope.new(:compile => self, :parser => self.parser) + @scope_graph.add_vertex!(@topscope) + end + # Set up all of our internal variables. def initvars # The table for storing class singletons. This will only actually @@ -456,18 +404,12 @@ class Puppet::Parser::Compile # A list of tags we've generated; most class names. @tags = [] - # Create our initial scope, our scope graph, and add the initial scope to the graph. - @topscope = Puppet::Parser::Scope.new(:compile => self, :type => "main", :name => "top", :parser => self.parser) - #@main = @parser.findclass("", "") - #@main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) - #@topscope.resource = @main_resource - - # For maintaining scope relationships. + # A graph for maintaining scope relationships. @scope_graph = GRATR::Digraph.new @scope_graph.add_vertex!(@topscope) # For maintaining the relationship between scopes and their resources. - @resource_graph = GRATR::Digraph.new + @configuration = Puppet::Node::Configuration.new(@node.name) end # Set the node's parameters into the top-scope as variables. @@ -510,20 +452,6 @@ class Puppet::Parser::Compile end end - # Add a tag. - def tag(*names) - names.each do |name| - name = name.to_s - @tags << name unless @tags.include?(name) - end - nil - end - - # Return the list of tags. - def tags - @tags.dup - end - # Return an array of all of the unevaluated resources. These will be definitions, # which need to get evaluated into native resources. def unevaluated_resources diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 05d694310..cbb7f4e2d 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -1,4 +1,3 @@ -# Grr require 'puppet/util/autoload' require 'puppet/parser/scope' diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index be1d73047..8cf0dcfe8 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -125,7 +125,7 @@ class Puppet::Parser::Parser # The recursive method used to actually look these objects up. def fqfind(namespace, name, table) namespace = namespace.downcase - name = name.downcase + name = name.to_s.downcase if name =~ /^::/ or namespace == "" classname = name.sub(/^::/, '') unless table[classname] diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 0fdb25748..3f5ca78ae 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -211,7 +211,7 @@ class Puppet::Parser::Resource def tags unless defined? @tags @tags = scope.tags - @tags << self.type + @tags << self.type unless @tags.include?(self.type) end @tags end @@ -251,12 +251,26 @@ class Puppet::Parser::Resource # Translate our object to a transportable object. def to_trans - unless builtin? - devfail "Tried to translate a non-builtin resource" + return nil if virtual? + + if builtin? + to_transobject + else + to_transbucket end + end - return nil if virtual? + def to_transbucket + bucket = Puppet::TransBucket.new([]) + + bucket.type = self.type + bucket.name = self.title + + # TransBuckets don't support parameters, which is why they're being deprecated. + return bucket + end + def to_transobject # Now convert to a transobject obj = Puppet::TransObject.new(@ref.title, @ref.type) to_hash.each do |p, v| diff --git a/lib/puppet/parser/resource/reference.rb b/lib/puppet/parser/resource/reference.rb index 0c3b61930..0bc732558 100644 --- a/lib/puppet/parser/resource/reference.rb +++ b/lib/puppet/parser/resource/reference.rb @@ -34,7 +34,11 @@ class Puppet::Parser::Resource::Reference name = self.title case type when "class": # look for host classes - tmp = @scope.findclass(self.title) + if self.title == :main + tmp = @scope.findclass("") + else + tmp = @scope.findclass(self.title) + end when "node": # look for node definitions tmp = @scope.parser.nodes[self.title] else # normal definitions diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 5f6f15ba6..4516715e1 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -19,20 +19,6 @@ class Puppet::Parser::Scope attr_accessor :base, :keyword, :nodescope attr_accessor :top, :translated, :compile - # Temporary accessors. - attr_accessor :name, :type, :title, :exported, :virtual - def exported? - exported - end - def virtual? - virtual - end - #[:name, :type, :title, :exported?, :virtual].each do |method| - # define_method(method) do - # @resource.send(method) - # end - #end - # Proxy accessors def host @compile.node.name @@ -62,15 +48,6 @@ class Puppet::Parser::Scope end end - # Is the type a builtin type? - def builtintype?(type) - if typeklass = Puppet::Type.type(type) - return typeklass - else - return false - end - end - # Are we the top scope? def topscope? @level == 1 @@ -379,27 +356,7 @@ class Puppet::Parser::Scope # Used mainly for logging def to_s - if self.name - return "%s[%s]" % [@type, @name] - else - return self.type.to_s - end - end - - # Convert our resource to a TransBucket. - def to_trans - bucket = Puppet::TransBucket.new([]) - - case self.type - when "": bucket.type = "main" - when nil: devfail "A Scope with no type" - else - bucket.type = @type - end - if self.name - bucket.name = self.name - end - return bucket + "Scope(%s)" % @resource.to_s end # Undefine a variable; only used for testing. diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb index acd69fb0c..aa7eb92f7 100644 --- a/lib/puppet/transportable.rb +++ b/lib/puppet/transportable.rb @@ -100,19 +100,6 @@ module Puppet end } - # Copy a scope's type and name. - def copy_type_and_name(scope) - case scope.type - when "": self.type = "main" - when nil: devfail "A Scope with no type" - else - self.type = scope.type - end - if scope.name - self.name = scope.name - end - end - # Remove all collectable objects from our tree, since the client # should not see them. def collectstrip! diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb index 6366c7f24..5905d85ab 100644 --- a/lib/puppet/type/component.rb +++ b/lib/puppet/type/component.rb @@ -152,19 +152,21 @@ Puppet::Type.newtype(:component) do # Component paths are special because they function as containers. def pathbuilder tmp = [] - if defined? @parent and @parent - tmp += [@parent.pathbuilder, self.title] - else - # The top-level name is always main[top], so we don't bother with - # that. - if self.title == "main[top]" - tmp << "" # This empty field results in "//" in the path - else - tmp << self.title + myname = "" + if self.title =~ /^class\[(.+)\]$/ + # 'main' is the top class, so we want to see '//' instead of + # its name. + unless $1 == "main" + myname = $1 end + else + myname = self.title + end + if self.parent + return [@parent.pathbuilder, myname] + else + return [myname] end - - tmp end # Remove an object. The argument determines whether the object's diff --git a/lib/puppet/util/methodhelper.rb b/lib/puppet/util/methodhelper.rb index 63158ab67..32fca1877 100644 --- a/lib/puppet/util/methodhelper.rb +++ b/lib/puppet/util/methodhelper.rb @@ -33,5 +33,3 @@ module Puppet::Util::MethodHelper end end end - -# $Id$ diff --git a/spec/unit/node/configuration.rb b/spec/unit/node/configuration.rb new file mode 100755 index 000000000..90bc56460 --- /dev/null +++ b/spec/unit/node/configuration.rb @@ -0,0 +1,125 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Node::Configuration, " when compiling" do + it "should accept tags" do + config = Puppet::Node::Configuration.new("mynode") + config.tag("one") + config.tags.should == %w{one} + end + + it "should accept multiple tags at once" do + config = Puppet::Node::Configuration.new("mynode") + config.tag("one", "two") + config.tags.should == %w{one two} + end + + it "should convert all tags to strings" do + config = Puppet::Node::Configuration.new("mynode") + config.tag("one", :two) + config.tags.should == %w{one two} + end + + it "should tag with both the qualified name and the split name" do + config = Puppet::Node::Configuration.new("mynode") + config.tag("one::two") + config.tags.include?("one").should be_true + config.tags.include?("one::two").should be_true + end + + it "should accept classes" do + config = Puppet::Node::Configuration.new("mynode") + config.add_class("one") + config.classes.should == %w{one} + config.add_class("two", "three") + config.classes.should == %w{one two three} + end + + it "should tag itself with passed class names" do + config = Puppet::Node::Configuration.new("mynode") + config.add_class("one") + config.tags.should == %w{one} + end +end + +describe Puppet::Node::Configuration, " when extracting" do + it "should return extraction result as the method result" do + config = Puppet::Node::Configuration.new("mynode") + config.expects(:extraction_format).returns(:whatever) + config.expects(:extract_to_whatever).returns(:result) + config.extract.should == :result + end +end + +describe Puppet::Node::Configuration, " when extracting transobjects" do + + def mkresource(type, name) + Puppet::Parser::Resource.new(:type => type, :title => name, :source => @source, :scope => @scope) + end + + # This isn't really a spec-style test, but I don't know how better to do it. + it "should transform the resource graph into a tree of TransBuckets and TransObjects" do + config = Puppet::Node::Configuration.new("mynode") + + @scope = mock 'scope' + @source = mock 'source' + + defined = mkresource("class", :main) + builtin = mkresource("file", "/yay") + + config.add_edge!(defined, builtin) + + bucket = [] + bucket.expects(:classes=).with(config.classes) + defined.stubs(:builtin?).returns(false) + defined.expects(:to_transbucket).returns(bucket) + builtin.expects(:to_transobject).returns(:builtin) + + config.extract_to_transportable.should == [:builtin] + end + + # Now try it with a more complicated graph -- a three tier graph, each tier + it "should transform arbitrarily deep graphs into isomorphic trees" do + config = Puppet::Node::Configuration.new("mynode") + + @scope = mock 'scope' + @scope.stubs(:tags).returns([]) + @source = mock 'source' + + # Create our scopes. + top = mkresource "class", :main + topbucket = [] + topbucket.expects(:classes=).with([]) + top.expects(:to_trans).returns(topbucket) + topres = mkresource "file", "/top" + topres.expects(:to_trans).returns(:topres) + config.add_edge! top, topres + + middle = mkresource "class", "middle" + middle.expects(:to_trans).returns([]) + config.add_edge! top, middle + midres = mkresource "file", "/mid" + midres.expects(:to_trans).returns(:midres) + config.add_edge! middle, midres + + bottom = mkresource "class", "bottom" + bottom.expects(:to_trans).returns([]) + config.add_edge! middle, bottom + botres = mkresource "file", "/bot" + botres.expects(:to_trans).returns(:botres) + config.add_edge! bottom, botres + + toparray = config.extract_to_transportable + + # This is annoying; it should look like: + # [[[:botres], :midres], :topres] + # but we can't guarantee sort order. + toparray.include?(:topres).should be_true + + midarray = toparray.find { |t| t.is_a?(Array) } + midarray.include?(:midres).should be_true + botarray = midarray.find { |t| t.is_a?(Array) } + botarray.include?(:botres).should be_true + end +end diff --git a/test/language/ast/resourceref.rb b/test/language/ast/resourceref.rb index 1145e5125..dd7e7037f 100755 --- a/test/language/ast/resourceref.rb +++ b/test/language/ast/resourceref.rb @@ -91,5 +91,3 @@ class TestASTResourceRef < Test::Unit::TestCase end end end - -# $Id$ diff --git a/test/language/compile.rb b/test/language/compile.rb index 3128b8e64..a4adcd963 100755 --- a/test/language/compile.rb +++ b/test/language/compile.rb @@ -255,19 +255,6 @@ class TestCompile < Test::Unit::TestCase end end - # Make sure our config object handles tags appropriately. - def test_tags - config = mkconfig - config.send(:tag, "one") - assert_equal(%w{one}, config.send(:tags), "Did not add tag") - - config.send(:tag, "two", "three") - assert_equal(%w{one two three}, config.send(:tags), "Did not add new tags") - - config.send(:tag, "two") - assert_equal(%w{one two three}, config.send(:tags), "Allowed duplicate tag") - end - def test_evaluate_node_classes config = mkconfig @node.classes = %w{one two three four} @@ -469,94 +456,6 @@ class TestCompile < Test::Unit::TestCase config.send(:finish) end - def test_extract - config = mkconfig - config.expects(:extraction_format).returns(:whatever) - config.expects(:extract_to_whatever).returns(:result) - assert_equal(:result, config.send(:extract), "Did not return extraction result as the method result") - end - - # We want to make sure that the scope and resource graphs translate correctly - def test_extract_to_transportable_simple - # Start with a really simple graph -- one scope, one resource. - config = mkconfig - resources = config.instance_variable_get("@resource_graph") - scopes = config.instance_variable_get("@scope_graph") - - # Get rid of the topscope - scopes.vertices.each { |v| scopes.remove_vertex!(v) } - - bucket = [] - scope = mock("scope") - bucket.expects(:copy_type_and_name).with(scope) - scope.expects(:to_trans).returns(bucket) - scopes.add_vertex! scope - - # The topscope is the key to picking out the top of the graph. - config.instance_variable_set("@topscope", scope) - - resource = mock "resource" - resource.expects(:to_trans).returns(:resource) - resources.add_edge! scope, resource - - result = nil - assert_nothing_raised("Could not extract transportable compile") do - result = config.send :extract_to_transportable - end - assert_equal([:resource], result, "Did not translate simple compile correctly") - end - - def test_extract_to_transportable_complex - # Now try it with a more complicated graph -- a three tier graph, each tier - # having a scope and a resource. - config = mkconfig - resources = config.instance_variable_get("@resource_graph") - scopes = config.instance_variable_get("@scope_graph") - - # Get rid of the topscope - scopes.vertices.each { |v| scopes.remove_vertex!(v) } - - fakebucket = Class.new(Array) do - attr_accessor :name - def initialize(n) - @name = n - end - end - - # Create our scopes. - top = mock("top") - topbucket = fakebucket.new "top" - topbucket.expects(:copy_type_and_name).with(top) - top.stubs(:copy_type_and_name) - top.expects(:to_trans).returns(topbucket) - # The topscope is the key to picking out the top of the graph. - config.instance_variable_set("@topscope", top) - middle = mock("middle") - middle.expects(:to_trans).returns(fakebucket.new("middle")) - scopes.add_edge! top, middle - bottom = mock("bottom") - bottom.expects(:to_trans).returns(fakebucket.new("bottom")) - scopes.add_edge! middle, bottom - - topres = mock "topres" - topres.expects(:to_trans).returns(:topres) - resources.add_edge! top, topres - - midres = mock "midres" - midres.expects(:to_trans).returns(:midres) - resources.add_edge! middle, midres - - botres = mock "botres" - botres.expects(:to_trans).returns(:botres) - resources.add_edge! bottom, botres - - result = nil - assert_nothing_raised("Could not extract transportable compile") do - result = config.send :extract_to_transportable - end - assert_equal([[[:botres], :midres], :topres], result, "Did not translate medium compile correctly") - end - def test_verify_uniqueness config = mkconfig diff --git a/test/language/resource.rb b/test/language/resource.rb index 892d8c293..f594691c6 100755 --- a/test/language/resource.rb +++ b/test/language/resource.rb @@ -209,7 +209,7 @@ class TestResource < PuppetTest::TestCase res.send(:paramcheck, :other) end - def test_to_trans + def test_to_transobject # First try translating a builtin resource. Make sure we use some references # and arrays, to make sure they translate correctly. source = mock("source") @@ -242,6 +242,21 @@ class TestResource < PuppetTest::TestCase assert_equal(["file", refs[3].title], obj["notify"], "Array with single resource reference was not turned into single value") end + # FIXME This isn't a great test, but I need to move on. + def test_to_transbucket + bucket = mock("transbucket") + Puppet::TransBucket.expects(:new).with([]).returns(bucket) + source = mock("source") + scope = mock("scope") + res = Parser::Resource.new :type => "mydefine", :title => "yay", + :source => source, :scope => scope + + bucket.expects(:name=).with("yay") + bucket.expects(:type=).with("mydefine") + + assert_equal(bucket, res.to_trans, "Resource did not return the bucket") + end + def test_evaluate # First try the most common case, we're not a builtin type. res = mkresource diff --git a/test/language/scope.rb b/test/language/scope.rb index 43cbfd47c..66bc632bf 100755 --- a/test/language/scope.rb +++ b/test/language/scope.rb @@ -506,20 +506,6 @@ Host <<||>>" assert_equal((ptags + %w{onemore subscope}).sort, newscope.tags.sort) end - # FIXME This isn't a great test, but I need to move on. - def test_to_trans - bucket = mock("transbucket") - Puppet::TransBucket.expects(:new).with([]).returns(bucket) - scope = mkscope - scope.type = "mytype" - scope.name = "myname" - - bucket.expects(:name=).with("myname") - bucket.expects(:type=).with("mytype") - - scope.to_trans - end - def test_namespaces parser, scope, source = mkclassframing diff --git a/test/language/snippets.rb b/test/language/snippets.rb index c3c60e77f..2c74543e7 100755 --- a/test/language/snippets.rb +++ b/test/language/snippets.rb @@ -496,9 +496,6 @@ class TestSnippets < Test::Unit::TestCase assert_nothing_raised { client.getconfig() } - #assert_nothing_raised { - # trans = client.apply() - #} Puppet::Type.eachtype { |type| type.each { |obj| @@ -520,5 +517,3 @@ class TestSnippets < Test::Unit::TestCase end } end - -# $Id$ |