Iteration and loops
Use iteration and loops to write more succinct code, and use data more effectively.
Iteration functions
Instead of using loop keywords, the Puppet language uses iterative functions that accept blocks of code called lambdas.
-
each
- Repeats a block of code a number of times, using a collection of values to provide different parameters each time. -
slice
- Repeats a block of code a number of times, using groups of values from a collection as parameters. -
filter
- Uses a block of code to transform a data structure by removing non-matching elements. -
map
- Uses a block of code to transform every value in a data structure. -
reduce
- Uses a block of code to create a new value, or data structure, by combining values from a provided data structure. -
with
- Evaluates a block of code one time, isolating it in its own local scope. It doesn’t iterate, but has a family resemblance to the iteration functions.
See the slice
and reduce
documentation for information on how these
functions handle parameters differently.
each
, filter
, and map
functions accept a lambda with either one or
two parameters. Depending on the number of parameters, and the type of data structure you’re
iterating over, the values passed into a lambda vary:Collection type | Single parameter | Two parameters |
---|---|---|
Array |
<VALUE>
|
<INDEX>, <VALUE>
|
Hash |
[<KEY>, <VALUE>] (two-element
array) |
<KEY>, <VALUE>
|
Arrays:
['a','b','c'].each |Integer $index, String $value| { notice("${index} = ${value}") }
Results in: Notice: Scope(Class[main]): 0 = a
Notice: Scope(Class[main]): 1 = b
Notice: Scope(Class[main]): 2 = c
2D Arrays:
$a = [['1', '2'], ['3', '4']]
$a.each |$array| {
$array.each |$int| {
notice($int)
}
}
Results in: Notice: Scope(Class[main]): 1
Notice: Scope(Class[main]): 2
Notice: Scope(Class[main]): 3
Notice: Scope(Class[main]): 4
Hashes:
$pets = {
'pet1' => 'dog',
'pet2' => 'cat',
'pet3' => 'goldfish'}
$pets.each |$key, $value| {notice($value)}
Results in:
Notice: Scope(Class[main]): dog
Notice: Scope(Class[main]): cat
Notice: Scope(Class[main]): goldfish
Hashes preserve the order in which their keys and values were written. When iterating over a hash’s members, the loops occur in the order that they are written. When interpolating a hash into a string, the resulting string is also constructed in the same order.
Declaring resources
each
function makes this
succinct.$binaries = ['facter', 'hiera', 'mco', 'puppet', 'puppetserver']
$binaries.each |String $binary| {
file {"/usr/bin/${binary}":
ensure => link,
target => "/opt/puppetlabs/bin/${binary}",
}
}
Iteration with defined resource types
In previous versions of Puppet, iteration functions did not exist and lambdas weren’t supported. By writing defined resource types and using arrays as resource titles you could achieve a clunkier form of iteration.
symlink.pp
file:define puppet::binary::symlink ($binary = $title) {
file {"/usr/bin/${binary}":
ensure => link,
target => "/opt/puppetlabs/bin/${binary}",
}
}
Use the defined type for the iteration somewhere ele in your manifest
file:$binaries = ['facter', 'hiera', 'mco', 'puppet', 'puppetserver']
puppet::binary::symlink { $binaries: }
The main problems with this approach are:-
The block of code doing the work was separated from the place where you used it, which makes a simple task complicated.
-
Every type of thing to iterate over would require its own one-off defined type.
The current Puppet style of iteration is much improved, but you might encounter code that uses this old style, and might have to use it to target older versions of Puppet.
Using iteration to transform data
To transform data into more useful forms, use iteration. For example:
[1,3]
: $filtered_array = [1,20,3].filter |$value| { $value < 10 }
This
returns 6
: $sum = reduce([1,2,3]) |$result, $value| { $result + $value }
This
returns {"key1"=>"first value", "key2"=>"second value",
"key3"=>"third
value"}
:$hash_as_array = ['key1', 'first value',
'key2', 'second value',
'key3', 'third value']
$real_hash = $hash_as_array.slice(2).reduce( {} ) |Hash $memo, Array $pair| {
$memo + $pair
}
Breaking out of the a loop
You can break out of a loop or skip the next iteration using the break()
or next()
functions respectively.