From 63c405d1a9e60ada3a683247835bf2a299dea2c1 Mon Sep 17 00:00:00 2001 From: Dan Bode Date: Sun, 3 Mar 2013 12:08:57 -0800 Subject: [PATCH] update code to support more simultaneous versions This commit is intended to resolve some of the issues related with create_resources support for multiple Puppet versions. Basically, create_resources often needs to be an external dependency if it's used in modules, so that those modules can work with 2.6.x. In this case, it needs to be added to the fixtures.yaml file. Since there is no way to specify that certain dependant modules should only be added for certain versions of Puppet, then create_resources will even override the native one for versions >= 2.7.0 in all unit tests. For this reason, the module needs to be able to simultaneously support the targeted versions: 2.6.x ,2.7.x, 3.0.x, and 3.1.x. This commit does the following: - back ports all 2.7.x changes to create_resources (so that it will work with 3.0) - modifies tests, unit tests so they pass with all targeted versions. - update Rakefile and spec_helper to use puppetlabs_spec_helper of coarse, there is a bigger question of if I should even be doing this, it has several pitfalls: - modules will be tested with an external create_resoures which may not be in sync with the one in core - I would recommend that modules not pull in create_resources as a dep in their module file, which means 1. it becomes an extra step for 2.6.x 2. module's are not tested and used consistenly --- Rakefile | 1 + .../parser/functions/create_resources.rb | 68 +++++++---- spec/spec.opts | 6 - spec/spec_helper.rb | 19 +-- .../parser/functions/create_resources_spec.rb | 109 +++++++----------- 5 files changed, 94 insertions(+), 109 deletions(-) create mode 100644 Rakefile delete mode 100644 spec/spec.opts diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..cd3d379 --- /dev/null +++ b/Rakefile @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/lib/puppet/parser/functions/create_resources.rb b/lib/puppet/parser/functions/create_resources.rb index 430f110..e8497c2 100644 --- a/lib/puppet/parser/functions/create_resources.rb +++ b/lib/puppet/parser/functions/create_resources.rb @@ -1,14 +1,39 @@ -Puppet::Parser::Functions::newfunction(:create_resources, :doc => ' -Converts a hash into a set of resources and adds them to the catalog. -Takes two parameters: - create_resource($type, $resources) - Creates resources of type $type from the $resources hash. Assumes that - hash is in the following form: - {title=>{parameters}} - This is currently tested for defined resources, classes, as well as native types -') do |args| - raise ArgumentError, ("create_resources(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2 - #raise ArgumentError, 'requires resource type and param hash' if args.size < 2 +Puppet::Parser::Functions::newfunction(:create_resources, :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', + group => allstaff, + groups => ['developers', 'operations', 'release'], } + 'dan' => { uid => '1308', + group => 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. + ENDHEREDOC + raise ArgumentError, ("create_resources(): wrong number of arguments (#{args.length}; must be 2 or 3)") if args.length < 2 || args.length > 3 + # figure out what kind of resource we are type_of_resource = nil type_name = args[0].downcase @@ -19,29 +44,32 @@ Takes two parameters: type_of_resource = :type elsif resource = find_definition(type_name.downcase) type_of_resource = :define - else + else raise ArgumentError, "could not create resource of unknown type #{type_name}" end end # iterate through the resources to create + defaults = args[2] || {} args[1].each do |title, params| - raise ArgumentError, 'params should not contain title' if(params['title']) + params = Puppet::Util.symbolizehash(defaults.merge(params)) + raise ArgumentError, 'params should not contain title' if(params[:title]) case type_of_resource - when :type - res = resource.hash2resource(params.merge(:title => title)) - catalog.add_resource(res) - when :define + # JJM The only difference between a type and a define is the call to instantiate_resource + # for a defined type. + when :type, :define p_resource = Puppet::Parser::Resource.new(type_name, title, :scope => self, :source => resource) - params.merge(:name => title).each do |k,v| + {:name => title}.merge(params).each do |k,v| p_resource.set_parameter(k,v) end - resource.instantiate_resource(self, p_resource) + if type_of_resource == :define then + resource.instantiate_resource(self, p_resource) + end compiler.add_resource(self, p_resource) when :class klass = find_hostclass(title) raise ArgumentError, "could not find hostclass #{title}" unless klass klass.ensure_in_catalog(self, params) - compiler.catalog.add_class([title]) + compiler.catalog.add_class(title) end end end diff --git a/spec/spec.opts b/spec/spec.opts deleted file mode 100644 index 91cd642..0000000 --- a/spec/spec.opts +++ /dev/null @@ -1,6 +0,0 @@ ---format -s ---colour ---loadby -mtime ---backtrace diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a4aeeae..2c6f566 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,18 +1 @@ -require 'pathname' -dir = Pathname.new(__FILE__).parent -$LOAD_PATH.unshift(dir, dir + 'lib', dir + '../lib') - -require 'mocha' -require 'puppet' -gem 'rspec', '=1.2.9' -require 'spec/autorun' - -Spec::Runner.configure do |config| - config.mock_with :mocha -end - -# We need this because the RAL uses 'should' as a method. This -# allows us the same behaviour but with a different method name. -class Object - alias :must :should -end +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/spec/unit/puppet/parser/functions/create_resources_spec.rb b/spec/unit/puppet/parser/functions/create_resources_spec.rb index 88a67e3..5c9946c 100755 --- a/spec/unit/puppet/parser/functions/create_resources_spec.rb +++ b/spec/unit/puppet/parser/functions/create_resources_spec.rb @@ -3,33 +3,43 @@ require 'spec_helper' describe 'function for dynamically creating resources' do - def get_scope - @topscope = Puppet::Parser::Scope.new - # This is necessary so we don't try to use the compiler to discover our parent. - @topscope.parent = nil - @scope = Puppet::Parser::Scope.new - @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) + def setup_scope + @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("floppy", :environment => 'production')) + if Puppet.version =~ /^3\./ + @scope = Puppet::Parser::Scope.new(@compiler) + else + @scope = Puppet::Parser::Scope.new(:compiler => @compiler) + end + @topscope = @topscope @scope.parent = @topscope - @compiler = @scope.compiler - end - before :each do - get_scope Puppet::Parser::Functions.function(:create_resources) end - it "should exist" do - Puppet::Parser::Functions.function(:create_resources).should == "function_create_resources" - end - it 'should require two arguments' do - lambda { @scope.function_create_resources(['foo']) }.should raise_error(ArgumentError, 'create_resources(): wrong number of arguments (1; must be 2)') + describe 'basic tests' do + + before :each do + setup_scope + end + + it "should exist" do + Puppet::Parser::Functions.function(:create_resources).should == "function_create_resources" + end + it 'should require two arguments' do + lambda { @scope.function_create_resources(['foo']) }.should raise_error(ArgumentError, 'create_resources(): wrong number of arguments (1; must be 2 or 3)') + end end + describe 'when creating native types' do before :each do - Puppet[:code]='notify{test:}' - get_scope - @scope.resource=Puppet::Parser::Resource.new('class', 't', :scope => @scope) + Puppet[:code]= +' +class t {} +notify{test:} +' + setup_scope end it 'empty hash should not cause resources to be added' do + @scope.function_create_resources(['file', {}]) @compiler.catalog.resources.size == 1 end @@ -39,34 +49,24 @@ describe 'function for dynamically creating resources' do end it 'should accept multiple types' do type_hash = {} - type_hash['foo'] = {'message' => 'one'} - type_hash['bar'] = {'message' => 'two'} + type_hash['notify'] = {'message' => 'one'} + type_hash['user'] = {'home' => true} @scope.function_create_resources(['notify', type_hash]) - @compiler.catalog.resource(:notify, "foo")['message'].should == 'one' - @compiler.catalog.resource(:notify, "bar")['message'].should == 'two' + @compiler.catalog.resource(:notify, "notify")['message'].should == 'one' + @compiler.catalog.resource(:notify, "user")['home'].should == true end it 'should fail to add non-existing type' do - lambda { @scope.function_create_resources(['foo', {}]) }.should raise_error(ArgumentError, 'could not create resource of unknown type foo') + lambda { + @scope.function_create_resources(['fooper', {}]) }.should raise_error(ArgumentError, 'could not create resource of unknown type fooper') end - it 'should be able to add edges' do - @scope.function_create_resources(['notify', {'foo'=>{'require' => 'Notify[test]'}}]) - @scope.compiler.compile - rg = @scope.compiler.catalog.to_ral.relationship_graph - test = rg.vertices.find { |v| v.title == 'test' } - foo = rg.vertices.find { |v| v.title == 'foo' } - test.should be - foo.should be - rg.path_between(test,foo).should be - end - end - describe 'when dynamically creating resource types' do - before :each do + before :each do Puppet[:code]= -'define foo($one){notify{$name: message => $one}} +' +class t {} +define foo($one){notify{$name: message => $one}} notify{test:} ' - get_scope - @scope.resource=Puppet::Parser::Resource.new('class', 't', :scope => @scope) + setup_scope Puppet::Parser::Functions.function(:create_resources) end it 'should be able to create defined resoure types' do @@ -78,7 +78,7 @@ notify{test:} end it 'should fail if defines are missing params' do @scope.function_create_resources(['foo', {'blah'=>{}}]) - lambda { @scope.compiler.compile }.should raise_error(Puppet::ParseError, 'Must pass one to Foo[blah] at line 1') + lambda { @scope.compiler.compile }.should raise_error(Puppet::ParseError, /Must pass one/) end it 'should be able to add multiple defines' do hash = {} @@ -91,27 +91,16 @@ notify{test:} @compiler.catalog.resource(:notify, "blah")['message'].should == 'two' @compiler.catalog.resource(:notify, "blaz")['message'].should == 'three' end - it 'should be able to add edges' do - @scope.function_create_resources(['foo', {'blah'=>{'one'=>'two', 'require' => 'Notify[test]'}}]) - @scope.compiler.compile - rg = @scope.compiler.catalog.to_ral.relationship_graph - test = rg.vertices.find { |v| v.title == 'test' } - blah = rg.vertices.find { |v| v.title == 'blah' } - test.should be - blah.should be - # (Yoda speak like we do) - rg.path_between(test,blah).should be - @compiler.catalog.resource(:notify, "blah")['message'].should == 'two' - end end describe 'when creating classes' do before :each do Puppet[:code]= -'class bar($one){notify{test: message => $one}} +' +class t {} +class bar($one){notify{test: message => $one}} notify{tester:} ' - get_scope - @scope.resource=Puppet::Parser::Resource.new('class', 't', :scope => @scope) + setup_scope Puppet::Parser::Functions.function(:create_resources) end it 'should be able to create classes' do @@ -123,15 +112,5 @@ notify{tester:} it 'should fail to create non-existing classes' do lambda { @scope.function_create_resources(['class', {'blah'=>{'one'=>'two'}}]) }.should raise_error(ArgumentError ,'could not find hostclass blah') end - it 'should be able to add edges' do - @scope.function_create_resources(['class', {'bar'=>{'one'=>'two', 'require' => 'Notify[tester]'}}]) - @scope.compiler.compile - rg = @scope.compiler.catalog.to_ral.relationship_graph - test = rg.vertices.find { |v| v.title == 'test' } - tester = rg.vertices.find { |v| v.title == 'tester' } - test.should be - tester.should be - rg.path_between(tester,test).should be - end end end