diff --git a/README.md b/README.md index 2ed9024..d3e2860 100644 --- a/README.md +++ b/README.md @@ -1,437 +1,444 @@ #Concat [](https://travis-ci.org/puppetlabs/puppetlabs-concat) ####Table of Contents 1. [Overview](#overview) 2. [Module Description - What the module does and why it is useful](#module-description) 3. [Setup - The basics of getting started with concat](#setup) * [What concat affects](#what-concat-affects) * [Setup requirements](#setup-requirements) * [Beginning with concat](#beginning-with-concat) 4. [Usage - Configuration options and additional functionality](#usage) * [API _deprecations_](#api-deprecations) 5. [Reference - An under-the-hood peek at what the module is doing and how](#reference) 5. [Limitations - OS compatibility, etc.](#limitations) 6. [Development - Guide for contributing to the module](#development) ##Overview This module constructs files from multiple fragments in an ordered way. ##Module Description This module lets you use many concat::fragment{} resources throughout your modules to construct a single file at the end. It does this through a shell (or ruby) script and a temporary holding space for the fragments. ##Setup ###What concat affects * Installs concatfragments.[sh|rb] based on platform. * Adds a concat/ directory into Puppets `vardir`. ###Beginning with concat To start using concat you need to create: * A concat{} resource for the final file. * One or more concat::fragment{}'s. A minimal example might be: ```puppet concat { '/tmp/file': ensure => present, } concat::fragment { 'tmpfile': target => '/tmp/file', content => 'test contents', order => '01' } ``` ##Usage Please be aware that there have been a number of [API _deprecations_](#api-deprecations). If you wanted a /etc/motd file that listed all the major modules on the machine. And that would be maintained automatically even if you just remove the include lines for other modules you could use code like below, a sample /etc/motd would be:
Puppet modules on this server: -- Apache -- MySQLLocal sysadmins can also append to the file by just editing /etc/motd.local their changes will be incorporated into the puppet managed motd. ```puppet class motd { $motd = '/etc/motd' concat { $motd: owner => 'root', group => 'root', mode => '0644' } concat::fragment{ 'motd_header': target => $motd, content => "\nPuppet modules on this server:\n\n", order => '01' } # local users on the machine can append to motd by just creating # /etc/motd.local concat::fragment{ 'motd_local': target => $motd, source => '/etc/motd.local', order => '15' } } # used by other modules to register themselves in the motd define motd::register($content="", $order='10') { if $content == "" { $body = $name } else { $body = $content } concat::fragment{ "motd_fragment_$name": target => '/etc/motd', order => $order, content => " -- $body\n" } } ``` To use this you'd then do something like: ```puppet class apache { include apache::install, apache::config, apache::service motd::register{ 'Apache': } } ``` ##Reference ###Classes ####Public classes ####Private classes * `concat::setup`: Sets up the concat script/directories. ###Parameters ###Defines ####concat #####`ensure` Controls if the combined file is present or absent. ######Example - ensure => present - ensure => absent #####`path` Controls the destination of the file to create. ######Example - path => '/tmp/filename' #####`owner` Set the owner of the combined file. ######Example - owner => 'root' #####`group` Set the group of the combined file. ######Example - group => 'root' #####`mode` Set the mode of the combined file. ######Example - mode => '0644' #####`warn` Determine if a warning message should be added at the top of the file to let users know it was autogenerated by Puppet. It should be a boolean or a string containing the contents of the warning message. ######Example - warn => true - warn => false - warn => '# This file is autogenerated!' #####`force` Determine if empty files are allowed when no fragments were added. ######Example - force => true - force => false #####`backup` Controls the filebucket behavior used for the file. ######Example - backup => 'puppet' #####`replace` Controls if Puppet should replace the destination file if it already exists. ######Example - replace => true - replace => false #####`order` Controls the way in which the shell script chooses to sort the files. It's rare you'll need to adjust this. ######Allowed Values - order => 'alpha' - order => 'numeric' #####`ensure_newline` Ensure there's a newline at the end of the fragments. ######Example - ensure_newline => true - ensure_newline => false +#####`validate_cmd` +Ensure the destination file passes the following validation command. + +######Example +- validate_cmd => '/usr/sbin/apache2 -t -f %' +- validate_cmd => '/usr/sbin/visudo -c -f %' + ####concat::fragment #####`target` Choose the destination file of the fragment. ######Example - target => '/tmp/testfile' #####`content` Create the content of the fragment. ######Example - content => 'test file contents' #####`source` Find the sources within Puppet of the fragment. ######Example - source => 'puppet:///modules/test/testfile' - source => ['puppet:///modules/test/1', 'puppet:///modules/test/2'] #####`order` Order the fragments. ######Example - order => '01' Best practice is to pass a string to this parameter but integer values are accepted. #####`ensure` Control the file of fragment created. ######Example - ensure => 'present' - ensure => 'absent' #####`mode` Set the mode of the fragment. ######Example - mode => '0644' #####`owner` Set the owner of the fragment. ######Example - owner => 'root' #####`group` Set the group of the fragment. ######Example - group => 'root' #####`backup` Control the filebucket behavior for the fragment. ######Example - backup => 'puppet' ### API _deprecations_ #### Since version `1.0.0` ##### `concat{}` `warn` parameter ```puppet concat { '/tmp/file': ensure => present, warn => 'true', # generates stringified boolean value warning } ``` Using stringified Boolean values as the `warn` parameter to `concat` is deprecated, generates a catalog compile time warning, and will be silently treated as the concatenated file header/warning message in a future release. The following strings are considered a stringified Boolean value: * `'true'` * `'yes'` * `'on'` * `'false'` * `'no'` * `'off'` Please migrate to using the Puppet DSL's native [Boolean data type](http://docs.puppetlabs.com/puppet/3/reference/lang_datatypes.html#booleans). ##### `concat{}` `gnu` parameter ```puppet concat { '/tmp/file': ensure => present, gnu => $foo, # generates deprecation warning } ``` The `gnu` parameter to `concat` is deprecated, generates a catalog compile time warning, and has no effect. This parameter will be removed in a future release. Note that this parameter was silently ignored in the `1.0.0` release. ##### `concat::fragment{}` `ensure` parameter ```puppet concat::fragment { 'cpuinfo': ensure => '/proc/cpuinfo', # generates deprecation warning target => '/tmp/file', } ``` Passing a value other than `'present'` or `'absent'` as the `ensure` parameter to `concat::fragment` is deprecated and generates a catalog compile time warning. The warning will become a catalog compilation failure in a future release. This type emulates the Puppet core `file` type's disfavored [`ensure` semantics](http://docs.puppetlabs.com/references/latest/type.html#file-attribute-ensure) of treating a file path as a directive to create a symlink. This feature is problematic in several ways. It copies an API semantic of another type that is both frowned upon and not generally well known. It's behavior may be surprising in that the target concatenated file will not be a symlink nor is there any common file system that has a concept of a section of a plain file being symbolically linked to another file. Additionally, the behavior is generally inconsistent with most Puppet types in that a missing source file will be silently ignored. If you want to use the content of a file as a fragment please use the `source` parameter. ##### `concat::fragment{}` `mode/owner/group` parameters ```puppet concat::fragment { 'foo': target => '/tmp/file', content => 'foo', mode => $mode, # generates deprecation warning owner => $owner, # generates deprecation warning group => $group, # generates deprecation warning } ``` The `mode` parameter to `concat::fragment` is deprecated, generates a catalog compile time warning, and has no effect. The `owner` parameter to `concat::fragment` is deprecated, generates a catalog compile time warning, and has no effect. The `group` parameter to `concat::fragment` is deprecated, generates a catalog compile time warning, and has no effect. These parameters had no user visible effect in version `1.0.0` and will be removed in a future release. ##### `concat::fragment{}` `backup` parameter ```puppet concat::fragment { 'foo': target => '/tmp/file', content => 'foo', backup => 'bar', # generates deprecation warning } ``` The `backup` parameter to `concat::fragment` is deprecated, generates a catalog compile time warning, and has no effect. It will be removed in a future release. In the `1.0.0` release this parameter controlled file bucketing of the file fragment. Bucketting the fragment(s) is redundant with bucketting the final concatenated file and this feature has been removed. ##### `class { 'concat::setup': }` ```puppet include concat::setup # generates deprecation warning class { 'concat::setup': } # generates deprecation warning ``` The `concat::setup` class is deprecated as a public API of this module and should no longer be directly included in the manifest. This class may be removed in a future release. ##### Parameter validation While not an API depreciation, users should be aware that all public parameters in this module are now validated for at least variable type. This may cause validation errors in a manifest that was previously silently misbehaving. ##Limitations This module has been tested on: * RedHat Enterprise Linux (and Centos) 5/6 * Debian 6/7 * Ubuntu 12.04 Testing on other platforms has been light and cannot be guaranteed. #Development Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing) ###Contributors The list of contributors can be found at: https://github.com/puppetlabs/puppetlabs-concat/graphs/contributors diff --git a/manifests/init.pp b/manifests/init.pp index 5d68489..9ac3fe5 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,253 +1,258 @@ # == Define: concat # # Sets up so that you can use fragments to build a final config file, # # === Options: # # [*ensure*] # Present/Absent # [*path*] # The path to the final file. Use this in case you want to differentiate # between the name of a resource and the file path. Note: Use the name you # provided in the target of your fragments. # [*owner*] # Who will own the file # [*group*] # Who will own the file # [*mode*] # The mode of the final file # [*force*] # Enables creating empty files if no fragments are present # [*warn*] # Adds a normal shell style comment top of the file indicating that it is # built by puppet # [*force*] # [*backup*] # Controls the filebucketing behavior of the final file and see File type # reference for its use. Defaults to 'puppet' # [*replace*] # Whether to replace a file that already exists on the local system # [*order*] # [*ensure_newline*] # [*gnu*] # Deprecated # # === Actions: # * Creates fragment directories if it didn't exist already # * Executes the concatfragments.sh script to build the final file, this # script will create directory/fragments.concat. Execution happens only # when: # * The directory changes # * fragments.concat != final destination, this means rebuilds will happen # whenever someone changes or deletes the final file. Checking is done # using /usr/bin/cmp. # * The Exec gets notified by something else - like the concat::fragment # define # * Copies the file over to the final destination using a file resource # # === Aliases: # # * The exec can notified using Exec["concat_/path/to/file"] or # Exec["concat_/path/to/directory"] # * The final file can be referenced as File["/path/to/file"] or # File["concat_/path/to/file"] # define concat( $ensure = 'present', $path = $name, $owner = undef, $group = undef, $mode = '0644', $warn = false, $force = false, $backup = 'puppet', $replace = true, $order = 'alpha', $ensure_newline = false, + $validate_cmd = undef, $gnu = undef ) { validate_re($ensure, '^present$|^absent$') validate_absolute_path($path) validate_string($owner) validate_string($group) validate_string($mode) if ! (is_string($warn) or $warn == true or $warn == false) { fail('$warn is not a string or boolean') } validate_bool($force) if ! concat_is_bool($backup) and ! is_string($backup) { fail('$backup must be string or bool!') } validate_bool($replace) validate_re($order, '^alpha$|^numeric$') validate_bool($ensure_newline) + if $validate_cmd and ! is_string($validate_cmd) { + fail('$validate_cmd must be a string') + } if $gnu { warning('The $gnu parameter to concat is deprecated and has no effect') } include concat::setup $safe_name = regsubst($name, '[/:]', '_', 'G') $concatdir = $concat::setup::concatdir $fragdir = "${concatdir}/${safe_name}" $concat_name = 'fragments.concat.out' $script_command = $concat::setup::script_command $default_warn_message = '# This file is managed by Puppet. DO NOT EDIT.' $bool_warn_message = 'Using stringified boolean values (\'true\', \'yes\', \'on\', \'false\', \'no\', \'off\') to represent boolean true/false as the $warn parameter to concat is deprecated and will be treated as the warning message in a future release' case $warn { true: { $warn_message = $default_warn_message } 'true', 'yes', 'on': { warning($bool_warn_message) $warn_message = $default_warn_message } false: { $warn_message = '' } 'false', 'no', 'off': { warning($bool_warn_message) $warn_message = '' } default: { $warn_message = $warn } } $warnmsg_escaped = regsubst($warn_message, '\'', '\'\\\'\'', 'G') $warnflag = $warnmsg_escaped ? { '' => '', default => "-w '${warnmsg_escaped}'" } $forceflag = $force ? { true => '-f', false => '', } $orderflag = $order ? { 'numeric' => '-n', 'alpha' => '', } $newlineflag = $ensure_newline ? { true => '-l', false => '', } File { backup => false, } # reset poisoned Exec defaults Exec { user => undef, group => undef, } if $ensure == 'present' { file { $fragdir: ensure => directory, mode => '0750', } file { "${fragdir}/fragments": ensure => directory, mode => '0750', force => true, ignore => ['.svn', '.git', '.gitignore'], notify => Exec["concat_${name}"], purge => true, recurse => true, } file { "${fragdir}/fragments.concat": ensure => present, mode => '0640', } file { "${fragdir}/${concat_name}": ensure => present, mode => '0640', } file { $name: - ensure => present, - owner => $owner, - group => $group, - mode => $mode, - replace => $replace, - path => $path, - alias => "concat_${name}", - source => "${fragdir}/${concat_name}", - backup => $backup, + ensure => present, + owner => $owner, + group => $group, + mode => $mode, + replace => $replace, + path => $path, + alias => "concat_${name}", + source => "${fragdir}/${concat_name}", + validate_cmd => $validate_cmd, + backup => $backup, } # remove extra whitespace from string interpolation to make testing easier $command = strip(regsubst("${script_command} -o \"${fragdir}/${concat_name}\" -d \"${fragdir}\" ${warnflag} ${forceflag} ${orderflag} ${newlineflag}", '\s+', ' ', 'G')) # make sure ruby is in the path for PE if $::is_pe { if $::kernel == 'windows' { $command_path = "${::env_windows_installdir}/bin:${::path}" } else { $command_path = "/opt/puppet/bin:${::path}" } } else { $command_path = $::path } # if puppet is running as root, this exec should also run as root to allow # the concatfragments.sh script to potentially be installed in path that # may not be accessible by a target non-root owner. exec { "concat_${name}": alias => "concat_${fragdir}", command => $command, notify => File[$name], subscribe => File[$fragdir], unless => "${command} -t", path => $command_path, require => [ File[$fragdir], File["${fragdir}/fragments"], File["${fragdir}/fragments.concat"], ], } } else { file { [ $fragdir, "${fragdir}/fragments", "${fragdir}/fragments.concat", "${fragdir}/${concat_name}" ]: ensure => absent, force => true, } file { $path: ensure => absent, backup => $backup, } $absent_exec_command = $::kernel ? { 'windows' => 'cmd.exe /c exit 0', default => 'true', } $absent_exec_path = $::kernel ? { 'windows' => $::path, default => '/bin:/usr/bin', } # Need to have an unless here for idempotency. exec { "concat_${name}": alias => "concat_${fragdir}", command => $absent_exec_command, unless => $absent_exec_command, path => $absent_exec_path, } } } # vim:sw=2:ts=2:expandtab:textwidth=79 diff --git a/spec/acceptance/validation_spec.rb b/spec/acceptance/validation_spec.rb new file mode 100644 index 0000000..08241d6 --- /dev/null +++ b/spec/acceptance/validation_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper_acceptance' + +describe 'concat validate_cmd parameter' do + basedir = default.tmpdir('concat') + context '=> "/usr/bin/test -e %"' do + before(:all) do + pp = <<-EOS + file { '#{basedir}': + ensure => directory + } + EOS + + apply_manifest(pp) + end + pp = <<-EOS + concat { '#{basedir}/file': + validate_cmd => '/usr/bin/test -e %', + } + concat::fragment { 'content': + target => '#{basedir}/file', + content => 'content', + } + EOS + + it 'applies the manifest twice with no stderr' do + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + describe file("#{basedir}/file") do + it { should be_file } + it { should contain 'content' } + end + end +end diff --git a/spec/unit/defines/concat_spec.rb b/spec/unit/defines/concat_spec.rb index e4d8fba..de12ab9 100644 --- a/spec/unit/defines/concat_spec.rb +++ b/spec/unit/defines/concat_spec.rb @@ -1,398 +1,416 @@ require 'spec_helper' describe 'concat', :type => :define do shared_examples 'concat' do |title, params, id| params = {} if params.nil? id = 'root' if id.nil? # default param values p = { :ensure => 'present', :path => title, :owner => nil, :group => nil, :mode => '0644', :warn => false, :force => false, :backup => 'puppet', :replace => true, :order => 'alpha', :ensure_newline => false, + :validate_cmd => nil, }.merge(params) safe_name = title.gsub('/', '_') concatdir = '/var/lib/puppet/concat' fragdir = "#{concatdir}/#{safe_name}" concat_name = 'fragments.concat.out' default_warn_message = '# This file is managed by Puppet. DO NOT EDIT.' file_defaults = { :backup => false, } let(:title) { title } let(:params) { params } let(:facts) do { :concat_basedir => concatdir, :id => id, :osfamily => 'Debian', :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', :kernel => 'Linux', :is_pe => false, } end if p[:ensure] == 'present' it do should contain_file(fragdir).with(file_defaults.merge({ :ensure => 'directory', :mode => '0750', })) end it do should contain_file("#{fragdir}/fragments").with(file_defaults.merge({ :ensure => 'directory', :mode => '0750', :force => true, :ignore => ['.svn', '.git', '.gitignore'], :purge => true, :recurse => true, })) end [ "#{fragdir}/fragments.concat", "#{fragdir}/#{concat_name}", ].each do |file| it do should contain_file(file).with(file_defaults.merge({ :ensure => 'present', :mode => '0640', })) end end it do should contain_file(title).with(file_defaults.merge({ - :ensure => 'present', - :owner => p[:owner], - :group => p[:group], - :mode => p[:mode], - :replace => p[:replace], - :path => p[:path], - :alias => "concat_#{title}", - :source => "#{fragdir}/#{concat_name}", - :backup => p[:backup], + :ensure => 'present', + :owner => p[:owner], + :group => p[:group], + :mode => p[:mode], + :replace => p[:replace], + :path => p[:path], + :alias => "concat_#{title}", + :source => "#{fragdir}/#{concat_name}", + :validate_cmd => p[:validate_cmd], + :backup => p[:backup], })) end cmd = "#{concatdir}/bin/concatfragments.sh " + "-o \"#{concatdir}/#{safe_name}/fragments.concat.out\" " + "-d \"#{concatdir}/#{safe_name}\"" # flag order: fragdir, warnflag, forceflag, orderflag, newlineflag if p.has_key?(:warn) case p[:warn] when TrueClass message = default_warn_message when 'true', 'yes', 'on' # should generate a stringified boolean warning message = default_warn_message when FalseClass message = nil when 'false', 'no', 'off' # should generate a stringified boolean warning message = nil else message = p[:warn] end unless message.nil? cmd += " -w \'#{message}\'" end end cmd += " -f" if p[:force] cmd += " -n" if p[:order] == 'numeric' cmd += " -l" if p[:ensure_newline] == true it do should contain_exec("concat_#{title}").with({ :alias => "concat_#{fragdir}", :command => cmd, :unless => "#{cmd} -t", }) end else [ fragdir, "#{fragdir}/fragments", "#{fragdir}/fragments.concat", "#{fragdir}/#{concat_name}", ].each do |file| it do should contain_file(file).with(file_defaults.merge({ :ensure => 'absent', :backup => false, :force => true, })) end end it do should contain_file(title).with(file_defaults.merge({ :ensure => 'absent', :backup => p[:backup], })) end it do should contain_exec("concat_#{title}").with({ :alias => "concat_#{fragdir}", :command => 'true', :unless => 'true', :path => '/bin:/usr/bin', }) end end end context 'title' do context 'without path param' do # title/name is the default value for the path param. therefore, the # title must be an absolute path unless path is specified ['/foo', '/foo/bar', '/foo/bar/baz'].each do |title| context title do it_behaves_like 'concat', '/etc/foo.bar' end end ['./foo', 'foo', 'foo/bar'].each do |title| context title do let(:title) { title } it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not an absolute path/) end end end end context 'with path param' do ['./foo', 'foo', 'foo/bar'].each do |title| context title do it_behaves_like 'concat', title, { :path => '/etc/foo.bar' } end end end end # title => context 'as non-root user' do it_behaves_like 'concat', '/etc/foo.bar', {}, 'bob' end context 'ensure =>' do ['present', 'absent'].each do |ens| context ens do it_behaves_like 'concat', '/etc/foo.bar', { :ensure => ens } end end context 'invalid' do let(:title) { '/etc/foo.bar' } let(:params) {{ :ensure => 'invalid' }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /#{Regexp.escape('does not match "^present$|^absent$"')}/) end end end # ensure => context 'path =>' do context '/foo' do it_behaves_like 'concat', '/etc/foo.bar', { :path => '/foo' } end ['./foo', 'foo', 'foo/bar', false].each do |path| context path do let(:title) { '/etc/foo.bar' } let(:params) {{ :path => path }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not an absolute path/) end end end end # path => context 'owner =>' do context 'apenney' do it_behaves_like 'concat', '/etc/foo.bar', { :owner => 'apenny' } end context 'false' do let(:title) { '/etc/foo.bar' } let(:params) {{ :owner => false }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not a string/) end end end # owner => context 'group =>' do context 'apenney' do it_behaves_like 'concat', '/etc/foo.bar', { :group => 'apenny' } end context 'false' do let(:title) { '/etc/foo.bar' } let(:params) {{ :group => false }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not a string/) end end end # group => context 'mode =>' do context '1755' do it_behaves_like 'concat', '/etc/foo.bar', { :mode => '1755' } end context 'false' do let(:title) { '/etc/foo.bar' } let(:params) {{ :mode => false }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not a string/) end end end # mode => context 'warn =>' do [true, false, '# foo'].each do |warn| context warn do it_behaves_like 'concat', '/etc/foo.bar', { :warn => warn } end end context '(stringified boolean)' do ['true', 'yes', 'on', 'false', 'no', 'off'].each do |warn| context warn do it_behaves_like 'concat', '/etc/foo.bar', { :warn => warn } it 'should create a warning' do skip('rspec-puppet support for testing warning()') end end end end context '123' do let(:title) { '/etc/foo.bar' } let(:params) {{ :warn => 123 }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not a string or boolean/) end end end # warn => context 'force =>' do [true, false].each do |force| context force do it_behaves_like 'concat', '/etc/foo.bar', { :force => force } end end context '123' do let(:title) { '/etc/foo.bar' } let(:params) {{ :force => 123 }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not a boolean/) end end end # force => context 'backup =>' do context 'reverse' do it_behaves_like 'concat', '/etc/foo.bar', { :backup => 'reverse' } end context 'false' do it_behaves_like 'concat', '/etc/foo.bar', { :backup => false } end context 'true' do it_behaves_like 'concat', '/etc/foo.bar', { :backup => true } end context 'true' do let(:title) { '/etc/foo.bar' } let(:params) {{ :backup => [] }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /backup must be string or bool/) end end end # backup => context 'replace =>' do [true, false].each do |replace| context replace do it_behaves_like 'concat', '/etc/foo.bar', { :replace => replace } end end context '123' do let(:title) { '/etc/foo.bar' } let(:params) {{ :replace => 123 }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not a boolean/) end end end # replace => context 'order =>' do ['alpha', 'numeric'].each do |order| context order do it_behaves_like 'concat', '/etc/foo.bar', { :order => order } end end context 'invalid' do let(:title) { '/etc/foo.bar' } let(:params) {{ :order => 'invalid' }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /#{Regexp.escape('does not match "^alpha$|^numeric$"')}/) end end end # order => context 'ensure_newline =>' do [true, false].each do |ensure_newline| context 'true' do it_behaves_like 'concat', '/etc/foo.bar', { :ensure_newline => ensure_newline} end end context '123' do let(:title) { '/etc/foo.bar' } let(:params) {{ :ensure_newline => 123 }} it 'should fail' do expect { should }.to raise_error(Puppet::Error, /is not a boolean/) end end end # ensure_newline => + context 'validate_cmd =>' do + context '/usr/bin/test -e %' do + it_behaves_like 'concat', '/etc/foo.bar', { :validate_cmd => '/usr/bin/test -e %' } + end + + [ 1234, true ].each do |cmd| + context cmd do + let(:title) { '/etc/foo.bar' } + let(:params) {{ :validate_cmd => cmd }} + it 'should fail' do + expect { should }.to raise_error(Puppet::Error, /\$validate_cmd must be a string/) + end + end + end + end # validate_cmd => + describe 'deprecated parameter' do context 'gnu =>' do context 'foo' do it_behaves_like 'concat', '/etc/foo.bar', { :gnu => 'foo'} it 'should create a warning' do skip('rspec-puppet support for testing warning()') end end end end end # vim:sw=2:ts=2:expandtab:textwidth=79