This patch introduces a new language construct:
Resource <| |> { parameter_override }

This construct allows to override resource parameters after the
resource is collected by a collector.

Note: the override still have to be issued in a child scope of
the original resource as with regular override

Signed-off-by: Brice Figureau <[email protected]>
---
 lib/puppet/parser/ast/collection.rb       |   31 +
 lib/puppet/parser/collector.rb            |   35 +-
 lib/puppet/parser/grammar.ra              |   22 +-
 lib/puppet/parser/parser.rb               | 1455 +++++++++++++++--------------
 spec/unit/parser/ast/collection.rb        |   63 ++
 spec/unit/parser/collector.rb             |   84 ++
 test/data/snippets/collection_override.pp |    8 +
 test/language/snippets.rb                 |    6 +
 8 files changed, 996 insertions(+), 708 deletions(-)
 create mode 100755 spec/unit/parser/ast/collection.rb
 create mode 100644 test/data/snippets/collection_override.pp

diff --git a/lib/puppet/parser/ast/collection.rb 
b/lib/puppet/parser/ast/collection.rb
index a51b9b4..db978c4 100644
--- a/lib/puppet/parser/ast/collection.rb
+++ b/lib/puppet/parser/ast/collection.rb
@@ -7,6 +7,7 @@ require 'puppet/parser/collector'
 class Puppet::Parser::AST
 class Collection < AST::Branch
     attr_accessor :type, :query, :form
+    attr_reader :override
 
     associates_doc
 
@@ -22,7 +23,37 @@ class Collection < AST::Branch
 
         scope.compiler.add_collection(newcoll)
 
+        add_override(newcoll, scope) if @override
+
         newcoll
     end
+
+    # Handle our parameter ourselves
+    def override=(override)
+        if override.is_a?(AST::ASTArray)
+            @override = override
+        else
+            @override = AST::ASTArray.new(
+                :line => override.line,
+                :file => override.file,
+                :children => [override]
+            )
+        end
+    end
+
+    def add_override(collection, scope)
+        params = @override.collect do |param|
+            param.safeevaluate(scope)
+        end
+
+        collection.add_override(
+            :params => params,
+            :file => @file,
+            :line => @line,
+            :source => scope.source,
+            :scope => scope
+        )
+    end
+
 end
 end
diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb
index 1dd1eb9..7dc1f72 100644
--- a/lib/puppet/parser/collector.rb
+++ b/lib/puppet/parser/collector.rb
@@ -1,9 +1,10 @@
 # An object that collects stored objects from the central cache and returns
 # them to the current host, yo.
 class Puppet::Parser::Collector
-    attr_accessor :type, :scope, :vquery, :equery, :form, :resources, 
:collected
+    attr_accessor :type, :scope, :vquery, :equery, :form, :resources, 
:collected, :overrides
 
     # Call the collection method, mark all of the returned objects as 
non-virtual,
+    # and optionnaly apply overrides on them.
     # The collector can also delete himself from the compiler if there is no 
more 
     # resources to collect (valid only for resource fixed-set collector
     # which get their resources from +collect_resources+ and not from the 
catalog)
@@ -29,6 +30,8 @@ class Puppet::Parser::Collector
             end
         end
 
+        apply_overrides(objects) if @overrides
+
         # filter out object that already have been collected by ourself
         # which can happen if we are called several times and we collect 
catalog
         # resources
@@ -58,8 +61,38 @@ class Puppet::Parser::Collector
         @form = form
     end
 
+    # add a resource override to the soon to be exported/realized resources
+    def add_override(hash)
+        unless hash[:params]
+            raise ArgumentError, "Exported resource try to override without 
parameters"
+        end
+
+        # schedule an override for an upcoming collection
+        @overrides = hash
+    end
+
     private
 
+    def apply_overrides(objects)
+        # tell the compiler we have some override for him unless we already
+        # collected those resources
+        objects.each do |res|
+            unless @collected.include?(res.ref)
+                res = Puppet::Parser::Resource.new(
+                    :type => res.type,
+                    :title => res.title,
+                    :params => overrides[:params],
+                    :file => overrides[:file],
+                    :line => overrides[:line],
+                    :source => overrides[:source],
+                    :scope => overrides[:scope]
+                )
+
+                scope.compiler.add_override(res)
+            end
+        end
+    end
+
     # Create our active record query.
     def build_active_record_query
         Puppet::Rails.init unless ActiveRecord::Base.connected?
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index 67303ab..9da2999 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -195,7 +195,27 @@ at:   AT { result = :virtual }
 
 # A collection statement.  Currently supports no arguments at all, but 
