About Templates
A cookbook template is an Embedded Ruby (ERB) template that is used to
dynamically generate static text files. Templates may contain Ruby
expressions and statements, and are a great way to manage configuration
files. Use the template resource to add cookbook templates to
recipes; place the corresponding Embedded Ruby (ERB) template file in a
cookbook’s /templates
directory.
Note
Requirements
To use a template, two things must happen:
- A template resource must be added to a recipe
- An Embedded Ruby (ERB) template must be added to a cookbook
For example, the following template file and template resource settings
can be used to manage a configuration file named /etc/sudoers
. Within
a cookbook that uses sudo, the following resource could be added to
/recipes/default.rb
:
template '/etc/sudoers' do
source 'sudoers.erb'
mode '0440'
owner 'root'
group 'root'
variables(sudoers_groups: node['authorization']['sudo']['groups'],
sudoers_users: node['authorization']['sudo']['users'])
end
And then create a template called sudoers.erb
and save it to
templates/default/sudoers.erb
:
#
# /etc/sudoers
#
# Generated by Chef for <%= node['fqdn'] %>
#
Defaults !lecture,tty_tickets,!fqdn
# User privilege specification
root ALL=(ALL) ALL
<% @sudoers_users.each do |user| -%>
<%= user %> ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL
<% end -%>
# Members of the sysadmin group may gain root privileges
%sysadmin ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL
<% @sudoers_groups.each do |group| -%>
# Members of the group '<%= group %>' may gain root privileges
<%= group %> ALL=(ALL) <%= "NOPASSWD:" if @passwordless %>ALL
<% end -%>
And then set the default attributes in attributes/default.rb
:
default['authorization']['sudo']['groups'] = %w(sysadmin wheel admin)
default['authorization']['sudo']['users'] = %w(jerry greg)
Variables
An Embedded Ruby (ERB) template allows Ruby code to be embedded inside a
text file within specially formatted tags. Ruby code can be embedded
using expressions and statements. An expression is delimited by <%=
and %>
. For example:
<%= "my name is #{$ruby}" %>
A statement is delimited by a modifier, such as if
, elsif
, and
else
. For example:
if false
# this won't happen
elsif nil
# this won't either
end
Using a Ruby expression is the most common approach for defining
template variables because this is how all variables that are sent to a
template are referenced. Whenever a template needs to use an each
,
if
, or end
, use a Ruby statement.
When a template is rendered, Ruby expressions and statements are
evaluated by Chef Infra Client. The variables listed in the template
resource’s variables
parameter and in the node object are evaluated.
Chef Infra Client then passes these variables to the template, where
they will be accessible as instance variables within the template. The
node object can be accessed just as if it were part of a recipe, using
the same syntax.
For example, a simple template resource like this:
node['fqdn'] = 'latte'
template '/tmp/foo' do
source 'foo.erb'
variables(x_men: 'are keen')
end
And a simple Embedded Ruby (ERB) template like this:
The node <%= node[:fqdn] %> thinks the x-men <%= @x_men %>
Would render something like:
The node latte thinks the x-men are keen
Even though this is a very simple example, the full capabilities of Ruby can be used to tackle even the most complex and demanding template requirements.
File Specificity
A cookbook is frequently designed to work across many platforms and is often required to distribute a specific template to a specific platform. A cookbook can be designed to support the distribution of templates across platforms, while ensuring that the correct template ends up on each system.
The pattern for template specificity depends on two things: the lookup path and the source. The first pattern that matches is used:
/host-$fqdn/$source
/$platform-$platform_version/$source
/$platform/$source
/default/$source
/$source
Note
To specify a particular Windows version, use the operating system
version
number.
For example, a template in templates/windows-6.3
will be deployed on
systems installed with Windows 8.1.
Use an array with the source
property to define an explicit lookup
path. For example:
template '/test' do
source ["#{node.chef_environment}.erb", 'default.erb']
end
The following example emulates the entire file specificity pattern by defining it as an explicit path:
template '/test' do
source %W(
host-#{node['fqdn']}/test.erb
#{node['platform']}-#{node['platform_version']}/test.erb
#{node['platform']}/test.erb
default/test.erb
)
end
A cookbook may have a /templates
directory structure like this:
/templates/
windows-10
windows-6.3
windows
default
and a resource that looks something like the following:
template 'C:\path\to\file\text_file.txt' do
source 'text_file.txt'
mode '0755'
owner 'root'
group 'root'
end
This resource would be matched in the same order as the /templates
directory structure. For a node named host-node-desktop
that is
running Windows 8.1, the second item would be the matching item and the
location:
/templates
windows-10/text_file.txt
windows-6.3/text_file.txt
windows/text_file.txt
default/text_file.txt
Host Notation
The naming of folders within cookbook directories must literally match
the host notation used for template specificity matching. For example,
if a host is named foo.example.com
, then the folder must be named
host-foo.example.com
.
Transfer Frequency
The Chef Infra Client caches a template when it is first requested. On each subsequent request for that template, the Chef Infra Client compares that request to the template located on the Chef Infra Server. If the templates are the same, no transfer occurs.
Partial Templates
A template can be built in a way that allows it to contain references to one (or more) smaller template files. (These smaller template files are also referred to as partials.) A partial can be referenced from a template file in one of the following ways:
- By using the
render
method in the template file - By using the template resource and the
variables
property.
variables Attribute
The variables
property of the template resource can be used to
reference a partial template file by using a Hash. For example:
template '/file/name.txt' do
variables partials: {
'partial_name_1.txt.erb' => 'message',
'partial_name_2.txt.erb' => 'message',
'partial_name_3.txt.erb' => 'message',
}
end
where each of the partial template files can then be combined using normal Ruby template patterns within a template file, such as:
<% @partials.each do |partial, message| %>
Here is <%= partial %>
<%= render partial, :variables => {:message => message} %>
<% end %>
render Method
Use the render
method in a template to reference a partial template
file:
<%= render 'partial_name.txt.erb', :option => {} %>
where partial_name
is the name of the partial template file and
:option
is one (or more) of the following:
Option | Description |
---|---|
:cookbook | By default, a partial template file is assumed to be located in the cookbook that contains the top-level template. Use this option to specify the path to a different cookbook |
:local | Indicates that the name of the partial template file should be interpreted as a path to a file in the local file system or looked up in a cookbook using the normal rules for template files. Set to true to interpret as a path to a file in the local file system and to false to use the normal rules for template files |
:source | By default, a partial template file is identified by its file name. Use this option to specify a different name or a local path to use (instead of the name of the partial template file) |
:variables | A hash of variable_name => value that will be made available to the partial template file. When this option is used, any variables that are defined in the top-level template that are required by the partial template file must have them defined explicitly using this option |
For example:
<%= render 'simple.txt.erb', :variables => {:user => Etc.getlogin }, :local => true %>