diff --git a/lib/puppet/parser/functions/create_resources.rb b/lib/puppet/parser/functions/create_resources.rb index 1c3b910b0..d0ac165c2 100644 --- a/lib/puppet/parser/functions/create_resources.rb +++ b/lib/puppet/parser/functions/create_resources.rb @@ -1,78 +1,82 @@ Puppet::Parser::Functions::newfunction(:create_resources, :arity => -3, :doc => <<-'ENDHEREDOC') do |args| Converts a hash into a set of resources and adds them to the catalog. This function takes two mandatory arguments: a resource type, and a hash describing a set of resources. The hash should be in the form `{title => {parameters} }`: # A hash of user resources: $myusers = { 'nick' => { uid => '1330', gid => allstaff, groups => ['developers', 'operations', 'release'], }, 'dan' => { uid => '1308', gid => allstaff, groups => ['developers', 'prosvc', 'release'], }, } create_resources(user, $myusers) A third, optional parameter may be given, also as a hash: $defaults = { 'ensure' => present, 'provider' => 'ldap', } create_resources(user, $myusers, $defaults) The values given on the third argument are added to the parameters of each resource present in the set given on the second argument. If a parameter is present on both the second and third arguments, the one on the second argument takes precedence. This function can be used to create defined resources and classes, as well as native resources. Virtual and Exported resources may be created by prefixing the type name with @ or @@ respectively. For example, the $myusers hash may be exported in the following manner: create_resources("@@user", $myusers) The $myusers may be declared as virtual resources using: create_resources("@user", $myusers) ENDHEREDOC raise ArgumentError, ("create_resources(): wrong number of arguments (#{args.length}; must be 2 or 3)") if args.length > 3 raise ArgumentError, ('create_resources(): second argument must be a hash') unless args[1].is_a?(Hash) if args.length == 3 raise ArgumentError, ('create_resources(): third argument, if provided, must be a hash') unless args[2].is_a?(Hash) end type, instances, defaults = args defaults ||= {} resource = Puppet::Parser::AST::Resource.new(:type => type.sub(/^@{1,2}/, '').downcase, :instances => instances.collect do |title, params| Puppet::Parser::AST::ResourceInstance.new( :title => Puppet::Parser::AST::Leaf.new(:value => title), :parameters => defaults.merge(params).collect do |name, value| Puppet::Parser::AST::ResourceParam.new( :param => name, :value => Puppet::Parser::AST::Leaf.new(:value => value)) end) end) if type.start_with? '@@' resource.exported = true elsif type.start_with? '@' resource.virtual = true end begin resource.safeevaluate(self) rescue Puppet::ParseError => internal_error - raise internal_error.original + if internal_error.original.nil? + raise internal_error + else + raise internal_error.original + end end end diff --git a/spec/fixtures/unit/parser/functions/create_resources/foo/manifests/init.pp b/spec/fixtures/unit/parser/functions/create_resources/foo/manifests/init.pp new file mode 100644 index 000000000..1772d0bae --- /dev/null +++ b/spec/fixtures/unit/parser/functions/create_resources/foo/manifests/init.pp @@ -0,0 +1,3 @@ +class foo { + create_resources('foo::wrongdefine', {'blah'=>{'one'=>'two'}}) +} diff --git a/spec/fixtures/unit/parser/functions/create_resources/foo/manifests/wrongdefine.pp b/spec/fixtures/unit/parser/functions/create_resources/foo/manifests/wrongdefine.pp new file mode 100644 index 000000000..c933d89ec --- /dev/null +++ b/spec/fixtures/unit/parser/functions/create_resources/foo/manifests/wrongdefine.pp @@ -0,0 +1,3 @@ +define foo::wrongdefine($one){ + $foo = $one, +} diff --git a/spec/unit/parser/functions/create_resources_spec.rb b/spec/unit/parser/functions/create_resources_spec.rb index 79ed02f22..350e4809a 100755 --- a/spec/unit/parser/functions/create_resources_spec.rb +++ b/spec/unit/parser/functions/create_resources_spec.rb @@ -1,206 +1,217 @@ require 'puppet' require 'spec_helper' require 'puppet_spec/compiler' describe 'function for dynamically creating resources' do include PuppetSpec::Compiler before :each do node = Puppet::Node.new("floppy", :environment => 'production') @compiler = Puppet::Parser::Compiler.new(node) @scope = Puppet::Parser::Scope.new(@compiler) @topscope = @scope.compiler.topscope @scope.parent = @topscope Puppet::Parser::Functions.function(:create_resources) end + after do + Puppet.settings.clear + end + it "should exist" do Puppet::Parser::Functions.function(:create_resources).should == "function_create_resources" end it 'should require two or three arguments' do expect { @scope.function_create_resources(['foo']) }.to raise_error(ArgumentError, 'create_resources(): Wrong number of arguments given (1 for minimum 2)') expect { @scope.function_create_resources(['foo', 'bar', 'blah', 'baz']) }.to raise_error(ArgumentError, 'create_resources(): wrong number of arguments (4; must be 2 or 3)') end it 'should require second argument to be a hash' do expect { @scope.function_create_resources(['foo','bar']) }.to raise_error(ArgumentError, 'create_resources(): second argument must be a hash') end it 'should require optional third argument to be a hash' do expect { @scope.function_create_resources(['foo',{},'foo']) }.to raise_error(ArgumentError, 'create_resources(): third argument, if provided, must be a hash') end describe 'when creating native types' do it 'empty hash should not cause resources to be added' do noop_catalog = compile_to_catalog("create_resources('file', {})") empty_catalog = compile_to_catalog("") noop_catalog.resources.size.should == empty_catalog.resources.size end it 'should be able to add' do catalog = compile_to_catalog("create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}})") catalog.resource(:file, "/etc/foo")['ensure'].should == 'present' end it 'should be able to add virtual resources' do catalog = compile_to_catalog("create_resources('@file', {'/etc/foo'=>{'ensure'=>'present'}})\nrealize(File['/etc/foo'])") catalog.resource(:file, "/etc/foo")['ensure'].should == 'present' end it 'should be able to add exported resources' do catalog = compile_to_catalog("create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}})") catalog.resource(:file, "/etc/foo")['ensure'].should == 'present' catalog.resource(:file, "/etc/foo").exported.should == true end it 'should accept multiple types' do catalog = compile_to_catalog("create_resources('notify', {'foo'=>{'message'=>'one'}, 'bar'=>{'message'=>'two'}})") catalog.resource(:notify, "foo")['message'].should == 'one' catalog.resource(:notify, "bar")['message'].should == 'two' end it 'should fail to add non-existing type' do expect do @scope.function_create_resources(['create-resource-foo', { 'foo' => {} }]) end.to raise_error(ArgumentError, /Invalid resource type create-resource-foo/) end it 'should be able to add edges' do rg = compile_to_relationship_graph("notify { test: }\n create_resources('notify', {'foo'=>{'require'=>'Notify[test]'}})") test = rg.vertices.find { |v| v.title == 'test' } foo = rg.vertices.find { |v| v.title == 'foo' } test.must be foo.must be rg.path_between(test,foo).should be end it 'should account for default values' do catalog = compile_to_catalog("create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}, '/etc/baz'=>{'group'=>'food'}}, {'group' => 'bar'})") catalog.resource(:file, "/etc/foo")['group'].should == 'bar' catalog.resource(:file, "/etc/baz")['group'].should == 'food' end end describe 'when dynamically creating resource types' do it 'should be able to create defined resource types' do catalog = compile_to_catalog(<<-MANIFEST) define foocreateresource($one) { notify { $name: message => $one } } create_resources('foocreateresource', {'blah'=>{'one'=>'two'}}) MANIFEST catalog.resource(:notify, "blah")['message'].should == 'two' end it 'should fail if defines are missing params' do expect { compile_to_catalog(<<-MANIFEST) define foocreateresource($one) { notify { $name: message => $one } } create_resources('foocreateresource', {'blah'=>{}}) MANIFEST }.to raise_error(Puppet::Error, 'Must pass one to Foocreateresource[blah] on node foonode') end it 'should be able to add multiple defines' do catalog = compile_to_catalog(<<-MANIFEST) define foocreateresource($one) { notify { $name: message => $one } } create_resources('foocreateresource', {'blah'=>{'one'=>'two'}, 'blaz'=>{'one'=>'three'}}) MANIFEST catalog.resource(:notify, "blah")['message'].should == 'two' catalog.resource(:notify, "blaz")['message'].should == 'three' end it 'should be able to add edges' do rg = compile_to_relationship_graph(<<-MANIFEST) define foocreateresource($one) { notify { $name: message => $one } } notify { test: } create_resources('foocreateresource', {'blah'=>{'one'=>'two', 'require' => 'Notify[test]'}}) MANIFEST test = rg.vertices.find { |v| v.title == 'test' } blah = rg.vertices.find { |v| v.title == 'blah' } test.must be blah.must be rg.path_between(test,blah).should be end it 'should account for default values' do catalog = compile_to_catalog(<<-MANIFEST) define foocreateresource($one) { notify { $name: message => $one } } create_resources('foocreateresource', {'blah'=>{}}, {'one' => 'two'}) MANIFEST catalog.resource(:notify, "blah")['message'].should == 'two' end end describe 'when creating classes' do it 'should be able to create classes' do catalog = compile_to_catalog(<<-MANIFEST) class bar($one) { notify { test: message => $one } } create_resources('class', {'bar'=>{'one'=>'two'}}) MANIFEST catalog.resource(:notify, "test")['message'].should == 'two' catalog.resource(:class, "bar").should_not be_nil end it 'should fail to create non-existing classes' do expect do compile_to_catalog(<<-MANIFEST) create_resources('class', {'blah'=>{'one'=>'two'}}) MANIFEST end.to raise_error(Puppet::Error, 'Could not find declared class blah at line 1 on node foonode') end it 'should be able to add edges' do rg = compile_to_relationship_graph(<<-MANIFEST) class bar($one) { notify { test: message => $one } } notify { tester: } create_resources('class', {'bar'=>{'one'=>'two', 'require' => 'Notify[tester]'}}) MANIFEST test = rg.vertices.find { |v| v.title == 'test' } tester = rg.vertices.find { |v| v.title == 'tester' } test.must be tester.must be rg.path_between(tester,test).should be end it 'should account for default values' do catalog = compile_to_catalog(<<-MANIFEST) class bar($one) { notify { test: message => $one } } create_resources('class', {'bar'=>{}}, {'one' => 'two'}) MANIFEST catalog.resource(:notify, "test")['message'].should == 'two' catalog.resource(:class, "bar").should_not be_nil end + + it 'should fail with a correct error message if the syntax of an imported file is incorrect' do + expect{ + Puppet[:modulepath] = my_fixture_dir + compile_to_catalog('include foo') + }.to raise_error(Puppet::Error, /Syntax error at.*/) + end end end