+.5 because I'm not 100% confident in my comprehension of the code. But I really like it!
On Mon, May 10, 2010 at 11:55 PM, Luke Kanies <[email protected]> wrote: > (Changes to parser.rb have been removed from this email.) > > This is just a first, simplest version, but you can > now specify relationships directly in the language: > > File[/foo] -> Service[bar] > > Specifies a normal dependency while: > > File[/foo] => Service[bar] > > Specifies a subscription. > > Next in the plan is to support complete resources or collections > on either side and to make the statements extendable: > > File[/foo] -> Package[baz] -> Service[bar] > > Signed-off-by: Luke Kanies <[email protected]> > --- > lib/puppet/parser/ast.rb | 1 + > lib/puppet/parser/ast/dependency.rb | 31 + > lib/puppet/parser/compiler.rb | 15 +- > lib/puppet/parser/dependency.rb | 24 + > lib/puppet/parser/grammar.ra | 9 +- > lib/puppet/parser/lexer.rb | 4 + > lib/puppet/parser/parser.rb | 2276 > ++++++++++++++++++----------------- > spec/unit/parser/ast/dependency.rb | 64 + > spec/unit/parser/compiler.rb | 11 +- > spec/unit/parser/dependency.rb | 60 + > 10 files changed, 1381 insertions(+), 1114 deletions(-) > create mode 100644 lib/puppet/parser/ast/dependency.rb > create mode 100644 lib/puppet/parser/dependency.rb > create mode 100644 spec/unit/parser/ast/dependency.rb > create mode 100644 spec/unit/parser/dependency.rb > > diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb > index 6af7745..41bd721 100644 > --- a/lib/puppet/parser/ast.rb > +++ b/lib/puppet/parser/ast.rb > @@ -116,3 +116,4 @@ require 'puppet/parser/ast/resourceparam' > require 'puppet/parser/ast/selector' > require 'puppet/parser/ast/tag' > require 'puppet/parser/ast/vardef' > +require 'puppet/parser/ast/dependency' > diff --git a/lib/puppet/parser/ast/dependency.rb > b/lib/puppet/parser/ast/dependency.rb > new file mode 100644 > index 0000000..5678ffa > --- /dev/null > +++ b/lib/puppet/parser/ast/dependency.rb > @@ -0,0 +1,31 @@ > +require 'puppet/parser/ast' > +require 'puppet/parser/ast/branch' > +require 'puppet/parser/dependency' > + > +class Puppet::Parser::AST::Dependency < Puppet::Parser::AST::Branch > + attr_accessor :source, :target, :type > + > + # Evaluate our object, but just return a simple array of the type > + # and name. > + def evaluate(scope) > + source = self.source.safeevaluate(scope) > + target = self.target.safeevaluate(scope) > + scope.compiler.add_dependency(Puppet::Parser::Dependency.new(source, > target, type)) > + end > + > + def initialize(left, right, type, args = {}) > + super(args) > + if type.include?(">") > + �...@source = left > + �...@target = right > + else > + �...@source = right > + �...@target = left > + end > + if type.include?("-") > + �...@type = :dependency > + else > + �...@type = :subscription > + end > + end > +end > diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb > index 8e84f5a..8208f9c 100644 > --- a/lib/puppet/parser/compiler.rb > +++ b/lib/puppet/parser/compiler.rb > @@ -21,13 +21,17 @@ class Puppet::Parser::Compiler > raise Puppet::Error, "#{detail} on node #{node.name}" > end > > - attr_reader :node, :facts, :collections, :catalog, :node_scope, > :resources > + attr_reader :node, :facts, :collections, :catalog, :node_scope, > :resources, :dependencies > > # Add a collection to the global list. > def add_collection(coll) > @collections << coll > end > > + def add_dependency(dep) > + �...@dependencies << dep > + end > + > # Store a resource override. > def add_override(override) > # If possible, merge the override in immediately. > @@ -144,6 +148,10 @@ class Puppet::Parser::Compiler > found > end > > + def evaluate_dependencies > + �[email protected] { |dep| dep.evaluate(catalog) } > + end > + > # Return a resource by either its ref or its type and title. > def findresource(*args) > @catalog.resource(*args) > @@ -337,6 +345,8 @@ class Puppet::Parser::Compiler > # Make sure all of our resources and such have done any last work > # necessary. > def finish > + evaluate_dependencies() > + > resources.each do |resource| > # Add in any resource overrides. > if overrides = resource_overrides(resource) > @@ -374,6 +384,9 @@ class Puppet::Parser::Compiler > # but they each refer back to the scope that created them. > @collections = [] > > + # The list of dependencies to evaluate. > + �...@dependencies = [] > + > # For maintaining the relationship between scopes and their resources. > @catalog = Puppet::Resource::Catalog.new(@node.name) > @catalog.version = known_resource_types.version > diff --git a/lib/puppet/parser/dependency.rb b/lib/puppet/parser/dependency.rb > new file mode 100644 > index 0000000..b085e6f > --- /dev/null > +++ b/lib/puppet/parser/dependency.rb > @@ -0,0 +1,24 @@ > +class Puppet::Parser::Dependency > + attr_accessor :source, :target, :type > + > + PARAM_MAP = {:dependency => :require, :subscription => :subscribe} > + > + def evaluate(catalog) > + unless source_resource = catalog.resource(source) > + raise ArgumentError, "Could not find resource #{source} for > dependency on #{target}" > + end > + unless target_resource = catalog.resource(target) > + raise ArgumentError, "Could not find resource #{target} for > dependency from #{source}" > + end > + source_resource[param_name] ||= [] > + source_resource[param_name] << target > + end > + > + def initialize(source, target, type) > + �...@source, @target, @type = source.to_s, target.to_s, type > + end > + > + def param_name > + PARAM_MAP[type] || raise(ArgumentError, "Invalid dependency type > #{type}") > + end > +end > diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra > index 5b9a609..6d19033 100644 > --- a/lib/puppet/parser/grammar.ra > +++ b/lib/puppet/parser/grammar.ra > @@ -11,7 +11,7 @@ token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN > LESSTHAN > token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN > token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSNAME CLASSREF > token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS > -token MATCH NOMATCH REGEX > +token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB > > prechigh > right NOT > @@ -74,6 +74,13 @@ statement: resource > | nodedef > | resourceoverride > | append > + | relationship > + > +relationship: resourceref edge resourceref { > + result = AST::Dependency.new(val[0], val[2], val[1], ast_context) > +} > + > +edge: IN_EDGE | OUT_EDGE | IN_EDGE_SUB | OUT_EDGE_SUB > > fstatement: NAME LPAREN funcvalues RPAREN { > args = aryfy(val[2]) > diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb > index 2a1f88e..628a37a 100644 > --- a/lib/puppet/parser/lexer.rb > +++ b/lib/puppet/parser/lexer.rb > @@ -129,6 +129,10 @@ class Puppet::Parser::Lexer > ':' => :COLON, > '@' => :AT, > '<<|' => :LLCOLLECT, > + '->' => :IN_EDGE, > + '<-' => :OUT_EDGE, > + '=>' => :IN_EDGE_SUB, > + '<=' => :OUT_EDGE_SUB, > '|>>' => :RRCOLLECT, > '<|' => :LCOLLECT, > '|>' => :RCOLLECT, > diff --git a/spec/unit/parser/ast/dependency.rb > b/spec/unit/parser/ast/dependency.rb > new file mode 100644 > index 0000000..52283d6 > --- /dev/null > +++ b/spec/unit/parser/ast/dependency.rb > @@ -0,0 +1,64 @@ > +#!/usr/bin/env ruby > + > +require File.dirname(__FILE__) + '/../../../spec_helper' > + > +describe Puppet::Parser::AST::Dependency do > + before do > + �...@class = Puppet::Parser::AST::Dependency > + end > + > + it "should set its type to :dependency if the relationship type is '->'" > do > + �[email protected](:left, :right, "->").type.should == :dependency > + end > + > + it "should set its type to :dependency if the relationship type is '<-'" > do > + �[email protected](:left, :right, "<-").type.should == :dependency > + end > + > + it "should set its type to :subscription if the relationship type is > '=>'" do > + �[email protected](:left, :right, "=>").type.should == :subscription > + end > + > + it "should set its type to :subscription if the relationship type is > '<='" do > + �[email protected](:left, :right, "<=").type.should == :subscription > + end > + > + it "should set its source to the first argument and target to the second > if the arrow points to the right" do > + dep = @class.new(:left, :right, "->") > + dep.source.should == :left > + dep.target.should == :right > + dep = @class.new(:left, :right, "=>") > + dep.source.should == :left > + dep.target.should == :right > + end > + > + it "should set its source to the second argument and target to the first > if the arrow points to the left" do > + dep = @class.new(:left, :right, "<-") > + dep.source.should == :right > + dep.target.should == :left > + dep = @class.new(:left, :right, "<=") > + dep.source.should == :right > + dep.target.should == :left > + end > + > + it "should set its line and file if provided" do > + dep = @class.new(:left, :right, "<=", :line => 50, :file => "/foo") > + dep.line.should == 50 > + dep.file.should == "/foo" > + end > + > + describe "when evaluating" do > + before do > + �...@compiler = > Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) > + �...@scope = Puppet::Parser::Scope.new(:compiler => @compiler) > + end > + > + it "should create a dependency with the evaluated source and target > and add it to the scope" do > + source = stub 'source', :safeevaluate => :left > + target = stub 'target', :safeevaluate => :right > + �[email protected](source, target, "->").evaluate(@scope) > + �[email protected][0].source.should == "left" > + �[email protected][0].target.should == "right" > + end > + end > +end > diff --git a/spec/unit/parser/compiler.rb b/spec/unit/parser/compiler.rb > index 6fd4d1f..35fff54 100755 > --- a/spec/unit/parser/compiler.rb > +++ b/spec/unit/parser/compiler.rb > @@ -134,7 +134,7 @@ describe Puppet::Parser::Compiler do > > def compile_methods > [:set_node_parameters, :evaluate_main, :evaluate_ast_node, > :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, > - :finish, :store, :extract] > + :finish, :store, :extract, :evaluate_dependencies] > end > > # Stub all of the main compile methods except the ones we're > specifically interested in. > @@ -394,6 +394,15 @@ describe Puppet::Parser::Compiler do > end > end > > + describe "when evaluating dependencies" do > + it "should evaluate each dependency with its catalog" do > + dep = stub 'dep' > + dep.expects(:evaluate).with(@compiler.catalog) > + �[email protected]_dependency dep > + �[email protected]_dependencies > + end > + end > + > describe "when told to evaluate missing classes" do > > it "should fail if there's no source listed for the scope" do > diff --git a/spec/unit/parser/dependency.rb b/spec/unit/parser/dependency.rb > new file mode 100644 > index 0000000..d8206c1 > --- /dev/null > +++ b/spec/unit/parser/dependency.rb > @@ -0,0 +1,60 @@ > +#!/usr/bin/env ruby > + > +require File.dirname(__FILE__) + '/../../spec_helper' > + > +require 'puppet/parser/dependency' > + > +describe Puppet::Parser::Dependency do > + before do > + �...@source = Puppet::Resource.new(:mytype, "source") > + �...@target = Puppet::Resource.new(:mytype, "target") > + �...@dep = Puppet::Parser::Dependency.new(@source, @target, > :dependency) > + end > + > + it "should set the source to the stringified version" do > + �[email protected] == "Mytype[source]" > + end > + > + it "should set the target to the stringified version" do > + �[email protected] == "Mytype[target]" > + end > + > + describe "when evaluating" do > + before do > + �...@catalog = Puppet::Resource::Catalog.new > + �[email protected]_resource(@source) > + �[email protected]_resource(@target) > + end > + > + it "should fail if the source resource cannot be found" do > + �...@catalog = Puppet::Resource::Catalog.new > + �[email protected]_resource @target > + lambda { @dep.evaluate(@catalog) }.should > raise_error(ArgumentError) > + end > + > + it "should fail if the target resource cannot be found" do > + �...@catalog = Puppet::Resource::Catalog.new > + �[email protected]_resource @source > + lambda { @dep.evaluate(@catalog) }.should > raise_error(ArgumentError) > + end > + > + it "should add the target as a 'require' value if the type is > 'dependency'" do > + �[email protected] = :dependency > + �[email protected](@catalog) > + �...@source[:require].should be_include("Mytype[target]") > + end > + > + it "should add the target as a 'subscribe' value if the type is > 'subscription'" do > + �[email protected] = :subscription > + �[email protected](@catalog) > + �...@source[:subscribe].should be_include("Mytype[target]") > + end > + > + it "should supplement rather than clobber existing relationship > values" do > + �...@source[:require] = "File[/bar]" > + �[email protected](@catalog) > + �...@source[:require].should be_include("Mytype[target]") > + �...@source[:require].should be_include("File[/bar]") > + end > + end > +end > -- > 1.6.1 > > -- > 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. > > -- 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.
