diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 7f4e8229807..367a7524890 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -204,25 +204,25 @@ def evaluate_classes(classes, scope, lazy_evaluate = true, fqname = false) class_parameters = classes classes = classes.keys end - classes.each do |name| - # If we can find the class, then make a resource that will evaluate it. - if klass = scope.find_hostclass(name, :assume_fqname => fqname) - - # If parameters are passed, then attempt to create a duplicate resource - # so the appropriate error is thrown. - if class_parameters - resource = klass.ensure_in_catalog(scope, class_parameters[name] || {}) - else - next if scope.class_scope(klass) - resource = klass.ensure_in_catalog(scope) - end - # If they've disabled lazy evaluation (which the :include function does), - # then evaluate our resource immediately. - resource.evaluate unless lazy_evaluate - else - raise Puppet::Error, "Could not find class #{name} for #{node.name}" + hostclasses = classes.collect do |name| + scope.find_hostclass(name, :assume_fqname => fqname) or raise Puppet::Error, "Could not find class #{name} for #{node.name}" + end + + if class_parameters + resources = ensure_classes_with_parameters(scope, hostclasses, class_parameters) + if !lazy_evaluate + resources.each(&:evaluate) end + + resources + else + already_included, newly_included = ensure_classes_without_parameters(scope, hostclasses) + if !lazy_evaluate + newly_included.each(&:evaluate) + end + + already_included + newly_included end end @@ -299,6 +299,27 @@ def is_binder_active? private + def ensure_classes_with_parameters(scope, hostclasses, parameters) + hostclasses.collect do |klass| + klass.ensure_in_catalog(scope, parameters[klass.name] || {}) + end + end + + def ensure_classes_without_parameters(scope, hostclasses) + already_included = [] + newly_included = [] + hostclasses.each do |klass| + class_scope = scope.class_scope(klass) + if class_scope + already_included << class_scope.resource + else + newly_included << klass.ensure_in_catalog(scope) + end + end + + [already_included, newly_included] + end + # If ast nodes are enabled, then see if we can find and evaluate one. def evaluate_ast_node return unless ast_nodes? diff --git a/lib/puppet/parser/functions/contain.rb b/lib/puppet/parser/functions/contain.rb index 8eb514561ad..791c7d91f30 100644 --- a/lib/puppet/parser/functions/contain.rb +++ b/lib/puppet/parser/functions/contain.rb @@ -15,12 +15,12 @@ ) do |classes| scope = self - scope.function_include(classes) + containing_resource = scope.resource - classes.each do |class_name| - class_resource = scope.catalog.resource("Class", class_name) - if ! scope.catalog.edge?(scope.resource, class_resource) - scope.catalog.add_edge(scope.resource, class_resource) + included = scope.function_include(classes) + included.each do |resource| + if ! scope.catalog.edge?(containing_resource, resource) + scope.catalog.add_edge(containing_resource, resource) end end end diff --git a/lib/puppet/parser/functions/include.rb b/lib/puppet/parser/functions/include.rb index 29ef45d403c..322d6770b1c 100644 --- a/lib/puppet/parser/functions/include.rb +++ b/lib/puppet/parser/functions/include.rb @@ -18,30 +18,12 @@ where they are declared. For that, see the `contain` function. It also does not create a dependency relationship between the declared class and the surrounding class; for that, see the `require` function.") do |vals| - if vals.is_a?(Array) - # Protect against array inside array - vals = vals.flatten - else - vals = [vals] - end - - # The 'false' disables lazy evaluation. - klasses = compiler.evaluate_classes(vals, self, false) - - missing = vals.find_all do |klass| - ! klasses.include?(klass) - end - - unless missing.empty? - # Throw an error if we didn't evaluate all of the classes. - str = "Could not find class" - str += "es" if missing.length > 1 - - str += " " + missing.join(", ") - - if n = namespaces and ! n.empty? and n != [""] - str += " in namespaces #{@namespaces.join(", ")}" - end - self.fail Puppet::ParseError, str - end + if vals.is_a?(Array) + # Protect against array inside array + vals = vals.flatten + else + vals = [vals] + end + + compiler.evaluate_classes(vals, self, false) end diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index d1e855a2024..4698479d34c 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -681,7 +681,7 @@ def compile it "should skip classes that have already been evaluated" do @compiler.catalog.stubs(:tag) - @scope.stubs(:class_scope).with(@class).returns("something") + @scope.stubs(:class_scope).with(@class).returns(@scope) @compiler.expects(:add_resource).never @@ -694,7 +694,7 @@ def compile it "should skip classes previously evaluated with different capitalization" do @compiler.catalog.stubs(:tag) @scope.stubs(:find_hostclass).with("MyClass",{:assume_fqname => false}).returns(@class) - @scope.stubs(:class_scope).with(@class).returns("something") + @scope.stubs(:class_scope).with(@class).returns(@scope) @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never diff --git a/spec/unit/parser/functions/contain_spec.rb b/spec/unit/parser/functions/contain_spec.rb index 3150e0c8e1a..d54e55b119f 100644 --- a/spec/unit/parser/functions/contain_spec.rb +++ b/spec/unit/parser/functions/contain_spec.rb @@ -3,11 +3,13 @@ require 'puppet_spec/compiler' require 'puppet/parser/functions' require 'matchers/containment_matchers' +require 'matchers/resource' require 'matchers/include_in_order' describe 'The "contain" function' do include PuppetSpec::Compiler include ContainmentMatchers + include Matchers::Resource it "includes the class" do catalog = compile_to_catalog(<<-MANIFEST) @@ -25,6 +27,41 @@ class container { expect(catalog.classes).to include("contained") end + it "includes the class when using a fully qualified anchored name" do + catalog = compile_to_catalog(<<-MANIFEST) + class contained { + notify { "contained": } + } + + class container { + contain ::contained + } + + include container + MANIFEST + + expect(catalog.classes).to include("contained") + end + + it "ensures that the edge is with the correct class" do + catalog = compile_to_catalog(<<-MANIFEST) + class outer { + class named { } + contain named + } + + class named { } + + include named + include outer + MANIFEST + + expect(catalog).to have_resource("Class[Named]") + expect(catalog).to have_resource("Class[Outer]") + expect(catalog).to have_resource("Class[Outer::Named]") + expect(catalog).to contain_class("outer::named").in("outer") + end + it "makes the class contained in the current class" do catalog = compile_to_catalog(<<-MANIFEST) class contained {