+1

I might be exhuasted, though. :)

On Jul 26, 2009, at 9:03 AM, Brice Figureau wrote:

>
> 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
>
>
> >


-- 
Censorship, like charity, should begin at home; but, unlike charity, it
should end there. --Clare Booth Luce
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com


--~--~---------~--~----~------------~-------~--~----~
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