This patch enhance AST::HostName to support regexes, and modifies
the parser to allow regex to be used as node name.

Signed-off-by: Brice Figureau <[email protected]>]
Signed-off-by: Brice Figureau <[email protected]>
---
 lib/puppet/parser/ast/definition.rb |    6 +-
 lib/puppet/parser/ast/leaf.rb       |   31 +-
 lib/puppet/parser/ast/node.rb       |    9 +
 lib/puppet/parser/grammar.ra        |    1 +
 lib/puppet/parser/loaded_code.rb    |   10 +-
 lib/puppet/parser/parser.rb         | 1121 +++++++++++++++++------------------
 spec/unit/parser/ast/definition.rb  |   18 +
 spec/unit/parser/ast/leaf.rb        |   40 ++-
 spec/unit/parser/ast/node.rb        |   20 +
 spec/unit/parser/loaded_code.rb     |   55 ++-
 10 files changed, 726 insertions(+), 585 deletions(-)

diff --git a/lib/puppet/parser/ast/definition.rb 
b/lib/puppet/parser/ast/definition.rb
index 092afef..00b0416 100644
--- a/lib/puppet/parser/ast/definition.rb
+++ b/lib/puppet/parser/ast/definition.rb
@@ -24,9 +24,13 @@ class Puppet::Parser::AST::Definition < 
Puppet::Parser::AST::Branch
         false
     end
 
+    def get_classname(scope)
+        self.classname
+    end
+
     # Create a resource that knows how to evaluate our actual code.
     def evaluate(scope)
-        resource = Puppet::Parser::Resource.new(:type => self.class.name, 
:title => self.classname, :scope => scope, :source => scope.source)
+        resource = Puppet::Parser::Resource.new(:type => self.class.name, 
:title => get_classname(scope), :scope => scope, :source => scope.source)
 
         scope.catalog.tag(*resource.tags)
 
diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb
index 866d2b6..a69535c 100644
--- a/lib/puppet/parser/ast/leaf.rb
+++ b/lib/puppet/parser/ast/leaf.rb
@@ -19,6 +19,10 @@ class Puppet::Parser::AST
             obj == value
         end
 
+        def match(value)
+            @value == value
+        end
+
         def to_s
             return @value.to_s unless @value.nil?
         end
@@ -85,12 +89,12 @@ class Puppet::Parser::AST
     # undef values; equiv to nil
     class Undef < AST::Leaf; end
 
-    # Host names, either fully qualified or just the short name
+    # Host names, either fully qualified or just the short name, or even a 
regex
     class HostName < AST::Leaf
         def initialize(hash)
             super
 
-            @value = @value.to_s.downcase
+            @value = @value.to_s.downcase unless @value.is_a?(Regex)
             if @value =~ /[^-\w.]/
                 raise Puppet::DevError,
                     "'%s' is not a valid hostname" % @value
@@ -98,7 +102,9 @@ class Puppet::Parser::AST
         end
 
         def to_classname
-            return @value
+            classname = @value.to_s.downcase
+            classname.gsub!(/[^-a-zA-Z0-9:.]/,'') if regex?
+            classname
         end
 
         def eql?(value)
@@ -109,6 +115,19 @@ class Puppet::Parser::AST
         def hash
             return @value.hash
         end
+
+        def match(value)
+            value = value.value if value.is_a?(HostName)
+            return @value.match(value)
+        end
+
+        def regex?
+            @value.is_a?(Regex)
+        end
+
+        def to_s
+            @value.to_s
+        end
     end
 
     # A simple variable.  This object is only used during interpolation;
@@ -151,8 +170,12 @@ class Puppet::Parser::AST
             matched
         end
 
+        def match(value)
+            @value.match(value)
+        end
+
         def to_s
-            return @value.source
+            return @value.source.to_s
         end
     end
 end
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
index b2d4044..4f75201 100644
--- a/lib/puppet/parser/ast/node.rb
+++ b/lib/puppet/parser/ast/node.rb
@@ -17,6 +17,15 @@ class Puppet::Parser::AST::Node < 
Puppet::Parser::AST::HostClass
         ""
     end
 
+    # in Regex mode, our classname can't be our Regex.
+    # so we use the currently connected client as our
+    # classname, mimicing exactly what would have happened
+    # if there was a specific node definition for this node.
+    def get_classname(scope)
+        return scope.host if name.regex?
+        classname
+    end
+
     # Make sure node scopes are marked as such.
     def subscope(*args)
         scope = super
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index ed55d21..4c74211 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -699,6 +699,7 @@ hostname: NAME { result = val[0][:value] }
         | SQTEXT { result = val[0][:value] }
         | DQTEXT { result = val[0][:value] }
         | DEFAULT { result = val[0][:value] }
