Habitat Templating Capability for Simple Math

I am in process of converting a cookbook template over to a habitat template and have the need to generate some of the content based upon some simple math counters. We start with a static number of lines that are numbered, for example with 1-9 and then the user can provide an array of additional items that need to generated the next X number of entries.

Here is a snippet of the chef cookbook template (erb) where we do this:

security.provider.1=com.ibm.jsse2.IBMJSSEProvider2
security.provider.2=com.ibm.crypto.provider.IBMJCE
security.provider.3=com.ibm.security.jgss.IBMJGSSProvider
security.provider.4=com.ibm.security.cert.IBMCertPath
security.provider.5=com.ibm.security.sasl.IBMSASL
security.provider.6=com.ibm.xml.crypto.IBMXMLCryptoProvider
security.provider.7=com.ibm.xml.enc.IBMXMLEncProvider
security.provider.8=com.ibm.security.jgss.mech.spnego.IBMSPNEGO
security.provider.9=sun.security.provider.Sun
<% counter = 9 %>
<% idx = -1 %>
<% if @config['serverConfiguration']['additionalSecurityProviders'] %>
  <% @config['serverConfiguration']['additionalSecurityProviders'].each do |provider| %>
    <% counter += 1 %>
security.provider.<%= counter %>=<%= provider %>
  <% end %>
<% end %>
<% if @config['hardwareCrypto'] %>
  <% @config['hardwareCrypto'].each do |token_label| %>
    <% counter += 1 %>
    <% idx += 1 %>
security.provider.<%= counter %>=com.ibm.crypto.pkcs11impl.provider.IBMPKCS11Impl <%= "#{@dirs['security']}/pkcs11.#{idx}.cfg" %>
  <% end %>
<% end %>

securerandom.source=file:/dev/urandom

And this is my conversion to a habitat template, but not sure how we can do the math to increment the counters (counter & idx which are ruby variables).

security.provider.1=com.ibm.jsse2.IBMJSSEProvider2
security.provider.2=com.ibm.crypto.provider.IBMJCE
security.provider.3=com.ibm.security.jgss.IBMJGSSProvider
security.provider.4=com.ibm.security.cert.IBMCertPath
security.provider.5=com.ibm.security.sasl.IBMSASL
security.provider.6=com.ibm.xml.crypto.IBMXMLCryptoProvider
security.provider.7=com.ibm.xml.enc.IBMXMLEncProvider
security.provider.8=com.ibm.security.jgss.mech.spnego.IBMSPNEGO
security.provider.9=sun.security.provider.Sun

#TODO convert from erb to habitat
<% counter = 9 %>
<% idx = -1 %>
{{#if cfg.server.additionalSecurityProviders ~}}
  {{#each cfg.server.additionalSecurityProviders as |provider| ~}}
    <% counter += 1 %>
security.provider.<%= counter %>={{provider}}
  {{/each ~}}
{{/if ~}}
{{#if cfg.server.hardwareCrypto ~}}
  {{#each cfg.server.hardwareCrypto as |token_label| ~}}
    <% counter += 1 %>
    <% idx += 1 %>
security.provider.<%= counter %>=com.ibm.crypto.pkcs11impl.provider.IBMPKCS11Impl {{pkg.svc_var_path}}/security/pkcs11.#{idx}.cfg
  {{/each ~}}
{{/if ~}}

securerandom.source=file:/dev/urandom

login.configuration.provider=com.ibm.security.auth.login.ConfigFile

Habitat uses handlebars templating, which are very basic. Advanced stuff can only be done with helpers, of which a few come with handlebars and a few are added by Habitat: https://www.habitat.sh/docs/reference/#helpers

It is unfortunately not possible to supply additional helpers at the plan level

There are two proposed paths toward allowing some dynamism in config that I'm tracking personally:

Yes I’ve also seen the helpers as well and they are a bit limiting. I’m curious to know as well if there is any way we can write our own custom helpers? There isn’t any documentation on this that i could find.

1 Like

Hi @krusolu ,
These first couple questions may not be relevant for your use case (I don’t know your full use case and I’m not terribly familiar with the Java ecosystem) but I thought it might be useful to go through some of the things I think about when moving from cookbooks to Habitat.

Do those values need to be configurable at run-time? Another way of asking that would be: does the application need to run with different security.providers based on the environment it’s deployed to? ex: dev runs with the defaults but prod has hardwareCrypto modules? Would an operator ever change them as part of troubleshooting/maintenence/etc? If the answer is no, the values are static across deployments, you could think about doing those calculations as part of the build. This would give you access to additional tooling (by adding to your pkg_build_deps), and give you additional confidence that the application will always be deployed with the security providers expected.

If the values do need to change at run-time, handlebars provides an @index within its #each across arrays. Ex:

{{#each cfg.providers}}
security.provider.{{@index}}={{this}}
{{/each}}

This would require all providers to be within the same config array though, which would require the source of the configuration to know more about system (must always have first 10 items, hardwareCrypto full names, …) which is not ideal.

Another option, which I believe Automate uses a variant of, is to use the toJson helper to render the necessary config as json. Then, in your hooks, you could use tooling of your choice to render it to a static file location such as $svc_var_path. This could be anything from sed token replacement to full ERB, if you wanted to add Ruby as a run-time dependency. I’d recommend keeping it to shell tooling to keep the footprint of the app small, but it could be a messy implementation and the trade off of carrying around a dependency on Language is worth it.

@smacfarlane

We are defining these templates in our java packages (we actually have multiple depending upon java vendor and version) that will be used by many application packages as a dependency.

There is a static number of security providers for each vendor/version of java and the application can define more. We need to keep it simple for our application users, where they just need to specify the java vendor/version and their additional security providers in their toml.

Another complicating factor, is that we are trying very hard to maintain the same structure in the toml config that they do today with their chef-infra attributes. We are validating their toml with the same schema, so we need to maintain the same if all possible. This will make application migration easier and supporting both the cookbook and hab package easier on the maintainers.

We have looked into using ruby for processing the .erb and believe that may be our only option at this point. Luckily since we are installing habitat using a cookbook, we already have ruby installed so there wouldn’t need to be a dependency within the package. If you have any links to good examples, it would be nice to have.

Extending the templating helpers would be a much better and slicker option. I’m sure we will stumble upon another gap beyond this use case.