This function was initially created to manage the /etc/sudoers file, but it aims to be more generic.
The function takes two arguments: a content and a command. The content is copied to a tempfile and checked with the command. If the command returns 0, the content is returned, otherwise the function raises a puppet error.
/usr/local/lib/site_ruby/puppet/parser/functions/validate.rb
#!/usr/bin/ruby
module Puppet::Parser::Functions
newfunction(:validate, :type => :rvalue) do |args|
content = args[0]
checkscript = args[1]
# Test content in a temporary file
tmpfile = Tempfile.new("puppet")
tmpfile.write(content)
tmpfile.close
checkcmd=checkscript+" "+tmpfile.path
system(checkcmd)
if $?==0
return(content)
end
raise Puppet::ParseError, "could not validate content with command "+checkscript
end
end
Usage
Here is an example of usage with a template to manage an /etc/sudoers file. The sudo.pp file contains the values of the variables to be used in the template, for the purpose of the example.
/var/lib/puppet/templates/sudo.erb
# /etc/sudoers # # This file was created and transferred by puppet # Do not edit manually! <% host_alias.each do |val| -%> Host_Alias <%= val %> <% end -%> # User alias specification # Cmnd alias specification <% cmnd_alias.each do |val| -%> Cmnd_Alias <%= val %> <% end -%> # Defaults Defaults <%= sudo_defaults %> # User privilege specification root ALL=(ALL) ALL <% sudo_rules.each do |val| -%> <%= val %> <% end -%>
/etc/puppet/manifest/classes/sudo.pp
# /etc/puppet/manifests/classes/sudo.pp
class sudo {
$host_alias = [ "LOCALNET = 192.168.0.0/24, localhost" ]
$cmnd_alias = [
"DEBIAN_TOOLS = /usr/bin/apt-get, /usr/bin/auto-get, \
/usr/bin/dpkg, /usr/bin/dselect, /usr/sbin/dpkg-reconfigure",
"PBUILDER = /usr/sbin/pbuilder"
]
$sudo_defaults = "!lecture,tty_tickets,!fqdn"
$sudo_rules = [
"%admin ALL=(ALL) ALL, NOPASSWD: DEBIAN_TOOLS",
"%pbuilder LOCALNET = NOPASSWD: PBUILDER",
"buildd ALL=(ALL) NOPASSWD:ALL"
]
$content = template("sudo.erb")
file { "/etc/sudoers":
owner => root,
group => root,
mode => 440,
content => validate($content,"/usr/sbin/visudo -cq -f")
}
}
With this template and these values in sudo.pp, the file is validated and copied. If an error is introduced in either, puppet will raise an error and use the cache for this file (if any).
Notes
As of version 0.22.4, puppet functions do not support other functions as arguments. When this is fixed, you will be able to call content-generating functions as arguments to validate(), and the current example could become validate(template("sudo.erb"),"/usr/sbin/visudo -cq -f").