Authors: Jeffrey McCune James Turnbull
Our first step in creating our first agent configuration is defining and extending thesite.pp
file. See an example of this file in
Listing 1-3
.
Listing 1-3.
Thesite.pp
File
import 'nodes.pp'
$puppetserver = 'puppet.example.com'
Note
Puppet manifest files are traditionally suffixed with.pp
. If your manifest file has the .pp suffix, you can drop the suffix when importing files.
Theimport
directive tells Puppet to load a file callednodes.pp
. This directive is used to include any Puppet configuration we want to load. For example, if we specify resources in a file calledresources.pp,
we would need to import it this way:
import 'resources.pp'
When Puppet starts, it will now load thenodes.pp
file and process the contents. In this case, this file will contain the node definitions we create for each agent we connect. You can also import multiple files like so:
import 'nodes/*'
import 'classes/*'
Theimport
statement will load all files with a suffix of.pp
in the directoriesnodes
andclasses
.
The$puppetserver
statement sets a variable. In Puppet, configuration statements starting with a dollar sign are variables used to specify values that you can use in Puppet configuration.
In
Listing 1-3
, we’ve created a variable that contains the fully qualified domain name of our Puppet master, enclosed in double quotes.
Note
In Puppet manifests, strings with double-quotes are subject to variable interpolation and strings with single quotes are not. If you want to use a variable in a string, you should enclose it in double-quotes, for example: “This is a $variable string”. You can also add braces,{ }
, to variables in strings to define them more clearly, “This is a ${variable} string”. You can find quoting rules for Puppet athttp://docs.puppetlabs.com/guides/more_language.html#quoting
.
Let’s add our first agent definition to thenodes.pp
file we’ve just asked Puppet to import. In Puppet manifests, agents are defined usingnode
statements.
# touch /etc/puppet/manifests/nodes.pp.
You can see the node definition we’re going to add in
Listing 1-4
.
Listing 1-4.
Our Node Configuration
node 'node1.example.com' {
include sudo
}
For a node definition we specify the node name, enclosed in single quotes, and then specify the configuration that applies to it inside curly braces{ }
. The client name can be the hostname or the fully qualified domain name of the client. At this stage, you can’t specify nodes with wildcards (e.g.,*.example.com
) but you can use regular expressions, such as:
node /^www\d+\.example\.com/ {
include sudo
}
This example will match all nodes from the domainexample.com
with the hostnamewww1
,www12
,www123
, etc.
Note
We’ll see more of node regular expressions in
Chapter 3
.
Next, we specify aninclude
directive in our node definition. Theinclude
directive specifies a collection of configuration that we want to apply to our host. There are two types of collections we can include in a node:
You can include multiple collections by using multipleinclude
directives or separating each collection with commas.
include sudo
include sshd
include vim, syslog-ng
In addition to including collections of resources, you can also specify individual resources to a node, like so:
node 'node1.example.com' {
include sudo
package { 'vim': ensure => present }
}
In this case however, as we’ve seen in
Listing 1-4
, we’re just going to add a single collection of resources: thesudo
module.
Note
Puppet also has an inheritance model in which you can have one node inherit values from another node. You can read about node inheritance athttp://docs.puppetlabs.com/guides/language_tutorial.html#nodes
; we’ll talk more about it in
Chapter 3
.
The next step is to create thesudo
module. A module is a collection of manifests, resources, files, templates, classes, and definitions. A single module would contain everything required to configure a particular application. For example, it could contain all the resources (specified in manifest files), files and associated configuration to configure Apache or thesudo
command on a host.
Each module needs a specific directory structure and a file calledinit.pp
. This structure allows Puppet to automatically load modules. To perform this automatic loading, Puppet checks a series of directories called the module path. The module path is configured with themodulepath
configuration option in the[main]
section of thepuppet.conf
file. By default, Puppet looks for modules in the/etc/puppet/modules
and/var/lib/puppet/modules
directories, but you can add additional locations if required:
[main]
moduledir = /etc/puppet/modules:/var/lib/puppet/modules:/opt/modules
The automatic loading of modules means, unlike ournodes.pp
file, modules don’t need to be loaded into Puppet using theimport
directive.
Let’s start by creating a module directory and file structure in
Listing 1-5
. We’re going to create this structure under the directory/etc/puppet/modules
. We will name the modulesudo
. Modules (and classes) must be normal words containing only letters, numbers, underscores and dashes.
Listing 1-5.
Module Structure
# mkdir –p /etc/puppet/modules/sudo/{files,templates,manifests}
# touch /etc/puppet/modules/sudo/manifests/init.pp
Themanifests
directory will hold ourinit.pp
file and any other configuration. Theinit.pp
file is the core of your module and every module must have one. Thefiles
directory will hold any files we wish to serve as part of our module. Thetemplates
directory will contain any templates that our module might use.
Now let’s look inside oursudo
module, starting with theinit.pp
file, which we can see in
Listing 1-6
.
Listing 1-6.
Thesudo
module’sinit.pp
file
class sudo {
package { sudo:
ensure => present,
}
if $operatingsystem == "Ubuntu" {
package { "sudo-ldap":
ensure => present,
require => Package["sudo"],
}
}
file { "/etc/sudoers":
owner => "root",
group => "root",
mode => 0440,
source => "puppet://$puppetserver/modules/sudo/etc/sudoers",
require => Package["sudo"],
}
}
Oursudo
module’sinit.pp
file contains a single class, also calledsudo
. There are three resources in the class, two packages and a file resource.
The first package resource ensures that thesudo
package is installed,ensure => present
. The second package resource uses Puppet’s if/else syntax to set a condition on the installation of thesudo-ldap
package.
Note
Puppet also has two other conditional statements, a case statement and a selector syntax. You can see more details of Puppet’s conditional syntaxes athttp://docs.puppetlabs.com/guides/more_language.html#conditionals
.
Puppet will check the value of theoperatingsystem
fact for each connecting client. If the value of the$operatingsystem
fact is Ubuntu, then Puppet should install thesudo-ldap
package.
Note
We discovered Facter and its values earlier in this chapter. Each fact is available as a variable, the fact name prefixed with a $ sign, in your Puppet manifests.