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