]> git.donarmstrong.com Git - dsa-puppet.git/commitdiff
Add concat module
authorStephen Gran <steve@lobefin.net>
Mon, 22 Apr 2013 19:30:55 +0000 (20:30 +0100)
committerStephen Gran <steve@lobefin.net>
Mon, 22 Apr 2013 19:30:55 +0000 (20:30 +0100)
Signed-off-by: Stephen Gran <steve@lobefin.net>
18 files changed:
modules/concat/.fixtures.yml [new file with mode: 0644]
modules/concat/.gemfile [new file with mode: 0644]
modules/concat/.gitignore [new file with mode: 0644]
modules/concat/.travis.yml [new file with mode: 0644]
modules/concat/CHANGELOG [new file with mode: 0644]
modules/concat/LICENSE [new file with mode: 0644]
modules/concat/Modulefile [new file with mode: 0644]
modules/concat/README.markdown [new file with mode: 0644]
modules/concat/Rakefile [new file with mode: 0644]
modules/concat/files/concatfragments.sh [new file with mode: 0755]
modules/concat/files/null/.gitignore [new file with mode: 0644]
modules/concat/lib/facter/concat_basedir.rb [new file with mode: 0644]
modules/concat/manifests/fragment.pp [new file with mode: 0644]
modules/concat/manifests/init.pp [new file with mode: 0644]
modules/concat/manifests/setup.pp [new file with mode: 0644]
modules/concat/spec/defines/init_spec.rb [new file with mode: 0644]
modules/concat/spec/fixtures/manifests/site.pp [new file with mode: 0644]
modules/concat/spec/spec_helper.rb [new file with mode: 0644]

