Isaac Isaac - 1 year ago 50
Ruby Question

loop over hash in defined order in ruby template

I have a ruby template (.erb) that I want to iterate over a hash. It should produce the same output file each time the puppet agent runs.

What I currently have is the following. This is my template (part of rsyslog config if anyone is wondering):

<% log_files.each do |log_file, tag| -%>
# <%= log_file %>
$InputFileName <%= log_file %>
$InputFileTag <%= tag %>:

This template outputs a hash that looks like this:

log_files => {
'/root/apache_auth.local' => 'httpd',
'/root/install.log' => 'hugo',

(Not real logfiles). This works and produces the config file I want. The problem with this is that each time I call the puppet agent, the order of the log files in the hash is changed, so the config file gets rewritten, and subsequently the daemon gets restarted. The functionality stays the same, but I would rather not have the config file rewritten and rsyslog restarted each time the puppet agent runs.

Now I assume that this is unsolvable with hashes, as their nature is that they do not have a defined order. What other options do I have to achieve what I want?

Answer Source

Since the scriptlets inside an ERB template consist of Ruby code, this is essentially a Ruby question. The possible answers therefore depend on which version of Ruby is running underneath your Puppet catalog compiler. However, although there may be better alternatives in newer Ruby, this variation on your code will work in any Ruby supported by Puppet since Puppet 2.7:

<% @log_files.sort.each do |log_file, tag| -%>
# <%= log_file %>
$InputFileName <%= log_file %>
$InputFileTag <%= tag %>:
<% end -%>

The key here (no pun intended) is the sort.