diff --git a/.fixtures.yml b/.fixtures.yml index f7f1eba..dc6b41f 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -1,7 +1,7 @@ fixtures: repositories: 'stdlib': repo: 'git://github.com/puppetlabs/puppetlabs-stdlib.git' - ref: '3.0.0' + ref: '4.0.0' symlinks: 'concat': '#{source_dir}' diff --git a/Modulefile b/Modulefile index 2e9b6b9..c90d6f4 100644 --- a/Modulefile +++ b/Modulefile @@ -1,9 +1,9 @@ name 'puppetlabs-concat' version '1.1.0-rc1' source 'git://github.com/puppetlabs/puppetlabs-concat.git' author 'Puppetlabs' license 'Apache 2.0' summary 'Concat module' description 'Concat module' project_page 'http://github.com/puppetlabs/puppetlabs-concat' -dependency 'puppetlabs/stdlib', '>= 3.0.0' +dependency 'puppetlabs/stdlib', '>= 4.0.0' diff --git a/manifests/fragment.pp b/manifests/fragment.pp index 9477653..69c1fba 100644 --- a/manifests/fragment.pp +++ b/manifests/fragment.pp @@ -1,112 +1,117 @@ # == Define: concat::fragment # # Puts a file fragment into a directory previous setup using concat # # === Options: # # [*target*] # The file that these fragments belong to # [*content*] # If present puts the content into the file # [*source*] # If content was not specified, use the source # [*order*] # By default all files gets a 10_ prefix in the directory you can set it to # anything else using this to influence the order of the content in the file # [*ensure*] # Present/Absent or destination to a file to include another file # [*mode*] # Deprecated # [*owner*] # Deprecated # [*group*] # Deprecated # [*backup*] # Deprecated # define concat::fragment( $target, $content = undef, $source = undef, $order = 10, - $ensure = 'present', + $ensure = undef, $mode = undef, $owner = undef, $group = undef, $backup = undef ) { validate_string($target) - if ! ($ensure in [ 'present', 'absent' ]) { - warning('Passing a value other than \'present\' or \'absent\' as the $ensure parameter to concat::fragment is deprecated. If you want to use the content of a file as a fragment please use the $source parameter.') - } validate_string($content) if !(is_string($source) or is_array($source)) { fail('$source is not a string or an Array.') } validate_string($order) if $mode { warning('The $mode parameter to concat::fragment is deprecated and has no effect') } if $owner { warning('The $owner parameter to concat::fragment is deprecated and has no effect') } if $group { warning('The $group parameter to concat::fragment is deprecated and has no effect') } if $backup { warning('The $backup parameter to concat::fragment is deprecated and has no effect') } + if $ensure == undef { + $_ensure = getparam(Concat[$target], 'ensure') + } else { + if ! ($ensure in [ 'present', 'absent' ]) { + warning('Passing a value other than \'present\' or \'absent\' as the $ensure parameter to concat::fragment is deprecated. If you want to use the content of a file as a fragment please use the $source parameter.') + } + $_ensure = $ensure + } include concat::setup $safe_name = regsubst($name, '[/:\n]', '_', 'GM') $safe_target_name = regsubst($target, '[/:\n]', '_', 'GM') $concatdir = $concat::setup::concatdir $fragdir = "${concatdir}/${safe_target_name}" # The file type's semantics are problematic in that ensure => present will # not over write a pre-existing symlink. We are attempting to provide # backwards compatiblity with previous concat::fragment versions that # supported the file type's ensure => /target syntax # be paranoid and only allow the fragment's file resource's ensure param to # be file, absent, or a file target - $safe_ensure = $ensure ? { + $safe_ensure = $_ensure ? { '' => 'file', undef => 'file', 'file' => 'file', 'present' => 'file', 'absent' => 'absent', - default => $ensure, + default => $_ensure, } # if it looks line ensure => /target syntax was used, fish that out - if ! ($ensure in ['', 'present', 'absent', 'file' ]) { - $ensure_target = $ensure + if ! ($_ensure in ['', 'present', 'absent', 'file' ]) { + $ensure_target = $_ensure } # the file type's semantics only allows one of: ensure => /target, content, # or source if ($ensure_target and $source) or ($ensure_target and $content) or ($source and $content) { fail('You cannot specify more than one of $content, $source, $ensure => /target') } if ! ($content or $source or $ensure_target) { crit('No content, source or symlink specified') } # punt on group ownership until some point in the distant future when $::gid # can be relied on to be present file { "${fragdir}/fragments/${order}_${safe_name}": ensure => $safe_ensure, owner => $::id, mode => '0640', source => $source, content => $content, backup => false, alias => "concat_fragment_${name}", notify => Exec["concat_${target}"] } } diff --git a/manifests/init.pp b/manifests/init.pp index fc78157..566e3bf 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,232 +1,232 @@ # == 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, $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) validate_string($backup) validate_bool($replace) validate_re($order, '^alpha$|^numeric$') validate_bool($ensure_newline) 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, } 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, } # 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')) # 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 => $::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 { $name: + 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', } exec { "concat_${name}": alias => "concat_${fragdir}", command => $absent_exec_command, path => $absent_exec_path } } } # vim:sw=2:ts=2:expandtab:textwidth=79 diff --git a/spec/acceptance/concat_spec.rb b/spec/acceptance/concat_spec.rb index 8fc0959..e31e489 100644 --- a/spec/acceptance/concat_spec.rb +++ b/spec/acceptance/concat_spec.rb @@ -1,154 +1,204 @@ require 'spec_helper_acceptance' describe 'basic concat test' do shared_examples 'successfully_applied' do |pp| it 'applies the manifest twice with no stderr' do expect(apply_manifest(pp, :catch_failures => true).stderr).to eq("") expect(apply_manifest(pp, :catch_changes => true).stderr).to eq("") end describe file("#{default['puppetvardir']}/concat") do it { should be_directory } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 755 } end describe file("#{default['puppetvardir']}/concat/bin") do it { should be_directory } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 755 } end describe file("#{default['puppetvardir']}/concat/bin/concatfragments.sh") do it { should be_file } it { should be_owned_by 'root' } #it { should be_grouped_into 'root' } it { should be_mode 755 } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file") do it { should be_directory } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 750 } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file/fragments") do it { should be_directory } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 750 } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file/fragments.concat") do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 640 } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file/fragments.concat.out") do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 640 } end end context 'owner/group root' do pp = <<-EOS concat { '/tmp/concat/file': owner => 'root', group => 'root', mode => '0644', } concat::fragment { '1': target => '/tmp/concat/file', content => '1', order => '01', } concat::fragment { '2': target => '/tmp/concat/file', content => '2', order => '02', } EOS it_behaves_like 'successfully_applied', pp describe file('/tmp/concat/file') do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 644 } it { should contain '1' } it { should contain '2' } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file/fragments/01_1") do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 640 } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file/fragments/02_2") do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 640 } end end context 'owner/group non-root' do before(:all) do shell "groupadd -g 42 bob" shell "useradd -u 42 -g 42 bob" end after(:all) do shell "userdel bob" end pp=" concat { '/tmp/concat/file': owner => 'bob', group => 'bob', mode => '0644', } concat::fragment { '1': target => '/tmp/concat/file', content => '1', order => '01', } concat::fragment { '2': target => '/tmp/concat/file', content => '2', order => '02', } " it_behaves_like 'successfully_applied', pp describe file('/tmp/concat/file') do it { should be_file } it { should be_owned_by 'bob' } it { should be_grouped_into 'bob' } it { should be_mode 644 } it { should contain '1' } it { should contain '2' } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file/fragments/01_1") do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 640 } it { should contain '1' } end describe file("#{default['puppetvardir']}/concat/_tmp_concat_file/fragments/02_2") do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode 640 } it { should contain '2' } end end + + context 'ensure' do + context 'works when set to present with path set' do + pp=" + concat { 'file': + ensure => present, + path => '/tmp/concat/file', + mode => '0644', + } + concat::fragment { '1': + target => 'file', + content => '1', + order => '01', + } + " + + it_behaves_like 'successfully_applied', pp + + describe file('/tmp/concat/file') do + it { should be_file } + it { should be_mode 644 } + it { should contain '1' } + end + end + context 'works when set to absent with path set' do + pp=" + concat { 'file': + ensure => absent, + path => '/tmp/concat/file', + mode => '0644', + } + concat::fragment { '1': + target => 'file', + content => '1', + order => '01', + } + " + + # Can't used shared examples as this will always trigger the exec when + # absent is set. + it 'applies the manifest twice with no stderr' do + expect(apply_manifest(pp, :catch_failures => true).stderr).to eq("") + expect(apply_manifest(pp, :catch_failures => true).stderr).to eq("") + end + + describe file('/tmp/concat/file') do + it { should_not be_file } + end + end + end end