The Puppet Language¶
OpenVox runs the Puppet language — the same declarative DSL described throughout this course. This chapter covers its core building blocks.
Variables¶
Variable names begin with a dollar sign ($) and can contain alphanumeric
characters plus the underscore (_). Variable names are case-sensitive.
Constants¶
Variables can only be assigned a value once in a given scope — in reality, Puppet "variables" are actually constants.
Data types¶
Data types determine the kind of data that a variable can contain. The Puppet language is strongly typed. Common data types include:
StringIntegerFloatNumericBooleanArrayHashEnumVariantOptional
Syntax¶
As class parameters, data types have the following syntax:
For example:
class test (
String $world_var = 'World',
Array[String] $packages = ['vim', 'git'],
Boolean $enjoyable_class = true,
) {}
Note
Capitalization, spacing, and quoting are all very important!
String¶
A String is a text fragment of any length.
Strings can be single- or double-quoted. Double-quoted strings allow for variable interpolation:
Regexp¶
A Regexp is a Ruby (PCRE-style) regular expression.
Pattern¶
Pattern restricts String values to only those that match the provided Regexp value.
Numbers¶
An Integer is a whole number:
A Float is a floating-point number:
The Numeric type can be either an Integer or a Float:
Boolean¶
A Boolean is a logical true or false value.
Array¶
An Array is a list of values.
Hash¶
A Hash is a key-value data map.
class test (
Hash[String, Boolean] $feature_flags = {
'logging' => true,
'metrics' => true,
'debug_mode' => false,
'beta_ui' => false,
}
) {}
Enum¶
An Enum matches one of a range of strings.
Variant¶
A Variant matches one of any listed types.
Undef¶
An Undef variable can contain only the value undef.
Optional¶
Optional is a modifier that allows undef as a value.
This is exactly equivalent to:
Sensitive¶
The Sensitive type is used for data that should not be printed in logs or
reports. Other data types can be converted to Sensitive using
Sensitive.new().
When a Sensitive variable is converted to a String, its value will always be
Sensitive [value redacted].
Other types¶
Quoting strings¶
Double quotes¶
Double quotes (") allow for strings that contain variable interpolation or
escape sequences. For example, variable interpolation:
The following escape sequences are available in double-quoted strings:
| Sequence | Result |
|---|---|
\\ |
Single backslash |
\n |
Newline |
\r |
Carriage return |
\t |
Tab |
\s |
Space |
\$ |
Literal dollar sign (to prevent interpolation) |
\uXXXX |
Unicode character number XXXX (a four-digit hex number) |
\u{XXXXXX} |
Unicode character XXXXXX (a hex number between two and six digits) |
\" |
Literal double quote |
\' |
Literal single quote |
According to the Puppet Language Style Guide, double quotes should only be used for strings that contain variable interpolation or escape sequences.
Single quotes¶
Single quotes (') denote literal strings, with minor exceptions. The only
escape sequences available in single-quoted strings are:
| Sequence | Result |
|---|---|
\\ |
Single backslash |
\' |
Literal single quote |
Facts¶
Facts are available in Puppet code via the $facts hash.
$ puppet apply -e 'notify { "${facts[os][name]}": }'
Notice: Compiled catalog for node1.test in environment production in 0.02 seconds
Notice: Fedora
Notice: /Stage[main]/Main/Notify[Fedora]/message: defined 'message' as 'Fedora'
Notice: Applied catalog in 0.02 seconds
The data in the $facts hash is the same data returned by the facter command.
Originally, facts were available via top-scope variables:
$ puppet apply -e 'notify { "${os[name]}": }'
Notice: Compiled catalog for node1.test in environment production in 0.02 seconds
Notice: Fedora
Notice: /Stage[main]/Main/Notify[Fedora]/message: defined 'message' as 'Fedora'
Notice: Applied catalog in 0.02 seconds
Warning
While top-scope fact variables still work and are in common use, they should
be avoided in favor of the $facts hash.
Conditionals¶
The Puppet language supports four types of conditionals:
ifunlesscase- Selectors
if¶
if conditionally executes blocks of code. It can be combined with elsif
("else if") and else to build more complex logic.
if $facts['is_virtual'] {
# Our NTP module is not supported on virtual machines:
warning('Tried to include class ntp on virtual machine; this node might be misclassified.')
}
elsif $facts['os']['name'] == 'Darwin' {
warning('This NTP module does not yet work on our Mac laptops.')
}
else {
# Normal node, include the class.
include ntp
}
Try it yourself
Save the following in a file called test.pp and apply it with
puppet apply test.pp:
unless¶
unless acts like a reversed if statement. It cannot contain elsif blocks,
but it can include an else block.
Try it yourself
Add the code above to a file and apply it with puppet apply.
case¶
Much like if, case executes a block of code based on a set of conditions.
case $facts['os']['name'] {
'Solaris': { include role::solaris } # Apply the solaris class
'RedHat', 'CentOS': { include role::redhat } # Apply the redhat class
/^(Debian|Ubuntu)$/: { include role::debian } # Apply the debian class
default: { include role::generic } # Apply the generic class
}
Try it yourself
Add the following to a file and apply it with puppet apply:
Selectors¶
Selectors are similar to a case statement, but they return a value instead of
executing a code block.
$rootgroup = $facts['os']['family'] ? {
'Solaris' => 'wheel',
/(Darwin|FreeBSD)/ => 'wheel',
default => 'root',
}
file { '/etc/passwd':
ensure => file,
owner => 'root',
group => $rootgroup,
}
Try it yourself
Add the following to a file and apply it with puppet apply as root:
Functions¶
Functions perform an action or return a result. They typically take one or more parameters and can be used to modify values in a catalog. In a typical server/agent configuration, they are executed on the server.
Functions · Function reference
Functions can be called in either prefix style (where all arguments follow the
function name) or chain style (where the first argument, followed by a .,
precedes the function name):
For example, the following are equivalent:
puppetlabs-stdlib¶
puppetlabs-stdlib is a standard library of resources for Puppet modules. It
contains many popular functions, defined types, data types, and facts not
included in the core Puppet language.
puppetlabs/stdlib on the Forge
YAML¶
A common way to represent data in Puppet modules is YAML, a data serialization
format similar in many ways to JSON. YAML files used by OpenVox contain keys and
values. The keys are always strings; the values can be any data type that exists
in the Puppet language and can be represented in YAML. Keys and values are
separated by a colon and one or more spaces (:).
YAML documents should always start with the following string:
Unlike JSON, YAML supports comments that start with #, but only (reliably)
when the comment is the only thing on a line.
Strings¶
In YAML, strings are bare words, or can be quoted with ' or ":
Multi-line strings can be represented using quoted or unquoted strings with line
breaks. Unquoted strings require that trailing lines are indented, but can have
issues when : and # characters are used. This is the flow scalar format:
key: 'This is a long single-quoted
string value.'
key2: This is an even longer completely
unquoted string value.
YAML also supports a block scalar format. Blocks begin with either > (to
fold lines) or | (to keep line breaks), and an optional + (to keep trailing
line breaks) or - (to drop the trailing line break):
key: >-
This is a long
string value.
Linefeeds will be
replaced with spaces,
and the trailing
linefeed will be chomped.
key2: |
# This is a string that
# will include linefeeds,
# but will have the
# indentation stripped.
[myconfig]
key = value
For a great way to visualize multi-line strings in YAML, see yaml-multiline.info.
Numeric values¶
Numeric values are unquoted decimal, hexadecimal, or octal integers, plus floating-point numbers:
Booleans¶
Booleans are unquoted true or false values:
YAML allows many spellings (yes/no, on/off, and various capitalizations),
but for maximum compatibility with Puppet code, true and false are the
recommended values.
undef¶
In YAML, undef can be represented with ~ or null:
To OpenVox these technically mean "no value," which may mean that defaults from another data layer are used instead.
Arrays¶
Arrays are represented with indented lines beginning with -:
Hashes¶
Hashes are represented with indented lines containing additional key/value pairs:
Module data¶
Module data can be declared in two ways: Hiera data and functions.
Hiera data¶
Hiera in modules should be used for relatively static data, which might be determined by facts.
# ntp/hiera.yaml
---
version: 5
hierarchy:
- name: "OS family"
path: "os/%{facts.os.family}.yaml"
- name: "common"
path: "common.yaml"
Functions¶
Functions can be used where logic is required to determine the values of data.
# ntp/functions/data.pp
function ntp::data() {
$base_params = {
'ntp::autoupdate' => false,
'ntp::service_name' => 'ntpd',
}
$os_params = case $facts['os']['family'] {
'AIX': {
{ 'ntp::service_name' => 'xntpd' }
}
'Debian': {
{ 'ntp::service_name' => 'ntp' }
}
default: {
{}
}
}
# Merge the hashes and return a single hash.
$base_params + $os_params
}