{"id":2999,"date":"2013-12-08T20:42:09","date_gmt":"2013-12-08T19:42:09","guid":{"rendered":"http:\/\/www.devco.net\/?p=2999"},"modified":"2013-12-09T13:36:07","modified_gmt":"2013-12-09T12:36:07","slug":"better-puppet-modules-using-hiera-data","status":"publish","type":"post","link":"https:\/\/www.devco.net\/archives\/2013\/12\/08\/better-puppet-modules-using-hiera-data.php","title":{"rendered":"Better Puppet Modules Using Hiera Data"},"content":{"rendered":"

When writing Puppet Modules there tend to be a ton of configuration data – generally things like different paths for different operating systems. Today the general pattern to manage this data is a class module::param<\/em> with a bunch of logic in it.<\/p>\n

Here’s a simplistic example below – for an example of the full horror of this pattern see the puppetlabs-ntp module<\/a>.<\/p>\n

<\/p>\n

\r\n# ntp\/manifests\/init.pp\r\nclass ntp (\r\n     $config = $ntp::params::config,\r\n     $keys_file = $ntp::params::keys_file\r\n   ) inherits ntp::params {\r\n\r\n   file{$config:\r\n      ....\r\n   }\r\n}\r\n<\/pre>\n

<\/code><\/p>\n

<\/p>\n

\r\n# ntp\/manifests\/params.pp\r\nclass ntp::params {\r\n   case $::osfamily {\r\n      'AIX': {\r\n         $config = \"\/etc\/ntp.conf\"\r\n         $keys_file = '\/etc\/ntp.keys'\r\n      }\r\n\r\n      'Debian': {\r\n         $config = \"\/etc\/ntp.conf\"\r\n         $keys_file = '\/etc\/ntp\/keys'\r\n      }\r\n\r\n      'RedHat': {\r\n         $config = \"\/etc\/ntp.conf\"\r\n         $keys_file = '\/etc\/ntp\/keys'\r\n      }\r\n\r\n      default: {\r\n         fail(\"The ${module_name} module is not supported on an ${::osfamily} based system.\")\r\n      }\r\n   }\r\n}\r\n<\/pre>\n

<\/code><\/p>\n

This is the exact reason Hiera exists – to remove this kind of spaghetti code and move it into data, instinctively now whenever anyone see code like this they think they should refactor this and move the data into Hiera.<\/p>\n

But there’s a problem. This works for your own modules in your own repos, you’d just use the Puppet 3 automatic parameter bindings and override the values in the ntp<\/em> class – not ideal, but many people do it. If however you wanted to write a module for the Forge though there’s a hitch because the module author has no idea what kind of hierarchy exist where the module is used. If the site even used Hiera and today the module author can’t ship data with his module. So the only sensible thing to do is to embed a bunch of data in your code – the exact thing Hiera is supposed to avoid.<\/p>\n

I proposed a solution to this problem<\/a> that would allow module authors to embed data in their modules as well as control the Hierarchy that would be used when accessing this data. Unfortunately a year on we’re still nowhere and the community – and the forge – is suffering as a result.<\/p>\n

The proposed solution would be a always-on Hiera backend that as a last resort would look for data inside the module. Critically the module author controls the hierarchy when it gets to the point of accessing data in the module. Consider the ntp::params<\/em> class above, it is a code version of a Hiera Hierarchy keyed on the $::osfamily<\/em> fact. But if we just allowed the module to supply data inside the module then the module author has to just hope that everyone has this tier in their hierarchy – not realistic. My proposal then adds a module specific Hierarchy and data that gets consulted after the site Hierarchy.<\/p>\n

So lets look at how to rework this module around this proposed solution:<\/p>\n

<\/p>\n

\r\n# ntp\/manifests\/init.pp\r\nclass ntp ($config, $keysfile)  {\r\n   validate_absolute_path($config)\r\n   validate_absolute_path($keysfile)\r\n\r\n   file{$config:\r\n      ....\r\n   }\r\n}\r\n<\/pre>\n

<\/code><\/p>\n

Next you configure Hiera to consult a hierarchy on the $::osfamily<\/em> fact, note the new data<\/em> directory that goes inside the module:<\/p>\n

<\/p>\n

\r\n# ntp\/data\/hiera.yaml\r\n---\r\n:hierarchy:\r\n  - \"%{::osfamily}\"\r\n<\/pre>\n

<\/code><\/p>\n

And finally we create some data files, here’s just the one for RedHat:<\/p>\n

<\/p>\n

\r\n# ntp\/data\/RedHat.yaml\r\n---\r\nntp::config: \/etc\/ntp.conf\r\nntp::keys_file: \/etc\/ntp\/keys\r\n<\/pre>\n

<\/code><\/p>\n

Users of the module could add a new OS without contributing back to the module or forking the module by simply providing similar data to the site specific hierarchy leaving the downloaded module 100% untouched!<\/p>\n

This is a very simple view of what this pattern allows, time will tell what the community makes of it. There are many advantages to this over the ntp::params<\/em> pattern:<\/p>\n

This helps the contributor to a public module:<\/B><\/p>\n