diff --git a/.travis.yaml b/.travis.yaml new file mode 100644 index 0000000..0e759e2 --- /dev/null +++ b/.travis.yaml @@ -0,0 +1,28 @@ +language: ruby +bundler_args: --without development +before_script: + - "[ $PUPPET_GEM_VERSION ~> 2.6 ] && git clone git://github.com/puppetlabs/puppetlabs-create_resources.git spec/fixtures/modules/create_resources || true" +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +rvm: + - 1.8.7 + - 1.9.3 + - ruby-head +env: + - PUPPET_GEM_VERSION="~> 2.6.0" + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.0.0" + - PUPPET_GEM_VERSION="~> 3.1.0" +matrix: + allow_failures: + - rvm: ruby-head + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: ruby-head + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.6.0" + - rvm: ruby-head + env: PUPPET_GEM_VERSION="~> 2.6.0" +notifications: + email: false diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..b058601 --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +source :rubygems + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end 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