Puppet ruby template with ip validation - skip failed values not error and stop - ruby

I have the following code in a Puppet (ruby) .erb template with a validate function on each value that is iterated over from an array. I want it to continue with next values and preferably issue a warning, or silently continue. The code below errors and stops if an entry fails validation.
For example if the array is $nameservers = ['1.1.1.1','/some.domain/2.2.2.2'] I want it to only process the 1.1.1.1 entry.
This is actually so I can use the same input array for either /etc/resolv.conf or /etc/dnsmask.conf as determined by other logic in my manifest that gets processed by one of 2 templates (no, I'm not crazy) :).
I know that validation can be done in the manifest or on top scope variables, however I have a need to do this in the template.
search <%= scope.lookupvar('dns_search') %>
<% scope.lookupvar('nameservers').each do |server| -%>
<% if scope.function_validate_ip_address([server]) -%>
nameserver <%= server -%>
<% end -%>
<% end -%>
Cheers

I managed to get it working with the following code. The only thing I can't get to add is a trailing newline after the LAST entry only. Any comments on how this block could be improved appreciated.
search <%= scope.lookupvar('dns_search') %>
<% scope.lookupvar('nameservers').each do |server| -%>
<% begin -%>
<% if scope.function_validate_ip_address([server]) %>
nameserver <%= server -%>
<% end -%>
<% rescue => e -%>
<% scope.call_function('warning',[e]) -%>
<% next %>
<% end -%>
<% end -%>

Related

erb sudoers iterate over hash of arrays

I try to generate sudoers file dynamically via puppet. Here i have hieradata like this:
---
sudoparameters:
cmnd_aliases:
CMND_ALIAS_A:
- /path/to/command_a *
- /path/to/command_b *
CMND_ALIAS_B:
- /path/to/command_c
- /path/to/command_d *
runas_aliases:
RUNAS_ALIAS_A:
- runas_user_a, runas_user_b
RUNAS_ALIAS_B:
- runas_user_a, runas_user_c
defaults:
- user!authenticate
- user!systlog
commands:
- user ALL=(root) NOPASSWD:/usr/sbin/puppetd --test agent --server=*
- user ALL=(root) NOPASSWD:/usr/bin/lsof -a *
- user ALL=(RUNAS_ALIAS_A) NOPASSWD: CMND_ALIAS_A
And an erb template like this:
<% #sudoparameters['cmnd_aliases'].each do |cmnd_alias| -%>
Cmnd_Alias <%= cmnd_alias %> <%= cmnd_alias.map { |path| path.join(', ') } %>
<% end -%>
<% #sudoparameters['runas_aliases'].each do |runas_alias| -%>
Runas_Aliases <%= runas_alias %> <%= runas_alias.map { |path| path.join(', ') } %>
<% end -%>
<% #sudoparameters['defaults'].each do |default| -%>
Defaults:<%= default %>
<% end -%>
<% #sudoparameters['commands'].each do |command| -%>
<%= command %>
<% end -%>
My approach to iterate over the cmnd_aliases and runas_aliases doesn't work. How can i accomplish it to generate a comma seperatet list für Cmnd_Aliases and Runas_Aliases if these arrays exists in sudoparameter hash?
Cheers
Christian
To solve the issue i wrapped the each blocks into:
<% if (#sudoparameters['cmnd_aliases'] != nil) then -%>
<% #sudoparameters['cmnd_aliases'].each do |cmnd_alias| -%>
Cmnd_Alias <%= cmnd_alias %> <%= cmnd_alias.map { |path| path.join(', ') } %>
<% end -%>
<% end -%>
But for now the cmnd_alias.mapdoesn't work as expected. i get the following error.
Detail: undefined method `join' for "CMND_ALIAS_A":String
To solve that i save key and value to different vars and apply the join directly to the values.
<% if (#sudoparameters['cmnd_aliases'] != nil) then -%>
<% #sudoparameters['cmnd_aliases'].each do |cmnd_alias_key, cmnd_alias_value| -%>
Cmnd_Alias <%= cmnd_alias_key %> <%= cmnd_alias_value.join(', ') %>
<% end -%>
<% end -%>
<% if (#sudoparameters['runas_aliases'] != nil) then -%>
<% #sudoparameters['runas_aliases'].each do |runas_alias_key, runas_alias_value| -%>
Runas_Aliases <%= runas_alias_key %> <%= runas_alias_value.join(', ') %>
<% end -%>
<% end -%>
<% if (#sudoparameters['defaults'] != nil) then -%>
<% #sudoparameters['defaults'].each do |default| -%>
Defaults:<%= default %>
<% end -%>
<% end -%>
<% #sudoparameters['commands'].each do |command| -%>
<%= command %>
<% end -%>
If some values in your Hash are nil, you can use this syntax :
<% (#sudoparameters['cmnd_aliases'] || {}).each do |cmnd_alias_key, cmnd_alias_values| -%>
Cmnd_Alias <%= cmnd_alias_key %> <%= cmnd_alias_values.join(', ') %>
<% end -%>
a || b syntax is helpful to avoid undefined method errors :
{:a => :b} || {}
#=> {:a => :b}
nil || {}
#=> {}
Finally, if cmnd_alias_value is an Array, it should be written as a plural : cmnd_alias_values. It's not a must, but it might make your code a bit easier to understand.

Use a variable name in an erb include call

I'm using Sinatra and I'd like to include another erb file in one of my views but using a variable for the file name.
I was thinking something like this may work but I get an error saying no such file could be found.
<%= erb :'layout/nav/#{device_type}' %>
Currently I'm using the below switch statement to achieve the desired results but the above would be cleaner and less code.
<% case device_type
when 'mobile'%>
<%= erb :'layout/nav/mobile' %>
<% when 'tablet' %>
<%= erb :'layout/nav/tablet' %>
<% else %>
<%= erb :'layout/nav/desktop' %>
<% end %>
Thanks
Giles
I think that could work, you just need to use double quotes to interpolate strings.
Try
<%= erb :"layout/nav/#{device_type}" %>

erb idiom to handle undefined variable

I'm trying to write some puppet .erb, I'd like to handle this "environment" variable if it's:
undefined
a string with newlines
an array.
I've got as far as this:
<% Array(environment).join("\n").split(%r{\n}).each do |f| %>
one line: <%= f %>
<% end %>
But haven't gotten around the undefined case yet. I've tried this
<% if (defined?(environment)).nil? %?
<% Array(environment).join("\n").split(%r{\n}).each do |f| %>
one line: <%= f %>
<% end %>
<% end %>
but am still getting "(erb):11: undefined local variable or method `environment' for main:Object (NameError)" when trying to test it like this:
ruby -rerb -e "environmentUNDEFINEME= [ 'cronvar=cronval', 'var2=val2' ];
puts ERB.new(File.read('templates/job.erb')).result"
Sorry this is so basic, but somebody's got to ask the easy questions. Any help?
I would do this:
<% if defined?(environment) %>
<% Array(environment).each do |f| %>
one line: <%= f %>
<% end %>
<% end %>
I didn't understand why you joining on new lines and then splitting on them again, so I removed it from the example.

Chef and erb templates. How to use boolean code blocks

I am new to chef, ruby, ruby DSL, and erb. I come from python.
In a ruby erb template I want to do something like this.
<% if node[:monit][:server]=='nginx' -%>
ALL OF MY NGINX TEXT
<% end -%>
<% if node[:monit][:server]=='redis' -%>
ALL OF MY REDIS TEXT
<% end -%>
Clearly I am missing something about proper syntax.
Thanks
Try this:
<% if node[:monit][:server]=='nginx' -%>
nginx_text=<%= node[:nginx][:text] %>
<% end -%>
<% if node[:monit][:server]=='redis' -%>
redis_text=<%= node[:redis][:text] %>
<% end -%>
Code wrapped in <% %> or <% -%> is a statement that is evaluated. Code wrapped in <%= %> is code that is evaluated and the result is placed into the file. Harcoded strings dont have to be wrapped in erb tags if they are constant, but Ruby code must be wrapped in erb tags if you want the result of that code to go into your file

reflection of erb variables

I'm using a puppet template, which does an erb interpretation of the template file. I'd like to know all the variables available to me, however, there are variables available (e.g., fqdn) that are not listed by any of the reflection methods I'm aware of, specifically, none of these:
<% Module.constants.each do |v| %># module constant: <%= v %>
<% end %>
<% Kernel.local_variables.each do |v| %># local variable: <%= v %>
<% end %>
<% Kernel.instance_variables.each do |v| %># instance variable: <%= v %>
<% end %>
<% Module.class_variables.each do |v| %># class variable: <%= v %>
<% end %>
<% Kernel.global_variables.each do |v| %># global variable: <%= v %>
<% end %>
Is there an extra reflection method for erb that will reveal these to me?
The "See all client variables" should do what you want
Don't really know about puppet templates, but if fqdn is a local variable, then calling local_variables (as in self.local_variables) should display it.
I don't know as much as I'd like to about erb's built-in means of reflection, but with Puppet, I think the hash returned by scope.to_hash is probably what you want. From the templating reference:
<% scope.to_hash.keys.each do |k| -%>
<%= k %>
<% end -%>
Alternately, if you just want a one-time look at the variables the agent node supplies, you can run facter on the node; that's how Puppet gets all that info in the first place.

Resources