summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/parser/compiler.rb21
-rw-r--r--lib/puppet/resource/type.rb14
-rwxr-xr-xspec/unit/parser/compiler_spec.rb95
-rwxr-xr-xspec/unit/resource/type_spec.rb32
4 files changed, 147 insertions, 15 deletions
diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
index c60e1d4fb..fdabd05c9 100644
--- a/lib/puppet/parser/compiler.rb
+++ b/lib/puppet/parser/compiler.rb
@@ -139,12 +139,23 @@ class Puppet::Parser::Compiler
def evaluate_classes(classes, scope, lazy_evaluate = true)
raise Puppet::DevError, "No source for scope passed to evaluate_classes" unless scope.source
found = []
+ param_classes = nil
+ # if we are a param class, save the classes hash
+ # and transform classes to be the keys
+ if classes.class == Hash
+ param_classes = 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)
- found << name and next if scope.class_scope(klass)
- resource = klass.ensure_in_catalog(scope)
+ if param_classes
+ resource = klass.ensure_in_catalog(scope, param_classes[name] || {})
+ else
+ found << name and 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.
@@ -432,7 +443,11 @@ class Puppet::Parser::Compiler
@resources = []
# Make sure any external node classes are in our class list
- @catalog.add_class(*@node.classes)
+ if @node.classes.class == Hash
+ @catalog.add_class(*@node.classes.keys)
+ else
+ @catalog.add_class(*@node.classes)
+ end
end
# Set the node's parameters into the top-scope as variables.
diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb
index d40adc145..92e1ef719 100644
--- a/lib/puppet/resource/type.rb
+++ b/lib/puppet/resource/type.rb
@@ -143,18 +143,26 @@ class Puppet::Resource::Type
# classes and nodes. No parameters are be supplied--if this is a
# parameterized class, then all parameters take on their default
# values.
- def ensure_in_catalog(scope)
+ def ensure_in_catalog(scope, parameters=nil)
type == :definition and raise ArgumentError, "Cannot create resources for defined resource types"
resource_type = type == :hostclass ? :class : :node
# Do nothing if the resource already exists; this makes sure we don't
# get multiple copies of the class resource, which helps provide the
# singleton nature of classes.
- if resource = scope.catalog.resource(resource_type, name)
+ # we should not do this for classes with parameters
+ # if parameters are passed, we should still try to create the resource
+ # even if it exists so that we can fail
+ # this prevents us from being able to combine param classes with include
+ if resource = scope.catalog.resource(resource_type, name) and !parameters
return resource
end
-
resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self)
+ if parameters
+ parameters.each do |k,v|
+ resource.set_parameter(k,v)
+ end
+ end
instantiate_resource(scope, resource)
scope.compiler.add_resource(scope, resource)
resource
diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb
index 95f3853e2..687f2ecb9 100755
--- a/spec/unit/parser/compiler_spec.rb
+++ b/spec/unit/parser/compiler_spec.rb
@@ -105,8 +105,15 @@ describe Puppet::Parser::Compiler do
node.classes = %w{foo bar}
compiler = Puppet::Parser::Compiler.new(node)
- compiler.classlist.should include("foo")
- compiler.classlist.should include("bar")
+ compiler.classlist.should =~ ['foo', 'bar']
+ end
+
+ it "should transform node class hashes into a class list" do
+ node = Puppet::Node.new("mynode")
+ node.classes = {'foo'=>{'one'=>'1'}, 'bar'=>{'two'=>'2'}}
+ compiler = Puppet::Parser::Compiler.new(node)
+
+ compiler.classlist.should =~ ['foo', 'bar']
end
it "should add a 'main' stage to the catalog" do
@@ -185,6 +192,14 @@ describe Puppet::Parser::Compiler do
@compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes }
end
+ it "should evaluate any parameterized classes named in the node" do
+ classes = {'foo'=>{'1'=>'one'}, 'bar'=>{'2'=>'two'}}
+ @node.stubs(:classes).returns(classes)
+ @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope)
+ @compiler.compile
+ end
+
+
it "should evaluate the main class if it exists" do
compile_stub(:evaluate_main)
main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "")
@@ -532,7 +547,7 @@ describe Puppet::Parser::Compiler do
@compiler.add_collection(coll)
- lambda { @compiler.compile }.should raise_error(Puppet::ParseError)
+ lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources something'
end
it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do
@@ -541,7 +556,7 @@ describe Puppet::Parser::Compiler do
@compiler.add_collection(coll)
- lambda { @compiler.compile }.should raise_error(Puppet::ParseError)
+ lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources one, two'
end
end
@@ -566,6 +581,14 @@ describe Puppet::Parser::Compiler do
@scope.expects(:find_hostclass).with("notfound").returns(nil)
@compiler.evaluate_classes(%w{notfound}, @scope)
end
+ # I wish it would fail
+ it "should log when it can't find class" do
+ klasses = {'foo'=>nil}
+ @node.classes = klasses
+ @compiler.topscope.stubs(:find_hostclass).with('foo').returns(nil)
+ Puppet.expects(:info).with('Could not find class foo for testnode')
+ @compiler.compile
+ end
end
describe "when evaluating found classes" do
@@ -586,6 +609,68 @@ describe Puppet::Parser::Compiler do
@compiler.evaluate_classes(%w{myclass}, @scope)
end
+ it "should ensure each node class hash is in catalog and have appropriate parameters" do
+ klasses = {'foo'=>{'1'=>'one'}, 'bar::foo'=>{'2'=>'two'}, 'bar'=>{'1'=> [1,2,3], '2'=>{'foo'=>'bar'}}}
+ @node.classes = klasses
+ ast_obj = Puppet::Parser::AST::String.new(:value => 'foo')
+ klasses.each do |name, params|
+ klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj})
+ @compiler.topscope.known_resource_types.add klass
+ end
+ catalog = @compiler.compile
+ catalog.classes.should =~ ['foo', 'bar::foo', 'settings', 'bar']
+
+ r1 = catalog.resources.detect {|r| r.title == 'Foo' }
+ r1.to_hash.should == {:'1' => 'one', :'2' => 'foo'}
+ r1.tags. should =~ ['class', 'foo']
+
+ r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' }
+ r2.to_hash.should == {:'1' => 'foo', :'2' => 'two'}
+ r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo']
+
+ r2 = catalog.resources.detect {|r| r.title == 'Bar' }
+ r2.to_hash.should == {:'1' => [1,2,3], :'2' => {'foo'=>'bar'}}
+ r2.tags.should =~ ['class', 'bar']
+ end
+
+ it "should ensure each node class is in catalog and has appropriate tags" do
+ klasses = ['bar::foo']
+ @node.classes = klasses
+ ast_obj = Puppet::Parser::AST::String.new(:value => 'foo')
+ klasses.each do |name|
+ klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj})
+ @compiler.topscope.known_resource_types.add klass
+ end
+ catalog = @compiler.compile
+
+ r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' }
+ r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo']
+ end
+
+ it "should fail if required parameters are missing" do
+ klass = {'foo'=>{'1'=>'one'}}
+ @node.classes = klass
+ klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil})
+ @compiler.topscope.known_resource_types.add klass
+ lambda { @compiler.compile }.should raise_error Puppet::ParseError, "Must pass 2 to Class[Foo]"
+ end
+
+ it "should fail if invalid parameters are passed" do
+ klass = {'foo'=>{'3'=>'one'}}
+ @node.classes = klass
+ klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil})
+ @compiler.topscope.known_resource_types.add klass
+ lambda { @compiler.compile }.should raise_error Puppet::ParseError, "Invalid parameter 3"
+ end
+
+ it "should ensure class is in catalog without params" do
+ @node.classes = klasses = {'foo'=>nil}
+ foo = Puppet::Resource::Type.new(:hostclass, 'foo')
+ @compiler.topscope.known_resource_types.add foo
+ catalog = @compiler.compile
+ catalog.classes.should include 'foo'
+ end
+
it "should not evaluate the resources created for found classes unless asked" do
@compiler.catalog.stubs(:tag)
@@ -759,7 +844,7 @@ describe Puppet::Parser::Compiler do
it "should fail if the compile is finished and resource overrides have not been applied" do
@compiler.add_override(@override)
- lambda { @compiler.compile }.should raise_error(Puppet::ParseError)
+ lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Could not find resource(s) File[/foo] for overriding'
end
end
end
diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb
index 7b240bb82..206616ef4 100755
--- a/spec/unit/resource/type_spec.rb
+++ b/spec/unit/resource/type_spec.rb
@@ -496,7 +496,7 @@ describe Puppet::Resource::Type do
it "should evaluate the parent's resource" do
@type.parent_type(@scope)
-
+
@type.evaluate_code(@resource)
@scope.class_scope(@parent_type).should_not be_nil
@@ -504,7 +504,7 @@ describe Puppet::Resource::Type do
it "should not evaluate the parent's resource if it has already been evaluated" do
@parent_resource.evaluate
-
+
@type.parent_type(@scope)
@parent_resource.expects(:evaluate).never
@@ -545,7 +545,7 @@ describe Puppet::Resource::Type do
it "should not evaluate the parent's resource if it has already been evaluated" do
@parent_resource.evaluate
-
+
@type.parent_type(@scope)
@parent_resource.expects(:evaluate).never
@@ -575,7 +575,7 @@ describe Puppet::Resource::Type do
@code = Puppet::Resource::TypeCollection.new("env")
@code.add @top
@code.add @middle
-
+
@node.environment.stubs(:known_resource_types).returns(@code)
end
@@ -601,12 +601,36 @@ describe Puppet::Resource::Type do
@compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
end
+ it "should add specified parameters to the resource" do
+ @top.ensure_in_catalog(@scope, {'one'=>'1', 'two'=>'2'})
+ @compiler.catalog.resource(:class, "top")['one'].should == '1'
+ @compiler.catalog.resource(:class, "top")['two'].should == '2'
+ end
+
+ it "should not require params for a param class" do
+ @top.ensure_in_catalog(@scope, {})
+ @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
+ end
+
it "should evaluate the parent class if one exists" do
@middle.ensure_in_catalog(@scope)
@compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
end
+ it "should evaluate the parent class if one exists" do
+ @middle.ensure_in_catalog(@scope, {})
+
+ @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
+ end
+
+ it "should fail if you try to create duplicate class resources" do
+ othertop = Puppet::Parser::Resource.new(:class, 'top',:source => @source, :scope => @scope )
+ # add the same class resource to the catalog
+ @compiler.catalog.add_resource(othertop)
+ lambda { @top.ensure_in_catalog(@scope, {}) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError)
+ end
+
it "should fail to evaluate if a parent class is defined but cannot be found" do
othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay"
@code.add othertop