eventually
 # will, I assume.
-collection:     classref collectrhand {
+collection:     classref collectrhand LBRACE anyparams endcomma RBRACE {
+    if val[0] =~ /^[a-z]/
+        Puppet.warning addcontext("Collection names must now be capitalized")
+    end
+    type = val[0].downcase
+    args = {:type => type}
+
+    if val[1].is_a?(AST::CollExpr)
+        args[:query] = val[1]
+        args[:query].type = type
+        args[:form] = args[:query].form
+    else
+        args[:form] = val[1]
+    end
+    if args[:form] == :exported and ! Puppet[:storeconfigs] and ! 
Puppet[:parseonly]
+        Puppet.warning addcontext("You cannot collect exported resources 
without storeconfigs being set; the collection will be ignored")
+    end
+    args[:override] = val[3]
+    result = ast AST::Collection, args
+}
+                | classref collectrhand {
     if val[0] =~ /^[a-z]/
         Puppet.warning addcontext("Collection names must now be capitalized")
     end
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
index 60a849c..c0b5435 100644
--- a/lib/puppet/parser/parser.rb
+++ b/lib/puppet/parser/parser.rb
[patch elided]
diff --git a/spec/unit/parser/ast/collection.rb 
b/spec/unit/parser/ast/collection.rb
new file mode 100755
index 0000000..c141bd7
--- /dev/null
+++ b/spec/unit/parser/ast/collection.rb
@@ -0,0 +1,63 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+describe Puppet::Parser::AST::Collection do
+    before :each do
+        @scope = stub_everything 'scope'
+        @compiler = stub_everything 'compile'
+        @scope.stubs(:compiler).returns(@compiler)
+
+        @overrides = stub_everything 'overrides'
+        @overrides.stubs(:is_a?).with(Puppet::Parser::AST).returns(true)
+
+    end
+
+    it "should evaluate its query" do
+        query = mock 'query'
+        collection = Puppet::Parser::AST::Collection.new :query => query, 
:form => :virtual
+
+        query.expects(:safeevaluate).with(@scope)
+
+        collection.evaluate(@scope)
+    end
+
+    it "should instantiate a Collector for this type" do
+        collection = Puppet::Parser::AST::Collection.new :form => :virtual, 
:type => "test"
+
+        Puppet::Parser::Collector.expects(:new).with(@scope, "test", nil, nil, 
:virtual)
+
+        collection.evaluate(@scope)
+    end
+
+    it "should tell the compiler about this collector" do
+        collection = Puppet::Parser::AST::Collection.new :form => :virtual, 
:type => "test"
+        Puppet::Parser::Collector.stubs(:new).returns("whatever")
+
+        @compiler.expects(:add_collection).with("whatever")
+
+        collection.evaluate(@scope)
+    end
+
+    it "should evaluate overriden paramaters" do
+        collector = stub_everything 'collector'
+        collection = Puppet::Parser::AST::Collection.new :form => :virtual, 
:type => "test", :override => @overrides
+        Puppet::Parser::Collector.stubs(:new).returns(collector)
+
+        @overrides.expects(:safeevaluate).with(@scope)
+
+        collection.evaluate(@scope)
+    end
+
+    it "should tell the collector about overrides" do
+        collector = mock 'collector'
+        collection = Puppet::Parser::AST::Collection.new :form => :virtual, 
:type => "test", :override => @overrides
+        Puppet::Parser::Collector.stubs(:new).returns(collector)
+
+        collector.expects(:add_override)
+
+        collection.evaluate(@scope)
+    end
+
+
+end
diff --git a/spec/unit/parser/collector.rb b/spec/unit/parser/collector.rb
index 56c684c..b0c23f6 100755
--- a/spec/unit/parser/collector.rb
+++ b/spec/unit/parser/collector.rb
@@ -39,6 +39,14 @@ describe Puppet::Parser::Collector, "when initializing" do
         @collector = Puppet::Parser::Collector.new(@scope, "resource::type", 
@equery, @vquery, @form)
         @collector.type.should == "Resource::Type"
     end
+
+    it "should accept an optional resource override" do
+        @collector = Puppet::Parser::Collector.new(@scope, "resource::type", 
@equery, @vquery, @form)
+        override = { :params => "whatever" }
+        @collector.add_override(override)
+        @collector.overrides.should equal(override)
+    end
+
 end
 
 describe Puppet::Parser::Collector, "when collecting specific virtual 
resources" do
@@ -167,6 +175,38 @@ describe Puppet::Parser::Collector, "when collecting 
virtual and catalog resourc
         @collector.evaluate.should == [one]
     end
 
+    it "should create a resource with overriden parameters" do
+        one = stub_everything 'one', :type => "Mytype", :virtual? => true, 
:title => "test"
+        param = stub 'param'
+        @compiler.stubs(:add_override)
+
+        @compiler.expects(:resources).returns([one])
+
+        @collector.add_override(:params => param )
+        Puppet::Parser::Resource.expects(:new).with { |h|
+            h[:params] == param
+        }
+
+        @collector.evaluate
+    end
+
+    it "should not override already overriden resources for this same 
collection in a previous run" do
+        one = stub_everything 'one', :type => "Mytype", :virtual? => true, 
:title => "test"
+        param = stub 'param'
+        @compiler.stubs(:add_override)
+
+        @compiler.expects(:resources).at_least(2).returns([one])
+
+        @collector.add_override(:params => param )
+        Puppet::Parser::Resource.expects(:new).once.with { |h|
+            h[:params] == param
+        }
+
+        @collector.evaluate
+
+        @collector.evaluate
+    end
+
     it "should not return resources that were collected in a previous run of 
this collector" do
         one = stub_everything 'one', :type => "Mytype", :virtual? => true, 
:title => "test"
         @compiler.stubs(:resources).returns([one])
@@ -176,6 +216,21 @@ describe Puppet::Parser::Collector, "when collecting 
virtual and catalog resourc
         @collector.evaluate.should be_false
     end
 
+
+    it "should tell the compiler about the overriden resources" do
+        one = stub_everything 'one', :type => "Mytype", :virtual? => true, 
:title => "test"
+        param = stub 'param'
+
+        one.expects(:virtual=).with(false)
+        @compiler.expects(:resources).returns([one])
+        @collector.add_override(:params => param )
+        Puppet::Parser::Resource.stubs(:new).returns("whatever")
+
+        @compiler.expects(:add_override).with("whatever")
+
+        @collector.evaluate
+    end
+
     it "should not return or mark non-matching resources" do
         @collector.vquery = proc { |res| res.name == :one }
 
@@ -282,6 +337,35 @@ describe Puppet::Parser::Collector, "when collecting 
exported resources" do
         @collector.evaluate.should == [resource]
     end
 
+    it "should override all exported collected resources if collector has an 
override" do
+        stub_rails()
+        Puppet::Rails::Host.stubs(:find_by_name).returns(nil)
+
+        one = stub 'one', :restype => "Mytype", :title => "one", :virtual? => 
true, :exported? => true, :ref => "one"
+        Puppet::Rails::Resource.stubs(:find).returns([one])
+
+        resource = mock 'resource'
+        one.expects(:to_resource).with(@scope).returns(resource)
+        resource.stubs(:exported=)
+        resource.stubs(:virtual=)
+        resource.stubs(:ref)
+        resource.stubs(:title)
+
+        @compiler.stubs(:resources).returns([])
+        @scope.stubs(:findresource).returns(nil)
+
+        param = stub 'param'
+        @compiler.stubs(:add_override)
+        @compiler.stubs(:add_resource)
+
+        @collector.add_override(:params => param )
+        Puppet::Parser::Resource.expects(:new).once.with { |h|
+            h[:params] == param
+        }
+
+        @collector.evaluate
+    end
+
     it "should store converted resources in the compile's resource list" do
         stub_rails()
         Puppet::Rails::Host.stubs(:find_by_name).returns(nil)
diff --git a/test/data/snippets/collection_override.pp 
b/test/data/snippets/collection_override.pp
new file mode 100644
index 0000000..b1b39ab
--- /dev/null
+++ b/test/data/snippets/collection_override.pp
@@ -0,0 +1,8 @@
+...@file {
+    "/tmp/collection":
+        content => "whatever"
+}
+
+File<| |> {
+    mode => 0600
+}
diff --git a/test/language/snippets.rb b/test/language/snippets.rb
index a1d0f5d..c52409e 100755
--- a/test/language/snippets.rb
+++ b/test/language/snippets.rb
@@ -475,6 +475,12 @@ class TestSnippets < Test::Unit::TestCase
         assert_not_file("/tmp/multilinecomments","Did create a commented 
resource");
     end
 
+    def snippet_collection_override
+        path = "/tmp/collection"
+        assert_file(path)
+        assert_mode_equal(0600, path)
+    end
+
     # Iterate across each of the snippets and create a test.
     Dir.entries(snippetdir).sort.each { |file|
         next if file =~ /^\./
-- 
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