diff --git a/modules/concat/.fixtures.yml b/modules/concat/.fixtures.yml
new file mode 100644 (file)
index 0000000..2d6fee0
--- /dev/null
@@ -0,0 +1,3 @@
+fixtures:
+  symlinks:
+    'concat': '#{source_dir}'
diff --git a/modules/concat/.gemfile b/modules/concat/.gemfile
new file mode 100644 (file)
index 0000000..d4241e8
--- /dev/null
@@ -0,0 +1,7 @@
+source :rubygems
+
+puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7']
+gem 'puppet', puppetversion
+gem 'puppet-lint'
+gem 'rspec-puppet'
+gem 'puppetlabs_spec_helper', '>= 0.1.0'
diff --git a/modules/concat/.gitignore b/modules/concat/.gitignore
new file mode 100644 (file)
index 0000000..5fff1d9
--- /dev/null
@@ -0,0 +1 @@
+pkg
diff --git a/modules/concat/.travis.yml b/modules/concat/.travis.yml
new file mode 100644 (file)
index 0000000..9d1c0cc
--- /dev/null
@@ -0,0 +1,12 @@
+language: ruby
+rvm:
+  - 1.8.7
+script:
+  - "rake lint"
+  - "rake spec"
+branches:
+  only:
+    - master
+env:
+  - PUPPET_VERSION=2.7.11
+gemfile: .gemfile
diff --git a/modules/concat/CHANGELOG b/modules/concat/CHANGELOG
new file mode 100644 (file)
index 0000000..139a638
--- /dev/null
@@ -0,0 +1,40 @@
+CHANGELOG:
+- 2010/02/19 - initial release
+- 2010/03/12 - add support for 0.24.8 and newer
+             - make the location of sort configurable
+             - add the ability to add shell comment based warnings to
+               top of files
+             - add the ablity to create empty files
+- 2010/04/05 - fix parsing of WARN and change code style to match rest
+               of the code
+             - Better and safer boolean handling for warn and force
+             - Don't use hard coded paths in the shell script, set PATH
+               top of the script
+             - Use file{} to copy the result and make all fragments owned
+               by root.  This means we can chnage the ownership/group of the
+               resulting file at any time.
+             - You can specify ensure => "/some/other/file" in concat::fragment
+               to include the contents of a symlink into the final file.
+- 2010/04/16 - Add more cleaning of the fragment name - removing / from the $name
+- 2010/05/22 - Improve documentation and show the use of ensure =>
+- 2010/07/14 - Add support for setting the filebucket behavior of files
+- 2010/10/04 - Make the warning message configurable
+- 2010/12/03 - Add flags to make concat work better on Solaris - thanks Jonathan Boyett
+- 2011/02/03 - Make the shell script more portable and add a config option for root group
+- 2011/06/21 - Make base dir root readable only for security
+- 2011/06/23 - Set base directory using a fact instead of hardcoding it
+- 2011/06/23 - Support operating as non privileged user
+- 2011/06/23 - Support dash instead of bash or sh
+- 2011/07/11 - Better solaris support
+- 2011/12/05 - Use fully qualified variables
+- 2011/12/13 - Improve Nexenta support
+- 2012/04/11 - Do not use any GNU specific extensions in the shell script
+- 2012/03/24 - Comply to community style guides
+- 2012/05/23 - Better errors when basedir isnt set
+- 2012/05/31 - Add spec tests
+- 2012/07/11 - Include concat::setup in concat improving UX
+- 2012/08/14 - Puppet Lint improvements
+- 2012/08/30 - The target path can be different from the $name
+- 2012/08/30 - More Puppet Lint cleanup
+- 2012/09/04 - RELEASE 0.2.0
+- 2012/12/12 - Added (file) $replace parameter to concat
diff --git a/modules/concat/LICENSE b/modules/concat/LICENSE
new file mode 100644 (file)
index 0000000..6a9e9a1
--- /dev/null
@@ -0,0 +1,14 @@
+   Copyright 2012 R.I.Pienaar
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/modules/concat/Modulefile b/modules/concat/Modulefile
new file mode 100644 (file)
index 0000000..3815edd
--- /dev/null
@@ -0,0 +1,8 @@
+name 'ripienaar-concat'
+version '0.2.0'
+source 'git://github.com/ripienaar/puppet-concat.git'
+author 'R.I.Pienaar'
+license 'Apache'
+summary 'Concat module'
+description 'Concat module'
+project_page 'http://github.com/ripienaar/puppet-concat'
diff --git a/modules/concat/README.markdown b/modules/concat/README.markdown
new file mode 100644 (file)
index 0000000..567fb9c
--- /dev/null
@@ -0,0 +1,154 @@
+What is it?
+===========
+
+A Puppet module that can construct files from fragments.
+
+Please see the comments in the various .pp files for details
+as well as posts on my blog at http://www.devco.net/
+
+Released under the Apache 2.0 licence
+
+Usage:
+------
+
+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:
+
+<pre>
+Puppet modules on this server:
+
+    -- Apache
+    -- MySQL
+</pre>
+
+Local sysadmins can also append to the file by just editing /etc/motd.local
+their changes will be incorporated into the puppet managed motd.
+
+<pre>
+# class to setup basic motd, include on all nodes
+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,
+      ensure  => "/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",
+      content => "    -- $body\n"
+   }
+}
+
+# a sample apache module
+class apache {
+   include apache::install, apache::config, apache::service
+
+   motd::register{"Apache": }
+}
+</pre>
+
+Detailed documentation of the class options can be found in the
+manifest files.
+
+Known Issues:
+-------------
+* Since puppet-concat now relies on a fact for the concat directory,
+  you will need to set up pluginsync = true on the [master] section of your
+  node's '/etc/puppet/puppet.conf' for at least the first run.
+  You have this issue if puppet fails to run on the client and you have
+  a message similar to
+  "err: Failed to apply catalog: Parameter path failed: File
+  paths must be fully qualified, not 'undef' at [...]/concat/manifests/setup.pp:44".
+
+Contributors:
+-------------
+**Paul Elliot**
+
+ * Provided 0.24.8 support, shell warnings and empty file creation support.
+
+**Chad Netzer**
+
+ * Various patches to improve safety of file operations
+ * Symlink support
+
+**David Schmitt**
+
+ * Patch to remove hard coded paths relying on OS path
+ * Patch to use file{} to copy the resulting file to the final destination.  This means Puppet client will show diffs and that hopefully we can change file ownerships now
+
+**Peter Meier**
+
+ * Basedir as a fact
+ * Unprivileged user support
+
+**Sharif Nassar**
+
+ * Solaris/Nexenta support
+ * Better error reporting
+
+**Christian G. Warden**
+
+ * Style improvements
+
+**Reid Vandewiele**
+
+ * Support non GNU systems by default
+
+**Erik DalĂ©n**
+
+ * Style improvements
+
+**Gildas Le Nadan**
+
+ * Documentation improvements
+
+**Paul Belanger**
+
+ * Testing improvements and Travis support
+
+**Branan Purvine-Riley**
+
+ * Support Puppet Module Tool better
+
+**Dustin J. Mitchell**
+
+ * Always include setup when using the concat define
+
+**Andreas Jaggi**
+
+ * Puppet Lint support
+
+**Jan Vansteenkiste**
+
+ * Configurable paths
+
+Contact:
+--------
+R.I.Pienaar / rip@devco.net / @ripienaar / http://devco.net
diff --git a/modules/concat/Rakefile b/modules/concat/Rakefile
new file mode 100644 (file)
index 0000000..14f1c24
--- /dev/null
@@ -0,0 +1,2 @@
+require 'rubygems'
+require 'puppetlabs_spec_helper/rake_tasks'
diff --git a/modules/concat/files/concatfragments.sh b/modules/concat/files/concatfragments.sh
new file mode 100755 (executable)
index 0000000..9b0be41
--- /dev/null
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+# Script to concat files to a config file.
+#
+# Given a directory like this:
+# /path/to/conf.d
+# |-- fragments
+# |   |-- 00_named.conf
+# |   |-- 10_domain.net
+# |   `-- zz_footer
+#
+# The script supports a test option that will build the concat file to a temp location and 
+# use /usr/bin/cmp to verify if it should be run or not.  This would result in the concat happening
+# twice on each run but gives you the option to have an unless option in your execs to inhibit rebuilds.
+# 
+# Without the test option and the unless combo your services that depend on the final file would end up 
+# restarting on each run, or in other manifest models some changes might get missed.
+#
+# OPTIONS:
+#  -o  The file to create from the sources
+#  -d  The directory where the fragments are kept
+#  -t  Test to find out if a build is needed, basically concats the files to a temp
+#       location and compare with what's in the final location, return codes are designed
+#       for use with unless on an exec resource
+#  -w   Add a shell style comment at the top of the created file to warn users that it 
+#       is generated by puppet
+#  -f   Enables the creation of empty output files when no fragments are found
+#  -n  Sort the output numerically rather than the default alpha sort
+#
+# the command: 
+#
+#   concatfragments.sh -o /path/to/conffile.cfg -d /path/to/conf.d
+#
+# creates /path/to/conf.d/fragments.concat and copies the resulting 
+# file to /path/to/conffile.cfg.  The files will be sorted alphabetically
+# pass the -n switch to sort numerically.
+# 
+# The script does error checking on the various dirs and files to make
+# sure things don't fail.
+
+OUTFILE=""
+WORKDIR=""
+TEST=""
+FORCE=""
+WARN=""
+SORTARG=""
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+
+## Well, if there's ever a bad way to do things, Nexenta has it.
+## http://nexenta.org/projects/site/wiki/Personalities
+unset SUN_PERSONALITY
+
+while getopts "o:s:d:tnw:f" options; do
+       case $options in
+               o ) OUTFILE=$OPTARG;;
+               d ) WORKDIR=$OPTARG;;
+               n ) SORTARG="-n";;
+               w ) WARNMSG="$OPTARG";;
+               f ) FORCE="true";;
+               t ) TEST="true";;
+               * ) echo "Specify output file with -o and fragments directory with -d"
+                   exit 1;;
+       esac
+done
+
+# do we have -o?
+if [ x${OUTFILE} = "x" ]; then
+       echo "Please specify an output file with -o"
+       exit 1
+fi
+
+# do we have -d?
+if [ x${WORKDIR} = "x" ]; then
+       echo "Please fragments directory with -d"
+       exit 1
+fi
+
+# can we write to -o?
+if [ -f ${OUTFILE} ]; then
+       if [ ! -w ${OUTFILE} ]; then
+               echo "Cannot write to ${OUTFILE}"
+               exit 1
+       fi
+else
+       if [ ! -w `dirname ${OUTFILE}` ]; then
+               echo "Cannot write to `dirname ${OUTFILE}` to create ${OUTFILE}"
+               exit 1
+       fi
+fi
+
+# do we have a fragments subdir inside the work dir?
+if [ ! -d "${WORKDIR}/fragments" ]  && [ ! -x "${WORKDIR}/fragments" ]; then
+       echo "Cannot access the fragments directory"
+       exit 1
+fi
+
+# are there actually any fragments?
+if [ ! "$(ls -A ${WORKDIR}/fragments)" ]; then
+       if [ x${FORCE} = "x" ]; then
+               echo "The fragments directory is empty, cowardly refusing to make empty config files"
+               exit 1
+       fi
+fi
+
+cd ${WORKDIR}
+
+if [ x${WARNMSG} = "x" ]; then
+       : > "fragments.concat"
+else
+       printf '%s\n' "$WARNMSG" > "fragments.concat"
+fi
+
+# find all the files in the fragments directory, sort them numerically and concat to fragments.concat in the working dir
+find fragments/ -type f -follow | sort ${SORTARG} | while read -r fragfile; do
+       cat "$fragfile" >> "fragments.concat"
+done
+
+if [ x${TEST} = "x" ]; then
+       # This is a real run, copy the file to outfile
+       cp fragments.concat ${OUTFILE}
+       RETVAL=$?
+else
+       # Just compare the result to outfile to help the exec decide
+       cmp ${OUTFILE} fragments.concat
+       RETVAL=$?
+fi
+
+exit $RETVAL
diff --git a/modules/concat/files/null/.gitignore b/modules/concat/files/null/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/modules/concat/lib/facter/concat_basedir.rb b/modules/concat/lib/facter/concat_basedir.rb
new file mode 100644 (file)
index 0000000..02e9c5b
--- /dev/null
@@ -0,0 +1,5 @@
+Facter.add("concat_basedir") do
+    setcode do
+        File.join(Puppet[:vardir],"concat")
+    end
+end
diff --git a/modules/concat/manifests/fragment.pp b/modules/concat/manifests/fragment.pp
new file mode 100644 (file)
index 0000000..f289712
--- /dev/null
@@ -0,0 +1,46 @@
+# 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      Mode for the file
+#   - owner     Owner of the file
+#   - group     Owner of the file
+#   - backup    Controls the filebucketing behavior of the final file and
+#               see File type reference for its use.  Defaults to 'puppet'
+define concat::fragment($target, $content=undef, $source=undef, $order=10, $ensure = 'present', $mode = '0644', $owner = $::id, $group = $concat::setup::root_group, $backup = 'puppet') {
+  $safe_name = regsubst($name, '[/\n]', '_', 'GM')
+  $safe_target_name = regsubst($target, '[/\n]', '_', 'GM')
+  $concatdir = $concat::setup::concatdir
+  $fragdir = "${concatdir}/${safe_target_name}"
+
+  # if content is passed, use that, else if source is passed use that
+  # if neither passed, but $ensure is in symlink form, make a symlink
+  case $ensure {
+    '', 'absent', 'present', 'file', 'directory': {
+      if ! ($content or $source) {
+        crit('No content, source or symlink specified')
+      }
+    }
+    default: {
+      # do nothing, make puppet-lint happy
+    }
+  }
+
+  file{"${fragdir}/fragments/${order}_${safe_name}":
+    ensure  => $ensure,
+    mode    => $mode,
+    owner   => $owner,
+    group   => $group,
+    source  => $source,
+    content => $content,
+    backup  => $backup,
+    alias   => "concat_fragment_${name}",
+    notify  => Exec["concat_${target}"]
+  }
+}
diff --git a/modules/concat/manifests/init.pp b/modules/concat/manifests/init.pp
new file mode 100644 (file)
index 0000000..b309059
--- /dev/null
@@ -0,0 +1,255 @@
+# A system to construct files using fragments from other files or templates.
+#
+# This requires at least puppet 0.25 to work correctly as we use some
+# enhancements in recursive directory management and regular expressions
+# to do the work here.
+#
+# USAGE:
+# The basic use case is as below:
+#
+# concat{"/etc/named.conf":
+#    notify => Service["named"]
+# }
+#
+# concat::fragment{"foo.com_config":
+#    target  => "/etc/named.conf",
+#    order   => 10,
+#    content => template("named_conf_zone.erb")
+# }
+#
+# # add a fragment not managed by puppet so local users
+# # can add content to managed file
+# concat::fragment{"foo.com_user_config":
+#    target  => "/etc/named.conf",
+#    order   => 12,
+#    ensure  => "/etc/named.conf.local"
+# }
+#
+# This will use the template named_conf_zone.erb to build a single
+# bit of config up and put it into the fragments dir.  The file
+# will have an number prefix of 10, you can use the order option
+# to control that and thus control the order the final file gets built in.
+#
+# You can also specify a path and use a different name for your resources:
+#
+# # You can make this something dynamic, based on whatever parameters your
+# # module/class for example.
+# $vhost_file = '/etc/httpd/vhosts/01-my-vhost.conf'
+#
+# concat{'apache-vhost-myvhost':
+#   path => $vhost_file,
+# }
+#
+# # We don't care where the file is located, just what to put in it.
+# concat::fragment {'apache-vhost-myvhost-main':
+#   target  => 'apache-vhost-myvhost',
+#   content => '<virtualhost *:80>',
+#   order   => 01,
+# }
+#
+# concat::fragment {'apache-vhost-myvhost-close':
+#   target  => 'apache-vhost-myvhost',
+#   content => '</virtualhost>',
+#   order   => 99,
+# }
+#
+#
+# SETUP:
+# The class concat::setup uses the fact concat_basedir to define the variable
+# $concatdir, where all the temporary files and fragments will be
+# durably stored. The fact concat_basedir will be set up on the client to
+# <Puppet[:vardir]>/concat, so you will be able to run different setup/flavours
+# of puppet clients.
+# However, since this requires the file lib/facter/concat_basedir.rb to be
+# deployed on the clients, so you will have to set "pluginsync = true" on
+# both the master and client, at least for the first run.
+#
+# There's some regular expression magic to figure out the puppet version but
+# if you're on an older 0.24 version just set $puppetversion = 24
+#
+# DETAIL:
+# We use a helper shell script called concatfragments.sh that gets placed
+# in <Puppet[:vardir]>/concat/bin to do the concatenation.  While this might
+# seem more complex than some of the one-liner alternatives you might find on
+# the net we do a lot of error checking and safety checks in the script to avoid
+# problems that might be caused by complex escaping errors etc.
+#
+# LICENSE:
+# Apache Version 2
+#
+# LATEST:
+# http://github.com/ripienaar/puppet-concat/
+#
+# CONTACT:
+# R.I.Pienaar <rip@devco.net>
+# Volcane on freenode
+# @ripienaar on twitter
+# www.devco.net
+
+
+# Sets up so that you can use fragments to build a final config file,
+#
+# OPTIONS:
+#  - 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.
+#  - mode       The mode of the final file
+#  - owner      Who will own the file
+#  - group      Who will own the 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
+#  - 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
+#
+# 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 referened as File["/path/to/file"] or
+#    File["concat_/path/to/file"]
+define concat(
+  $path = $name,
+  $owner = $::id,
+  $group = $concat::setup::root_group,
+  $mode = '0644',
+  $warn = false,
+  $force = false,
+  $backup = 'puppet',
+  $replace = true,
+  $gnu = undef,
+  $order='alpha'
+) {
+  include concat::setup
+
+  $safe_name   = regsubst($name, '/', '_', 'G')
+  $concatdir   = $concat::setup::concatdir
+  $version     = $concat::setup::majorversion
+  $fragdir     = "${concatdir}/${safe_name}"
+  $concat_name = 'fragments.concat.out'
+  $default_warn_message = '# This file is managed by Puppet. DO NOT EDIT.'
+
+  case $warn {
+    'true', true, yes, on: {
+      $warnmsg = $default_warn_message
+    }
+    'false', false, no, off: {
+      $warnmsg = ''
+    }
+    default: {
+      $warnmsg = $warn
+    }
+  }
+
+  $warnmsg_escaped = regsubst($warnmsg, "'", "'\\\\''", 'G')
+  $warnflag = $warnmsg_escaped ? {
+    ''      => '',
+    default => "-w '${warnmsg_escaped}'"
+  }
+
+  case $force {
+    'true', true, yes, on: {
+      $forceflag = '-f'
+    }
+    'false', false, no, off: {
+      $forceflag = ''
+    }
+    default: {
+      fail("Improper 'force' value given to concat: ${force}")
+    }
+  }
+
+  case $order {
+    numeric: {
+      $orderflag = '-n'
+    }
+    alpha: {
+      $orderflag = ''
+    }
+    default: {
+      fail("Improper 'order' value given to concat: ${order}")
+    }
+  }
+
+  File {
+    owner   => $::id,
+    group   => $group,
+    mode    => $mode,
+    backup  => $backup,
+    replace => $replace
+  }
+
+  file { $fragdir:
+    ensure => directory,
+  }
+
+  $source_real = $version ? {
+    24      => 'puppet:///concat/null',
+    default => undef,
+  }
+
+  file { "${fragdir}/fragments":
+    ensure   => directory,
+    force    => true,
+    ignore   => ['.svn', '.git', '.gitignore'],
+    notify   => Exec["concat_${name}"],
+    purge    => true,
+    recurse  => true,
+    source   => $source_real,
+  }
+
+  file { "${fragdir}/fragments.concat":
+    ensure   => present,
+  }
+
+  file { "${fragdir}/${concat_name}":
+    ensure   => present,
+  }
+
+  file { $name:
+    ensure   => present,
+    path     => $path,
+    alias    => "concat_${name}",
+    group    => $group,
+    mode     => $mode,
+    owner    => $owner,
+    source   => "${fragdir}/${concat_name}",
+  }
+
+  exec { "concat_${name}":
+    alias       => "concat_${fragdir}",
+    command     => "${concat::setup::concatdir}/bin/concatfragments.sh -o ${fragdir}/${concat_name} -d ${fragdir} ${warnflag} ${forceflag} ${orderflag}",
+    notify      => File[$name],
+    require     => [
+      File[$fragdir],
+      File["${fragdir}/fragments"],
+      File["${fragdir}/fragments.concat"],
+    ],
+    subscribe   => File[$fragdir],
+    unless      => "${concat::setup::concatdir}/bin/concatfragments.sh -o ${fragdir}/${concat_name} -d ${fragdir} -t ${warnflag} ${forceflag} ${orderflag}",
+  }
+
+  if $::id == 'root' {
+    Exec["concat_${name}"] {
+      user  => root,
+      group => $group,
+    }
+  }
+}
+
+# vim:sw=2:ts=2:expandtab:textwidth=79
diff --git a/modules/concat/manifests/setup.pp b/modules/concat/manifests/setup.pp
new file mode 100644 (file)
index 0000000..5e90bfa
--- /dev/null
@@ -0,0 +1,50 @@
+# Sets up the concat system.
+#
+# $concatdir is where the fragments live and is set on the fact concat_basedir.
+# Since puppet should always manage files in $concatdir and they should
+# not be deleted ever, /tmp is not an option.
+#
+# $puppetversion should be either 24 or 25 to enable a 24 compatible
+# mode, in 24 mode you might see phantom notifies this is a side effect
+# of the method we use to clear the fragments directory.
+#
+# The regular expression below will try to figure out your puppet version
+# but this code will only work in 0.24.8 and newer.
+#
+# It also copies out the concatfragments.sh file to ${concatdir}/bin
+class concat::setup {
+  $id = $::id
+  $root_group = $id ? {
+    root    => 0,
+    default => $id
+  }
+
+  if $::concat_basedir {
+    $concatdir = $::concat_basedir
+  } else {
+    fail ("\$concat_basedir not defined. Try running again with pluginsync=true on the [master] section of your node's '/etc/puppet/puppet.conf'.")
+  }
+
+  $majorversion = regsubst($::puppetversion, '^[0-9]+[.]([0-9]+)[.][0-9]+$', '\1')
+  $fragments_source = $majorversion ? {
+    24      => 'puppet:///concat/concatfragments.sh',
+    default => 'puppet:///modules/concat/concatfragments.sh'
+  }
+
+  file{"${concatdir}/bin/concatfragments.sh":
+    owner  => $id,
+    group  => $root_group,
+    mode   => '0755',
+    source => $fragments_source;
+
+  [ $concatdir, "${concatdir}/bin" ]:
+    ensure => directory,
+    owner  => $id,
+    group  => $root_group,
+    mode   => '0750';
+
+  ## Old versions of this module used a different path.
+  '/usr/local/bin/concatfragments.sh':
+    ensure => absent;
+  }
+}
diff --git a/modules/concat/spec/defines/init_spec.rb b/modules/concat/spec/defines/init_spec.rb
new file mode 100644 (file)
index 0000000..172929a
--- /dev/null
@@ -0,0 +1,115 @@
+require 'spec_helper'
+
+describe 'concat' do
+  basedir = '/var/lib/puppet/concat'
+  let(:title) { '/etc/foo.bar' }
+  let(:facts) { {
+    :concat_basedir => '/var/lib/puppet/concat',
+    :id             => 'root',
+  } }
+  let :pre_condition do
+    'include concat::setup'
+  end
+
+  directories = [
+    "#{basedir}/_etc_foo.bar",
+    "#{basedir}/_etc_foo.bar/fragments",
+  ]
+
+  directories.each do |dirs|
+    it do
+      should contain_file(dirs).with({
+        'ensure'  => 'directory',
+        'backup'  => 'puppet',
+        'group'   => 0,
+        'mode'    => '0644',
+        'owner'   => 'root',
+      })
+    end
+  end
+
+  files = [
+    "/etc/foo.bar",
+    "#{basedir}/_etc_foo.bar/fragments.concat",
+  ]
+
+  files.each do |file|
+    it do
+      should contain_file(file).with({
+        'ensure'  => 'present',
+        'backup'  => 'puppet',
+        'group'   => 0,
+        'mode'    => '0644',
+        'owner'   => 'root',
+      })
+    end
+  end
+
+  it do
+    should contain_exec("concat_/etc/foo.bar").with_command(
+      "#{basedir}/bin/concatfragments.sh " +
+      "-o #{basedir}/_etc_foo.bar/fragments.concat.out " +
+      "-d #{basedir}/_etc_foo.bar   "
+    )
+  end
+end
+
+describe 'concat' do
+
+  basedir = '/var/lib/puppet/concat'
+  let(:title) { 'foobar' }
+  let(:target) { '/etc/foo.bar' }
+  let(:facts) { {
+    :concat_basedir => '/var/lib/puppet/concat',
+    :id             => 'root',
+  } }
+  let :pre_condition do
+    'include concat::setup'
+  end
+
+  directories = [
+    "#{basedir}/foobar",
+    "#{basedir}/foobar/fragments",
+  ]
+
+  directories.each do |dirs|
+    it do
+      should contain_file(dirs).with({
+        'ensure'  => 'directory',
+        'backup'  => 'puppet',
+        'group'   => 0,
+        'mode'    => '0644',
+        'owner'   => 'root',
+      })
+    end
+  end
+
+  files = [
+    "foobar",
+    "#{basedir}/foobar/fragments.concat",
+  ]
+
+  files.each do |file|
+    it do
+      should contain_file(file).with({
+        'ensure'  => 'present',
+        'backup'  => 'puppet',
+        'group'   => 0,
+        'mode'    => '0644',
+        'owner'   => 'root',
+      })
+    end
+  end
+
+  it do
+    should contain_exec("concat_foobar").with_command(
+      "#{basedir}/bin/concatfragments.sh " +
+      "-o #{basedir}/foobar/fragments.concat.out " +
+      "-d #{basedir}/foobar   "
+    )
+  end
+
+
+end
+
+# vim:sw=2:ts=2:expandtab:textwidth=79
diff --git a/modules/concat/spec/fixtures/manifests/site.pp b/modules/concat/spec/fixtures/manifests/site.pp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/modules/concat/spec/spec_helper.rb b/modules/concat/spec/spec_helper.rb
new file mode 100644 (file)
index 0000000..2c6f566
--- /dev/null
@@ -0,0 +1 @@
+require 'puppetlabs_spec_helper/module_spec_helper'