
Conditional Statements
Prerequisites
- Welcome Quest
- Power of Puppet Quest
- Resources Quest
- Manifest Quest
- Classes Quest
- Variables Quest
Quest Objectives
- Learn how to use conditional logic to make your manifests adaptable.
- Understand the syntax and function of the
if
,unless
,case
, andselector
statements.
Getting Started
Conditional statements allow you to write Puppet code that will return different values or execute different blocks of code depending on conditions you specify. This, in conjunction with Facter facts, will enable you to write Puppet code that accomodates different platforms, operating systems, and functional requirements.
To start this quest enter the following command:
quest --start conditionals
Conditions
Just dropped in (to see what condition my condition was in)
-Mickey Newbury
Conditional statements let your Puppet code behave differently in different situations. They are most helpful when combined with facts or with data pertaining to the systems. This enables you to write code to perform as desired on a variety of operating systems and under differing system conditions. Pretty neat, don't you think?
Puppet supports a few different ways of implementing conditional logic:
-
if
statements -
unless
statements - case statements, and
- selectors
The 'if' Statement
Puppet’s if
statements behave much like those in many other programming and scripting languages.
An if
statement includes a condition followed by a block of Puppet code that will only be executed if that condition evaluates as true. Optionally, an if
statement can also include any number of elsif
clauses and an else
clause. Here are some rules:
- If the
if
condition fails, Puppet moves on to theelsif
condition (if one exists) - If both the
if
andelsif
conditions fail, Puppet will execute the code in theelse
clause (if one exists) - If all the conditions fail, and there is no
else
block, Puppet will do nothing and move on
The following is an example of an if
statement you might use to raise a warning when a class is included on an unsupported system:
if $::is_virtual == 'true' {
# Our NTP module is not supported on virtual machines:
warning( 'Tried to include class ntp on virtual machine.' )
}
elsif $::operatingsystem == 'Darwin' {
# Our NTP module is not supported on Darwin:
warning( 'This NTP module does not yet work on Darwin.' )
}
else {
# Normal node, include the class.
include ntp
}
In addition to the ==
operator, which tests for equality, there is also a regular expression match operator =~
. The ==
operator is not case sensitive. In the above example, if you had:
if $::is_virtual == 'TRUE' {
# Our NTP module is not supported on virtual machines:
warning( 'Tried to include class ntp on virtual machine.' )
}
elsif $::operatingsystem == 'darwin' {
# Our NTP module is not supported on Darwin:
warning( 'This NTP module does not yet work on Darwin.' )
}
else {
# Normal node, include the class.
include ntp
}
... the behavior would remain unchanged.
The Warning Function
The warning()
function will not affect the execution of the rest of the manifest, but if you were running Puppet in the usual Master-Agent setup, it would log a message on the server at the 'warn' level.
The regular expression operator =~
helps you test whether a string matches a pattern you specify. For example, in the following, we capture the digits that follow www
in the hostname, such as www01
or www12
and store them in the $1
variable for use in the notice()
function.
if $::hostname =~ /^www(\d+)\./ {
notice("Welcome to web server number $1")
}
Task 1 :
Just as we have done in the Variables Quest, let's create a manifest and add a simple conditional statement. The file should report on how long the VM has been up and running.
nano ~/conditionals.pp
Enter the following code into your conditionals.pp
manifest:
if $::uptime_hours < 2 {
$myuptime = "Uptime is less than two hours.\n"
}
elsif $::uptime_hours < 5 {
$myuptime = "Uptime is less than five hours.\n"
}
else {
$myuptime = "Uptime is greater than four hours.\n"
}
file {'/root/conditionals.txt':
ensure => present,
content => $myuptime,
}
Use the puppet parser
tool to check your syntax, then simulate the change in --noop
mode without enforcing it. If the noop looks good, enforce the conditionals.pp
manifest using the puppet apply
tool.
Have a look at the conditionals.txt
file using the cat
command.
Task 2 :
Use the command facter uptime_hours
to check the uptime yourself. The notice you saw when you applied your manifest should describe the uptime returned from the Facter tool.
The 'unless' Statement
The unless
statement works like a reversed if
statement. An unless
statements takes a condition and a block of Puppet code. It will only execute the block if the condition is false. If the condition is true, Puppet will do nothing and move on. Note that there is no equivalent of elsif
or else
clauses for unless
statements.
The 'case' Statement
Like if
statements, case statements choose one of several blocks of Puppet code to execute. Case statements take a control expression, a list of cases, and a series of Puppet code blocks that correspond to those cases. Puppet will execute the first block of code whose case value matches the control expression.
A special default
case matches anything. It should always be included at the end of a case statement to catch anything that did not match an explicit case.
Task 3 :
Create a case.pp
manifest with the following conditional statement and file
resource declaration.
case $::operatingsystem {
'CentOS': { $apache_pkg = 'httpd' }
'Redhat': { $apache_pkg = 'httpd' }
'Debian': { $apache_pkg = 'apache2' }
'Ubuntu': { $apache_pkg = 'apache2' }
default: { fail("Unrecognized operating system for webserver") }
}
file {'/root/case.txt':
ensure => present,
content => "Apache package name: ${apache_pkg}\n"
}
When you've validated your syntax and run a --noop
, apply the manifest:
puppet apply case.pp
Use the cat
command to inspect the case.txt
file. Because the Learning VM is running CentOS, you will see that the selected Apache package name is 'httpd'.
For the sake of simplicity, we've output the result of the case statement to a file, but keep in mind that instead of using the result of the case statement like the one above to define the contents of a file, you could use it as the title of a package
resource declaration, as shown below:
package { $apache_pkg :
ensure => present,
}
This would allow you to always install and manage the right Apache package for a machine's operating system. Aaccounting for the differences between various platforms is an important part of writing flexible and re-usable Puppet code. It is a paradigm you will encounter frequently in published Puppet modules.
Also note that Puppet will choose the appropriate provider for the package depending on the operating system, without you having to mention it. On Debian-based systems, for example, it may use apt
and on RedHat systems, it will use yum
.
The 'selector' Statement
Selector statements are very similar to case
statements, but instead of executing a block of code, a selector assigns a value directly. A selector might look something like this:
$rootgroup = $::osfamily ? {
'Solaris' => 'wheel',
'Darwin' => 'wheel',
'FreeBSD' => 'wheel',
'default' => 'root',
}
Here, the value of the $rootgroup
is determined based on the control variable $osfamily
. Following the control variable is a ?
(question mark) symbol. In the block surrounded by curly braces are a series of possible values for the $::osfamily fact, followed by the value that the selector should return if the value matches the control variable.
Because a selector can only return a value and cannot execute a function like fail()
or warning()
, it is up to you to make sure your code handles unexpected conditions gracefully. You wouldn't want Puppet to forge ahead with an inappropriate default value and encounter errors down the line.
Task 4 :
By writing a Puppet manifest that uses a selector, create a file /root/architecture.txt
that lists whether the VM is a 64-bit or a 32-bit machine.
To accomplish this, create a file in the root directory, called architecture.pp
:
nano architecture.pp
We know that i386 machines have a 32-bit architecture, and x86_64 machines have a 64-bit architecture. Let's set the content of the file based on this fact:
file { '/root/architecture.txt' :
ensure => file,
content => $::architecture ? {
'i386' => "This machine has a 32-bit architecture.\n",
'x86_64' => "This machine has a 64-bit architecture.\n",
}
}
See what we did here? Instead of having the selector return a value and saving it in a variable, as we did in the previous example with $rootgroup
, we use it to specify the value of the content
attribute in-line.
Once you have created the manifest, check the syntax and apply it.
Inspect the contents of the /root/architecture.txt
file to ensure that the content is what you expect.
Before you move on
We have discussed some intense information in the Variables Quest and this Quest. The information contained in all the quests to this point has guided you towards creating flexible manifests. Should you not understand any of the topics previously discussed, we highly encourage you to revisit those quests before moving on to the Resource Ordering Quest.