The Puppet Future Parser is the new implementation of the manifest parser which will become the default in 4.0, so I thought I’d take a look to see what I’d need to update.
Also, there are some fancy new features like iteration and that you can use [1,2]
array notation or {a=>b}
hash notation anywhere that you’d previously used a variable containing an array or hash.
The iteration and lambda features are intended to replace create_resources
calls, as they are more flexible and can loop round repeatedly to create individual definitions.
For example, here’s a dumb “sudo” profile which uses the each
construct to iterate over an array:
class profiles::sudo { # This is a particularly dumb version of use of sudo, to allow any commands: $admin_users = hiera_array('admin_users') # Additional users with special sudo rights, but no ssh access (e.g. root): $sudo_users = hiera_array('sudo_users') class { ::sudo: } $all_sudo_users = concat($sudo_users, $admin_users) # Create a resource for each entry in the array: each($all_sudo_users) |$u| { sudo::entry { $u: comment => "Allow ${u} to run anything as any user", username => $u, host => 'ALL', as_user => 'ALL', as_group => 'ALL', nopasswd => false, cmd => 'ALL', } } }
Making this work with create_resources
and trying to splice in the the username for each user in the list into a hash looked like it would be messy, requiring at least an additional layer of define
— this method is much neater.
This makes it much easier to create data abstractions over existing modules — you can programmatically massage the data you read from your hiera files and call definitions using that data in a much more flexible way than when passing hashes to create_resources
. This “glue” can be separated into your roles and profiles (which could be the subject of another post but are described well in this blog post), creating a layer which separates the use of the module from the data which drives that use nicely.
So this all sounds pretty great, but there are a few changes you’ll possibly encounter when switching to the future parser:
- Similar to the switch from puppet master to puppet server, the future parser is somewhat more strict about data formats. e.g. I found that my hiera data definitely needed to be properly quoted when I started using puppet server, so entries like
mode : 644
in a file hash wouldn’t give the number you were expecting… (needsmode : 0644
ormode : '644'
to avoid conversion from octal to decimal…). The future parser extends this to being more strict in your manifests, so a similarly-incorrectfile { ... mode => 644 }
declaration needs quoting or a leading zero. If you use puppet-lint you’ll catch this anyway — so use it! 🙂 - It’s necessary to use
{}
instead ofundef
when setting default values forhiera_hash
(and likewise[]
instead ofundef
forhiera_array
), to allow conditional expressions of the formif $var { ... }
to work as intended. It seems that in terms of falseness for arrays and hashes thatundef
is in fact true… (could be a bug, as this page in the docs says: “When used as a boolean, undef is false”) - Dynamically-scoped variables (which are pretty mad and difficult to follow anyway, which is why most languages avoid them like the plague…) don’t pass between a class and any sub-classes which it creates. This is in the docs here, but it’s such a common pattern that it could well have made it through from your old (pre-Puppet 2.7) manifests and still have been working OK until the switch to the future parser. e.g.:
class foo { $var = "x" } class bar { include foo # $var isn't defined here, as dynamic scope rules don't allow it in Puppet >2.7 }
Instead you need to explicitly qualify your variables to pull them out of the correct scope —
$foo::var
in this case. In your erb templates, as a common place where the dynamically-scoped variables might have ended up getting used, you can now usescope['::foo::var']
as a shorthand for the previously-longerscope.lookupvar('::foo::var')
to explicitly qualify the lookup of variables. The actual scope rules for Puppet < 2.7 are somewhat more complicated and often led to confusing situations if you unintentionally used dynamic scoping, especially when combined with overriding variables from the parent scope… - I’m not sure that expressions of the form
if "foo" in $arrayvar { ... }
work how they should, but I’ve not had a chance to investigate this properly yet.
Most of these are technically the parser more strictly adhering to the specifications, but it’s easy to have accidentally had them creep into your manifests if you’re not being good and using puppet-lint and other tools to check them.
In conclusion : Start using the Future Parser soon! It adds excellent features for iteration which make abstracting data a whole lot easier than using the non-future (past?) parser allows. Suddenly the combination of roles, profiles and the iteration facilities in the future parser mean that abstraction using Puppet and hiera makes an awful lot more sense!