A solution for Puppet hiera yaml sprawl

A solution for Puppet hiera yaml sprawl hiera is a very powerful way to separate code and data in Puppet code. But sometimes, you need more than the standard functionality for levels that hiera provides. One way to keep configuration data from being duplicated over multiple levels would be to include other yaml files. Unfortunately, yaml doesn’t have any means to do this. That is why we built an extended yaml hiera backend that allows you to do this.

A use case for yaml include

One of the use cases that we run into regularly with our clients, is doing version upgrades of for example Oracle software. We have one role yaml that contains all of the hiera data needed to ensure a correct Oracle database is built and configured by Puppet. Here is an excerpt of such a yaml file:

ora_profile::database::dbname:                  MYDB
ora_profile::database::version:                 19.0.0.0
ora_profile::database::db_patches::patch_level: JUL2021RU
ora_profile::database::oracle_home:             /u01/app/oracle/product/19.0.0.0/db_home1
ora_profile::database::db_software::file_name:  LINUX.X64_193000_db_home
ora_profile::database::db_software::dirs:
- /u01/app/oracle/product
- /u01/app/oracle/product/DB19"

As you can see, this yaml file contains some references to the version used. In this case, Oracle 19.

When we want to start using Oracle 21, however, we want to do this gradually. So first on one development machine and then on more or even on all development machines and then through testing and acceptance finally to production. One of the ways to do this is to add the updated hiera data to the node-specific hiera levels. This allows you to start rolling out Oracle 21 controlled on a node-by-node basis. This, however leads to a sprawl of hiera entries throughout your yaml files. Wouldn’t it be great if you could include a file containing all entries for a specific version? Yes, you can!!

How does it look?

Here is how this looks in a yaml file:

---
some::other::data:  yes
...
# @include '../includes/oracle_21.yaml'
#
other::entry::in::your:yaml: 	just_some value
...

This little snippet will include the file oracle_21.yaml from the directory includes below the directory of the current yaml file into your current yaml file.

The include process itself replaces the include line with the content of the included files and then passes it to the Ruby yaml processor as one entity.

But why is it a comment (e.g. behind a #)? The reson for this, is that many Puppet CI tools check the validity of yaml files. If we would have put the include as a “normal” stratement, all of the CI checks for yaml will fail. That is not what we want.

How do we enable this?

The hiera backend is called easy_type::yaml_with_include and as the name implies, it is part of the easy_type module. The enterprisemodules-easy_type module is a collection of functions, custom types and other Puppet goodies we use for all of our commercial modules. It holds a proprietary license, but you are free to install and use it. Check the documentation on the Puppet forge on how to install this module into your Puppet code base.

After the installation, we have to configure hiera to use it. The easiest way to do this is to replace the defaults in your hiera.yaml file. We need to change the value for the data_hash key from yaml_data to easy_type::yaml_with_include. Here is an example of how your hiera.yaml could look.

---
version: 5
defaults:  # Used for any hierarchy level that omits these keys.
  datadir: hieradata                          # This path is relative to hiera.yaml's directory.
  data_hash: easy_type::yaml_with_include  

Let’s try it.

Our role yaml now contains the include statement # @include '../includes/oracle_21.yaml'. Our oracle_21.yaml` file looks like this:

ora_profile::database::version:                  21.0.0.0
ora_profile::database::db_patches::patch_level:  JUL2021RU
ora_profile::database::oracle_home:              /u01/app/oracle/product/21.0.0.0/db_home1
ora_profile::database::db_software::file_name:   LINUX.X64_213000_db_home
ora_profile::database::db_software::dirs:
- /u01/app/oracle/product
- /u01/app/oracle/product/DB21"

The best way to check if it works and also to debug lookups, is to use the puppet lookup utility. Let’s use this utility to lookup the version of Oracle.

$ puppet lookup ora_profile::database::version
--- 21.0.0.0

And it works. But it would be nice to get a bit more details. Fortunately, the puppet lookup utility has a way of explaining what it does. Let’s try again and use the --explain option:

$ puppet lookup ora_profile::database::version --explain

...

  Environment Data Provider (hiera configuration version 5)
    Using configuration "/etc/puppetlabs/code/environments/production/hiera.yaml"
    Merge strategy hash
      Hierarchy entry "Per-node data"
        Merge strategy hash
          Path "/etc/puppetlabs/code/environments/production/hieradata/nodes/db190.example.com.yaml"
            Original path: "nodes/%{trusted.certname}.yaml"
            No such key: "lookup_options"
            Including file '/etc/puppetlabs/code/environments/production/hieradata/includes/oracle_21.yaml' into contents of '/etc/puppetlabs/code/environments/production/hieradata/nodes/db190.example.com.yaml'
          Path "/etc/puppetlabs/code/environments/production/hieradata/nodes/db190.yaml"
            Original path: "nodes/%{hostname}.yaml"
            Path not found

...

Searching for "ora_profile::database::version"
  Global Data Provider (hiera configuration version 5)
    Using configuration "/etc/puppetlabs/puppet/hiera.yaml"
    No such key: "ora_profile::database::version"
  Environment Data Provider (hiera configuration version 5)
    Using configuration "/etc/puppetlabs/code/environments/production/hiera.yaml"
    Hierarchy entry "Per-node data"
      Path "/etc/puppetlabs/code/environments/production/hieradata/nodes/db190.example.com.yaml"
        Original path: "nodes/%{trusted.certname}.yaml"
        Found key: "ora_profile::database::version" value: "21.0.0.0"

As you can see by the line /etc/puppetlabs/code/environments/production/hieradata/includes/oracle_21.yaml' into contents of '/etc/puppetlabs/code/environments/production/hieradata/nodes/db190.example.com.yaml' hiera tells you it is including the file. It only notifies you once. Because for performance reasons, it only reads the files one time. Every additional hiera lookup will use the cached data and not know anything about the inclusion of the file.

Conclusion

When sprawl of hiera data over your many hiera levels is an issue for you, we have a way to help you. The yaml_with_include hiera backend allows you to use include statements in your hiera yaml data. We hope this will help you. If you could use a hand, we are here to help. Making good Puppet code, is our bread and butter at Enterprise Modules. But besides developing our own modules, we are also helping customers build the best possible Puppet code. Do you think you could need some assistance? Don’t hesitate to contact us at info@enterprisemodules.com or by phone: +31 (0)30-601 6000 for some consultancy.

About us

Enterprise modules is the leading developer of enterprise-ready puppet modules for Oracle databases,Oracle WebLogic, and IBM MQ software. Our puppet modules help sysadmins and DBAs to automate the installation, configuration, and management of their databases and application server systems. These modules allow them to make managed, consistent, repeatable, and fast changes to their infrastructure and automatically enforce the consistency. We are a proud member of the Conclusion family. Conclusion is thé multidisciplinary service provider in the field of Business Transformation and IT Services. Our tagline? Business Done Differently. Our 1250 specialists and professionals live up to that every day by truly combining our IT knowledge with business and domain know-how. With dedication, creativity, and flexibility, we take responsibility for the social and mission-critical business processes and systems of our customers and enable organizations to digitally transform their business model. Our primary focus is on the Dutch market and more specifically on the domains of Public Transport, Healthcare, Finance, and Industry. Conclusion. Business Done Differently