Can ERB templates be used to process hashes of arbitrary depth?

107 views
Skip to first unread message

Andreas Ntaflos

unread,
Apr 7, 2014, 7:58:40 PM4/7/14
to puppet...@googlegroups.com
Hi list,

I am in the process of writing a module to manage strongSwan, an IKE
keying daemon for IPsec VPNs on Linux [1]. The strongSwan daemon's
(charon) configuration file is basically formatted like a hash, with
sections containing key-value pairs that may themselves contain further
sections [2]. Sections may also be empty. I don't think there is a
maximum depth defined.

It seems to me that this kind of configuration file format is easily
modelled as a Puppet/Ruby hash, so now I am wondering how I can go about
rendering the configuration file from such a hash.

Is it feasibly to use an ERB template for this? You may notice that I am
(still) not much of a Ruby guy and I don't want to reinvent any wheels,
so I am asking: what are my options?

Can ERB templates in Puppet be used to render hashes of arbitrary depth?
Or should I look to something else? Can this even be done?

I'd appreciate any and all pointers and ideas.

Here is an example hash that I would supply as a parameter to my
strongswan class:

charon_config => {
ikesa_limit => '0',
install_virtual_ip => 'yes',
keep_alive => '20s',
host_resolver => {
max_threads => '3'
},
processor => {
priority_threads => {
high => '1',
medium => '4'
}
},
tls => {},
x509 => {}
}

This should result in the following configuration file content (sorting
the keys is only of secondary concern):

charon {
ikesa_limit = 0
install_virtual_ip = yes
keep_alive = 20s
host_resolver {
max_threads = 3
}
processor {
priority_threads {
high = 1
medium = 4
}
}
tls {
}
x509 {
}
}

Thanks in advance,

Andreas

[1] http://www.strongswan.org/
[2] http://wiki.strongswan.org/projects/strongswan/wiki/StrongswanConf

signature.asc

jcbollinger

unread,
Apr 8, 2014, 10:31:33 AM4/8/14
to puppet...@googlegroups.com


On Monday, April 7, 2014 6:58:40 PM UTC-5, Andreas Ntaflos wrote:
Hi list,

I am in the process of writing a module to manage strongSwan, an IKE
keying daemon for IPsec VPNs on Linux [1]. The strongSwan daemon's
(charon) configuration file is basically formatted like a hash, with
sections containing key-value pairs that may themselves contain further
sections [2]. Sections may also be empty. I don't think there is a
maximum depth defined.

It seems to me that this kind of configuration file format is easily
modelled as a Puppet/Ruby hash, so now I am wondering how I can go about
rendering the configuration file from such a hash.

Is it feasibly to use an ERB template for this? You may notice that I am
(still) not much of a Ruby guy and I don't want to reinvent any wheels,
so I am asking: what are my options?



ERB is just a convenient way of writing a Ruby program to output data containing (usually) substantial portions of static text mixed with some (or many) dynamic bits.  Pretty much anything you can do with Ruby, you can do with Ruby in ERB.

 
Can ERB templates in Puppet be used to render hashes of arbitrary depth?


Ruby has no built-in limit on hash depth.  Whether you can use ERB to render your particular hashes is more likely to depend on how clever you are than on the limits of the tool.  It is, however, an interesting problem.

 
Or should I look to something else? Can this even be done?



Of course it can be done.  You can plug arbitrary software into Puppet via a variety of techniques, so any computation you want to perform, you can perform in Puppet.

 
I'd appreciate any and all pointers and ideas.

Here is an example hash that I would supply as a parameter to my
strongswan class:

charon_config => {
  ikesa_limit        => '0',
  install_virtual_ip => 'yes',
  keep_alive         => '20s',
  host_resolver => {
    max_threads => '3'
  },
  processor => {
    priority_threads => {
      high   => '1',
      medium => '4'
    }
  },
  tls  => {},
  x509 => {}
}

This should result in the following configuration file content (sorting
the keys is only of secondary concern):


Actually, no, sorting the keys is a primary concern because otherwise the computed content may not be stable, which could cause Puppet to needlessly update the file.

 

charon {
  ikesa_limit = 0
  install_virtual_ip = yes
  keep_alive = 20s
  host_resolver {
    max_threads = 3
  }
  processor {
    priority_threads {
      high = 1
      medium = 4
    }
  }
  tls {
  }
  x509 {
  }
}



So, here's an attempt at an ERB template to produce that output from the given input:

strongswan.conf.erb:
-----------------------------
<%
  default_handler = Proc.new do |v|
%> = <%= v.to_s %>
<%
  end

  hash_handler = Proc,new do |v|
%> {
<%
    v.sort_by { |k,v| k }.each do |pair|
%><%= pair[0] %><%
      (v.is_a? Hash ? hash_handler : default_handler).call(v)
    end
%>
}
<%
  end
-%>
charon <%
  hash_handler.call(@charon_config)
%>
----

I've never tried using procs inside an ERB before, but I think it will work, and I haven't come up with another means to handle recursion inside a template.  Do note, however, that the template is almost all Ruby.  That's a good sign that you would be better off writing it as a custom function (http://docs.puppetlabs.com/guides/custom_functions.html) to generate the output.

Note, too, that the above ERB, if it works, leaves indentation as an exercise for the reader.  :-)


John

Andreas Ntaflos

unread,
Apr 8, 2014, 11:11:48 AM4/8/14
to puppet...@googlegroups.com
John,

thank you very much for the most informative reply.

On 2014-04-08 16:31, jcbollinger wrote:
> Actually, no, sorting the keys is a primary concern because otherwise
> the computed content may not be stable, which could cause Puppet to
> needlessly update the file.

By that statement I meant that I am aware that we should sort hash keys
before using them, and I usually know how to do it :)

> So, here's an attempt at an ERB template to produce that output from the
> given input:
>
> strongswan.conf.erb:
> -----------------------------
> <%
> default_handler = Proc.new do |v|
> %> = <%= v.to_s %>
> <%
> end
>
> hash_handler = Proc,new do |v|
> %> {
> <%
> v.sort_by { |k,v| k }.each do |pair|
> %><%= pair[0] %><%
> (v.is_a? Hash ? hash_handler : default_handler).call(v)
> end
> %>
> }
> <%
> end
> -%>
> charon <%
> hash_handler.call(@charon_config)
> %>
> ----

Thanks very much for this, I'll get to work on it as soon as we dealt
with the ramifications of CVE-2014-0160.

> I've never tried using procs inside an ERB before, but I think it will
> work, and I haven't come up with another means to handle recursion
> inside a template. Do note, however, that the template is almost all
> Ruby. That's a good sign that you would be better off writing it as a
> custom function
> (http://docs.puppetlabs.com/guides/custom_functions.html) to generate
> the output.

A colleague suggested the same, sounds like a good approach.

> Note, too, that the above ERB, if it works, leaves indentation as an
> exercise for the reader. :-)

I think we can manage that :)

Thanks again!

Andreas

signature.asc
Reply all
Reply to author
Forward
0 new messages