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