erb sudoers iterate over hash of arrays - ruby

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.

Related

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

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 -%>

Make counter variable in each method increase itself

How to make a variable within .each method increasable, so the output would be 1, 2, 3...Instead of this:
<% #all_posts.each do |p, a = 0| %>
<% a += 1 %>
<%= a %>
<% end %>
Output: 1, 1, 1...
Just use each_with_index instead of each
Only caveat: the index starts with 0.
<% #all_posts.each_with_index do |p, a| %>
<%= a %>
<%= p.name %>
<% end %>
See http://ruby-doc.org/core-2.3.0/Enumerable.html#method-i-each_with_index
To make it accessible, initialize it outside of the loop.
<% a = 0 %>
<% #all_posts.each do |p| %>
<% a += 1 %>
<%= a %>
<% end %>
But a better way is:
<% #all_posts.each.with_index(1) do |p, a| %>
<%= a %>
<% end %>

How can I refactor this code for Ruby?

I created the following helper method and it is working fine but I want to improve the way it is being called.
def display_if_present(attribute, i18key, hours=nil)
unless attribute.blank?
content_tag :li do
if hours.present?
concat content_tag :h2, get_translation_by_model_attr('achievment', i18key) + ' ('+t(:hours)+'): '
else
concat content_tag :h2, get_translation_by_model_attr('achievment', i18key)+': '
end
if i18key.include? 'date'
concat content_tag :p, attribute.strftime('%m/%Y')
elsif hours.present?
concat content_tag :p, h(attribute) + "(" + t(:hours) + ")"
else
concat content_tag :p, h(attribute)
end
end
end
end
And on the view side I have the following:
<%= display_if_present(academic_achievment.institution,'institution') %>
<%= display_if_present(academic_achievment.ativs_description,'ativs_description') %>
<%= display_if_present(academic_achievment.date_start,'date_start') %>
<%= display_if_present(academic_achievment.date_finished,'date_finished') %>
<%= display_if_present(academic_achievment.load, 'load', :hours) %>
<%= display_if_present(academic_achievment.tags, 'tags') %>
which I want to do some refactor. So I tried:
<% elements = %w(institution ativs_description date_start date_finished load tags) %>
<% elements.collect! do |item| %>
<% params = (item == "load") ? "item, :hours" : "item" %>
<%= display_if_present("academic_achievment.#{item}".constantize, params.constantize) %>
<% end %>
the above block of code returned the error:
wrong constant name academic_achievment.institution
Extracted source (around line #15):
12:
13: <% elements.collect! do |item| %>
14: <% params = (item == "load") ? "item, :hours" : "item" %>
15: <%= display_if_present("academic_achievment.#{item}".constantize, params.constantize) %>
16: <% end %>
17:
18: </ul>
I appreciate some help for a better piece of code.
I recommend using the send method like this:
<% %w(institution ativs_description date_start date_finished load tags).each do |item| %>
<%= display_if_present(academic_achievment.send(item), item, item == 'load' ? :hours : nil) %>
<% end %>

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

Resources