Specialising validate_re with wrapper functions in Puppet
Once your puppet code base reaches a certain size you’ll often have a
number of validate_
functions testing parameters and configuration
values for compliance with local rules and requirements. These
invocations often look like this:
validate_re($private_gpg_key_fingerprint, '^[[:alnum:]]{40}$', 'Must supply full GPG fingerprint')
Once you’ve spent a minute or two reading that you’ll probably
be able to understand it; but wouldn’t it be nice to not have
to care about the exact details and focus on what you’re actually
testing? An approach I’ve been experimenting with on one larger code
base is to specialise those kind of calls using a wrapper function.
Instead of the detailed call above we can now do
validate_gpg_key_fingerprint($finger_print)
. Which of those are
easier to read? Which is simpler for people new to puppet?
Implementing this kind of wrapper is much easier than you’d expect. All
you need is a small stub function that wraps an existing one, in our
case validate_re
, and supplies some sensible defaults and local
validation. You can see an example below.
cat modules/wrapper_functions/lib/puppet/parser/functions/validate_gpg_key_fingerprint.rb
module Puppet::Parser::Functions
newfunction(:validate_gpg_key_fingerprint, :doc => <<-'ENDHEREDOC') do |args|
A simple wrapper function to show specialisation of 'validate_re' from the stdlib
and how it can make manifests more domain specific and easier to read.
ENDHEREDOC
unless (args.length >= 1 && args.length <= 2)
message = 'wrong arguments - <GPG fingerprint> [error message]'
raise ArgumentError, "validate_gpg_key_fingerprint(): #{message}"
end
fingerprint = args[0]
# here we set the local rules and sensible defaults
message = args[1] || 'Must supply full GPG fingerprint'
regex = '^[[:alnum:]]{40}$'
# here we run the original function
function_validate_re( [ fingerprint, regex, message ] )
end
end
This is easy to test in isolation, a nice place to encompass more
complicated validations while presenting a simple usage case and
requires only a small amount of shim coding. If this approach interests
you it’s also quite easy to achieve a similar benefit with custom
wrappers to the is_
functions in stdlib
that have a greater
understanding of what you’re testing than the basic, but common
cases, provided in things like stdlib
. For example you can wrap
is_ip_address
and only return true for addresses in your own
valid ranges.
The most obvious downside to this approach, other than the custom coding
required, is that if used often enough it’s easy to convince people that
puppet has a number of basic validate_
and is_
functions that don’t
actually exist anywhere outside of your repos.
Although these kinds of changes are not going to revolutionise your code base they are nice, gradual improvements. If you have a number of users that don’t work with puppet on a regular basis, changing the vocabulary of your functions to closer align with your local domain terms can be a nicer way to ease them into reading, and eventually contributing, to the code.