Learning lambdas and functions
Functions have long been an essential part of Puppet. Due to the new type system, a complete new set of functions have been possible—functions with different behavior based on parameter data types.
To understand functions, we first have to take a look at lambdas, which are introduced in Puppet 4. Lambdas represent a snippet of Puppet code, which can be used in functions. Syntactically, lambdas consist of an optional type and at least one variable with optional defaults set, enclosed in pipe signs (|) followed by Puppet code inside a block of curly braces:
$packages = ['htop', 'less', 'vim']
each($packages) |String $package| {
package { $package:
ensure => latest,
}
}
Lambdas are typically used on functions. The preceding example uses the each
function on the $packages
variable, iterating over its contents, setting the lambda variable $package
within each iteration to the values htop
, less
, and vim
, respectively. The Puppet code block afterwards uses the lambda variable inside a resource type declaration.
Since Puppet now knows about data types, you can interact and work with variables, and the data inside, in a far more elegant way.
Puppet 4 comes with a whole set of built in functions for arrays and hashes:
each
slice
filter
map
reduce
with
We already saw the each
function in action. Prior to Puppet 4, one needed to wrap the desired Puppet resource types into define
and declare the define
type using an array:
class puppet_symlinks { $symlinks = [ puppet', 'facter', 'hiera' ] puppet_symlinks::symlinks { $symlinks: } } define puppet_symlinks::symlinks { file { "/usr/local/bin/${title}": ensure => link, target => "/opt/puppetlabs/bin/${title}", } }
With this concept, the action (create the symlink) was put into a define type and was no longer directly visible in the manifest. The new iteration approach keeps the action in the same location:
class puppet_symlinks { $symlinks = [ 'puppet', 'facter', 'hiera' ] $symlinks.each | String $symlink | { file { "/usr/local/bin/${symlink}": ensure => link, target => "/opt/puppetlabs/bin/${symlink}", } } }
Did you recognize that this time, we used another approach of using a function? In the first example, we used the Puppet 3 style function calls:
function($variable)
Puppet 4 also supports the postfix notation, where the function is appended to its parameter using a dot:
$variable.function
Puppet 4 supports both ways of using a function. This allows you to keep adhering to your code style and make use of the new functionality.
Let's run though the other functions for arrays and hashes:
- The
slice
function allows you to split up and group an array or a hash. It needs an additional parameter (integer), defining how many objects should be grouped together:$array = [ '1', '2', '3', '4'] $array.slice(2) |$slice| { notify { "Slice: ${slice}": } }
This code will produce the following output:
Notice: Slice: [1, 2] Notice: Slice: [3, 4]
When using the slice functions on a hash, one receives the keys (according to the amount of grouped keys) and accordingly, the sub hash:
$hash = { 'key 1' => {'value11' => '11', 'value12' => '12',}, 'key 2' => {'value21' => '21', 'value22' => '22',}, 'key 3' => {'value31' => '31', 'value32' => '32',}, 'key 4' => {'value41' => '41', 'value42' => '42',}, } $hash.slice(2) |$hslice| { notify { "HSlice: ${hslice}": } }
This will return the following output:
Notice: HSlice: [[key1, {value11 => 11, value12 => 12}], [key2, {value21 => 21, value22 => 22}]] Notice: HSlice: [[key3, {value31 => 31, value32 => 32}], [key4, {value41 => 41, value42 => 42}]]
- The
filter
function can be used to filter out specific entries inside an array or hash.When used on an array, all elements are passed to the code block and the code block evaluates whether the entry does match. This is very useful if you want to filter out items of an array (for example, packages which should be installed:
$pkg_array = [ 'libjson', 'libjson-devel', 'libfoo', 'libfoo-devel' ] $dev_packages = $pkg_array.filter |$element| { $element =~ /devel/ } notify { "Packages to install: ${dev_packages}": }
This will return the following output:
Notice: Packages to install: [libjson-devel, libfoo-devel]
The behavior on hashes is different. When using hashes, one has to provide two lambda variables: key and value. You might want to only add users that have a specific
gid
set:$hash = { 'jones' => { 'gid' => 'admin', }, 'james' => { 'gid' => 'devel', }, 'john' => { 'gid' => 'admin', }, } $user_hash = $hash.filter |$key, $value| { $value['gid'] =~ /admin/ } $user_list = keys($user_hash) notify { "Users to create: ${user_list}": }
This will return only the users from the
admin
gid:Notice: Users to create: [jones, john]