+        | regex
 
 nil:    {
     result = nil
diff --git a/lib/puppet/parser/loaded_code.rb b/lib/puppet/parser/loaded_code.rb
index 4bb03ee..a9133fa 100644
--- a/lib/puppet/parser/loaded_code.rb
+++ b/lib/puppet/parser/loaded_code.rb
@@ -18,7 +18,15 @@ class Puppet::Parser::LoadedCode
     end
 
     def node(name)
-        @nodes[check_name(name)]
+        name = check_name(name)
+        unless node = @nodes[name]
+            @nodes.each do |nodename, n|
+                if nodename.regex? and nodename.match(name)
+                    return n
+                end
+            end
+        end
+        node
     end
 
     def nodes?
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
index dbe0be4..376b818 100644
--- a/lib/puppet/parser/parser.rb
+++ b/lib/puppet/parser/parser.rb
<Patch Elided>
diff --git a/spec/unit/parser/ast/definition.rb 
b/spec/unit/parser/ast/definition.rb
index a58e4d0..b8f0941 100755
--- a/spec/unit/parser/ast/definition.rb
+++ b/spec/unit/parser/ast/definition.rb
@@ -29,6 +29,24 @@ describe Puppet::Parser::AST::Definition, "when evaluating" 
do
         @definition.evaluate_code(@resource)
     end
 
+    it "should have a get_classname method" do
+        @definition.should respond_to :get_classname
+    end
+
+    it "should return the current classname with get_classname" do
+        @definition.expects(:classname)
+
+        @definition.get_classname(@scope)
+    end
+
+    describe "when evaluating" do
+        it "should create a resource whose title comes from get_classname" do
+            @definition.expects(:get_classname).returns("classname")
+
+            @definition.evaluate(@scope)
+        end
+    end
+
 #    it "should copy its namespace to the scope"
 #
 #    it "should mark the scope virtual if the resource is virtual"
diff --git a/spec/unit/parser/ast/leaf.rb b/spec/unit/parser/ast/leaf.rb
index 38b753b..c9593b9 100755
--- a/spec/unit/parser/ast/leaf.rb
+++ b/spec/unit/parser/ast/leaf.rb
@@ -5,6 +5,8 @@ require File.dirname(__FILE__) + '/../../../spec_helper'
 describe Puppet::Parser::AST::Leaf do
     before :each do
         @scope = stub 'scope'
+        @value = stub 'value'
+        @leaf = Puppet::Parser::AST::Leaf.new(:value => @value)
     end
 
     it "should have a evaluate_match method" do
@@ -12,11 +14,6 @@ describe Puppet::Parser::AST::Leaf do
     end
 
     describe "when evaluate_match is called" do
-        before :each do
-            @value = stub 'value'
-            @leaf = Puppet::Parser::AST::Leaf.new(:value => @value)
-        end
-
         it "should evaluate itself" do
             @leaf.expects(:safeevaluate).with(@scope)
 
@@ -45,6 +42,16 @@ describe Puppet::Parser::AST::Leaf do
             Puppet::Parser::AST::Leaf.new( :value => value ).to_s
         end
     end
+
+    it "should have a match method" do
+        @leaf.should respond_to(:match)
+    end
+
+    it "should delegate match to ==" do
+        @value.expects(:==).with("value")
+
+        @leaf.match("value")
+    end
 end
 
 describe Puppet::Parser::AST::FlatString do
@@ -131,6 +138,15 @@ describe Puppet::Parser::AST::Regex do
 
         val.to_s
     end
+
+    it "should delegate match to the underlying regexp match method" do
+        regex = Regexp.new("/ab/")
+        val = Puppet::Parser::AST::Regex.new :value => regex
+
+        regex.expects(:match).with("value")
+
+        val.match("value")
+    end
 end
 
 describe Puppet::Parser::AST::HostName do
@@ -146,6 +162,10 @@ describe Puppet::Parser::AST::HostName do
         lambda { Puppet::Parser::AST::HostName.new( :value => "not an 
hostname!" ) }.should raise_error
     end
 
+    it "should not raise an error if hostname is a regex" do
+        lambda { Puppet::Parser::AST::HostName.new( :value => 
Puppet::Parser::AST::Regex.new(:value => "/test/") ) }.should_not raise_error
+    end
+
     it "should stringify the value" do
         value = stub 'value', :=~ => false
 
@@ -175,6 +195,11 @@ describe Puppet::Parser::AST::HostName do
         host.to_classname.should == "klassname"
     end
 
+    it "should return a string usable as classname when calling to_classname" 
do
+        host = Puppet::Parser::AST::HostName.new( :value => 
Puppet::Parser::AST::Regex.new(:value => "/^this-is n...@a classname$/") )
+        host.to_classname.should == "this-isnotaclassname"
+    end
+
     it "should delegate eql? to the underlying value if it is an HostName" do
         @value.expects(:eql?).with("value")
         @host.eql?("value")
@@ -190,4 +215,9 @@ describe Puppet::Parser::AST::HostName do
         @value.expects(:hash)
         @host.hash
     end
+
+    it "should return true when regex? is called and value is a Regex" do
+        @value.expects(:is_a?).with(Puppet::Parser::AST::Regex).returns(true)
+        @host.regex?.should be_true
+    end
 end
diff --git a/spec/unit/parser/ast/node.rb b/spec/unit/parser/ast/node.rb
index aaba4c2..5a4a5ef 100755
--- a/spec/unit/parser/ast/node.rb
+++ b/spec/unit/parser/ast/node.rb
@@ -12,6 +12,26 @@ describe Puppet::Parser::AST::Node do
         @scope = @compiler.topscope
     end
 
+    describe "when calling get_classname" do
+        it "should return current node name if name is a Regex" do
+            name = stub 'name', :regex? => true
+            node = @parser.newnode("node").shift
+            node.stubs(:name).returns(name)
+
+            @scope.expects(:host).returns("testnode")
+
+            node.get_classname(@scope).should == "testnode"
+        end
+
+        it "should return the current node classname if name is not a Regex" do
+            name = stub 'name', :regex? => false
+            node = @parser.newnode("node").shift
+            node.stubs(:name).returns(name)
+
+            node.get_classname(@scope).should == "node"
+        end
+    end
+
     describe Puppet::Parser::AST::Node, "when evaluating" do
 
         before do
diff --git a/spec/unit/parser/loaded_code.rb b/spec/unit/parser/loaded_code.rb
index d33bda9..4bfd119 100644
--- a/spec/unit/parser/loaded_code.rb
+++ b/spec/unit/parser/loaded_code.rb
@@ -129,10 +129,18 @@ describe Puppet::Parser::LoadedCode do
     end
 
     describe "when finding nodes" do
+        before :each do
+            @loader = Puppet::Parser::LoadedCode.new
+
+            @nodename1 = stub 'nodename1', :is_a? => true
+            @node1 = stub 'node1'
+            @nodename2 = stub 'nodename2', :is_a? => true
+            @node2 = stub 'node2'
+        end
         it "should create an HostName if nodename is a string" do
             Puppet::Parser::AST::HostName.expects(:new).with(:value => "foo")
-            loader = Puppet::Parser::LoadedCode.new
-            loader.node("foo")
+
+            @loader.node("foo")
         end
 
         it "should not create an HostName if nodename is an HostName" do
@@ -140,17 +148,50 @@ describe Puppet::Parser::LoadedCode do
 
             Puppet::Parser::AST::HostName.expects(:new).with(:value => 
"foo").never
 
-            loader = Puppet::Parser::LoadedCode.new
-            loader.node(name)
+            @loader.node(name)
         end
 
         it "should be able to find nobe by HostName" do
             namein = Puppet::Parser::AST::HostName.new(:value => "foo")
             nameout = Puppet::Parser::AST::HostName.new(:value => "foo")
-            loader = Puppet::Parser::LoadedCode.new
 
-            loader.add_node(namein, "bar")
-            loader.node(nameout) == "bar"
+            @loader.add_node(namein, "bar")
+            @loader.node(nameout) == "bar"
+        end
+
+        it "should return the first matching regex nodename" do
+            @nodename1.stubs(:regex?).returns(true)
+            @nodename1.expects(:match).returns(true)
+            @nodename2.stubs(:regex?).returns(false)
+
+            @loader.add_node(@nodename1, @node1)
+            @loader.add_node(@nodename2, @node2)
+
+            @loader.node("test").should == @node1
+        end
+
+        it "should not scan non-regex node" do
+            @nodename1.stubs(:regex?).returns(true)
+            @nodename1.stubs(:match).returns(false)
+            @nodename2.stubs(:regex?).returns(false)
+            @nodename2.expects(:match).never
+
+            @loader.add_node(@nodename1,@node1)
+            @loader.add_node(@nodename2,@node2)
+
+            @loader.node("test")
+        end
+
+        it "should prefer non-regex nodes to regex nodes" do
+            @nodename1.stubs(:regex?).returns(false)
+            @nodename1.expects(:match).never
+            @nodename2.stubs(:regex?).returns(true)
+            @nodename2.expects(:match).never
+
+            @loader.add_node(@nodename1,@node1)
+            @loader.add_node(@nodename2,@node2)
+
+            @loader.node(@nodename1)
         end
     end
 end
-- 
1.6.0.2


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to