Check if a variable is undef in puppet template - erb

What is the proper way to check if a variable is undef in a puppet template?
In the manifest the variable is defined as follows
$myvar = undef
How is this checked in the template?
Is saw the following two variants
<% if #myvar -%>
<% end -%>
and
<% if not #myvar.nil? and #myvar -%>
<% end -%>
They both seem to work in my case, but I wonder if the first approach fails in on certain cases?

The Puppet documentation (at the time of writing this answer) explains it very well: https://puppet.com/docs/puppet/latest/lang_template_erb.html#concept-5365
Since undef is not the same as false, just using an if is not a good way to check for it. Also when a variable is defined, but has a value of false or nil it is also impossible to check with a simple if.
This is why you want to use scope.lookupvar(‘variable’) and check its return value for :undef or :undefined (or nil) to know if it was set to undef, or never set at all.

I'd say the check depends on whether you want an alternative when the variable is not defined.
I'm using the following rules:
Required variable
Check in your puppet script whether the variable contains the expected value before even considering template rendering:
if $myvar == undef {
fail {"You really must set myvar, seriously."}
}
if ! $anothervar {
fail {"anothervar is false, undefined or empty."}
}
You can make your life easier by setting the type of parameters explicitly. This spares you type comparisons and conversions.
In your template you simply write the variables then:
<%= #myvar %>
<%= #anothervar %>
Optional variable that must be defined
If you assume the variable is defined, you can treat it as boolean.
The mapping is as follows (source):
falsey: empty string, false, undef
truthy: everything else
In Puppet >=4:
falsey: false, undef
truthy: everything else
Examples:
print 'something' if #myvar evaluates to true, otherwise 'something else'.
<% if #myvar %>something<% else %>something else<% end %>
print 'something' if #myvar evaluates to true
<% if #myvar %>something<% end %>
print #myvar if it evaluates to true, otherwise 'alternative' %>
<%= #myvar ? #myvar : 'alternative' %>
Optional variable that may be defined
If you are not sure a variable is even defined and don't want to make wrong assumptions, check it in the template.
Examples:
print 'something' followed by #myvar if #myvar is defined and not empty
<% if defined?(#myvar) && ! #myvar.empty? %>something<%= #myvar %><% end %>
print #myvar if it's defined and greater than 10
<%= #myvar if defined?(#myvar) && #myvar > 10 %>

The first one should work like a charm, it's what is being taught in the courses as well.
Number two seems... redundant.

Related

dustjs OR condition over multiple parameters

I have an object with multiple properties viz propA, propB propC and propD. I want to write a condition with OR checking more than one parameter like below.
{#if cond="'{obj.propA}'.length > 0
|| '{obj.propB}'.length > 0 || '{obj.propC}'.length> 0}
...
{/if}
Now since #if is deprecated in dust, how do i write an equivalent of this with eq or select. Or is there a new helper i can utilize for such scenarios.
I'm assuming that the props you're testing are strings.
This example requires dustjs-helpers >= 1.6.
You can use the {#any} and {#none} helpers mentioned by #rragan like this:
{#select}
{#ne key=obj.propA value="" /}
{#ne key=obj.propB value="" /}
{#ne key=obj.propC value="" /}
{#any}At least one of the above tests was true. At least one prop is not an empty string.{/any}
{#none}None of the tests above passed. All the props are empty{/none}
{/select}
select was recently extended with #any and #none that let you do multiple OR logic. Note that the .length only works because the deprecated #if uses eval. Dust tests use existence/non-existence so I think you can avoid using .length.
If you still prefer #if, see https://github.com/rragan/dust-motes/tree/master/src/helpers/control/if for an interpretive version of it that does not use eval.

How to calculate value in puppet erb file

I'm new to puppet and realy need some help with it:
I have the following value im my application my_app.pp value:
akka_application_cluster_seed_nodes => '"akka.tcp://ActorSystem#host1:2551","akka.tcp://ActorSystem#host2:2551","akka.tcp://ActorSystem#host3:2551"'
Now in my erb file min-nr-of-members value should be calculated by getting the size of akka_application_cluster_seed_nodes array divide by 2 plus 1
$min-nr-of-members = $akka_application_cluster_seed_nodes.size/2 +1
For example:
auto-down-unreachable-after = <%= get_param('akka_cluster_auto_down_unreachable_after')%>
and something like this:
<% $cluster= get_param('akka_cluster_auto_down_unreachable_after') %>
<% $minNumOfNodes = ($cluster.size / 2)+1 %>
min-nr-of-members = <% $minNumOfNodes %>
Can you please help?
'"akka.tcp://ActorSystem#host1:2551","akka.tcp://ActorSystem#host2:2551","akka.tcp://ActorSystem#host3:2551"'
is not an array in puppet.
Use split function to create an array from it :
$array_nodes = split($akka_application_cluster_seed_nodes, ',')
Next use size function from stdlib to calculate array size in puppet, and calculate desired value:
$array_size = size($array_nodes)
Then use it in your erb file:
min-nr-of-members = <%= Integer(#array_size) / 2 + 1 %>

Jade Template - SELECT OPTION with for

select(id="xxx", name="xxxyyy")
- for(var i = 1;i<10;i++){
option(value="#{i}") Some value for #{i}
- }
but it generates the following HTML
<select id="xxxx" name "xxxyyy"></select>
<option value="1">Some value for 1</option>
....
I've tried to include the select inside the for loop and it works as expected (it generates 10 select drop controls with one item on each one of them).
What am I missing here?
I think you've got your indentation messed up. Jade is like coffeescript, in that indentation is significant and donates nesting. See here. So that the Jade engine knows that your option loop should be within the select tag, the option loop needs to be indented from the select statement, whereas you've got yours level with the select statement.
select(id="xxx", name="xxxyyy")
-for(var i = 1;i<10;i++){
option(value="#{i}") Some value for #{i}
-}

puppet, set template variable based on hostname value or unique random value

I want to deploy a config file on several linux machines and need to make sure that a certain variable in that config file has a unique value for each machine. Until now I've been shipping the same config file the following way:
file {
"/etc/nxserver/node.conf":
mode => 644,
ensure => file,
source => "puppet:///modules/nxserver/nxserver-node.conf";
}
I assume that switching to templates is as easy as replacing the source with content:
file {
"/etc/nxserver/node.conf":
mode => 644,
group => root,
content => template("nxserver/nxserver-node.erb"),
}
In the nxserver-node.erb file I could set the variable of interest the following way:
# This file is managed by puppet
... random config stuff ...
somevariable = <%= hostname %>
My questions:
Is there a way to process & extract parts of <%= hostname %> inside the erb file? E.g. my hostnames could be "desktop-01234", and I would want to use the "01234" bit only. How would I achieve this?
Alternatively, could I specify a range of valid values that get uniquely but randomly assigned to the variable inside the erb file? E.g. somevariable could be a variable representing some port number, in the range of 1000 - 4000. I need to make sure that on each puppet host this variable has a different value. How would I achieve this?
EDIT: I can use
fqdn_rand(MAX, [SEED]) to generate a random number that's unique for each hostname, but according to the docs it will return a random whole number greater than or equal to 0 and less than MAX.
<% Puppet::Parser::Functions.function('fqdn_rand') -%>
<% value = scope.function_fqdn_rand(['10000']) -%>
somevariable=<%= value.to_s %>
I've tried using + as well as add to add an offset but + seems to be reserved for strings and the add function is unknown.
the following does exactly what I need:
<% Puppet::Parser::Functions.function('fqdn_rand') -%>
<% offset = "1000".to_i -%>
<% value = scope.function_fqdn_rand(['10000']).to_i + offset -%>
somevariable=<%= value.to_s %>
In ERB templates, you can do everything Ruby can. The String methods are especially helpful.
For example, to cut the prefix from your hostname, you could use
somevariable = <%= #hostname.sub(/^desktop-/, '') %>

Check if string variable is null or empty, or full of white spaces

How can I check if a string variable is null or empty, or full with space characters in Twig? (Shortest possible, maybe an equivalent to CSharp's String.IsNullOrWhiteSpace() method)
{% if your_variable is null or your_variable is empty %}
should check whether the variable is null or empty.
If you want to see if it's not null or empty just use the notoperator.
{% if foo is not null and foo is not empty %}
See the docs:
empty
null
"is" operator
logical operators like "not"
Perhaps you might be interested in tests in twig generally.
There are already good answers, but I give my 2 cents too:
{% if foo|length %}
I was inspired by #GuillermoGutiérrez's filter trick.
But I think |length is safer as the "0"|trim expression will evaluates to false.
References :
length
codepad
boolval
I'd rather use just trim and empty:
{% if foo|trim is empty %}
{% if foo|trim is not empty %}
empty evaluates to true if the foo variable is:
null
false
empty array
empty string
{% if foo|trim %} seems to be enough (assuming that foo is the variable to check). If foo is not null, trim removes whitespaces. Also, if handles empty string or null as false, and true otherwise, so no more is required.
References:
trim

Resources