Using Hiera in Puppet
Using the Hiera functions, the data stored in Hiera can be retrieved by the Puppet Master while compiling the catalog. In our manifests, we can have something like the following:
$dns_servers = hiera("dns_servers")
Note that the name of the Puppet variable need not be the same as that of the Hiera one, so the previous code can also be as follows:
$my_dns_servers = hiera("dns_servers")
This assigns to the variable $my_dns_servers
the top value (the first one found while crossing the hierarchy of data sources) retrieved by Hiera for the key dns_servers
.
We can also merge arrays and hashes here; so in order to retrieve an array of all the values in the hierarchy's data sources of a given key and not just the first one, we can use hiera_array()
as follows:
$my_dns_servers = hiera_array("dns_servers")
If we expect a hash value for a given key, we can use the hiera()
function to retrieve the top value found, or we can use hiera_hash()
to merge all the found values in a single hash:
$openssh_settings = hiera_hash("openssh_settings")
All these Hiera functions may receive additional parameters, which are as follows:
- Second argument: If present and not blank, then this is the default value to use if no value is found for the given key.
- Third argument: This overrides the configured hierarchy by adding a custom data source at the top of it. This might be useful in cases where we need to evaluate data using a logic that is not contemplated by the current hierarchy and for which it isn't worth the effort to add an extra layer in the global configuration.
The following code shows the usage of additional parameters:
$my_dns_servers = hiera("dns_servers","8.8.8.8","$country")
Dealing with hashes in the Puppet code
With a hash, it is possible to express complex and structured data that has to be managed inside the Puppet code.
Remember that Hiera always returns the value of the first defined level keys, for example, we have a hash with nested hashes as shown in the following code:
network::interfaces_hash: eth0: ipaddress: 10.0.0.193 netmask: 255.255.255.0 network: 10.0.0.0 broadcast: 10.0.0.255 gateway: 10.0.0.1 post_up: - '/sbin/ifconfig eth3 up' - '/sbin/ifconfig eth4 up' eth2: enable_dhcp: true eth3: auto: false method: manual eth4: auto: false method: manual
We can create a variable as follows inside our Puppet code that loads it:
$int_hash=hiera('network::interfaces_hash')
Then, refer to single values inside its data structure with the following code:
$ip_eth0=$int_hash['eth0']['ipaddress']
Otherwise, if we need to access this value from a template, we can directly write it in our erb
file:
ipaddress <%= @int_hash['eth0']['ipaddress'] %>
Puppet 3 automatic parameter lookup
With Puppet 3, Hiera is shipped directly with the core code, but the integration goes far beyond; an automatic Hiera lookup is done for each class' parameter using the key $class::$argument
; this functionality is called data bindings or automatic parameter lookup.
For example, consider the following class definition:
class openssh ( $template = 'openssh/sshd.config.erb', ) { . . . }
The value of $template
will be evaluated according to the following logic:
- If the user directly and explicitly passes the template argument, then its value is used:
class { 'openssh': template => 'site/openssh/sshd_config.erb', }
- If no value is explicitly set, Puppet 3 automatically looks for the Hiera key
openssh::template
. - Finally, if no value is found in Hiera, then the default
'openssh/sshd.config.erb'
value is used.
To emulate a similar behavior on Puppet versions earlier than Version 3, we should write something like the following:
class openssh ( $template = hiera("openssh::template",'openssh/sshd.config.erb'), ) { . . . }
Evolving usage patterns for class parameters
This strong integration has definitely boosted the adoption of Hiera and is changing the way Puppet code is organized and classes are declared.
Before Puppet 2.6, we could declare classes by just including them, optionally, more than once in our catalog, using the following code line:
include openssh
We could also manage the behavior of the class just by setting variables and having them dynamically evaluated inside the class, with something like the following code:
class openssh { file { 'sshd_config': content => template($openssh_template), } }
This approach suffered the risks of having inconsistent values due to dynamic scoping of variables and parse ordering. Also, when using variables inside the module's code, it wasn't easy to understand what were the variables that could affect the class's behavior, and there was no public API that was easily accessible.
The introduction of parameterized classes in Puppet 2.6 allowed classes to expose their arguments in a clear way using the following code:
class openssh ( $template = 'openssh/sshd.config.erb', ) { . . . }
Now, we can pass them explicitly and consistently in a class declaration as shown:
class { 'openssh': template => 'site/openssh/sshd_config.erb', }
But the fact that we can declare a specific class using parameters only once in a node's catalog presented new challenges on how our custom code had to be organized; we could not include classes wherever needed, but we had to reorganize our manifests in order to avoid duplicate declarations.
From Puppet 3 onwards, it is possible to have the best of both worlds, as we can use the original way to include classes:
include openssh
We can use this statement in different parts of our manifests and be sure that the value of the template
parameter is always looked up on the Hiera key openssh::template
.