Chef and erb templates. How to use boolean code blocks
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
Chef template - Conditionally inserting a block of text
assuming you have an attribute
node[:test][:bool] = true
in the template would have to do
<% if node[:apache][:bool] -%>
ServerAlias <% = node[:apache][:aliasl]%>
<% end -%>
another option is to check if the attribute is null
<% unless node [:icinga][:core][:server_alias].nil? %>
ServerAlias <% = node[:icinga][:core][:server_alias]%>
<% end%>
Write value for template using erb+Chef if value is set
I'm not sure what's wrong with your approach, but if you want a one-liner, this should work:
<%= "c.NotebookApp.certfile = #{node[:ipynb][:NotebookApp][:certfile]}" if node[:ipynb][:NotebookApp][:certfile] %>
For boolean expressions in Ruby, nil
and false
values translate as false
, everything else is true
. If you have any value in node[:ipynb][:NotebookApp][:certfile]
, it will evaluate to true and print the string. Otherwise, it will print nothing.
erb file with chef syntax
Okay, so let's rewind a bit. The first thing is that you generally don't want to reference node attributes directly in templates. In some cases like attributes coming from Ohai it can be okay as a shorthand, but for important data I would also pass it in via the variables
property like this:
template '/etc/whatever.conf' do
source 'whatever.conf.erb'
variables a: node['a']
end
With that in place we've now expose the data as a template variable. The second piece of improving this is to let Ruby do the heavy lifting of generating YAML. We can do this using the .to_yaml
method in the template:
<%= @a.to_yaml %>
That should be all you need!
Chef and ruby templates - how to loop though key value pairs?
There are some slight inconsistencies in your setup. In your data bag, you assign IP addresses a name (by using a hash in your JSON). However, you don't seem to use the name in your generated template at all. This has some implications which you should be aware of:
When using associative arrays (called hashes in Ruby or objects in Javascript), the order of the elements is generally not preserved and can significantly change when adding additional elements. While some effort is done on Ruby 1.9 to preserve the insertion order when looping over the hash, you shouldn't generally rely on that. This leads to two possible alternatives to improving your data bag. Which one to choose depends on your actual use case:
- Use an array instead of the hash. In an array, the order is guaranteed to be kept. If you don't use the name anyway (i.e. the key in your original hash), you can simply use a hash and be safe here. When going this road, we can loop over the array in the template and generate the count from that.
- If the order doesn't matter, you should use the key in the hash to name the server as generated into your template. Right now, you use
server<Number>
in your data bag butserver.<Number>
in your template. That way, we can use the key to name your servers and possibly override the generated names.
Using an Array
When using the array in your data bag, i.e. when you have something like this:
"zookeeper": [
"111.111.111.111",
"222.222.222.222"
],
you can loop over the array like this in your template:
<% @zookeeper.each_with_index do |ipaddress, index| %>
<%= "server.#{index}=#{ipaddress}:2888:3888" %>
<% end %>
This used the ERB template language to create your file. It used the each_with_index method to iterate over each element in the array.
Using a Hash
When using the hash variant instead, assuming you have changed the keys in your data bag to match the syntax in your final generated file, you can loop over the hash like this:
<% @zookeeper.each_pair do |name, ipaddress| %>
<%= "#{name}=#{ipaddress}:2888:3888" %>
<% end %>
This uses the each_pair method of the Hash to loop over each key-value pair and thus generates a line of output for each of these pairs.
Passing data to the template
As a final remark, your syntax to pass data to the template in your recipe is odd. At first, you should never use names that start with an uppercase letter for variables (like your ZOOKEEPER
variable). In Ruby, these identify constants (like value constants, classes, modules, ...). Use a lowercase name instead. Ruby uses snake_case for variable names by convention.
When passing the value to your template, you can then just pass the variable:
db = data_bag_item("mydb", "rtb")
zookeeper = db['zookeeper']
template "/etc/zookeeper/conf/zoo.cfg" do
path "/etc/zookeeper/conf/"
source "zoo.cfg.erb"
owner "root"
group "root"
mode "0644"
variables :zookeeper => zookeeper
end
Related Topics
Does Rails Come with a "Not Authorized" Exception
How to Sort a Ruby Hash Alphabetically by Keys
Provide Value for Self When Using Proc#Call
Rspec Any_Instance Deprecation: How to Fix It
How to Test Instance Variable Was Instantiated in Controller with Rspec
A Selenium Webdriver Exception
What Rails Plugins Are Good, Stable and *Really* Enhance Your Code
Safe Navigation Equivalent to Rails Try for Hashes
Ruby Gems in Stand-Alone Ruby Scripts
How to Integrate Rubocop with Rake
Ruby Find and Return Objects in an Array Based on an Attribute
Cheat Sheet for All Design Patterns Implemented in Ruby
Gem Install Mongrel Fails with Ruby 1.9.1