
Manifests and Classes
Prerequisites
- Welcome Quest
- Power of Puppet Quest
- Resources Quest
Quest Objectives
- Understand the concept of a Puppet manifest
- Construct and apply manifests to manage resources
- Understand what a class means in Puppet's Language
- Learn how to use a class definition
- Understand the difference between defining and declaring a class
Getting Started
In the Resources quest you learned about resources and the syntax used to
declare them in the Puppet DSL. You used the puppet resource
, puppet
describe
, and puppet apply
tools to inspect, learn about, and change
resources on the system. In this quest, we're going to cover two key Puppet
concepts that will help you organize and implement your resource declarations:
classes and manifests. Proper use of classes and manifests is the first step
towards writing testable and reusable Puppet code.
When you're ready to get started, enter the following command to begin:
quest --start manifests_classes
Manifests
Imagination is a force that can actually manifest a reality.
--James Cameron
A manifest is a text file that contains Puppet code and is appended by the .pp
extension. It's the same stuff you saw using the puppet resource
tool and
applied with the puppet apply
tool, just saved as a file.
While it's nice to be able to edit and save your Puppet code as a file, manifests also give you a way to keep your code organized in a way that Puppet itself can understand. In theory, you could put whatever bits and pieces of syntactically valid Puppet code you like into a manifest. However, for the broader Puppet architecture to work effectively, you'll need to follow some patterns in how you write your manifests and where you put them. A key aspect of proper manifest management is related to Puppet classes.
Classes
In Puppet's DSL a class is a named block of Puppet code. A class will generally manage a set of resources related to a single function or system component. Classes often contain other classes; this nesting provides a structured way to bring together functions of different classes as components of a larger solution.
Using a Puppet class requires two steps. First, you'll need to define it by writing a class definition and saving it in a manifest file. When Puppet runs, it will parse this manifest and store your class definition. The class can then be declared to apply it to nodes in your infrastructure.
There are a few different ways to tell Puppet where and how to apply classes to nodes. You already saw the PE Console's node classifier in the Power of Puppet quest, and we'll discuss other methods of node classification in a later quest. For now, though, we'll show you how to write class definitions and use test manifests to declare these classes locally.
One more note on the topic of classes. In Puppet, classes are singleton, which means that a class can only be declared once on a given node. In this sense, Puppet's classes are different than the kind of classes you may have encountered in Object Oriented programming, which are often instantiated multiple times.
Cowsayings
You had a taste of how Puppet can manage users in the Resources quest. In this quest we'll use the package resource as our example.
First, you'll use Puppet to manage the cowsay package. Cowsay lets you print a message in the speech bubble of an ascii art cow. It may not be a mission critical software (unless your mission involves lots of ascii cows!), but it works well as a simple example. You'll also install the fortune package, which will give you and your cow access to a database of sayings and quotations.
We've already created a cowsayings
module directory in Puppet's modulepath,
and included two subdirectories: manifests
and tests
. Before getting started
writing manifests, change directories to save yourself some typing:
cd /etc/puppetlabs/puppet/environments/production/modules
Cowsay
Task 1 :
You'll want to put the manifest with your cowsay class definition in the
manifests directory. Use vim to create a cowsay.pp
manifest:
vim cowsayings/manifests/cowsay.pp
Enter the following class definition, then save and exit (:wq
):
class cowsayings::cowsay {
package { 'cowsay':
ensure => 'present',
}
}
Now that you're working with manifests, you can use some validation tools to
check your code before you apply it. Use the puppet parser
tool to check the
syntax of your new manifest:
puppet parser validate cowsayings/manifests/cowsay.pp
The parser will return nothing if there are no errors. If it does detect a syntax error, open the file again and fix the problem before continuing.
If you try to apply this manifest, nothing on the system will change. (Give it a shot if you like.) This is because you have defined a cowsay class, but haven't declared it anywhere. Whenever Puppet runs, it parses everything in the modulepath, including your cowsay class definition. So Puppet knows that the cowsay class contains a resource declaration for the cowsay package, but hasn't yet been told to do anything with it.
Task 2 :
To actually declare the class, create a cowsay.pp
test in the tests directory.
vim cowsayings/tests/cowsay.pp
In this manifest, declare the cowsay class with the include
keyword.
include cowsayings::cowsay
Save and exit.
Before applying any changes to your system, it's always a good idea to use the
--noop
flag to do a 'dry run' of the Puppet agent. This will compile the
catalog and notify you of the changes that Puppet would have made without
actually applying any of those changes to your system.
puppet apply --noop cowsayings/tests/cowsay.pp
You should see an output like the following:
Notice: Compiled catalog for learn.localdomain in environment production in
0.62 seconds
Notice: /Stage[main]/Cowsayings::Cowsay/Package[cowsay]/ensure: current_value
absent, should be present (noop)
Notice: Class[Cowsayings::Cowsay]: Would have triggered 'refresh' from 1
events
Notice: Stage[main]: Would have triggered 'refresh' from 1 events
Notice: Finished catalog run in 1.08 seconds
Task 3 :
If your dry run looks good, go ahead and run puppet apply
again without the
--noop
flag. If everything went according to plan, the cowsay package is now
installed on the Learning VM. Give it a try!
cowsay Puppet is awesome!
Your bovine friend clearly knows what's up.
____________________
< Puppet is awesome! >
--------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Fortune
But this module isn't just about cowsay; it's about cow sayings. With the fortune package, you can provide your cow with a whole database of wisdom.
Task 4 :
Create a new manifest for your fortune class definition:
vim cowsayings/manifests/fortune.pp
Write your class definition here:
class cowsayings::fortune {
package { 'fortune-mod':
ensure => 'present',
}
}
Task 5 :
Again, you'll want to validate your new manifests syntax with the puppet parser
validate
command. When everything checks out, you're ready to make your test
manifest:
vim cowsayings/tests/fortune.pp
As before, use include
to declare your cowsayings::fortune
class.
Task 6 :
Apply the cowsayings/tests/fortune.pp
manifest with the --noop
flag. If
everything looks good, apply again without the flag.
Now that you have both packages installed, you can use them together. Try piping
the output of the fortune
command to cowsay
:
fortune | cowsay
So you've installed two packages that can work together to do something more interesting than either would do on its own. This is a bit of a silly example, of course, but it's not so different than, say, installing packages for both Apache and PHP on a webserver.
Main Class: init.pp
Often a module will gather several classes that work together into a single class to let you declare everything at once.
Before creating the main class for cowsayings, however, a note on scope. You
may have noticed that the classes you wrote for cowsay and fortune were both
prepended by cowsayings::
. When you declare a class, this scope syntax tells
Puppet where to find that class; in this case, it's in the cowsayings module.
For the main class of a module, however, things are a little different. The main
class shares the name of the module itself. Instead of following the pattern of
the manifest for the class it contains, however, Puppet recognizes the special
file name init.pp
as designating the manifest that will contain a module's
main class.
Task 7 :
So to contain your main cowsayings
class, create an init.pp
manifest in the
cowsayings/manifests
directory:
vim cowsayings/manifests/init.pp
Here, you'll define the cowsayings
class. Within it, use the same
include
syntax you used in your tests to declare the cowsayings::cowsay
and
cowsayings::fortune
classes.
class cowsayings {
include cowsayings::cowsay
include cowsayings::fortune
}
Save the manifest, and check your syntax with the puppet parser
tool.
Task 8 :
Next, create a test for the init.pp
manifest in the tests directory.
vim cowsayings/tests/init.pp
Here, just declare the cowsayings
class:
include cowsayings
At this point, you've already got both packages you want installed on the
Learning VM. Applying the changes again wouldn't actually do anything. For the
sake of demonstration, go ahead and use a puppet apply -e
to delete them so
you can test the functionality of your new cowsayings
class:
puppet apply -e "package { 'fortune-mod': ensure => 'absent', } \
package {'cowsay': ensure => 'absent', }"
Task 9 :
Good. Now that the packages are gone, do a --noop
first, then apply your
cowsayings/tests/init.pp
test.
Review
We covered a lot in this quest. We promised manifests and classes, but you got a little taste of how Puppet modules work as well.
A class is a collection of related resources and other classes which, once defined, can be declared as a single unit. Puppet classes are also singleton, which means that unlike classes in object oriented programming, a Puppet class can only be declared a single time on a given node.
A manifest is a file containing Puppet code, and appended with the .pp
extension. In this quest, we used manifests in the ./manifests
directory each
to define a single class, and used a corresponding test manifest in the
./tests
directory to declare each of those classes.
There are also a few details about classes and manifests we haven't gotten to just yet. As we mentioned in the Power of Puppet quest, for example, classes can also be declared with parameters to customize their functionality. Don't worry, we'll get there soon enough!