Www.eBooks IT.org Packt Puppet Beginners Guide
PacktPuppetBeginnersGuide
PacktPuppetBeginnersGuide
User Manual: Pdf
Open the PDF directly: View PDF .
Page Count: 205 [warning: Documents this large are best viewed by clicking the View PDF Link!]
Puppet 3 Beginner's Guide
Copyright © 2013 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system,
or transmied in any form or by any means, without the prior wrien permission of the
publisher, except in the case of brief quotaons embedded in crical arcles or reviews.
Every eort has been made in the preparaon of this book to ensure the accuracy of the
informaon presented. However, the informaon contained in this book is sold without
warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers
and distributors will be held liable for any damages caused or alleged to be caused directly
or indirectly by this book.
Packt Publishing has endeavored to provide trademark informaon about all of the
companies and products menoned in this book by the appropriate use of capitals.
However, Packt Publishing cannot guarantee the accuracy of this informaon.
First published: April 2013
Producon Reference: 1050413
Published by Packt Publishing Ltd.
Livery Place
35 Livery Street
Birmingham B3 2PB, UK.
ISBN 978-1-78216-124-0
www.packtpub.com
Cover Image by Faiz Faohi (faizfattohi@gmail.com)
eBooks-IT.org
Credits
Author
John Arundel
Reviewers
Ugo Bellavance
Jason Slagle
Johan De Wit
Acquision Editor
Joanne Fitzpatrick
Lead Technical Editor
Joanne Fitzpatrick
Technical Editors
Sharvari Baet
Kaustubh S. Mayekar
Project Coordinator
Anugya Khurana
Proofreader
Lawrence A. Herman
Indexer
Monica Ajmera Mehta
Graphics
Ronak Dhruv
Adi Gajjar
Producon Coordinator
Melwyn D'sa
Cover Work
Melwyn D'sa
eBooks-IT.org
About the Author
John Arundel is an infrastructure consultant who helps people make their computer
systems more reliable, useful, and cost-eecve and has fun doing it. He has what Larry
Wall describes as the three great virtues of a programmer: laziness, impaence, and hubris.
Laziness, because he doesn't like doing work that a computer could do instead. Impaence,
because he wants to get stu done right away. Hubris, because he likes building systems that
are as good as he can make them.
He was formerly a senior operaons engineer at global telco Verizon, designing resilient,
high-performance infrastructures for corporaons such as Ford, McDonald's, and Bank of
America. He now works independently, helping to bring enterprise-grade performance and
reliability to clients with slightly smaller pockets but very big ideas.
He likes wring books, especially about Puppet. It seems that at least some people enjoy
reading them, or maybe they just like the pictures. He also occasionally provides training and
coaching on Puppet, which turns out to be far harder than simply doing the work himself.
O the clock, he can usually be found driving a Land Rover up some mountain or other.
He lives in a small coage in Cornwall and believes, like Cicero, that if you have a garden
and a library, then you have everything you need.
You can follow him on Twier at @bitfield.
Thanks are due to my friend Luke Kanies, who created a conguraon
management tool that sucks less, and also to the many proofreaders and
contributors to this book, including Andy Brockhurst, Tim Eilers, Marn
Ellis, Adam Garside, Stefan Goethals, Jennifer Harbison, Kanthi Kiran,
Crisan Leonte, Habeeb Rahman, John Smith, Sebasaan van Steenis,
Je Sussna, Nate Walck, Bryan Weber, and Ma Willsher.
eBooks-IT.org
About the Reviewers
Ugo Bellavance has done most of his studies in e-commerce, started using Linux at Red
Hat 5.2, got Linux training from Savoir-Faire-Linux at the age of 20, and got his RHCE on RHEL
6 in 2011. He's been a consultant in the past, but he's now an employee for a provincial
government agency for which he manages the infrastructure (servers, workstaons,
network, security, virtualizaon, SAN/NAS, PBX). He's a big fan of open-source soware
and its underlying philosophy. He's worked with Debian, Ubuntu, and SUSE, but what he
knows best is RHEL-based distribuons. He's known for his contribuons to the MailScanner
project (he has been a technical reviewer for the MailScanner book), but he also gave me to
dierent open-source projects, such as mondorescue, OTRS, SpamAssassin, pfSense, and a
few others.
I thank my lover, Lysanne, who accepted allowing me some free me slots
for this review even with a 2-year-old and a 6-month-old to take care of.
The presence of these 3 human beings in my life is simply invaluable.
I must also thank my friend Sébasen, whose generosity is only matched
by his knowledge and kindness. I would never have reached that high in my
career if it wasn't for him.
eBooks-IT.org
Jason Slagle is a 15-year veteran of Systems and Network administraon. Having worked
on everything from Linux systems to Cisco networks and SAN Storage, he is always looking
for ways to make his work repeatable and automated. When he is not hacking at a computer
for work or pleasure, he enjoys running, cycling, and occasionally geocaching.
He is currently employed by CNWR, Inc., an IT and Infrastructure consulng company in his
home town of Toledo, Ohio. There he supports several larger customers in their quest to
automate and improve their infrastructure and development operaons.
I'd like to thank my wife, Heather, for being paent through the challenges
of being married to a lifelong systems guy, and my new son, Jacob, for
bringing a smile to my face on even the longest days.
Johan De Wit was an early Linux user and he sll remembers those days building a 0.9x
Linux kernel on his brand-new 486 computer that took a whole night, and always had
a great love for the UNIX Operang System.
It is not surprising that he started a career as a UNIX system administrator.
Since 2009, he has been working as an open-source consultant at Open-Future, where he
got the opportunity to work with Puppet. Right now, Puppet has become Johan's biggest
interest, and recently he became a Puppet trainer.
Besides his work with Puppet, he spends a lot of his free me with his two lovely kids
and his two Belgian dra horses, and if me and the weather permit, he likes to drive
his chopper.
eBooks-IT.org
www.PacktPub.com
Support les, eBooks, discount offers and more
You might want to visit www.PacktPub.com for support les and downloads related
to your book.
Did you know that Packt oers eBook versions of every book published, with PDF and ePub les
available? You can upgrade to the eBook version at www.PacktPub.com and as a print book
customer, you are entled to a discount on the eBook copy. Get in touch with us at service@
packtpub.com for more details.
At www.PacktPub.com, you can also read a collecon of free technical arcles, sign up for a
range of free newsleers and receive exclusive discounts and oers on Packt books and eBooks.
http://PacktLib.PacktPub.com
Do you need instant soluons to your IT quesons? PacktLib is Packt's online digital book library.
Here, you can access, read and search across Packt's enre library of books.
Why Subscribe?
Fully searchable across every book published by Packt
Copy and paste, print and bookmark content
On demand and accessible via web browser
Free Access for Packt account holders
If you have an account with Packt at www.PacktPub.com, you can use this to access PacktLib
today and view nine enrely free books. Simply use your login credenals for immediate access.
eBooks-IT.org
Table of Contents
Preface 1
Chapter 1: Introducon to Puppet 7
The problem 8
Conguraon management 8
A day in the life of a sysadmin 8
Keeping the conguraon synchronized 9
Repeang changes across many servers 10
Self-updang documentaon 10
Coping with dierent plaorms 10
Version control and history 11
Solving the problem 11
Reinvenng the wheel 11
A waste of eort 12
Transferable skills 12
Conguraon management tools 12
Infrastructure as code 13
Dawn of the devop 13
Job sasfacon 14
The Puppet advantage 14
Welcome aboard 15
The Puppet way 15
Growing your network 16
Cloud scaling 16
What is Puppet? 16
The Puppet language 16
Resources and aributes 17
Summary 18
Conguraon management 18
What Puppet does 18
eBooks-IT.org
Table of Contents
[ ii ]
The Puppet advantage 19
Scaling 19
The Puppet language 19
Chapter 2: First steps with Puppet 21
What you'll need 22
Time for acon – preparing for Puppet 22
Time for acon – installing Puppet 23
Your rst manifest 26
How it works 26
Applying the manifest 27
Modifying exisng les 28
Exercise 28
Organizing your manifests 28
Time for acon – creang a directory structure 29
Creang a nodes.pp le 29
Time for acon – creang a node declaraon 30
Summary 31
Installing Puppet 31
Manifests 31
Nodes 32
Chapter 3: Packages, Files, and Services 33
Packages 34
Time for acon – installing Nginx 34
More about packages 36
Installing specic versions 36
Removing packages 37
Updang packages 37
Modules 38
Time for acon – creang an Nginx module 38
Time for acon – making a "puppet apply" command 40
Services 41
Time for acon – adding the Nginx service 41
Requiring resources 43
More about services 44
Starng a service at boot me 44
Services that don't support "status" 45
Specifying how to start, stop, or restart a service 46
Files 46
Time for acon – deploying a virtual host 46
Nofying other resources 49
eBooks-IT.org
Table of Contents
[ iii ]
The package–le–service paern 49
Exercise 50
Summary 50
Packages 50
Modules 50
Services 51
Starng services at boot 51
Service status opons 51
Service control commands 51
Resource dependencies 51
Files 52
Chapter 4: Managing Puppet with Git 53
What is version control? 54
Time for acon – imporng your manifests into Git 55
Time for acon – comming and inspecng changes 56
How oen should I commit? 60
Branching 60
Distribung Puppet manifests 61
Reliability 61
Scalability 61
Simplicity 61
Time for acon – creang a master Git repo 62
Time for acon – cloning the repo to a new machine 63
Time for acon – adding a new node 65
Time for acon – pushing changes to the master repo 65
Exercise 66
Pulling changes automacally 67
Time for acon – automac pull-and-apply script 67
Learning more about Git 68
Summary 68
Why version control? 69
Geng started with Git 69
Networking Puppet 69
Chapter 5: Managing users 71
Users 72
Security and access control 72
What Puppet can do 72
Time for acon – creang a user 73
Removing user accounts 74
eBooks-IT.org
Table of Contents
[ iv ]
Access control 75
What is SSH? 75
Managing SSH keys 75
Time for acon – adding an SSH authorized key 76
Generang new SSH keys 78
Special-purpose keys 78
Locking user accounts 78
Managing SSH conguraon 79
Time for acon – deploying an SSH conguraon le 79
User privileges 80
sudo 81
Time for acon – deploying a sudoers le 81
Summary 83
Security pracces 83
User resources 83
Removing or locking accounts 84
Managing SSH keys 84
Conguring SSH 84
Managing privileges with sudo 85
Chapter 6: Tasks and templates 87
Running commands with exec resources 88
Time for acon – running an arbitrary command 88
Running commands selecvely 89
Triggering commands 90
Chaining commands 90
Command search paths 91
Scheduled tasks 92
Time for acon – scheduling a backup 92
More scheduling opons 94
Running jobs at regular intervals 94
Running a job as a specied user 94
Exercise 94
Distribung les 95
Time for acon – using a recursive le resource 95
Using templates 97
Time for acon – templang an Nginx virtual host 97
Inline templates 101
System facts 101
Doing the math 102
Pung it all together 102
eBooks-IT.org
Table of Contents
[ v ]
Summary 103
Exec resources 103
Scheduled jobs 104
Recursive le resources 105
Templates 105
Chapter 7: Denions and Classes 107
Grouping resources into arrays 108
Denions 109
Passing parameters to denions 111
Oponal parameters 112
Time for acon – creang a denion for Nginx websites 112
Mulple instances of denions 115
Exercise 115
Classes 115
Dening classes 115
Pung classes inside modules 116
Declaring classes 116
What's the dierence between a class and a denion? 117
Time for acon – creang an NTP class 117
Summary 120
Arrays 120
Denions 120
Classes 121
Chapter 8: Expressions and Logic 123
Condionals 123
If statements 124
else and elsif 124
Unless statements 125
Case statements 125
The default case 127
Matching mulple cases 127
Selectors 127
Expressions 128
Comparisons 128
Equality 128
Magnitude 129
Substrings 129
Boolean operators 130
Combining Boolean operators 130
Arithmec operators 130
eBooks-IT.org
Table of Contents
[ vi ]
Regular expressions 131
Operators 132
Syntax 132
Condionals 133
Capture variables 133
Substuons 134
Node denions 135
Arrays and hashes 136
Grouping resources with arrays 136
Geng values out of arrays 137
Hashes 138
Mullevel hashes 138
Tesng hash keys 139
Summary 139
Condionals 139
Operators 140
Regular expressions 140
Text substuon 141
Arrays 141
Hashes 142
Chapter 9: Reporng and troubleshoong 143
Reporng 144
Summary reports 144
Enabling reports 145
What's in a report? 145
Time for acon – generang a report 146
Using reports 150
Debug runs 150
Noop runs 151
Syntax checking 152
Debug output 152
Nofy resources 153
Exec output 153
Specifying expected exit status 155
Monitoring 155
Managing monitoring with Puppet 155
What to monitor 156
Monitoring Puppet status 156
Problems with Puppet 157
Staying in sync 157
eBooks-IT.org
Table of Contents
[ vii ]
Errors 157
Compilaon errors 158
Diagnosing errors 158
Missing le sources 158
Missing parent directory 159
Mistyped command line opons 160
Summary 160
Reporng 160
Debug and dry-run modes 160
Prinng messages 161
Monitoring Puppet 161
Common Puppet errors 161
Chapter 10: Moving on Up 163
Puppet style 164
Break out code into modules 164
Refactor common code into denions 164
Keep node declaraons simple 166
Use puppet-lint 167
Make comments superuous 168
Puppet learning resources 169
Reference 169
Resource types 169
Language and syntax 170
Facts 170
Style 170
Modules and code 171
Puppet Forge 171
The Puppet Cookbook 171
Projects 172
Puppet everywhere 173
User accounts 173
System toolbox 173
Time sync 173
Monitoring server 174
Puppeze your key services 174
Automate backups 175
Set up staging servers 175
Automate everything 175
Last word 176
Index 179
eBooks-IT.org
Preface
If you work with computer systems, then you know how me-consuming it can be to install
and congure soware, to do administraon tasks such as backups and user management,
and to keep the machines up to date with security patches and new releases. Maybe you've
already come up with some wrien procedures, shell scripts, and other ways to document
your work and make it more automated and reliable.
Perhaps you've read about how Puppet can help with this, but aren't sure how to get started.
The online documentaon is great for reference, but doesn't really explain the whole thing
from scratch. Many of the books and tutorials available spend a lot of me explaining how to
set up your Puppet server and infrastructure before ever geng to the point where you can
use Puppet to actually do something.
In my work as an infrastructure consultant I do a good deal of Puppet training, mostly for
absolute beginners, and I've found that the most eecve and fun way to do this is to get
into some real work right away. In the rst ve minutes, I have people making changes to
their systems using Puppet. If there was a re alarm and we had to terminate the class aer
that rst ve minutes, they would sll go away knowing something useful that could help
them in their jobs.
I've taken the same approach in this book. Without going into lots of theory or background
detail, I'll show you how to do useful things with Puppet right away: install packages
and cong les, create users, set up scheduled jobs, and so on. Every exercise deals with
something real and praccal that you're likely to need in your work, and you'll see the
complete Puppet code to make it happen, along with step-by-step instrucons for what to
type and what output you'll see.
Aer each exercise, I'll explain in detail what each line of code does and how it works, so that
you can adapt it to your own purposes, and feel condent that you understand everything
that's happened. By the end of the book, you will have all the skills you need to do real,
useful, everyday work with Puppet.
So let's get started.
eBooks-IT.org
What this book covers
Chapter 1, Introducon to Puppet, explains the problem of conguraon management and
why tradional manual approaches to them don't scale. It shows how Puppet deals with
these problems eciently, and introduces the basic architecture of Puppet.
Chapter 2, First Steps with Puppet, guides you through installing Puppet for the rst me,
creang a simple manifest, and applying it to a machine. You'll see how to use the Puppet
language to describe and modify resources, such as a text le.
Chapter 3, Packages, Files, and Services, shows you how to use these key resource types,
and how they work together. We'll work through a complete and useful example based on
the Nginx web server.
Chapter 4, Managing Puppet with Git, describes a simple and powerful way to connect
machines together using Puppet, and to distribute your manifests and work on them
collaboravely using the version control system Git.
Chapter 5, Managing Users, outlines some good pracces for user administraon and shows
how to use Puppet to implement them. You'll also see how to control access using SSH and
manage user privileges using sudo.
Chapter 6, Tasks and Templates, covers more key aspects of automaon: scheduling tasks,
and building conguraon les from dynamic data using Puppet's template mechanism.
Chapter 7, Denions and Classes, builds on previous chapters by showing you how to
organize Puppet code into reusable modules and objects. We'll see how to create denions
and classes, and how to pass parameters to them.
Chapter 8, Expressions and Logic, delves into the Puppet language and shows how to control
ow using condional statements and logical expressions, and how to build arithmec and
string expressions. It also covers operators, arrays, and hashes.
Chapter 9, Reporng and Troubleshoong, looks at the praccal side of working with
Puppet: how to diagnose and solve common problems, debugging Puppet's operaons,
and understanding Puppet error messages.
Chapter 10, Moving on Up, shows you how to make your Puppet code more elegant, more
readable, and more maintainable. It oers some links and suggesons for further reading,
and outlines a series of praccal projects that will help you deliver measurable business
value using Puppet.
eBooks-IT.org
What you need for this book
You'll need a computer system (preferably, but not essenally, Ubuntu Linux-based) and
access to the Internet. You won't need to be a UNIX expert or an experienced sysadmin;
I'll assume you can log in, run commands, and edit les, but otherwise I'll explain everything
you need as we go.
Who this book is for
This book is aimed at system administrators, developers, and others who need to do system
administraon, who have grasped the basics of working with the command line, eding les,
and so on, but want to learn how to use Puppet to get more done, and make their
lives easier.
Conventions
In this book, you will nd several headings appearing frequently.
To give clear instrucons on how to complete a procedure or task, we use:
Time for action – heading
1. Acon 1
2. Acon 2
3. Acon 3
Instrucons oen need some extra explanaon to make sense, so they are followed with:
What just happened?
This heading explains the working of tasks or instrucons that you have just completed.
You will also nd some other learning aids in the book, including:
Pop quiz – heading
These are short mulple-choice quesons intended to help you test your own understanding.
eBooks-IT.org
Preface
[ 4 ]
Have a go hero – heading
These praccal challenges give you ideas for experimenng with what you have learned.
You will also nd a number of styles of text that disnguish between dierent kinds of
informaon. Here are some examples of these styles, and an explanaon of their meaning.
Code words in text, database table names, folder names, lenames, le extensions,
pathnames, dummy URLs, user input, and Twier handles are shown as follows: "To have
Puppet read a manifest le and apply it to the server, use the puppet apply command."
A block of code is set as follows:
file { '/tmp/hello':
content => "Hello, world\n",
}
When we wish to draw your aenon to a parcular part of a code block, the relevant lines
or items are set in bold:
file { '/tmp/hello':
content => "Hello, world\n",
}
Any command-line input or output is wrien as follows:
ubuntu@demo:~$ puppet apply site.pp
Notice: /Stage[main]//Node[demo]/File[/tmp/hello]/ensure: defined content
as '{md5}bc6e6f16b8a077ef5fbc8d59d0b931b9'
Notice: Finished catalog run in 0.05 seconds
New terms and important words are shown in bold. Words that you see on the screen, in
menus or dialog boxes for example, appear in the text like this: "On the Select Desnaon
Locaon screen, click on Next to accept the default desnaon."
Warnings or important notes appear in a box like this.
Tips and tricks appear like this.
eBooks-IT.org
Preface
[ 5 ]
Reader feedback
Feedback from our readers is always welcome. Let us know what you think about this
book—what you liked or may have disliked. Reader feedback is important for us to
develop tles that you really get the most out of.
To send us general feedback, simply send an e-mail to feedback@packtpub.com,
and menon the book tle in the subject of your message.
If there is a topic that you have experse in and you are interested in either wring
or contribung to a book, see our author guide at www.packtpub.com/authors.
Customer support
Now that you are the proud owner of a Packt book, we have a number of things to help
you to get the most from your purchase.
Errata
Although we have taken every care to ensure the accuracy of our content, mistakes do
happen. If you nd a mistake in one of our books—maybe a mistake in the text or the
code—we would be grateful if you would report this to us. By doing so, you can save other
readers from frustraon and help us improve subsequent versions of this book. If you nd
any errata, please report them by vising http://www.packtpub.com/submit-errata,
selecng your book, clicking on the errata submission form link, and entering the details of
your errata. Once your errata are veried, your submission will be accepted and the errata
will be uploaded to our website, or added to any list of exisng errata, under the Errata
secon of that tle.
eBooks-IT.org
Preface
[ 6 ]
Piracy
Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt,
we take the protecon of our copyright and licenses very seriously. If you come across any
illegal copies of our works, in any form, on the Internet, please provide us with the locaon
address, or website name immediately so that we can pursue a remedy.
Please contact us at copyright@packtpub.com with a link to the suspected
pirated material.
We appreciate your help in protecng our authors, and our ability to bring you
valuable content.
Questions
You can contact us at questions@packtpub.com if you are having a problem with any
aspect of the book, and we will do our best to address it.
eBooks-IT.org
1
Introduction to Puppet
For a list of all the ways technology has failed to improve the quality of life,
please press three.
— Alice Kahn
In this chapter, you'll learn what Puppet is, and what it can help you do. Whether you're
a system administrator, a developer who needs to x servers from me to me, or just
someone who's annoyed at how long it takes to set up a new laptop, you'll have come
across the kind of problems that Puppet is designed to solve.
WORK, YOU
$# * !
LICENSE
INVALID
UNEXPECTED
ERROR NOT
INSTALLED
RETRY
PASSWORD
WRONG
USER NOT
FOUND
A TYPICAL DAY...
eBooks-IT.org
Introducon to Puppet
[ 8 ]
The problem
We have the misfortune to be living in the present. In the future, of course, computers will
be smart enough to just gure out what we want, and do it. Unl then, we have to spend a
lot of me telling telling the computer things it should already know.
When you buy a new laptop, you can't just plug it in, get your e-mail, and start work.
You have to tell it your name, your e-mail address, the address of your ISP's e-mail servers,
and so on.
Also, you need to install the programs you use: your preferred web browser, word processor,
and so on. Some of this soware may need license keys. Your various logins and accounts
need passwords. You have to set all the preferences up the way you're used to.
This is a tedious process. How long does it take you to get from a box-fresh computer to
being producve? For me, it probably takes about a week to get things just as I want them.
It's all the lile details.
Conguration management
This problem is called conguraon management, and thankfully we don't have it with
a new laptop too oen. But imagine mulplying it by y or a hundred computers, and
seng them all up manually.
When I started out as a system administrator, that's prey much what I did. A large part
of my me was spent conguring server machines and making them ready for use. This is
more or less the same process as seng up a new laptop: installing soware, licensing it,
conguring it, seng passwords, and so on.
A day in the life of a sysadmin
Let's look at some of the tasks involved in preparing a web server, which is something
sysadmins do prey oen. I'll use a cous, but all too plausible, website as an example.
Congratulaons: you're in charge of seng up the server for an excing, innovave social
media applicaon called cat-pictures.com.
Assuming the machine has been physically put together, racked, cabled, and powered,
and the operang system is installed, what do we have to do to make it usable as a server
for cat-pictures.com?
Add some user accounts and passwords
Congure security sengs and privileges
Install all the packages needed to run the applicaon
eBooks-IT.org
Chapter 1
[ 9 ]
Customize the conguraon les for each of these packages
Create databases and database user accounts; load some inial data
Congure the services that should be running
Deploy the cat-pictures applicaon
Add some necessary les: uploaded cat pictures, for example
Congure the machine for monitoring
That's a lot of work. It may take a day or two if this is the rst me you're seng up the
server. If you're smart, you'll write down everything you do, so next me you can simply
run through the steps and copy and paste all the commands you need. Even so, the next
me you build a cat-pictures server, it'll sll take you a couple of hours to do this.
If the live server goes down and you suddenly need to build a replacement, that's a couple
of hours of downme, and with a pointy-haired boss yelling at you, it's a bad couple
of hours.
Wouldn't it be nice if you could write a specicaon of how the server should be set up,
and you could apply it to as many machines as you liked?
Keeping the conguration synchronized
So the rst problem with building servers by hand (arsan server craing, as it's been called)
is that it's complicated and tedious and it takes a long me. There's another problem. The
next me you need to build an idencal server, how do you do it?
Your painstaking notes will no longer be up to date with reality. While you were on vacaon,
the developers installed a couple of new Ruby gems that the applicaon now depends on—I
guess they forgot to tell you. Even if everybody keeps the build document up to date with
changes, no one actually tests the modied build process, so there's no way to know if it sll
works end-to-end.
Also, the latest version of MySQL in the Linux distribuon has changed, and now it doesn't
support some of the conguraon parameters you used before. So the dierences start
to accumulate.
By the me you've got four or ve servers, they're all a lile dierent. Which is the
authoritave one? Or are they all slightly wrong? The longer they're around, the
more they will dri apart.
Wouldn't it be nice if the conguraon on all your machines could be regularly checked
and synchronized with a central, standard version?
eBooks-IT.org
Introducon to Puppet
[ 10 ]
Repeating changes across many servers
The latest feature on cat-pictures.com is that people can now upload movies of their
cats doing adorable things. To roll out the new version to your ve web servers, you need
to install a couple of new package dependencies and change a conguraon le. And you
need to do this same process on each machine.
Humans just aren't good at accurately repeang complex tasks over and over; that's why
we invented robots. It's easy to make mistakes, leave things out, or be interrupted and lose
track of what you've done.
Changes happen all the me, and it becomes increasingly dicult to keep things up to date
and in sync as your infrastructure grows.
Wouldn't it be nice if you only had to make changes in one place, and they rolled out to
your whole network automacally?
Self-updating documentation
A new sysadmin joins your organizaon, and she needs to know where all the servers are,
and what they do. Even if you keep scrupulous documentaon, it can't always be relied on.
In real life, we're too busy to stop every ve minutes and document what we just did.
The only accurate documentaon, in fact, is the servers themselves. You can look at a
server to see how it's congured, but that only applies while you sll have the machine.
If something goes wrong and you can't access the machine, or the data on it, your only
opon is to reconstruct the lost conguraon from scratch.
Wouldn't it be nice if you had a conguraon document which was guaranteed to be up
to date?
Coping with different platforms
Ideally, all your machines would have the same hardware and the same operang system.
If only things were that easy. What usually happens is that we have a mix of dierent types
of machines and dierent operang systems and we have to know about all of them.
The command to create a new user account is slightly dierent for Red Hat Linux from
the equivalent command for Ubuntu, for example. Solaris is a lile dierent again. Each
command is doing basically the same job, but has dierences in syntax, arguments, and
default values.
This means that any aempt to automate user management across your network has to
take account of all these dierences, and if you add another plaorm to the mix, then
that further increases the complexity of the code required to handle it.
eBooks-IT.org
Chapter 1
[ 11 ]
Wouldn't it be nice if you could just say how things should be, and not worry about the
details of how to make it happen?
Version control and history
Somemes you start trying to x a problem and instead make things worse. Or things were
working yesterday, and you want to go back to the way things were then. Sorry, no do-overs.
When you're making manual, ad hoc changes to systems, you can't roll back to a point in
me. It's hard to undo a whole series of changes. You don't have a way of keeping track of
what you did and how things changed.
This is bad enough if there's just one of you. When you're working in a team, it gets even
worse, with everybody making independent changes and geng in each other's way.
When you have a problem, you need a way to know what changed, and when, and who did
it. Ideally, you could look at your conguraon document and say, "Hmm, Carol checked in
a change to the FTP server last night, and today no one can log in. It looks like she made a
typo." You can x or back out of the change, and have Carol buy the team lunch.
Wouldn't it be nice if you could go back in me?
Solving the problem
Most of us have tried to solve these problems of conguraon management in various
ways. Some write shell scripts to automate builds and installs, some use makeles to
generate conguraons, some use templates and disk images, and so on. Oen these
techniques are combined with version control, to solve the history problem. Systems like
these can be quite eecve, and even a lile bit of automaon is much beer than none.
Reinventing the wheel
The disadvantage with this kind of home-brewed soluon is that each sysadmin has
to reinvent the wheel, oen many mes. The ways in which organizaons solve the
conguraon management problem are usually proprietary and highly site-specic.
So for every new place you work, you need to build a new conguraon management
system (CM system).
Because everyone has his own proprietary, unique system, the skills associated with it
aren't transferable. When you get a new job, all the me and eort you spent becoming
a wizard on your organizaon's CM system goes to waste; you have to learn a new one.
eBooks-IT.org
Introducon to Puppet
[ 12 ]
A waste of effort
Also, there's a whole lot of duplicated eort. The world really doesn't need more template
engines, for example. Once a decent one exists, it would make sense for everybody to use it,
and take advantage of ongoing improvements and updates.
It's not just the CM system itself that represents duplicated, wasted eort. The conguraon
scripts and templates you write could also be shared and improved by others, if only they
had access to them. Aer all, most server soware is prey widely used. A program in
conguraon language that sets up Apache could be used by everybody who uses
Apache—if it were a standard language.
Transferable skills
Once you have a CM system with a crical mass of users, you get a lot of benets. A new
system administrator doesn't have to write his own CM tool, he just grabs one o the shelf.
Once he learns to use it, and to write programs in the standard language, he can take that
skill with him to other jobs.
He can choose from a large library of exisng programs in the standard conguraon
language, covering most of the popular soware in use. These programs are updated and
improved to keep up with changes in the soware and operang systems they manage.
This kind of benecial network eect is why we don't have a million dierent operang
systems, or programming languages, or processor chips. There's strong pressure for people
to converge on a standard. On the other hand, we don't have just one of each of those things
either. There's never just one soluon that pleases everybody.
If you're not happy with an exisng CM system, and you have the skills, you can write one
that works the way you prefer. If enough other people feel the same way, they will form a
crical mass of users for the new system. But this won't happen indenitely; standardizaon
pressure means the market will tend to converge on a small number of compeng systems.
Conguration management tools
This is roughly the situaon we have now. Several dierent CM systems have been developed
over the years, with new ones coming along all the me, but only a few have achieved
signicant market share. At the me of wring, at least for UNIX-like systems, these CM
systems are Puppet, Chef, and CFEngine.
There really isn't much to choose between these dierent systems. They all solve more or
less the same problems—the ones we saw earlier in this chapter—in more or less the same
way. Some people prefer the Puppet way of doing things; some people are more comfortable
with Chef, and so on.
eBooks-IT.org
Chapter 1
[ 13 ]
But essenally, these, and many other CM systems, are all great soluons to the CM
problem, and it's not very important which one you choose as long as you choose one.
Infrastructure as code
Once we start wring programs to congure machines, we get some benets right away.
We can adopt the tools and techniques that regular programmers—who write code in Ruby
or Java, for example—have used for years:
Powerful eding and refactoring tools
Version control
Tests
Pair programming
Code reviews
This can make us more agile and exible as system administrators, able to deal with
fast-changing requirements and deliver things quickly to the business. We can also
produce higher-quality, more reliable work.
Dawn of the devop
Some of the benets are more subtle, organizaonal, and psychological. There is oen
a divide between "devs", who wrangle code, and "ops", who wrangle conguraon.
Tradionally, the skill sets of the two groups haven't overlapped much. It was common unl
recently for system administrators not to write complex programs, and for developers to
have lile or no experience of building and managing servers.
That's changing fast. System administrators, facing the challenge of scaling systems to
enormous size for the web, have had to get smart about programming and automaon.
Developers, who now oen build applicaons, services, and businesses by themselves,
couldn't do what they do without knowing how to set up and x servers.
The term "devops" has begun to be used to describe the growing overlap between these
skill sets. It can mean sysadmins who happily turn their hand to wring code when needed,
or developers who don't fear the command line, or it can simply mean the people for whom
the disncon is no longer useful.
Devops write code, herd servers, build apps, scale systems, analyze outages, and x bugs.
With the advent of CM systems, devs and ops are now all just people who work with code.
eBooks-IT.org
Introducon to Puppet
[ 14 ]
Job satisfaction
Being a sysadmin, in the tradional sense, is not usually a very excing job. Instead of
geng to apply your experience and ingenuity to make things beer, faster, and more
reliable, you spend a lot of me just xing problems, and making manual conguraon
changes that could really be done by a machine. The following carefully-researched
diagram shows how tradional system administraon compares to some other jobs in
both excitement and stress levels:
Stressful
Relaxing SOFA
TESTER
JET-POWERED
SOFA TESTER
SUPERSPY
Job Stress / Excitement Matrix
Boring Exciting
SYSADMIN
!
We can see from this that manual sysadmin work is both more stressful and more boring
than we would like. Boring, because you're not really using your brain, and stressful, because
things keep going wrong despite your best eorts.
Automang at least some of the dull manual work can make sysadmin work more excing,
because it frees you for things that are more important and challenging, such as guring out
how to make your systems more resilient or more performant.
Having an automated infrastructure means your servers are consistent, up to date, and
well-documented, so it can also make your job a lile less stressful. Or, at any rate, it can
give you the freedom to be stressed about more interesng things.
The Puppet advantage
So how do you do system administraon with Puppet? Well, it turns out, not too dierently
from the way you already do it. But because Puppet handles the low-level details of creang
users, installing packages, and so on, you're now free to think about your conguraon at
a slightly higher level.
Let's look at an example sysadmin task and see how it's handled the tradional way and then
the Puppet way.
eBooks-IT.org
Chapter 1
[ 15 ]
Welcome aboard
A new developer has joined the organizaon. She needs a user account on all the servers.
The tradional approach will be as follows:
1. Log in to server 1.
2. Run the useradd rachel command to create the new user.
3. Create Rachel's home directory.
4. Log in to server 2 and repeat these steps.
5. Log in to server 3 and repeat these steps.
6. Log in to server 4 and repeat these steps.
7. Log in to server 5 and repeat these steps.
8. The rst three steps will be repeated for all the servers.
The Puppet way
Here's what you might do to achieve the same result in a typical Puppet-powered
infrastructure:
Add the following lines to your Puppet code:
user { 'rachel':
ensure => present,
}
Puppet runs automacally a few minutes later on all your machines and picks up the
change you made. It checks the list of users on the machine, and if Rachel isn't on the list,
Puppet will take acon. It detects what kind of operang system is present and knows what
commands need to be run in that environment to add a user. Aer Puppet has completed its
work, the list of users on the machine will match the ones in your Puppet code.
The key dierences from the tradional, manual approach are as follows:
You only had to specify the steps to create a new user once, instead of doing
them every me for each new user
You only had to add the user in one place, instead of on every machine in
your infrastructure
You didn't have to worry about the OS-specic details of how to add users
eBooks-IT.org
Introducon to Puppet
[ 16 ]
Growing your network
It's not hard to see that, if you have more than a couple of servers, the Puppet way scales
much beer than the tradional way. Years ago, perhaps many companies would have had
only one or two servers. Nowadays it's common for a single infrastructure to have tens or
even hundreds of servers.
By the me you've got to, say, ve servers, the Puppet advantage is obvious. Not counng
the inial investment in seng up Puppet, you're geng things done ve mes faster. Your
colleague doing things the tradional, hand-craed way is sll only on machine number 2 by
the me you're heading home.
Above ten servers the tradional approach becomes almost unmanageable. You spend most
of your me simply doing repeve tasks over and over just to keep up with changes. To look
at it in another, more commercial way, your rm needs ten sysadmins to get as much work
done as one person with Puppet.
Cloud scaling
Beyond ten or so servers, there simply isn't a choice. You can't manage an infrastructure
like this by hand. If you're using a cloud compung architecture, where servers are created
and destroyed minute-by-minute in response to changing demand, the arsan approach to
server craing just won't work.
What is Puppet?
We've seen the problems that Puppet solves, and how it solves them, by leng you express
the way your servers should be congured in code form. Puppet itself is an interpreter that
reads those descripons (wrien in the Puppet language) and makes conguraon changes
on a machine so that it conforms to your specicaon.
The Puppet language
What does this language look like? It's not a series of instrucons, such as a shell script or
a Ruby program. It's more like a set of declaraons about the way things should be:
package { 'curl':
ensure => installed,
}
In English, this code says, "The curl package should be installed". This snippet of code
results in Puppet doing the following:
Checking the list of installed packages to see if curl is already installed
If not, installing it
eBooks-IT.org
Chapter 1
[ 17 ]
Another example is as follows:
user { 'jen':
ensure => present,
}
This is Puppet language for the declaraon "The jen user should be present." Again, this
results in Puppet checking for the existence of the jen user on the system, and creang
it if necessary.
So you can see that the Puppet program—the Puppet manifest—for your conguraon
is a set of declaraons about what things should exist, and how they should be congured.
You don't give commands, such as "Do this, then do that." Rather, you describe how things
should be, and let Puppet take care of making it happen. These are two quite dierent kinds
of programming. The rst (procedural style) is the tradional model used by languages, such
as C, Python, shell, and so on. Puppet's is called declarave style because you declare what
the end result should be, rather than specifying the steps to get there.
This means that you can apply the same Puppet manifest repeatedly to a machine and the
end result will be the same, no maer how many mes you run the "program". It's beer to
think of Puppet manifests as a kind of executable specicaon rather than as a program in
the tradional sense.
Resources and attributes
This is powerful because the same manifest—"The curl package should be installed and
the jen user should be present"—can be applied to dierent machines all running dierent
operang systems.
Puppet lets you describe conguraon in terms of resources—what things should exist—and
their aributes. You don't have to get into the details of how resources are created and
congured on dierent plaorms. Puppet just takes care of it.
Here are some of the kinds of resources you can describe in Puppet:
Packages
Files
Services
Users
Groups
YUM repos
Nagios conguraon
eBooks-IT.org
Introducon to Puppet
[ 18 ]
Log messages
/etc/hosts entries
Network interfaces
SSH keys
SELinux sengs
Kerberos conguraon
ZFS aributes
E-mail aliases
Mailing lists
Mounted lesystems
Scheduled jobs
VLANs
Solaris zones
In fact, since you can dene custom resources to manage anything that's not covered by the
built-in resources, there are no limits. Puppet allows you to automate every possible aspect
of system conguraon.
Summary
A quick rundown of what we've learned in this chapter.
Conguration management
Manual conguraon management is tedious and repeve, it's error-prone, and it
doesn't scale well. Puppet is a tool for automang this process.
You describe your conguraon in terms of resources such as packages and les.
This descripon is called a manifest.
What Puppet does
When Puppet runs on a computer, it compares the current conguraon to the manifest. It
will take whatever acons are needed to change the machine so that it matches the manifest.
Puppet supports a wide range of dierent plaorms and operang systems, and it will
automacally run the appropriate commands to apply your manifest in each environment.
eBooks-IT.org
Chapter 1
[ 19 ]
The Puppet advantage
Using Puppet addresses a number of key problems with manual conguraon management:
You can write a manifest once and apply it to many machines, avoiding
duplicated work
You can keep all your servers in sync with each other, and with the manifest
The Puppet manifest also acts as live documentaon, which is guaranteed to
be up to date
Puppet copes with dierences between operang systems, plaorms, command
syntaxes, and so on
Because Puppet manifests are code, you can version and manage them in the
same way as any other source code
Scaling
The problems with manual conguraon management become acute when your
infrastructure scales to 5-10 servers. Beyond that, especially when you're operang in
the cloud where servers can be created and destroyed in response to changing demand,
some way of automang your conguraon management is essenal.
The Puppet language
Puppet manifests are wrien in a special language for describing system conguraon. This
language denes units called resources, each of which describes some aspect of the system:
a user, a le, a soware package, and so on:
package { 'curl':
ensure => installed,
}
Puppet is a declarave programming language: that is, it describes how things should be,
rather than lisng a series of acons to take, as in some other programming languages, such
as Perl or shell. Puppet compares the current state of a server to its manifest, and changes
only those things that don't match. This means you can run Puppet as many mes as you
want and the end result will be the same.
eBooks-IT.org
First steps with Puppet
Beginnings are such delicate times.
— Frank Herbert, "Dune"
In this chapter you'll learn how to install Puppet, how to write your rst manifest, and how
to put Puppet to work conguring a server. You'll also understand how Puppet reads and
applies a manifest.
File Edit View Text Navigation Bundles Window Help
class memcache {
package { 'memcache':
ensure => present,
}
service { 'memcache':
ensure => running,
}
}
ACME
CHAIR
CO
2
eBooks-IT.org
First steps with Puppet
[ 22 ]
What you'll need
To follow the examples in this chapter, you'll need a computer, preferably running Linux,
connected to the Internet. You'll also need to be able to run commands in a terminal and do
simple eding of the text les. You'll also need to be able to acquire root-level access via sudo.
Although Puppet runs on a number of dierent plaorms, I'm not going to provide detailed
instrucons for all of them. Throughout this book I'll be using the Ubuntu 12.04 LTS "Precise"
distribuon of Linux for my examples. I'll point out where specic commands or le locaons
are likely to be dierent for other operang systems.
I'm using an Amazon EC2 cloud instance to demonstrate seng up Puppet, though you
may prefer to use a physical server, a Linux workstaon, or a Vagrant virtual machine (with
Internet access). I'll log in as the Ubuntu user and use sudo to run commands that need root
privileges (the default setup on Ubuntu).
Time for action – preparing for Puppet
We need to do a few things to make the server ready for installing Puppet.
1. Set a suitable hostname for your server (ignore any warning from sudo):
ubuntu@domU-12-31-39-09-51-23:~$ sudo hostname demo
ubuntu@domU-12-31-39-09-51-23:~$ sudo su -c 'echo demo >/etc/
hostname'
sudo: unable to resolve host demo
2. Log out and log back in to check that the hostname is now correctly set:
ubuntu@demo:~$
3. Find out the local IP address of the server:
ubuntu@demo:~$ ip addr list |grep eth0$
inet 10.210.86.209/23 brd 10.210.87.255 scope global eth0
4. Copy the IP address of your server (here it's 10.210.86.209) and add this to
the /etc/hosts le (use your own hostname and domain):
ubuntu@demo:~$ sudo su -c 'echo 10.210.86.209 demo demo.example.
com >>/etc/hosts'
sudo: unable to resolve host demo
eBooks-IT.org
Chapter 2
[ 23 ]
Time for action – installing Puppet
You can get a Puppet package for most Linux distribuons from Puppet Labs. Here's how to
install the package for Ubuntu 12.04 Precise:
1. Download and install the Puppet Labs repo package as follows:
ubuntu@demo:~$ wget http://apt.puppetlabs.com/puppetlabs-release-
precise.deb
--2013-01-09 13:38:24-- http://apt.puppetlabs.com/puppetlabs-
release-precise.deb
Resolving apt.puppetlabs.com (apt.puppetlabs.com)...
96.126.116.126, 2600:3c00::f03c:91ff:fe93:711a
Connecting to apt.puppetlabs.com (apt.puppetlabs.
com)|96.126.116.126|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3392 (3.3K) [application/x-debian-package]
Saving to: `puppetlabs-release-precise.deb'
100%[======================================>] 3,392 --.-K/s
in 0.001s
2013-01-09 13:38:25 (2.54 MB/s) - `puppetlabs-release-precise.deb'
saved [3392/3392]
ubuntu@demo:~$ sudo dpkg -i puppetlabs-release-precise.deb
Selecting previously unselected package puppetlabs-release.
(Reading database ... 33153 files and directories currently
installed.)
Unpacking puppetlabs-release (from puppetlabs-release-precise.deb)
...
Setting up puppetlabs-release (1.0-5) ...
2. Update your APT conguraon as follows:
ubuntu@demo:~$ sudo apt-get update
Ign http://us-east-1.ec2.archive.ubuntu.com precise InRelease
Ign http://us-east-1.ec2.archive.ubuntu.com precise-updates
InRelease
Get:1 http://us-east-1.ec2.archive.ubuntu.com precise Release.gpg
[198 B]
Get:2 http://us-east-1.ec2.archive.ubuntu.com precise-updates
Release.gpg [198 B]
eBooks-IT.org
First steps with Puppet
[ 24 ]
Ign http://apt.puppetlabs.com precise InRelease
Get:3 http://apt.puppetlabs.com precise Release.gpg [836 B]
Get:4 http://apt.puppetlabs.com precise Release [8,859 B]
...
Fetched 12.6 MB in 6s (1,943 kB/s)
Reading package lists... Done
You can find out how to configure your particular system for the Puppet
Labs repos at the following page:
http://docs.puppetlabs.com/guides/puppetlabs_
package_repositories.html
3. Install Puppet as follows:
ubuntu@demo:~$ sudo apt-get -y install puppet
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
augeas-lenses debconf-utils facter hiera libaugeas-ruby1.8
libaugeas0
libjson-ruby libreadline5 libruby libruby1.8 libshadow-ruby1.8
puppet-common
ruby ruby-json ruby1.8
Suggested packages:
augeas-doc augeas-tools puppet-el vim-puppet libselinux-ruby1.8
ruby-selinux
librrd-ruby1.8 librrd-ruby1.9 ri ruby-dev ruby1.8-examples ri1.8
Recommended packages:
rdoc
The following NEW packages will be installed:
augeas-lenses debconf-utils facter hiera libaugeas-ruby1.8
libaugeas0
libjson-ruby libreadline5 libruby libruby1.8 libshadow-ruby1.8
puppet
puppet-common ruby ruby-json ruby1.8
0 upgraded, 16 newly installed, 0 to remove and 76 not upgraded.
Need to get 3,428 kB of archives.
After this operation, 12.2 MB of additional disk space will be
eBooks-IT.org
Chapter 2
[ 25 ]
used.
Get:1 http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise/main
libreadline5 amd64 5.2-11 [128 kB]
...
Setting up puppet (3.0.2-1puppetlabs1) ...
* Starting puppet agent
puppet not configured to start, please edit /etc/default/puppet to
enable
[ OK ]
Processing triggers for libc-bin ...
ldconfig deferred processing now taking place
If you're using Red Hat Enterprise Linux, CentOS, or another Linux distribution
that uses the Yum package system, you should run $ sudo yum install
puppet to install Puppet.
If you're on a Mac, you can download and install suitable DMG images from
Puppet Labs:
https://downloads.puppetlabs.com/mac/
If you're using Windows, you can download the MSI packages from the Puppet
Labs website:
https://downloads.puppetlabs.com/windows/
4. Run the following command to check that Puppet is properly installed:
ubuntu@demo:~$ puppet --version
3.0.2
If the version of Puppet you've installed is not exactly the same, it doesn't maer; you'll get
whatever is the latest version made available by Puppet Labs. If you're installing Puppet from
a dierent place, or from source les, then as long as your version is newer than 3.0, you'll
have no trouble running the examples in this book.
If you have a version of Puppet that is older (for example, Puppet 2.6 or 2.7) you may nd
that some things don't work or work dierently from the way you'd expect. Many changes
in syntax that were deprecated in older versions, for example, no longer work at all in
Puppet 3.0. I recommend that you upgrade to Puppet 3.0 or later if at all possible.
eBooks-IT.org
First steps with Puppet
[ 26 ]
Your rst manifest
To see what Puppet code looks like, and how Puppet makes changes to a machine,
we'll create a manifest le and have Puppet apply it.
Create the le site.pp anywhere you like, with the following contents:
file { '/tmp/hello':
content => "Hello, world\n",
}
How it works
You can probably guess what this manifest will do, but I'll explain the code in detail rst.
file { '/tmp/hello':
The word file begins a resource declaraon for a le resource. Recall that a resource is
some bit of conguraon that you want Puppet to manage: for example, a le, user account,
or package. A resource declaraon looks like this:
RESOURCE { NAME:
ATTRIBUTE => VALUE,
...
}
RESOURCE indicates the type of resource you're declaring; in this case, it's a file.
NAME is a unique idener that disnguishes this instance of the resource from any other
that Puppet knows about. With le resources, it's usual for this to be the full path to the le,
in this case, /tmp/hello.
There follows a list of aributes that describe how the resource should be congured. The
aributes available depend on the type of resource. For a le, you can set aributes such as
content, owner, group, and mode.
content => "Hello, world\n",
The content aribute sets the contents of a le to a string value you provide. Here, the
contents of the le are declared to be Hello, world followed by a newline character.
Note that content species the enre content of the le; the string you provide will replace
anything already in the le, rather than being appended to it.
eBooks-IT.org
Chapter 2
[ 27 ]
Applying the manifest
To have Puppet read a manifest le, apply it to the server, and use the puppet apply
command.
Run the following command in the same directory where you created site.pp:
ubuntu@demo:~$ puppet apply site.pp
Notice: /Stage[main]//Node[demo]/File[/tmp/hello]/ensure: defined
content as '{md5}bc6e6f16b8a077ef5fbc8d59d0b931b9'
Notice: Finished catalog run in 0.05 seconds
What just happened?
Here's how your manifest is processed. First, Puppet reads the manifest le and the list
of resources it contains (in this case, there's just one resource).
Puppet then works through the list, applying each resource in turn:
First, it checks if the resource exists on the server. If not, Puppet creates it.
In the example, we've declared that the le /tmp/hello should exist. The rst
me you run puppet apply, this won't be the case, so Puppet will create the le
for you.
Then, for each resource, it checks the value of each aribute in the manifest against
what actually exists on the server.
In our example, there's just one aribute, content. We've specied that the
content of the le should be Hello, world. If the le is empty, or contains
something else, Puppet will overwrite the le with what the manifest says it
should contain.
In this case, the le will be empty the rst me you apply the manifest, so Puppet
will write the string Hello, world into it.
To check the results, run the following command:
ubuntu@demo:~$ cat /tmp/hello
Hello, world
eBooks-IT.org
First steps with Puppet
[ 28 ]
Modifying existing les
What happens if the le already exists when Puppet runs, and it contains something else?
Will Puppet change it?
ubuntu@demo:~$ echo Goodbye, world >/tmp/hello
ubuntu@demo:~$ puppet apply site.pp
Notice:/Stage[main]//File[/tmp/hello]/content: content
changed '{md5}cb2e4f7a21c01004462dd0a5ed9bd02a' to '{md5}
a7966bf58e23583c9a5a4059383ff850'
Notice: Finished catalog run in 0.04 seconds
ubuntu@demo:~$ cat /tmp/hello
Hello, world
The answer is yes. If any aribute of the le, including its contents, doesn't match the
manifest, Puppet will change it so that it does.
This can lead to some surprising results if you manually edit a le managed by Puppet.
If you make changes to a le without also changing the Puppet manifest to match,
Puppet will overwrite the le the next me it runs, and your changes will be lost.
So it's a good idea to add a comment to les that Puppet is managing; something like:
# This file is managed by Puppet - any manual edits will be lost
Add this to Puppet's copy of the le when you rst deploy it, and it will remind you and
others not to make manual changes.
Exercise
Modify your manifest to have Puppet write a message to the system's /etc/motd le.
It should be a cheerful, encouraging message so that users logging on to the system will
feel that Puppet has things under control.
Organizing your manifests
So far your manifest for this machine is contained in a single le, but we're going to expand
on that. Before things get more complicated, it's a good idea to set up a directory layout to
keep les organized, like any source code.
eBooks-IT.org
Chapter 2
[ 29 ]
Time for action – creating a directory structure
1. The top-level directory for Puppet manifests is usually named puppet, so rst of all
create this in your home directory:
ubuntu@demo:~$ cd /home/ubuntu
ubuntu@demo:~$ mkdir puppet
2. Within this directory, create a subdirectory named manifests:
ubuntu@demo:~$ cd puppet
ubuntu@demo:~/puppet$ mkdir manifests
3. Move your exisng site.pp le into the manifests subdirectory:
ubuntu@demo:~/puppet$ mv ../site.pp manifests/
4. Check that everything sll works:
ubuntu@demo:~/puppet$ puppet apply manifests/site.pp
Notice: Finished catalog run in 0.03 seconds
Your directory structure should now look as shown in the following diagram:
puppet
manifests
site.pp
Creating a nodes.pp le
So far we've only dealt with one server, the demo server. But of course Puppet can manage
many machines, each with dierent conguraons, so we need a way to tell Puppet which
conguraon belongs to each machine.
This is done with a node declaraon ("node" is the Puppet term for an individual machine
that has a Puppet conguraon). A node declaraon looks like this:
node NODENAME {
RESOURCE
RESOURCE
...
}
eBooks-IT.org
First steps with Puppet
[ 30 ]
Here NODENAME is the hostname of the relevant machine, and RESOURCE is a resource
declaraon.
If resources are not contained inside a node declaraon, Puppet will always apply them
(as we saw with the /tmp/hello le). But if they are inside a node declaraon, Puppet
will apply them only on a machine whose hostname matches the node name.
You could put all your Puppet manifests in a single le, and it would make no dierence to
Puppet. But it's much beer and easier to manage if you break them up into several les.
Convenonally, the top-level "master" le that includes everything else is named site.pp.
You should put your node declaraons in a le named nodes.pp, and we'll do this in the
next example.
Time for action – creating a node declaration
Let's reorganize the manifest to move the /tmp/hello le within a node declaraon for the
demo server.
1. Create the le manifests/nodes.pp with the following contents:
node 'demo' {
file { '/tmp/hello':
content => "Hello, world\n",
}
}
2. Change the manifests/site.pp le so it contains:
import 'nodes.pp'
3. Your puppet directory should now look as shown in the following diagram:
puppet
manifests
nodes.pp
site.pp
4. Check whether everything sll works:
ubuntu@demo:~/puppet$ puppet apply manifests/site.pp
Notice: Finished catalog run in 0.03 seconds
eBooks-IT.org
Chapter 2
[ 31 ]
What just happened?
When you run puppet apply, Puppet looks at the hostname of the machine (demo in this
case) and tries to nd a node declaraon that matches it. It nds one:
node 'demo' {
file { '/tmp/hello':
content => "Hello, world\n",
}
}
So it will apply everything within the node 'demo' declaraon, which in our example has
already been applied, so there's nothing for Puppet to do for now.
Although Puppet doesn't really mind how you organize your manifests within les—you can
have everything within one big site.pp le if you like—it's a good idea to split them up into
logical divisions. A common pracce is to keep site.pp fairly small and just use it to load
other manifest les, such as nodes.pp.
Summary
A quick rundown of what we've learned in this chapter.
Installing Puppet
You can install Puppet by downloading and installing the Puppet Labs APT repo package,
then running apt-get install puppet.
Manifests
A manifest consists of a list of resource declaraons. A resource declaraon species
a parcular aspect of system conguraon that you want Puppet to manage: a le,
for example.
Resource declaraons consist of a name and a list of aributes. The resource name is a
unique idener, which you can use to refer to this specic resource, if you need to. Its
aributes specify various things about the resource that you want to control with Puppet.
Dierent types of resources have dierent aributes, but for a file resource, aributes
include content, which species the contents of the le as a string.
eBooks-IT.org
First steps with Puppet
[ 32 ]
Puppet processes a manifest by comparing the specied resources to what currently exists
on the machine. Any missing resources will be created; aributes that do not match will be
changed to match the manifest.
Manual changes to a le managed by Puppet will be lost when Puppet next applies
the manifest.
Nodes
Node declaraons idenfy a specic machine by its hostname, and tell Puppet which
resources should be applied to that node. Any resources that are not part of a node
declaraon will be applied to all nodes. Put your node declaraons in nodes.pp.
eBooks-IT.org
Packages, Files, and Services
It's not denial. I'm just selective about the reality I accept.
– Bill Watterson, "Calvin & Hobbes"
The most common types of resources you'll manage with Puppet are packages, les, and
services. They oen occur together, with a package providing a service, and the service
requiring a conguraon le. In this chapter you'll see how to use Puppet to manage these
resources eecvely.
WILL THAT
BE ALL, SIR?
COFFEE
THANKS,
ROBOT
BUTLER!
BACON
3
eBooks-IT.org
Packages, Files, and Services
[ 34 ]
Packages
Puppet's package resource will install, update, or remove a package for you, using the system
nave package management tools (in the case of Ubuntu, that's the Advanced Package Tool
(APT). If you were seng up a server manually, you might run a command such as:
apt-get install nginx
With Puppet, you can give a resource declaraon such as:
package { 'nginx':
ensure => installed,
}
Puppet will take the necessary acons by running apt-get behind the scenes.
Time for action – installing Nginx
Your mission for today is to use Puppet to install the Nginx web server and deploy a holding
page for the cat-pictures.com website. Let's start by recalling what your Puppet
directory structure should look like, as shown in the following diagram:
1. Edit the nodes.pp le so it looks like this:
node 'demo' {
package { 'nginx':
ensure => installed,
}
}
Replace demo with the hostname of the machine you're using.
eBooks-IT.org
Chapter 3
[ 35 ]
2. Run Puppet:
ubuntu@demo:~/puppet$ sudo puppet apply manifests/site.pp
Notice: /Stage[main]//Node[demo]/Package[nginx]/ensure: ensure
changed 'purged' to 'present'
Notice: Finished catalog run in 3.10 seconds
What just happened?
Let's look at the preceding code in detail:
node 'demo' {
...
}
Remember that the node keyword introduces a node declaraon, a list of resources that are
to be applied only to node demo.
package { 'nginx':
ensure => installed,
}
In this case, there is one resource, of type package. As with the file resource we created
in Chapter 2, First steps with Puppet, the resource declaraon consists of the following:
The type of resource: package
The name of the instance: nginx
A list of aributes
Each resource type has a dierent list of aributes that you can control. A useful aribute
for package resources is ensure. We use this aribute to install (or somemes remove)
packages.
ensure => installed,
When we apply this manifest, Puppet checks whether the nginx package is installed.
If this is the rst me you've applied the manifest, the package probably won't be present,
so Puppet prints a message telling us that the package is being installed:
Notice: /Stage[main]//Node[demo]/Package[nginx]/ensure: ensure changed
'purged' to 'present'
As we saw with the file resource, once the resource has been created the rst me,
subsequent Puppet runs will do nothing because the state of the system already matches
the manifest:
ubuntu@demo:~/puppet$ sudo puppet apply manifests/site.pp
Notice: Finished catalog run in 0.08 seconds
eBooks-IT.org
Packages, Files, and Services
[ 36 ]
More about packages
We've seen how to use the package resource to install packages, but it has a few
other tricks.
Installing specic versions
If you specify ensure => installed for a package, Puppet will install whatever is the
current version of the package available from the repository at the me. This can cause
dierences between machines that are built at dierent mes. Say you build webserver1
on Monday, and on Tuesday morning a new version of Nginx is released upstream and
pushed to the Ubuntu repositories. When you build webserver2 on Tuesday aernoon,
it will pick up a dierent version of Nginx than webserver1. So the machines end up with
dierent conguraons.
We'd prefer that our servers all be in the same state. To make sure this is the case, you can
specify a version idener for the package instead of installed:
package { 'nginx':
ensure => '1.1.19-1ubuntu0.1',
}
The exact version string will depend on the Linux distribuon and package repository you're
using. To see what version of a package you currently have installed on Ubuntu, you can run
the following command:
ubuntu@demo:~/puppet$ apt-cache policy nginx
nginx:
Installed: 1.1.19-1ubuntu0.1
Candidate: 1.1.19-1ubuntu0.1
Version table:
*** 1.1.19-1ubuntu0.1 0
500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise-
updates/universe amd64 Packages
100 /var/lib/dpkg/status
1.1.19-1 0
500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise/
universe amd64 Packages
eBooks-IT.org
Chapter 3
[ 37 ]
What if package names are dierent on dierent operang systems? This does
happen; for example, the package that manages NTP may be called ntp on
some distribuons and ntpd on others. If you have to write Puppet code that
takes account of plaorm dierences like this, you can use a Puppet construct
called a selector to choose the appropriate package name. This is explained in
detail later in the book, in Chapter 8, Expressions and Logic.
Removing packages
Occasionally you need to make sure a package is removed enrely from a machine, perhaps
because it could cause conicts with a package you're installing. If you're using the Nginx
web server, for example, it's a good idea to remove the Apache package that ships with
Ubuntu by default. If Apache is running, Nginx can't start, because Apache will grab the web
server port.
package { 'apache2.2-common':
ensure => absent,
}
Using ensure => absent will remove the package if it's installed.
Updating packages
Another value that ensure can take on a package resource is latest. This will cause
Puppet to check which version of the package is available in the repository (if you're using
Ubuntu, this includes any addional APT sources that you may have congured, such as the
Puppet Labs repo). If it is newer than the installed version, Puppet will upgrade the package
to the latest version.
package { 'puppet':
ensure => latest,
}
Just because you can do this doesn't mean it's necessarily a good idea. Upgrading a
package version can cause unexpected failures or problems, so I tend to avoid doing this on
producon systems. I certainly don't want it happening automacally, in the middle of the
night, when I'm not around to respond to any issues.
If you run a staging server on which you can test any updates or changes before applying
them to producon (an approach I hearly endorse), this can be a good way to do it. You can
have your staging server ensure => latest for crical packages and thus nd out straight
away if a new upstream package release breaks your system.
eBooks-IT.org
Packages, Files, and Services
[ 38 ]
Also, ensure => latest can be a good way of managing updates if you control the
package repository (for example, if you run your own APT repo. You can nd a recipe to
do this in Chapter 5, Working with Files and Packages of The Puppet Cookbook, Packt
Publishing). In this situaon, you only release a package to your repository once you have
tested it thoroughly and veried that it doesn't cause any problems. Once it's available in the
repo, all machines will update their versions automacally using ensure => latest.
Modules
To make your Puppet manifests more readable and maintainable, it's a good idea to arrange
them into modules. A Puppet module is a way of grouping related resources. In our example,
we're going to make an nginx module that will contain all Puppet code relang to Nginx.
Time for action – creating an Nginx module
1. In your puppet directory, create the following subdirectories:
ubuntu@demo:~/puppet$ mkdir modules
ubuntu@demo:~/puppet$ mkdir modules/nginx
ubuntu@demo:~/puppet$ mkdir modules/nginx/manifests
2. Create the le modules/nginx/manifests/init.pp with the following
contents:
# Manage nginx webserver
class nginx {
package { 'nginx':
ensure => installed,
}
}
3. Edit the manifests/nodes.pp le as follows:
node 'demo' {
include nginx
}
4. Run Puppet to make sure everything is correct. There should be no changes:
ubuntu@demo:~/puppet$ sudo puppet apply manifests/site.pp
--modulepath=/home/ubuntu/puppet/modules/
Notice: Finished catalog run in 0.08 seconds
eBooks-IT.org
Chapter 3
[ 39 ]
Your directory structure should now look like this:
What just happened?
We've reorganized the code without changing what it actually does (a process called
refactoring). Before the refactoring, our node declaraon looked like this:
node 'demo' {
package { 'nginx':
ensure => installed,
}
}
Now the node declaraon looks like this:
node 'demo' {
include nginx
}
You can see that the nginx resource has been replaced by the line include nginx.
To Puppet, this means, "Look for a class called nginx and include all the resources in
it on this node."
A class in Puppet is simply a named bundle of resources that you want to apply together.
A module might contain many classes, but our example nginx module just contains one
class, also named nginx:
class nginx {
package { 'nginx':
ensure => installed,
}
}
The class keyword declares a group of resources (here, the package resource for Nginx)
idened by the name nginx. We can then use the include keyword elsewhere to include
all the resources in the class at once.
eBooks-IT.org
Packages, Files, and Services
[ 40 ]
Why do this? Well, for one thing, it means we could include the nginx class on many nodes
without repeang the same resource declaraons over and over:
node 'demo' {
include nginx
}
node 'demo2' {
include nginx
}
node 'demo3' {
include nginx
}
But we're geng ahead of ourselves. For now, let's just say that grouping resources into
classes and modules helps us organize our code so it's easy to read and maintain.
Did you noce we used a slightly dierent form of the puppet apply
command?
puppet apply manifests/site.pp --modulepath=/home/ubuntu/puppet/modules/
We haven't needed to give a modulepath argument before, but now we're
using a module, so we need to tell Puppet where to nd it.
Time for action – making a "puppet apply" command
You'll be running puppet apply prey oen, so to save typing I suggest you make a lile
script to wrap this command up with all the arguments you need.
1. Create the le /usr/local/bin/papply using the following command:
ubuntu@demo:~/puppet$ sudo vi /usr/local/bin/papply
2. Add the following contents (the sudo puppet apply... command should all be
on one line):
#!/bin/sh
sudo puppet apply /home/ubuntu/puppet/manifests/site.pp
--modulepath=/home/ubuntu/puppet/modules/ $*
3. Set execute permissions on this le:
ubuntu@demo:~/puppet$ sudo chmod a+x /usr/local/bin/papply
Now whenever you need to run Puppet, you can simply run:
ubuntu@demo:~/puppet$ papply
eBooks-IT.org
Chapter 3
[ 41 ]
Services
So we're using a module to manage Nginx on the server. That's great, but so far we've only
installed the nginx package. In order to run the web server, we would need to start and stop
it manually using the command line. Fortunately, we can automate this with Puppet as well.
Time for action – adding the Nginx service
1. Edit the modules/nginx/manifests/init.pp le as follows:
# Manage nginx webserver
class nginx {
package { 'apache2.2-common':
ensure => absent,
}
package { 'nginx':
ensure => installed,
require => Package['apache2.2-common'],
}
service { 'nginx':
ensure => running,
require => Package['nginx'],
}
}
2. Run Puppet as follows:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Nginx/Package[apache2.2-common]/ensure:
removed
Notice: /Stage[main]/Nginx/Service[nginx]/ensure: ensure changed
'stopped' to 'running'
Notice: Finished catalog run in 0.47 seconds
What just happened?
Let's look at the code you added in detail:
package { 'apache2.2-common':
ensure => absent,
}
eBooks-IT.org
Packages, Files, and Services
[ 42 ]
On Ubuntu, the default setup includes the Apache web server, which would conict with
Nginx if we tried to run it at the same me. So by specifying ensure => absent, we
remove the Apache package.
The next secon declares the nginx package:
package { 'nginx':
ensure => installed,
require => Package['apache2.2-common'],
}
The require aribute tells Puppet that this resource depends on another resource,
which must be applied rst. In this case, we want the removal of Apache to be applied
before the installaon of Nginx. We'll see more about the require aribute in the
Requiring resources secon.
Is there any implied order to aributes? In other words, does Puppet do the
ensure part before the require part, or doesn't it maer what order you
list them in? Actually, it doesn't maer; Puppet will consider all the aributes
of a resource before making any changes, so you can think of them as all
being applied at the same me. If a resource uses ensure, it's good style to
put that rst, but it doesn't make any dierence to Puppet.
Next, we declare the nginx service:
service { 'nginx':
ensure => running,
require => Package['nginx'],
}
By now you know that this declares a resource of type service. Service resources manage
daemons, or background processes, on the server. The ensure aribute tells Puppet what
state the service should be in:
ensure => running,
When you ran Puppet, it checked the status of the nginx service and found it stopped,
so Puppet started the service for you:
Notice: /Stage[main]/Nginx/Service[nginx]/ensure: ensure changed
'stopped' to 'running'
If you ran Puppet again, there would be no change because Nginx is already running, so the
server matches the manifest.
eBooks-IT.org
Chapter 3
[ 43 ]
On Ubuntu, packages that provide a service (such as Nginx) are oen
congured to start the service automacally when they're installed. However,
we make this explicit in Puppet by saying the following:
ensure => running,
On other operang systems, services may not be set up to auto-start when
installed, and in any case we want to have Puppet ensure that the service is
always running. If it gets stopped for any reason, Puppet will restart it when
the manifest is applied.
Requiring resources
What about that require aribute? require species a dependency between resources.
For example, we have to have the Nginx package installed before we can run the Nginx
service. That makes sense, and the require aribute expresses this relaonship between
the two resources.
require => Package['nginx'],
Any resource can have a require aribute, and the value must be another resource
declared somewhere in your manifest.
Did you noce that Package is capitalized? That tells Puppet you're referring
to a named instance of a package resource, with the name following in square
brackets:
Package['nginx']
You might wonder what happens if your resources require each other in a loop: one
resource requires another, which requires another, which requires the rst resource,
similar to this:
file { '/tmp/file1':
require => File['/tmp/file2'],
}
file { '/tmp/file2':
require => File['/tmp/file3'],
}
file { '/tmp/file3':
require => File['/tmp/file1'],
}
eBooks-IT.org
Packages, Files, and Services
[ 44 ]
Will Puppet just go round and round in circles forever? Let's see:
ubuntu@demo:~/puppet$ papply
Error: Could not apply complete catalog: Found 1 dependency cycle:
(File[/tmp/file1] => File[/tmp/file3] => File[/tmp/file2] => File[/tmp/
file1])
Try the '--graph' option and opening the resulting '.dot' file in
OmniGraffle or GraphViz
Somemes dependency cycles can be more subtle than this, and harder to gure out.
As the error message suggests, you can get some help by giving the --graph opon to
Puppet, which will then produce a diagram of the dependency cycle for you.
Note that Puppet can only gure out explicit dependency cycles, as in this example. More
problemac are cycles caused by side eects; if a le noes a service, and the service itself
causes the le to change, Puppet will detect that the le has changed and so nofy the
service, and this will connue forever. Happily, this situaon doesn't arise very oen, but it
can be hard to work out what's going on when it does.
More about services
Puppet's service resource has a few other facilies, depending on the underlying operang
system and what it supports. Here are some of the features you are most likely to use
(and that are supported on Ubuntu).
Starting a service at boot time
Puppet can control whether a service starts during the system boot process, using the
enable aribute:
service { 'nginx':
ensure => running,
enable => true,
}
Seng enable => true will congure the service to start at boot me (specically, on
Ubuntu, to start in runlevels 2, 3, 4, and 5, and stop in runlevels 0, 1, and 6). To disable
the automac service startup (for example, if the service is managed by a high-availability
framework such as Heartbeat), set enable => false.
eBooks-IT.org
Chapter 3
[ 45 ]
Services that don't support "status"
Most init and upstart (service management) scripts on Ubuntu support the start and
stop commands; for example:
ubuntu@demo:~/puppet$ sudo service nginx stop
Stopping nginx: nginx.
ubuntu@demo:~/puppet$ sudo service nginx start
Starting nginx: nginx.
Some also support a status command, which determines whether or not the service is
currently running:
ubuntu@demo:~/puppet$ sudo service nginx status
* nginx is running
When Puppet manages a service, it will try to use the status command to check the
service's status. In some cases this doesn't work, either because the script doesn't support
the status argument or because it returns an incorrect exit code. If you have this problem,
you can use the hasstatus aribute to change this behavior:
service { 'my-service':
ensure => running,
hasstatus => false,
}
If hasstatus is false for a service, Puppet will instead look at the system process list
(such as that produced by the ps command) and see if the service name is listed in it.
If it is, Puppet assumes the service is running. Otherwise, it will aempt to start it.
If the service name itself wouldn't appear in the process list, you can specify a dierent
paern for Puppet to search for using the pattern aribute:
service { 'my-service':
ensure => running,
hasstatus => false,
pattern => 'ruby myservice.rb',
}
If the service status can't be detected from the process list, you can give Puppet a command
to run that will return an appropriate exit status (0 for running, any other value for not
running) using the status aribute:
service { 'my-service':
ensure => running,
hasstatus => false,
status => 'grep running /var/lib/myservice/status.txt',
}
eBooks-IT.org
Packages, Files, and Services
[ 46 ]
Specifying how to start, stop, or restart a service
Somemes Puppet needs to restart the service (for example, if its cong le changes and
you are using notify to tell the service about it). By default Puppet will stop the service,
then start it.
However, some services support a restart or reload command, which may be preferable
to stopping and starng the service. For example, some daemons keep a lot of state
informaon in memory, and if you stopped the service this would be lost.
In this case, you can specify a command that Puppet should use to restart the service using
the restart aribute:
service { 'ssh':
ensure => running,
restart => '/usr/sbin/service ssh reload',
}
If you need to, you can also provide a start or stop aribute, specifying commands to start
or stop the service. This isn't usually necessary, but it's there just in case.
Files
So Nginx is installed and running, but it's not yet serving a website. To do that, we have to
have Puppet install a cong le on the server to dene an Nginx virtual host. This will tell
Nginx how to respond to requests for the cat-pictures website.
Time for action – deploying a virtual host
First, we'll create a simple website for Nginx to serve.
1. Create the directory /var/www/cat-pictures:
ubuntu@demo:~/puppet$ sudo mkdir -p /var/www/cat-pictures
2. Add a small HTML le:
ubuntu@demo:~/puppet$ sudo su -c 'echo "I can haz cat pictures?"
>/var/www/cat-pictures/index.html'
Next, we'll create the virtual host le for Puppet to deploy:
3. Create the directory modules/nginx/files:
ubuntu@demo:~/puppet$ mkdir modules/nginx/files
eBooks-IT.org
Chapter 3
[ 47 ]
4. Create the le modules/nginx/files/cat-pictures.conf with the
following contents:
server {
listen 80;
root /var/www/cat-pictures;
server_name cat-pictures.com;
}
Next, we'll add a resource that will deploy this le to the server.
5. Edit the le modules/nginx/manifests/init.pp so it looks like this:
# Manage nginx webserver
class nginx {
package { 'nginx':
ensure => installed,
}
service { 'nginx':
ensure => running,
require => Package['nginx'],
}
file { '/etc/nginx/sites-enabled/default':
source => 'puppet:///modules/nginx/cat-pictures.conf',
notify => Service['nginx'],
}
}
Be careful with the source value in the code above. It starts with
puppet followed by three slashes, not two:
puppet:///modules/nginx...
Not
puppet://modules/nginx...
6. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Nginx/File[/etc/nginx/sites-enabled/default]/
ensure: defined content as '{md5}0750fd1b8da76b84f2597de76c1b9bce'
Notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh'
from 1 events
Notice: Finished catalog run in 0.36 seconds
eBooks-IT.org
Packages, Files, and Services
[ 48 ]
7. Finally, to make sure everything worked properly, request the website:
ubuntu@demo:~/puppet$ curl localhost
I can haz cat pictures?
What just happened?
Here's the new Puppet code we added:
file { '/etc/nginx/sites-enabled/default':
source => 'puppet:///modules/nginx/cat-pictures.conf',
notify => Service['nginx'],
}
Again, we'll go through it line by line.
file { '/etc/nginx/sites-enabled/default':
We're declaring a file resource with the path /etc/nginx/sites-enabled/default.
source => 'puppet:///modules/nginx/cat-pictures.conf',
source is a le aribute that we haven't seen before. Previously we used content to
supply the contents of the le as a string. Here, source tells Puppet where to nd a copy
of the le:
puppet:///modules/nginx/cat-pictures.conf
This looks a bit like a URL, but it tells Puppet to look in the modules/nginx/files
directory for a le named cat-pictures.conf.
Noce that the source URL doesn't contain the word files. It's just
puppet:///modules/MODULENAME/FILENAME. When Puppet
translates this URL into a disk path, it becomes modules/MODULENAME/
files/FILENAME. If you nd this confusing, you're in good company.
One queson that might occur to you is, "What about when I'm running Puppet on several
dierent machines? Where does the le come from in that case? Will each machine have its
own copy of the le, or will it come from some central place?"
The answer depends on how you run Puppet across mulple machines; whether you use a
central server (known as a Puppetmaster) or whether each machine gets its own copy of the
manifest. We'll explore this in detail later, and build a complete working soluon, in Chapter
4, Managing Puppet with Git.
eBooks-IT.org
Chapter 3
[ 49 ]
Notifying other resources
notify is another aribute that we haven't seen before:
notify => Service['nginx'],
It means "whenever this le is changed, tell Service['nginx'] to restart". That's what we
saw happen as Puppet deployed the le (which of course counts as a change):
Notice: /Stage[main]/Nginx/File[/etc/nginx/sites-enabled/default]/ensure:
defined content as '{md5}0750fd1b8da76b84f2597de76c1b9bce'
Notice:/Stage[main]/Nginx/Service[nginx]: Triggered 'refresh' from 1
events
When a file resource noes a service resource, the le must be
present before the service is started. So if a le noes a service, it's just
another way of saying that the service requires the le. You can express the
relaonship either way, and the result will be the same.
The package–le–service pattern
The paern you've just learnt is a very useful one. It'll cover most services that you
need to automate.
class THE_STUFF {
package { THE_STUFF:
ensure => installed,
}
service { THE_STUFF:
ensure => running,
require => Package[THE_STUFF],
}
file { '/etc/THE_STUFF.conf':
source => 'puppet:///modules/THE_STUFF/THE_STUFF.conf',
notify => Service[THE_STUFF],
}
}
eBooks-IT.org
Packages, Files, and Services
[ 50 ]
In English, this says:
The service THE_STUFF should be running
Before the service THE_STUFF is started, the package THE_STUFF should
be installed
Before the service THE_STUFF is started, the le /etc/THE_STUFF.conf
should be present (remember that "A noes B" implies "B requires A")
If the le /etc/THE_STUFF.conf changes, restart the service THE_STUFF
Exercise
Modify the nginx class to create /var/www/cat-pictures and the index.html le you
previously set up manually.
Summary
A quick rundown of what we've learnt in this chapter.
Packages
The package resource is used to manage packages. To install a package, you set the ensure
aribute to installed.
To remove the package, use ensure => absent.
To install a specic version VERSION, use ensure => VERSION.
To install the latest version of the package available in the repo, use ensure => latest.
Modules
To help organize your code, you can put related resources into a module. For example, to
create an nginx module, create the le modules/nginx/manifests/init.pp and put
this in it:
# Manage nginx webserver
class nginx {
...
}
To apply this to a node, use:
include nginx
eBooks-IT.org
Chapter 3
[ 51 ]
Services
To manage services, use the service resource type. The ensure aribute controls whether
or not the service should be running. To specify that the service should be running, use
ensure => running. To specify that it should be stopped, use ensure => stopped.
Starting services at boot
The enable aribute controls whether or not a service is started at boot me. To start the
service at boot me, use enable => true. If you don't want it to start on boot (unlikely,
but possible) use enable => false.
Service status options
Puppet will use the service's own control script to determine whether the service is running,
by calling service SERVICENAME status (at least on UNIX-like systems).
If a service's control script doesn't support a status command, you can set hasstatus =>
false for the service resource. In this case, Puppet will look in the system process table to
see if the service is running.
If you need Puppet to search the process table for something other than the service's name,
you can specify what to search for using the pattern aribute.
If searching the process table won't work, you can provide a command for Puppet to use to
determine the service's status, using the status aribute.
Service control commands
If you want to restart a service some other way than just stopping and starng the service,
you can give Puppet the command you want to use via the restart aribute.
You can also specify custom service start and stop commands using the start and
stop aributes.
Resource dependencies
You can specify a dependency between resources using the require aribute:
require => Package['nginx'],
If resource B requires resource A, then Puppet will make sure the resources are applied in
the right order.
eBooks-IT.org
Packages, Files, and Services
[ 52 ]
Files
You can have Puppet deploy a copy of a le using the source aribute:
file { '/etc/nginx/sites-enabled/default':
source => 'puppet:///modules/nginx/cat-pictures.conf',
}
File resources can trigger a service to be restarted using the notify aribute. This is useful
for conguraon les, for which changes oen don't take eect unl the relevant service
is restarted:
notify => Service['nginx'],
eBooks-IT.org
Managing Puppet with Git
If you do not change direction, you may end up where you are heading.
– Lao-tzu
In this chapter you'll learn how to use the Git version control system to manage your Puppet
manifests. I'll also show you how to use Git to distribute the manifests to mulple machines,
so that you can start managing your whole network with Puppet.
IT’S GREAT BEING
IN CONTROL!
If you're already familiar with Git, you can save some reading by skipping ahead
to the Time for acon – imporng your manifests into Git secon. If not, here's
a gentle introducon.
4
eBooks-IT.org
Managing Puppet with Git
[ 54 ]
What is version control?
If you haven't used Git, or a similar version control tool (CVS and Subversion are some other
examples), you might be wondering what it is and why we should use it. To explain this,
let's look back to one of the system administraon problems we talked about in Chapter 1,
Introducon to Puppet: the problem of tracking code changes.
Even if you're the only person who works on a piece of source code (for example, Puppet
manifests), it's sll useful to be able to see what changes you made, and when. For example,
you might remember that you xed a bug last week, but not exactly how, and it would be
handy to be able to see exactly what lines in which le were changed.
When you're working on code with others, you need a way to communicate changes to the
rest of the team. A version control tool such as Git not only tracks everyone's changes, but
lets you record a message about what you did and why. For example, a change might be
marked with the following message:
Author: John Arundel <john@bitfieldconsulting.com>
Date: Wed Aug 8 18:57:25 2012 +0100
Increase conntrack table size on proxy servers (fixes issue #110)
It tells you when the change happened, who made it, and (if the commit message is well
wrien) why it was made. You can also see what le was changed, and which lines were
added, altered, or removed as follows:
modules/proxy/files/sysctl.conf
+net.ipv4.netfilter.ip_conntrack_max = 256000
Imagine you're trying to track down a bug; having a complete history of code changes would
be a big help. It also means you can, if necessary, roll back the state of the code to any point
in history and examine it.
You might think this introduces a lot of extra complicaons. In fact, it's very simple. Git keeps
out of your way unl you need it, and all you have to do is write a commit message when
you decide to record changes to the code.
Another very important role of version control is to allow several people to work
independently on the code, and to merge all their separate changes back together and
resolve any conicts. Git provides very powerful tools for doing this. If you're working on
Puppet code in a team, it's crical that you use some kind of version control to handle it.
In this chapter we'll add Git version control to the manifests we've been developing, and I'll
show you some of the useful things Git can do.
eBooks-IT.org
Chapter 4
[ 55 ]
Time for action – importing your manifests into Git
1. Run the following command:
ubuntu@demo:~$ sudo apt-get install git
2. Check if Git is correctly installed (the exact version number doesn't maer,
as long as it's reasonably up-to-date):
ubuntu@demo:~$ git --version
git version 1.7.9.5
3. Now inialize Git in your /home/ubuntu/puppet directory:
ubuntu@demo:~$ cd puppet
ubuntu@demo:~/puppet$ git init
Initialized empty Git repository in /home/ubuntu/puppet/.git/
4. Now set your idencaon details for Git (use your own name and e-mail):
ubuntu@demo:~/puppet$ git config --global user.name "John Arundel"
ubuntu@demo:~/puppet$ git config --global user.email john@
bitfieldconsulting.com
5. Tell Git to manage all the les and subdirectories in this directory:
ubuntu@demo:~/puppet$ git add .
6. Finally, have Git take a snapshot of the current state of the code:
ubuntu@demo:~/puppet$ git commit -m "importing"
[master (root-commit) 36f88cb] importing
4 files changed, 25 insertions(+)
create mode 100644 manifests/nodes.pp
create mode 100644 manifests/site.pp
create mode 100644 modules/nginx/files/cat-pictures.conf
create mode 100644 modules/nginx/manifests/init.pp
What just happened?
Git tracks changes to a parcular set of les. The changes are stored in Git's database, known
as a repository ("repo" for short). When you run the git init command, it tells Git to
create a new repository in the current directory.
When you create a new repo, it contains no les, so the git add command adds les to the
list that Git should track:
git add .
eBooks-IT.org
Managing Puppet with Git
[ 56 ]
This command adds everything in this directory. The full stop (.) is UNIX shorthand for the
current directory.
Instead of storing every successive version of a le, Git just keeps the dierences.
For example, if you add a line to a le and then commit that change, Git stores only
the new line and the details of which le it modies.
For this to work, of course, there has to be an inial commit; a snapshot of the starng state
that Git will then track changes from. This rst commit is what you created when you ran the
following command:
git commit -m "Importing"
The -m switch lets you aach a message to the commit, so that you or other people can see
your comments in the history.
Time for action – committing and inspecting changes
Let's make a change to the manifest and then use Git to see some informaon about it.
1. Edit the le modules/nginx/manifests/init.pp and nd the secon dening
the nginx service:
service { 'nginx':
ensure => running,
require => Package['nginx'],
}
2. Add the following line:
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx'],
}
3. Save the le and run the following command:
ubuntu@demo:~/puppet$ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working
directory)
#
# modified: modules/nginx/manifests/init.pp
eBooks-IT.org
Chapter 4
[ 57 ]
#
no changes added to commit (use "git add" and/or "git commit -a")
4. Use git diff to show you how the code diers from the snapshot taken at
the last commit:
ubuntu@demo:~/puppet$ git diff
diff --git a/modules/nginx/manifests/init.pp b/modules/nginx/
manifests/init.pp
index b152f17..f272a7c 100644
--- a/modules/nginx/manifests/init.pp
+++ b/modules/nginx/manifests/init.pp
@@ -5,6 +5,7 @@ class nginx {
service { 'nginx':
ensure => running,
+ enable => true,
require => Package['nginx'],
}
5. Add the changed le to the set that will be included in the next commit:
ubuntu@demo:~/puppet$ git add modules/nginx/manifests/init.pp
6. Commit the change:
ubuntu@demo:~/puppet$ git commit -m "Have nginx start at boot
time"
[master ad71988] have nginx start at boot time
1 file changed, 1 insertion(+)
7. Check the log of changes:
ubuntu@demo:~/puppet$ git log
commit ad719887ef68535dd6b76bab8bcee9b76edb3c98
Author: John Arundel <john@bitfieldconsulting.com>
Date: Mon Oct 22 17:08:34 2012 +0000
Have nginx start at boot time
commit 36f88cbf36782bd8e74499bb23a3a8aa5cc44ef9
Author: John Arundel <john@bitfieldconsulting.com>
Date: Mon Oct 22 16:38:58 2012 +0000
Importing
eBooks-IT.org
Managing Puppet with Git
[ 58 ]
8. Use git whatchanged to have Git display a diff showing what was changed in
the commit:
ubuntu@demo:~/puppet$ git whatchanged -p -n 1
commit ad719887ef68535dd6b76bab8bcee9b76edb3c98
Author: John Arundel <john@bitfieldconsulting.com>
Date: Mon Oct 22 17:08:34 2012 +0000
Have nginx start at boot time
diff --git a/modules/nginx/manifests/init.pp b/modules/nginx/
manifests/init.pp
index b152f17..f272a7c 100644
--- a/modules/nginx/manifests/init.pp
+++ b/modules/nginx/manifests/init.pp
@@ -5,6 +5,7 @@ class nginx {
service { 'nginx':
ensure => running,
+ enable => true,
require => Package['nginx'],
}
What just happened?
The line you added to nginx.pp is useful; it tells Puppet to congure the nginx service so
that it starts when the machine boots.
enable => true,
You have now changed the code so that it diers from that stored in Git's database, and you
can see which les are dierent using git status:
# modified: modules/nginx/manifests/init.pp
To see exactly what the dierences are, use git diff:
service { 'nginx':
ensure => running,
+ enable => true,
require => Package['nginx'],
}
eBooks-IT.org
Chapter 4
[ 59 ]
The + indicates a line was added.
The next step was to tell Git to include this change in the next commit, by using the
git add command:
ubuntu@demo:~/puppet$ git add modules/nginx/manifests/init.pp
Now you make the actual commit, with a suitable explanatory message:
ubuntu@demo:~/puppet$ git commit -m "have nginx start at boot time"
The change (or more accurately, set of changes; in this case we only made one) is now stored
in Git's database, and we can see it using the git log command:
ubuntu@demo:~/puppet$ git log
commit ad719887ef68535dd6b76bab8bcee9b76edb3c98
Author: John Arundel <john@bitfieldconsulting.com>
Date: Mon Oct 22 17:08:34 2012 +0000
Have nginx start at boot time
commit 36f88cbf36782bd8e74499bb23a3a8aa5cc44ef9
Author: John Arundel <john@bitfieldconsulting.com>
Date: Mon Oct 22 16:38:58 2012 +0000
Importing
The long string of hexadecimal characters following commit is called the commit hash,
and it uniquely idenes the commit in this repo:
commit ad719887ef68535dd6b76bab8bcee9b76edb3c98
Whenever you need to refer to a parcular commit, you can use this hash to idenfy it.
As me goes on, you will sll be able to see every change you've commied to the repo right
back to the inial import. The git whatchanged command shows you the eect of each
change, just like git diff does for uncommied changes:
service { 'nginx':
ensure => running,
+ enable => true,
require => Package['nginx'],
}
eBooks-IT.org
Managing Puppet with Git
[ 60 ]
You can skip the git add step by using the -a ag to git commit, as
follows:
git commit -a -m "Have nginx start at boot time"
This automacally adds all changed les to the commit. However, it's a good
idea to use git status and git add to see precisely what changes you
are comming. Somemes you may want to split your changes into two or
more separate commits.
Also, if you have added new les that Git doesn't know about yet, you'll sll
need to use git add to tell Git they should be placed under its control.
How often should I commit?
A common pracce is to commit when the code is in a consistent, working state, and
have the commit include a set of related changes made for some parcular purpose.
So, for example, if you are working to x bug number 75 in your issue-tracking system,
you might make changes to quite a few separate les and then, once you're happy the
work is complete, make a single commit with a message such as:
Make nginx restart more reliable (fixes issue #75)
On the other hand, if you are making a large number of complicated changes and you are
not sure quite when you'll be done, it might be wise to make a few separate commits along
the way, so that if necessary you can roll the code back to a previous state. Commits cost
nothing, so when you feel a commit is needed, go ahead and make it.
Branching
Git has a powerful feature called branching, which lets you create a parallel copy of the code
(a branch) and make changes to it independently. At any me you can choose to merge those
changes back into the master branch. Or, if changes have been made to the master branch in
the meanme, you can merge those into your working branch and carry on.
This is extremely useful when working with Puppet, because it means you can switch a single
machine to your branch while you're tesng it and working on it. The changes you make
won't be visible to other machines that aren't on your branch, so there's no danger
of accidentally rolling out changes before you're ready.
Once you're done, you can merge your changes back into that master and have them roll out
to all machines.
Similarly, two or more people can work independently on their own branches, exchanging
individual commits with each other and with the master branch as they choose. This is a very
exible and useful way of working.
eBooks-IT.org
Chapter 4
[ 61 ]
Distributing Puppet manifests
So far in this book we've only applied Puppet manifests to one server, using puppet apply
with a local copy of the manifest. To manage several servers at once, we need to distribute
the Puppet manifests to each machine so that they can be applied.
There are several ways to do this, and Puppet has a built-in server capability (Puppetmaster),
which lets each client machine request its own compiled manifest via HTTP. However, when I
work with clients to help them build Puppet infrastructures, I usually recommend a dierent
approach, using Git to distribute the manifests.
This has a number of advantages over the Puppetmaster approach, and is in some
ways simpler.
Reliability
Although your master Git server (or even GitHub) may go down, you will sll be able to run
Puppet on all your client machines and push changes to them using Git. Git is inherently
distributed, unlike the Puppetmaster architecture.
Scalability
You can keep on adding machines indenitely, and each one looks aer itself. By contrast, using
a Puppetmaster moves all the workload of compiling manifests from the client machine to a
single server, which places heavy demands on that server as the network grows.
Simplicity
All you need to do is clone a Git repo. By contrast, adding new Puppet nodes using a
Puppetmaster requires you to generate a cercate request on the client, and sign it on the
server before Puppet can run. Automang this process adds complexity, and changing the
Puppetmaster SSL cercate (for example, if the master server is replaced) requires resigning
all the client cercates. You can set up autosigning, but this introduces a potenally quite
serious security hole.
It's only fair to admit that there are dierent opinions about this, and some people favor the
Puppetmaster approach, and even think it's simpler than using Git. However, what's simple
to you depends on what you already know. Lots of people already know how to use Git; if
not, it's a very useful thing to learn, and you can apply that knowledge to more than
just Puppet.
In the following secons, we'll create a "master" repo, use it to distribute our manifests to a
new server, and then set up an automac method of pulling changes and applying them to
each machine.
eBooks-IT.org
Managing Puppet with Git
[ 62 ]
Time for action – creating a master Git repo
We're going to make a copy of our exisng Puppet repo, which we can then clone on
a new machine.
1. Create a directory to hold the repo:
ubuntu@demo:~/puppet$ sudo mkdir /var/git
2. Clone the repo, using the --bare ag:
ubuntu@demo:~/puppet$ cd /var/git
ubuntu@demo:/var/git$ sudo git clone --bare /home/ubuntu/puppet
Cloning into bare repository 'puppet.git'...
done.
3. Now create a git user that will own the master repo and control access to it:
ubuntu@demo:/var/git$ sudo useradd -d /var/git git
ubuntu@demo:/var/git$ sudo chown -R git:git /var/git
4. Just to verify that these steps have worked, check out a temporary clone of the
master repo:
ubuntu@demo:/tmp$ cd /tmp
ubuntu@demo:/tmp$ git clone /var/git/puppet.git
Cloning into 'puppet'...
done.
ubuntu@demo:/tmp$ ls puppet
manifests modules
ubuntu@demo:/tmp$ rm -r puppet
5. Now create a secure shell (SSH) keypair for the git user so that it can log in
from remote machines to clone and update the Git repo. When prompted for a
passphrase, just hit Enter.
ubuntu@demo:/tmp$ sudo su - git
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/var/git/.ssh/id_rsa):
Created directory '/var/git/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/git/.ssh/id_rsa.
Your public key has been saved in /var/git/.ssh/id_rsa.pub.
eBooks-IT.org
Chapter 4
[ 63 ]
The key fingerprint is:
87:12:a4:3d:e3:da:79:01:19:d1:0b:1c:15:f8:7c:93 git@demo
The key's randomart image is:
+--[ RSA 2048]----+
| .=*o. |
| ++o. |
| . B+ . . |
| . =+.E |
| o S... |
| o o o |
| . o . |
| . |
| |
+-----------------+
The ngerprint and image will be dierent for your key, but that's ne.
6. Create an authorized_keys le for git containing the public key you just
generated:
git@demo:~$ cd .ssh
git@demo:~/.ssh$ ls
git@demo:~/.ssh$ cp id_rsa.pub authorized_keys
7. You should now be able to log into the git account via SSH using this key:
git@demo:~/.ssh$ ssh git@localhost
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-virtual x86_64)
...
You now have a master Git repo containing your manifests, and an SSH key that you can use
to check out the repo on other machines.
Time for action – cloning the repo to a new machine
You'll need a second machine similar to the one you have been using so far (either a cloud
instance, a Vagrant VM, or a physical machine, whichever is convenient). Install Puppet and
its dependencies as you did for the rst machine in Chapter 2, First steps with Puppet, in the
Time for acon – preparing for Puppet and Time for acon – installing Puppet secons.
eBooks-IT.org
Managing Puppet with Git
[ 64 ]
1. Once the machine is set up, create the git user:
ubuntu@demo2:~$ sudo useradd -m git
2. Create a .ssh directory and private key le, and set appropriate permissions:
ubuntu@demo2:~$ sudo su - git
git@demo2:~$ mkdir .ssh
git@demo2:~$ chmod 700 .ssh
git@demo2:~$ touch .ssh/id_rsa
git@demo2:~$ chmod 600 .ssh/id_rsa
3. On your rst server, display the SSH private key for git and copy it to the clipboard:
ubuntu@demo:~$ sudo cat ~git/.ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA1wR9i+bkwsNIcyd1ojhBH13ecuOhGfoJpjdjSjocBjf2fJRa
...
GOTLXyqpcrez/Ijbc9TJsaFNisnb1HqBR31J/N2StjHmwjtOmlwL
-----END RSA PRIVATE KEY-----
4. Now edit the private key le on the new server:
git@demo2:~$ vi .ssh/id_rsa
5. Press i to enter insert mode and paste the key from the clipboard:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA1wR9i+bkwsNIcyd1ojhBH13ecuOhGfoJpjdjSjocBjf2fJRa
...
GOTLXyqpcrez/Ijbc9TJsaFNisnb1HqBR31J/N2StjHmwjtOmlwL
-----END RSA PRIVATE KEY-----
6. Save the le and exit (:wq).
7. Test the private key by logging into the old server from the new (use the public IP
address of your rst server):
git@demo2:~$ ssh git@23.20.119.201
The authenticity of host '23.20.119.201 (23.20.119.201)' can't be
established.
ECDSA key fingerprint is 29:9d:2a:09:85:d1:2d:24:a2:e5:ff:0a:4a:75
:c2:6b.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '23.20.119.201' (ECDSA) to the list of
known hosts.
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-virtual x86_64)
eBooks-IT.org
Chapter 4
[ 65 ]
8. You should now be able to clone the repo onto the new machine:
git@demo2:~$ git clone 23.20.119.201:/var/git/puppet.git
Cloning into 'puppet'...
remote: Counting objects: 17, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 17 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (17/17), 1.27 KiB, done.
Resolving deltas: 100% (1/1), done.
Time for action – adding a new node
Before we can run Puppet on the new machine, we need to add a node declaraon for it.
1. On the new server, edit /home/git/puppet/manifests/nodes.pp and add the
following secon:
node 'demo2' {
include nginx
}
2. Now run Puppet:
ubuntu@demo2:~$ sudo puppet apply /home/git/puppet/manifests/site.
pp --modulepath=/home/git/puppet/modules/
Notice: /Stage[main]/Nginx/Package[nginx]/ensure: ensure changed
'purged' to 'present'
Notice: /Stage[main]/Nginx/File[/etc/nginx/sites-enabled/default]/
ensure: defined content as '{md5}0750fd1b8da76b84f2597de76c1b9bce'
Notice: /Stage[main]/Nginx/Service[nginx]/ensure: ensure changed
'stopped' to 'running'
Notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh'
from 1 events
Notice: Finished catalog run in 11.84 seconds
Time for action – pushing changes to the master repo
We have made a change to our working copy of the Puppet repo on demo2, but so far we
haven't commied and pushed the change to the master repo. We need to do this so that
the changes will be available to all other machines using the repo.
eBooks-IT.org
Managing Puppet with Git
[ 66 ]
1. Commit the changes:
ubuntu@demo2:~$ sudo su - git
$ bash
git@demo2:~$ cd puppet
git@demo2:~/puppet$ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working
directory)
#
# modified: manifests/nodes.pp
#
no changes added to commit (use "git add" and/or "git commit -a")
git@demo2:~/puppet$ git add manifests/nodes.pp
git@demo2:~/puppet$ git commit -m "Adding node demo2"
--author="john@bitfieldconsulting.com"
[master 967cb8b] Adding node demo2
...
1 file changed, 5 insertions(+)
2. Now push all changes back to the master repo:
git@demo2:~/puppet$ git push
Counting objects: 7, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 412 bytes, done.
Total 4 (delta 0), reused 0 (delta 0)
To 23.20.119.201:/var/git/puppet.git
0ce98c0..967cb8b master -> master
Exercise
If you work as part of a team, have one of your colleagues clone the master repo and make
some changes. She'll need the private SSH key you created for git (or you can add her SSH
public key to the authorized_keys le for the git user).
Have her push the changes to the master repo, and then update the working copy on the
demo2 box and apply it.
eBooks-IT.org
Chapter 4
[ 67 ]
Now everyone in your team can work independently on the Puppet manifests, making and
pushing changes, and applying them to all the machines controlled by Puppet.
Pulling changes automatically
You now have your machines set up so that they can receive changes to the Puppet
manifests using Git, and those changes can then be applied locally. However, you sll have to
log into each machine to do this. It would be helpful to have each machine update itself and
apply any changes automacally. Then all you need to do is push a change to the repo, and it
will go out to all your machines within a certain me.
The simplest way to do this is with a cron job, which updates the repo and then runs Puppet
if anything has changed.
Time for action – automatic pull-and-apply script
1. Create the le /usr/local/bin/pull-updates with the following contents:
#!/bin/sh
PUPPETDIR=/home/git/puppet
cd ${PUPPETDIR}
git pull && sudo /usr/local/bin/papply
2. Create the le /usr/local/bin/papply with the following contents:
#!/bin/sh
PUPPETDIR=/home/git/puppet
/usr/bin/puppet apply --modulepath ${PUPPETDIR}/modules
${PUPPETDIR}/manifests/site.pp
3. Set execute permissions on both scripts:
ubuntu@demo2:~$ sudo chmod a+x /usr/local/bin/pull-updates
ubuntu@demo2:~$ sudo chmod a+x /usr/local/bin/papply
4. Edit the sudoers le:
ubuntu@demo2:~$ sudo visudo
5. Add the following line to give git permission to run the papply script as root:
git ALL = (root) NOPASSWD: /usr/local/bin/papply
6. Test that git can run the papply script:
git@demo2:~$ sudo papply
Notice: Finished catalog run in 1.88 seconds
eBooks-IT.org
Managing Puppet with Git
[ 68 ]
7. Test that git can run the pull-updates script:
git@demo2:~$ pull-updates
Already up-to-date.
Notice: Finished catalog run in 1.80 seconds
8. Edit the crontab for git:
git@demo2:~$ crontab -e
9. Add a cron job for git to run this script automacally, and save the le:
*/10 * * * * /usr/local/bin/pull-updates
10. Check that the git user's crontab has been updated:
git@demo2:~$ crontab -l |grep update
*/10 * * * * /usr/local/bin/pull-updates
What just happened?
The pull-updates script will now run automacally every 10 minutes. When it runs, it will
aempt to execute git pull in the Puppet repo directory. If there are no changes to pull,
nothing will happen.
If any changes are pulled, the script will go on to run papply to apply the changes.
So now whenever you push a change to the master Puppet repo, the demo2 machine will
automacally pick it up and apply it.
Learning more about Git
As you get familiar with Git, or even if you've been using it for a while, you may nd it helpful
to read the excellent "Pro Git" by Sco Chacon, available online here:
http://git-scm.com/book/
Summary
A quick rundown of what we've learned in this chapter.
eBooks-IT.org
Chapter 4
[ 69 ]
Why version control?
Version control is very useful for tracking changes to any source code, including Puppet
manifests. It's especially important when several people are working on the same code,
so that they can communicate with one another about their changes. Version control can
also detect and alert you to conicts when the same le is edited by dierent
people independently.
Getting started with Git
To use the Git version control tool, you create a repo using git init and make an inial
snapshot using git add and git commit. Thereaer, every me you want to record a set
of changes, use git add and git commit again to store them with an appropriate message.
As you're working on a set of changes, you can see how the current code diers from Git's
stored version using git diff. The git status command will show you which les Git
thinks may need to be commied.
You can see the complete history of changes to your repo using the git log command. git
whatchanged will show you the dierences in each le before and aer the commit.
Networking Puppet
The problem of distribung your Puppet manifests securely and eciently to a number
of machines can be solved in several ways. The tradional way is to use a special extra
server called a Puppetmaster, which authencates all the other machines and gives
them their manifests. For small infrastructures, this is overkill; for large infrastructures,
it's slow. Consequently, I usually recommend a dierent approach: using Git as the
distribuon mechanism.
Using Git to distribute your Puppet manifests to mulple machines is a simple, reliable,
and scalable alternave to using a Puppetmaster. All you need is a Git repo from which
each machine clones its own working copy and runs Puppet locally via a cron job.
An easy way to make this secure is to use Git over SSH, with a private key you distribute
to each machine that is authorized to pull Puppet manifests.
Since it's a very good idea to use Git anyway, to manage changes to your Puppet code, and
to enable your team members to work on the Puppet manifests in a distributed way, this is
simply a logical extension of that idea.
You don't need an extra server (which would in any case be a single point of failure), and it
also makes it easy for you to test changes and upgrades using Git branches.
You can set up a script to pull updates from Git and run Puppet automacally if there are any
changes. It's a good idea to trigger the script to run at intervals using cron.
eBooks-IT.org
Managing users
The real problem isn't whether machines think but whether people do.
— B.F. Skinner
In this chapter, you'll learn how to use Puppet to create and manage user accounts,
congure SSH access and keys, and control user privileges via sudo.
UNIX
LOUNGE
YOUR NAME’S NOT DOWN...
YOU’RE NOT COMING IN!
SSH
5
eBooks-IT.org
Managing users
[ 72 ]
Users
One of the most common system administraon tasks is seng up user accounts.
We'll see how Puppet can help with this in a moment, but rst a word about the kind
of user conguraon we should be aiming for.
Security and access control
Organizaons with good security and access control pracces tend to use some or all of the
following policies:
Everyone who needs access to a machine has her own user account with an SSH key
(not a password)
Access to special-purpose accounts, such as those used to deploy and run
applicaons, or a database, is controlled by authorizing specic SSH keys, rather
than using passwords
Accounts that need certain, specic superuser privileges can get them via the
sudo mechanism
The root account is not accessible via the network (but there is secure, out-of-band
access to the system console)
Third pares, such as contractors and support sta, get temporary access with
limited privileges, which can be revoked once a job is nished
Seng up policies like these, while highly desirable from a security point of view, is
me-consuming to do by hand and dicult to maintain. If a new user arrives, someone
has to add and congure his account on every server. If a user leaves, the accounts have
to be removed or locked everywhere.
It's not surprising that many organizaons, under me pressure and needing things to work
right away, don't bother too much about security and access control. In many cases the
simplest thing to do is for everyone to log in as root using the same password, oen for all
machines. Even if there are ocial policies about security, people oen don't follow them,
because it's more convenient to do things an insecure way.
What Puppet can do
One of the biggest wins that Puppet can deliver in an organizaon is making it quick and easy
to manage user accounts securely across a large network. You can add or remove individual
and shared accounts, control their access via SSH, manage their privileges via sudo, and
have the changes immediately applied to every machine under Puppet's control, all without
logging into a single server.
eBooks-IT.org
Chapter 5
[ 73 ]
When this is the case, it's much easier to ensure that security policies are followed, without
hindering people from doing their jobs. When your SSH key works everywhere, you
don't need to share or write down passwords, and when your account has the necessary
privileges, you don't need to use root. So everybody benets.
Puppet provides a number of ways to help you manage users. The user resource type
controls user accounts, and the ssh_authorized_key resource type controls SSH access to
accounts. You can use Puppet to control user privileges by managing the sudoers le, and
you can also replace the default SSH conguraon le with a more secure version managed
by Puppet.
In the rest of this chapter, we'll see how to use these techniques, again using our
cat-pictures.com example site.
Time for action – creating a user
There's a new developer on the cat-pictures project, named Art Vandelay. You'll need to
create a user account for him on the server using Puppet.
1. Edit your manifests/nodes.pp le as follows:
node 'demo' {
user { 'art':
ensure => present,
comment => 'Art Vandelay',
home => '/home/art',
managehome => true,
}
}
2. Apply the manifest:
ubuntu@demo:~/puppet$ papply
Notice:/Stage[main]//Node[demo]/User[art]/ensure: created
Notice: Finished catalog run in 0.25 seconds
3. Make sure the user has been created correctly:
ubuntu@demo:~/puppet$ sudo su - art
$ pwd
/home/art
eBooks-IT.org
Managing users
[ 74 ]
What just happened?
Puppet's user resource type creates a user (or modies it if the user already exists).
The following line declares a user whose login name is art:
user { 'art':
The user should be present:
ensure => present,
We can also specify here some informaon about the user:
comment => 'Art Vandelay',
The comment aribute sets the user's full name.
home => '/home/art',
The home aribute sets the path to the user's home directory. Puppet will not create this
directory for you unless you also set the managehome aribute:
managehome => true,
So the manifest says that a user named art should exist, whose full name is Art Vandelay,
and that his home directory should be /home/art, and that that directory should exist.
Note that we have not specied a password for the user, and as a result art
will not yet be able to log in. Although Puppet can set passwords for users
(with the password aribute) I recommend you use SSH authencaon
instead, which is much more secure than using a password. We'll see how to
do this later in the Access control secon.
Removing user accounts
To remove a user from the system altogether, use the ensure => absent aribute:
user { 'art':
ensure => absent,
}
When you run Puppet, the art account will be removed (though Art's home directory and
any les he owned will remain).
eBooks-IT.org
Chapter 5
[ 75 ]
Just removing the user resource declaraon from your Puppet code won't
actually remove the user's account from your machines. If you think about it,
this makes sense. Otherwise, Puppet would remove all accounts it hasn't been
specically told about, including root!
So when you want to remove a user, change their ensure aribute from
present to absent, and Puppet will delete the account for you. Once
this change has been applied to all machines, you can remove the user
declaraon from your Puppet manifest.
Access control
Having created the user's account, we now need to provide a secure way for him to log in.
We can do this using the SSH protocol.
What is SSH?
SSH is a more secure way of controlling user access than the tradional "username and
password" approach. Instead of using a password, which the user has to keep secret, it uses
two pieces of informaon: the public key and the private key. Only the private key has to be
secret. You can put your public key on any computer, or publish it to the world if you like.
But no one can log in to an account controlled by your public key unless they also have the
matching private key.
This has the pleasant consequence that you only need one SSH key, and you can use it for
everything. It's a very bad idea to use the same password for mulple accounts, but with
SSH, that's no problem. So long as you keep the private key secret, you can use your public
key everywhere.
Managing SSH keys
Puppet can manage SSH public keys and authorize them for user accounts, using the
ssh_authorized_key resource type.
eBooks-IT.org
Managing users
[ 76 ]
Time for action – adding an SSH authorized key
1. You'll need your own SSH public key for this. If you already have one on your own
computer, display the contents:
john@T-Bone:~$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA3ATqENg+GWACa2B
zeqTdGnJhNoBer8x6pfWkzNzeM8Zx7/2Tf2pl7kHdbsiTXEUawq
zXZQtZzt/j3Oya+PZjcRpWNRzprSmd2UxEEPTqDw9LqY5S2B8og/
NyzWaIYPsKoatcgC7VgYHplcTbzEhGu8BsoEVBGYu3IRy5RkAcZik=
2. If you don't have an SSH key, you can generate one for this exercise:
ubuntu@demo:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ubuntu/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ubuntu/.ssh/id_rsa.
Your public key has been saved in /home/ubuntu/.ssh/id_rsa.pub.
3. Now display the id_rsa.pub le to see the public key:
ubuntu@demo:~$ cat /home/ubuntu/.ssh/id_rsa.pub
ssh-rsa
CveowByzhgEFMOXi7Ycxr1h958BjVyqGRUTkSoz8bfjqeXmJAvMl/5V3sTl/YV9r9y
sM7Rzu7K51YB+Bg6CQr0QJjABev56rTsbVtyAHi7Ju9zfu6JJ7pfnSfKajwBpHSW0e
yTYm8Fnkry920ikoeQOwN+DsYt5NY3h+sPISb98oXRWe0EetFanJ8AwlUuYQ9DmO+3
kArMyyT
IzgWR7wE6SMKG5RujzWk0Hb7ngGWyjXJtG7F3k3SD06W3UmGPK1AXPRbW4vJDL+hhL
ubuntu@
6FtxIzgWR7wE6SMKG5RujzWk0Hb7ngGWyjXJtG7F3k3SD06W3UmGPK1AXPRbW4vJDL
+hhL ubuntu@demo
The key itself is the long string of numbers and leers, without the ssh-rsa part
at the beginning, or the ubuntu@demo part at the end. It's this string you'll put into
the Puppet manifest in the next step.
4. Edit your manifests/nodes.pp le as follows (using your own key string as
the value for key):
node 'demo' {
user { 'art':
ensure => present,
comment => 'Art Vandelay',
home => '/home/art',
managehome => true,
eBooks-IT.org
Chapter 5
[ 77 ]
}
ssh_authorized_key { 'art_ssh':
user => 'art',
type => 'rsa',
key => 'AAAAB3NzaC1yc2EAAAABIwAAAIEA3ATqENg+GWAC
a2BzeqTdGnJhNoBer8x6pfWkzNzeM8Zx7/2Tf2pl7kHdbsiTXEUaw
qzXZQtZzt/j3Oya+PZjcRpWNRzprSmd2UxEEPTqDw9LqY5S2B8og/
NyzWaIYPsKoatcgC7VgYHplcTbzEhGu8BsoEVBGYu3IRy5RkAcZik=',
}
}
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Ssh_authorized_key[art_ssh]/
ensure: created
Notice: Finished catalog run in 0.05 seconds
6. Now test that you have access to the art account using this key. On a machine that
has your SSH key, run the following command:
$ ssh art@demo
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-virtual x86_64)
What just happened?
The following line declares an ssh_authorized_key resource:
ssh_authorized_key { 'art_ssh':
The name (art_ssh in this case) can be anything you like, so long as it's unique. It will be
added as a comment at the end of the key in the authorized_keys le.
We need to specify the user account for which this key will grant access:
user => 'art',
We also have to tell Puppet the key type (rsa or dsa; you'll know which it is because the key
le itself contains ssh-rsa or ssh-dsa at the beginning):
type => 'rsa',
And lastly the key string, which in this case should be your own key instead of mine:
key => 'AAAAB3NzaC1yc2EAAAABIwAAAIEA3ATqENg+GWACa2B
zeqTdGnJhNoBer8x6pfWkzNzeM8Zx7/2Tf2pl7kHdbsiTXEUawq
zXZQtZzt/j3Oya+PZjcRpWNRzprSmd2UxEEPTqDw9LqY5S2B8og/
NyzWaIYPsKoatcgC7VgYHplcTbzEhGu8BsoEVBGYu3IRy5RkAcZik=',
eBooks-IT.org
Managing users
[ 78 ]
Puppet will then add this key to the le /home/art/.ssh/authorized_keys. When you
try to log in to Art's account via SSH, the system will look in this le to see if your private key
matches any of the public keys listed there. Assuming it does, you'll be able to log in.
Generating new SSH keys
For managing users other than yourself, you can generate new keys for them using the
ssh-keygen command:
ubuntu@demo:~/puppet$ ssh-keygen -f fabian
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in fabian.
Your public key has been saved in fabian.pub.
Give the user the secret key le (fabian) and put the matching public key into Puppet as an
ssh_authorized_key resource for that user.
Special-purpose keys
Somemes an automac process on one machine needs access to another machine.
For example, you might have a daily cron job that uploads logs to a central storage server.
So how do you manage this securely?
One simple way is to create a user account on the target machine dedicated to the purpose:
log uploading, for example. This account is secured with SSH, and access is restricted to a
special private key that you create. The private key is distributed with Puppet to only those
machines that need it, and can be removed or changed at any me.
This is exactly the approach we took in an earlier chapter for seng up automac access
to a Git server, so that machines can pull their Puppet cong at regular intervals and apply
changes. You can use this idea to manage access for any automated task.
For even greater security, you can give each machine its own private key, and authorize the
target machine for all the corresponding public keys.
Locking user accounts
If you want to be able to block a user from logging in, you can do this by temporarily
removing his SSH key in Puppet:
ssh_authorized_key { 'art_ssh':
user => 'art',
eBooks-IT.org
Chapter 5
[ 79 ]
type => 'rsa',
key => '',
}
The value for key in the example above is an empty single-quoted string (''). This will
disable SSH logins for the user. If you have enabled password authencaon (which I don't
recommend, but you might need it in some situaons) then this won't stop the user from
logging in using his password. To do this, set a password of a single star (*) in Puppet:
user { 'art':
ensure => present,
comment => 'Art Vandelay',
home => '/home/art',
managehome => true,
password => '*',
}
This will block the user from logging in via password (though SSH will sll work unless you
also disable that, as shown above). To unlock the account, remove the password aribute
and re-set the user's password using the passwd command.
Managing SSH conguration
Although it's not necessary if you just want to set up user accounts with SSH keys, you can
use Puppet to manage the global SSH conguraon for your system, for example, to allow
only a specied list of users to log in. We'll see how to do that in the following secon.
Time for action – deploying an SSH conguration le
1. Create the directories needed for a new ssh module:
ubuntu@demo:~/puppet$ mkdir modules/ssh
ubuntu@demo:~/puppet$ mkdir modules/ssh/manifests
ubuntu@demo:~/puppet$ mkdir modules/ssh/files
2. Create the le modules/ssh/manifests/init.pp with the following contents:
# Manage the SSH service
class ssh {
service { 'ssh':
ensure => running,
}
file { '/etc/ssh/sshd_config':
source => 'puppet:///modules/ssh/sshd_config',
notify => Service['ssh'],
eBooks-IT.org
Managing users
[ 80 ]
owner => 'root',
group => 'root',
}
}
3. Create the le modules/ssh/files/sshd_config with the following contents
(if you're not logging in as ubuntu, add the user you're logging in as to the list of
AllowUsers. Only the named users will be able to log in once you've applied this
change with Puppet, so be careful):
Port 22
Protocol 2
PermitRootLogin no
PasswordAuthentication no
AllowUsers ubuntu art
UsePAM yes
4. Add this to your node denion in manifests/nodes.pp:
include ssh
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Ssh/File[/etc/ssh/sshd_config]/content:
content changed '{md5}5f15065f987c4d9851ad3448d4aadfa6' to '{md5}6
e96247a35996ba5adc36acbf34faf9b'
Notice: /Stage[main]/Ssh/Service[ssh]: Triggered 'refresh' from 1
events
Notice: Finished catalog run in 0.23 seconds
6. Check that you can sll log in from another machine as ubuntu or art:
john@T-Bone:~$ ssh ubuntu@demo
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-virtual x86_64)
User privileges
Linux and other UNIX-like operang systems commonly have two levels of user privilege: the
root user, who can edit system les and perform operaons tasks, such as reboong the
machine, and normal users, who can only edit and read les owned by themselves, and have
no special privileges. This ensures that users don't get access to les or commands that they
shouldn't have. However, somemes you need to grant special privileges to a user, without
giving her full access to the root account. You can do this using a UNIX command called sudo.
eBooks-IT.org
Chapter 5
[ 81 ]
sudo
The sudo command allows normal users to run commands with root privileges, if this is
specically authorized by the system administrator. For example, a developer user might be
given privileges to run service nginx restart as root.
The set of users allowed to assume root privileges, and the specic commands they can
run, is specied in the le /etc/sudoers. We can use Puppet to manage this le, and thus
control user privileges on the machine.
Time for action – deploying a sudoers le
1. Create the directories for a sudoers module:
ubuntu@demo:~/puppet$ mkdir modules/sudoers
ubuntu@demo:~/puppet$ mkdir modules/sudoers/manifests
ubuntu@demo:~/puppet$ mkdir modules/sudoers/files
2. Create the le modules/sudoers/manifests/init.pp with the following
contents:
# Manage the sudoers file
class sudoers {
file { '/etc/sudoers':
source => 'puppet:///modules/sudoers/sudoers',
mode => '0440',
owner => 'root',
group => 'root',
}
}
3. Create the le modules/sudoers/files/sudoers with the following contents:
# User privilege specification
root ALL = (ALL) ALL
ubuntu ALL = (ALL) NOPASSWD:ALL
art ALL = (ALL) NOPASSWD: /bin/ls
4. Check the syntax of the sudoers le:
ubuntu@demo:~/puppet$ visudo -c -f modules/sudoers/files/sudoers
modules/sudoers/files/sudoers: parsed OK
eBooks-IT.org
Managing users
[ 82 ]
5. If there are any errors, correct them before moving on. If you use Puppet to deploy a
sudoers le that contains syntax errors, no users will be able to sudo anything, and
you will need to log in as root in order to x the problem. So whenever you make a
change to Puppet's copy of the sudoers le, use the visudo command as above to
check the syntax.
6. Add this to your node denion in manifests/nodes.pp:
include sudoers
7. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Sudoers/File[/etc/sudoers]/content: content
changed '{md5}5755c84fcb480985818c6daa9faa386c' to '{md5}
f9d8dbf9b36280c3e860af7eede92fd1'
Notice: Finished catalog run in 0.10 seconds
8. Run the following command as the ubuntu user to verify that the changes have
taken eect:
ubuntu@demo:~/puppet$ sudo whoami
root
9. Run the following command as the art user, to test whether he has the privilege to
run /bin/ls as root:
art@demo:~$ sudo /bin/ls -l /
total 80
drwxr-xr-x 2 root root 4096 Aug 22 05:49 bin
drwxr-xr-x 3 root root 4096 Jan 9 13:54 boot
drwxr-xr-x 12 root root 3840 Jan 9 13:47 dev
drwxr-xr-x 89 root root 4096 Jan 14 15:29 etc
drwxr-xr-x 3 root root 4096 Aug 22 05:48 home
What just happened?
When you use Puppet to deploy the sudoers le, the privilege sengs listed in the le will
immediately take eect. When any user runs a command using sudo, the system will consult
/etc/sudoers to see whether or not the command is allowed.
The line root ALL = (ALL) ALL allows user root to sudo any command as root
(this might seem unnecessary, but it's included for consistency, and to make sure any scripts
that use sudo don't suddenly fail if run as root).
eBooks-IT.org
Chapter 5
[ 83 ]
The line ubuntu ALL = (ALL) NOPASSWD:ALL allows user ubuntu to run any
command, on any system, as any user, without having to enter a password. (You can have
sudo require the user's password, if you use passwords, to make things a lile more secure.
Generally though, sudoers entries are used for scripts and automated jobs that can't enter
a password anyway.)
The line art ALL = (ALL) NOPASSWD: /bin/ls is more specic. It allows user art to
run only the command /bin/ls (with any arguments). No other commands will work:
art@demo:~$ sudo /sbin/halt
[sudo] password for art:
Sorry, user art is not allowed to execute '/sbin/halt' as root on
demo.
Summary
A quick rundown of what we've learned in this chapter.
Security practices
If you follow good security pracces for your network, each user should have her own
named account with SSH (not password) access. Any special-purpose accounts should be
authorized for the SSH keys of the specic users that need access to them. Login as root
should be disallowed (except on a secure console).
User resources
Puppet can manage users directly using the user resource:
user { 'art':
ensure => present,
...
}
You can specify the user's full name with the comment aribute:
comment => 'Art Vandelay',
Create a home directory with the home and managehome aributes:
home => '/home/art',
managehome => true,
eBooks-IT.org
Managing users
[ 84 ]
Removing or locking accounts
To remove a user, change ensure to absent:
user { 'art':
ensure => absent,
...
}
Just removing the user resource from Puppet won't remove the user account from the
server, so if you need to delete the account, make sure you use ensure => absent.
To lock an account, for example to temporarily disable access, set the ssh_authorized_
key to an empty string and the password to a * character.
Managing SSH keys
You can control the SSH keys authorized to log into the user's account using the
ssh_authorized_key resource type:
ssh_authorized_key { 'art_ssh':
...
}
Specify the key metadata with the user, type, and key aributes:
user => 'art',
type => 'rsa',
key => 'AAAAB3...',
If you need to generate new SSH keys for users, you can do it with the ssh-keygen command:
ssh-keygen -f fabian
Conguring SSH
Puppet can also manage global SSH conguraon by deploying the /etc/ssh/sshd_
config le. You can limit the list of users allowed to log in by specifying the AllowUsers
parameter in this le:
AllowUsers ubuntu art
eBooks-IT.org
Chapter 5
[ 85 ]
Managing privileges with sudo
User privileges, and permission for normal users to run certain commands as root, are
controlled by the /etc/sudoers le. By managing this le (carefully) with Puppet you
can control all user sudo rights on a machine, using a syntax like this:
# User privilege specification
root ALL = (ALL) ALL
ubuntu ALL = (ALL) NOPASSWD:ALL
art ALL = (ALL) NOPASSWD: /bin/ls
eBooks-IT.org
Tasks and templates
You can tell whether a man is clever by his answers. You can tell whether a man
is wise by his questions.
— Naguib Mahfouz
In this chapter, you'll learn how to use Puppet's resource types to run commands, schedule
regular tasks, and distribute large trees of les. You'll also nd out how to insert values
dynamically into les using templates.
11:00 Get groceries
15:00 Pick up kids
18:30 Start dinner 03:00:00 REBOOT WEBSERVER
05:15:00 START BACKUP
07:00:00 WORLD DOMINATION
TASK
SCHEDULE
TODO.TXT
6
eBooks-IT.org
Tasks and templates
[ 88 ]
Running commands with exec resources
We've seen that Puppet lets you model various aspects of a system using resources, such as
user or file resources. You describe how the system should be congured, and Puppet will
run appropriate commands behind the scenes to bring about the desired state.
But what if you want Puppet to run a certain command directly? You can do this using
an exec resource. This is a very exible and powerful resource, and you can use it to
implement almost anything in Puppet. In this secon we'll see how to get the most from
exec resources.
Time for action – running an arbitrary command
1. Modify your manifests/nodes.pp le as follows:
node 'demo' {
exec { 'Run my arbitrary command':
command => '/bin/echo I ran this command on `/bin/date` >/tmp/
command.output.txt',
}
}
2. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Exec[Run my arbitrary command]/
returns: executed successfully
Notice: Finished catalog run in 0.14 seconds
3. Check the output produced (you won't see exactly the same date and me shown
here, unless you're a Time Lord):
ubuntu@demo:~/puppet$ cat /tmp/command.output.txt
I ran this command on Mon Dec 17 16:14:04 UTC 2012
What just happened?
The line exec { 'Run my arbitrary command': declares an exec resource with the
name Run my arbitrary command. The name can be anything; it's not otherwise used
by Puppet, except that like all resource names it can't be the same as another instance of the
same resource type.
The command to run is specied by the following line:
command => '/bin/echo I ran this command on `/bin/date` >/tmp/command.
output.txt',
eBooks-IT.org
Chapter 6
[ 89 ]
Note that the UNIX commands, echo and date, are specied with their full path. This is
because Puppet wants to be sure exactly which command you mean.
When Puppet runs, it applies the exec resource by running the command:
/bin/echo I ran this command on `/bin/date` >/tmp/command.output.txt
This command will write the following text to /tmp/command.output.txt:
I ran this command on Mon Dec 17 16:14:04 UTC 2012
Running commands selectively
The exec resource we've created will be applied every me Puppet runs, but that's not
always what we want. Say we are using an exec resource to download a le, for example.
Once the le is downloaded the rst me we don't need to do it again. Here's an example:
exec { 'Download public key for John':
cwd => '/tmp',
command => '/usr/bin/wget http://bitfieldconsulting.com/files/john.
pub',
creates => '/tmp/john.pub',
}
The creates aribute species the full path to a le. Puppet will check to see if this le
already exists. If it does, the exec won't be run. This is a neat way to have a command run
only if it is needed, and not otherwise.
Did you noce we also added the cwd aribute? This tells Puppet the
directory in which to run the command (cwd stands for current working
directory), so that any les created by the command, like john.pub in this
example, will end up in that directory.
You can also use the unless or onlyif aributes to control when an exec is run. unless
or onlyif both specify a command for Puppet to run to test whether the exec needs to
be applied.
The exit status of the test command determines what Puppet should do. For example:
exec { 'add-cloudera-apt-key':
command => '/usr/bin/apt-key add /tmp/cloudera.pub',
unless => '/usr/bin/apt-key list |grep Cloudera',
}
eBooks-IT.org
Tasks and templates
[ 90 ]
Here, we're using an exec to add an APT repository key to the system keyring. This only
needs to be done once, so the unless command checks whether the key has already been
added. If the grep succeeds, we know the key is already present, so we don't need to do
anything. The exit status will be zero, so Puppet won't apply the exec. On the other hand,
if the grep fails, the exit status will be non-zero so Puppet will apply the exec.
Using onlyif, the opposite logic applies; the exec will be run only if the test command
succeeds (exits with a zero status).
Triggering commands
Another way to control when an exec is run is to use the refreshonly aribute:
exec { 'icinga-config-check':
command => '/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/
sbin/service icinga restart',
refreshonly => true,
subscribe => File['/etc/icinga/icinga.cfg'],
}
When refreshonly is set, Puppet will not apply the exec unless it's triggered by
subscribe or notify from some other resource. In this example, the exec subscribes
to the le /etc/icinga/icinga.cfg. If this le changes, Puppet will run the exec,
but not otherwise.
This is a very useful paern when you want to take some acon if a cong le changes,
especially if you want to sanity-check the le's contents (as in the example) before restarng
the service that reads it.
Chaining commands
Oen you have a series of commands that need to run in a parcular order (for example,
if you're installing soware from source, you might need to download a le, unpack it, build
it, and install it). To do this, for short sequences, you can use the shell && construct as shown
in the preceding example:
/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/sbin/service icinga
restart
This will chain the commands together in the order you specify, bailing out if any of the
commands fail.
eBooks-IT.org
Chapter 6
[ 91 ]
For more complicated sequences, or where you may also need to trigger individual
commands from other resources, you can use the require aribute to specify the
ordering explicitly:
exec { 'command-1':
command => '/bin/echo Step 1',
}
exec { 'command-2':
command => '/bin/echo Step 2',
require => Exec['command-1'],
}
exec { 'command-3':
command => '/bin/echo Step 3',
require => Exec['command-2'],
}
Command search paths
As we've seen, Puppet requires us to specify the full path to any command referenced in an
exec resource. However, if you like, you can provide a list of paths for Puppet to search for
commands, using the path aribute. For example:
exec { 'Run my arbitrary command':
command => 'echo I ran this command on `date` >/tmp/command.output.
txt',
path => ['/bin', '/usr/bin'],
}
Now when Puppet sees a command name, it will search the directories you specify looking
for the matching commands.
If you want to specify a set of default search paths for all exec resources, you can put this in
your site.pp le:
Exec {
path => ['/bin', '/usr/bin'],
}
eBooks-IT.org
Tasks and templates
[ 92 ]
Note the capital E for Exec. This means "make this the default for all exec resources."
Then you can use unqualied commands without an explicit path aribute:
exec { 'Run my arbitrary command':
command => 'echo I ran this command on `date` >/tmp/command.output.
txt',
}
Puppet will use the default paths you specied: /bin and /usr/bin.
Scheduled tasks
Typically, when you want a command to be run at a certain me of day, or at regular
intervals, you can use the UNIX cron facility. For example, a backup job might run every
night at 4 a.m., or a queue processing task might run every 5 minutes.
Puppet can manage cron jobs directly using the cron resource type. Here's an example.
Time for action – scheduling a backup
1. Modify your manifests/nodes.pp le as follows:
node 'demo' {
cron { 'Back up cat-pictures':
command => '/usr/bin/rsync -az /var/www/cat-pictures/ /cat-
pictures-backup/',
hour => '04',
minute => '00',
}
}
2. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Cron[Back up cat-pictures]/
ensure: created
Notice: Finished catalog run in 0.12 seconds
3. Check that the cron job was correctly congured:
ubuntu@demo:~/puppet$ sudo crontab -l
# HEADER: This file was autogenerated on Tue Dec 18 12:50:11 +0000
2012 by puppet.
eBooks-IT.org
Chapter 6
[ 93 ]
# HEADER: While it can still be managed manually, it is definitely
not recommended.
# HEADER: Note particularly that the comments starting with
'Puppet Name' should
# HEADER: not be deleted, as doing so could cause duplicate cron
jobs.
# Puppet Name: Back up cat-pictures
0 4 * * * /usr/bin/rsync -avz /var/www/cat-pictures/ /cat-
pictures-backup/
What just happened?
The line cron { 'Back up cat-pictures': declares a cron resource named Back up
cat-pictures (as with exec resources, the name doesn't maer, but it must be unique).
command => '/usr/bin/rsync -avz /var/www/cat-pictures/ /cat-pictures-
backup/',
The preceding line sets the command to run (in this case, an rsync command to back up
all les and directories under /var/www/cat-pictures to /cat-pictures-backup).
As with exec resources, commands need to be qualied with their full path.
We now go on to specify the me at which the job should run.
hour => '04',
This is in 24-hour format, with 00 being midnight, and 23 being 11 p.m.
minute => '00',
If minute is not specied, it defaults to *; that is, it runs every minute! So always
specify both hour and minute (if there is no hour, the job runs every hour at the
minute you specify).
Note that Puppet adds a helpful header to the crontab le, warning you
not to meddle in the aairs of Puppet. In fact, you can safely add, remove,
and modify any cron jobs not managed by Puppet. Puppet idenes the
cron jobs it's managing by the Puppet Name comment above each job.
So, as the warning suggests, don't remove or edit these comments or
Puppet will think the job is missing and add a new copy of it.
eBooks-IT.org
Tasks and templates
[ 94 ]
More scheduling options
The cron resources can have several other aributes to set the me for the scheduled job:
weekday – the day of the week, for example, Friday
month – not oen used, but can be used to run jobs only during a specic month,
for example, January
monthday – the day of the month, for example 1 to run a job on the rst day of
each month
If any of these aributes are not supplied, they default to *; that is, every weekday, every
month, or every day of the month.
Running jobs at regular intervals
If you want to run a job every 5 minutes, say, you can specify an interval such as this:
minute => '*/5',
hour => '*',
You can use the same paern with the other me aributes, for example, to run a job every
6 hours on the hour:
hour => '*/6',
minute => '00',
Running a job as a specied user
The default user for cron jobs is root, but if you want to run the job as a dierent user,
just give the cron resource a user aribute:
user => 'www-data',
The job will be added to the crontab le for www-data.
Exercise
Use a cron resource to automate the pull-updates job you set up in Chapter 4, Managing
Puppet with Git, which automacally pulls Git changes and applies Puppet on each machine.
Make this part of every machine's base conguraon.
eBooks-IT.org
Chapter 6
[ 95 ]
Distributing les
We've seen in previous chapters how to use Puppet's file resource to deploy a single le to
a server. Somemes, though, you need to copy a whole directory tree of les, without having
to list each individual le in your Puppet manifest. The recurse aribute allows you to do
this. We'll see how to use it in the next example.
Time for action – using a recursive le resource
The cat-pictures applicaon is nearly complete, but it needs some pictures of cats added
in me for the launch. The art department has sent over a set of feline stock photos for you
to deploy to the website.
1. Create the directories for a new cat-pictures module:
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files
2. Create a directory for the images, and some placeholder image les (for extra credit,
download some real pictures of cats from the Internet):
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img/
cat_001.jpg
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img/
cat_002.jpg
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img/
cat_003.jpg
3. Modify your manifests/nodes.pp le as follows:
node 'demo' {
file { '/var/www/cat-pictures':
ensure => directory,
}
file { '/var/www/cat-pictures/img':
source => 'puppet:///modules/cat-pictures/img',
recurse => true,
require => File['/var/www/cat-pictures'],
}
}
eBooks-IT.org
Tasks and templates
[ 96 ]
4. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/File[/var/www/cat-pictures]/
ensure: created
Notice: /Stage[main]//Node[demo]/File[/var/www/cat-pictures/img]/
ensure: created
Notice: /File[/var/www/cat-pictures/img/cat_002.jpg]/ensure:
created
Notice: /File[/var/www/cat-pictures/img/cat_001.jpg]/ensure:
created
Notice: /File[/var/www/cat-pictures/img/cat_003.jpg]/ensure:
created
Notice: Finished catalog run in 0.08 seconds
What just happened?
First we created a top-level directory for the site les to live in:
file { '/var/www/cat-pictures':
ensure => directory,
}
We haven't seen a file resource before without either a source or a content aribute.
ensure => directory will create a directory, as you might expect. If you said ensure =>
present instead, with no other aributes, Puppet would create an empty le.
The following code is the part that does the heavy liing:
file { '/var/www/cat-pictures/img':
source => 'puppet:///modules/cat-pictures/img',
recurse => true,
require => File['/var/www/cat-pictures'],
}
The source aribute is as you've used it before, but the recurse => true aribute tells
Puppet to copy all les and directories contained in the source. This includes our handful of
cat pictures, but it could be thousands of les in a tree of directories many levels deep.
In pracce Puppet is rather slow to manage large le trees, because it has
to examine every le in the tree on every run to determine if it is up to
date with the source. In this situaon, you might be beer o using Git, for
example, to manage large trees of les.
eBooks-IT.org
Chapter 6
[ 97 ]
Using templates
In a previous example we had Puppet deploy an Nginx virtual host le for the
cat-pictures applicaon. In this case we simply used a file resource with
the cat-pictures.conf le distributed from Puppet.
If we wanted to generalize this soluon to manage many dierent websites, it would quickly
become tedious to supply an almost idencal virtual host le for each site, altering only the
name and domain of the site.
What we would prefer is to give Puppet a template le into which it could just insert these
variables for each dierent site. The template funcon serves just this purpose. Anywhere
you have mulple les that dier only slightly, or les that need to contain dynamic
informaon, you can use a template.
Time for action – templating an Nginx virtual host
Things are looking up at cat-pictures.com headquarters. They've just got VC
funding to build three new sites: dog-pictures.com, hamster-pictures.com,
and fish-pictures.com. To prepare for this, your job is to change the Puppet cong for
cat-pictures.com to use a template, so that you can later use the same template for the
new sites.
1. Modify the modules/nginx/manifests/init.pp le as follows:
# Manage nginx webserver
class nginx {
package { 'nginx':
ensure => installed,
}
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx'],
}
file { '/etc/nginx/sites-enabled/default':
ensure => absent,
}
}
eBooks-IT.org
Tasks and templates
[ 98 ]
2. Create a new templates directory in the nginx module:
ubuntu@demo:~/puppet$ mkdir modules/nginx/templates
3. Create the le modules/nginx/templates/vhost.conf.erb with the following
contents:
server {
listen 80;
root /var/www/<%= @site_name %>;
server_name <%= @site_domain %>;
}
4. Modify your manifests/nodes.pp le as follows:
node 'demo' {
include nginx
$site_name = 'cat-pictures'
$site_domain = 'cat-pictures.com'
file { '/etc/nginx/sites-enabled/cat-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
}
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice:/Stage[main]//Node[demo]/File[/etc/nginx/sites-enabled/cat-
pictures.conf]/ensure: defined content as '{md5}0750fd1b8da76b84f2
597de76c1b9bce'
Notice: /Stage[main]/Nginx/File[/etc/nginx/sites-enabled/default]/
ensure: removed
Notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh'
from 1 events
Notice: Finished catalog run in 0.74 seconds
6. Check the resulng virtual host le:
ubuntu@demo:~/puppet$ cat /etc/nginx/sites-enabled/cat-pictures.
conf
server {
listen 80;
root /var/www/cat-pictures;
server_name cat-pictures.com;
}
eBooks-IT.org
Chapter 6
[ 99 ]
What just happened?
First some housekeeping; we previously used the le /etc/nginx/sites-enabled/
default as the virtual host for cat-pictures.com, so we need to remove that:
file { '/etc/nginx/sites-enabled/default':
ensure => absent,
}
We create a template le for the virtual host denion:
server {
listen 80;
root /var/www/<%= @site_name %>;
server_name <%= @site_domain %>;
}
The <%= %> signs mark where parameters will go; we will supply site_name and
site_domain later, when we use the template. Puppet will replace <%= @site_name %>
with the value of the site_name variable.
Then in the nodes.pp le, we include the nginx module on the node:
node 'demo' {
include nginx
Before using the template, we need to set values for the variables site_name and
site_domain:
$site_name = 'cat-pictures'
$site_domain = 'cat-pictures.com'
Note that when we refer to these variables in Puppet code, we use a $ prex ($site_name),
but in the template it's an @ prex (@site_name). This is because in templates we're actually
wring Ruby, not Puppet!
When you use a variable name inside a quoted string, it's a good idea to wrap
it in curly brackets as follows:
"The domain is ${site_domain}"
Not:
"The domain is $site_domain"
This helps to disnguish the variable name from the literal string it's used in
(and any other variables you might be using in the same string).
eBooks-IT.org
Tasks and templates
[ 100 ]
Now we can use the template to generate the Nginx virtual host le:
file { '/etc/nginx/sites-enabled/cat-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
This looks just like any other file resource, with a content aribute, but we previously
gave the contents of the le as a literal string:
content => "Hello, world\n",
Instead, here there is a call to the template funcon:
content => template('nginx/vhost.conf.erb'),
The argument to template tells Puppet where to nd the template le. The path
nginx/vhost.conf.erb
Translates to
modules/nginx/templates/vhost.conf.erb
Puppet now evaluates the template, inserng the values of any variables referenced in
<%= %> signs, and generates the nal output:
server {
listen 80;
root /var/www/cat-pictures;
server_name cat-pictures.com;
}
You might think this is a lot of trouble to go to just to end up with the same le we had
before. Of course, having gone to the trouble of using a template, we can now easily create
virtual hosts for other sites using the same template le:
node 'demo2' {
include nginx
$site_name = 'dog-pictures'
$site_domain = 'dog-pictures.com'
file { '/etc/nginx/sites-enabled/dog-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
}
eBooks-IT.org
Chapter 6
[ 101 ]
Inline templates
You don't need to use a separate template le to take advantage of the power of templates.
The inline_template funcon lets you put a template string right in your Puppet code:
file { '/tmp/the_answer.txt':
content => inline_template("What do you get if you multiply six by
nine? <%= 6 * 7 %>.\n")
}
System facts
It's oen useful to be able to get informaon about the system, such as its IP address
or operang system version. Puppet's companion tool, Facter, provides this informaon.
To see the list of facts available about your system, run the command:
ubuntu@demo:~/puppet$ facter
architecture => amd64
...
uptime_hours => 2109
uptime_seconds => 7593471
virtual => xenu
You can reference any of these facts in a template (or in your Puppet code) just like
a variable:
content => inline_template("My address is <%= @ipaddress %>.\n")
There are a lot of facts. The ones you will most likely use in Puppet manifests are:
architecture – reports the system processor architecture and bitness
(32- or 64-bit)
fqdn – the fully-qualied domain name of the machine; for example,
demo.cat-pictures.com
hostname – just the hostname part; for example, demo
ipaddress – the IP address of the primary or rst network interface. If there
are mulple interfaces, you can nd their addresses with ipaddress_eth0,
ipaddress_eth1, and so on
memorysize – the amount of physical memory present
operatingsystem – the name of the machine's OS (for example, Ubuntu
or CentOS)
operatingsystemrelease – the specic OS version (for example, 12.04
for Ubuntu Precise)
eBooks-IT.org
Tasks and templates
[ 102 ]
Doing the math
Actually, you can do more than just insert variables and facts in templates. Puppet's
templang engine is called ERB, which uses Ruby, and in fact, everything between the
<%= and %> signs is Ruby code. So you can do math:
Two plus two is <%= 2 + 2 %>
Or call Ruby methods:
The time is <%= Time.now %>
Or evaluate Ruby expressions:
$vagrant_vm = inline_template("<%= FileTest.exists?('/tmp/vagrant-
puppet') ? 'true' : 'false' %>")
Putting it all together
You can combine facts, variables, arithmec, string operaons, and Ruby logic to do some
quite sophiscated things in templates. Here's an example that uses the memorysize fact
to modify a conguraon le based on the physical RAM present. Unfortunately for us,
memorysize isn't returned as a simple integer represenng the number of megabytes,
say. It's a string that includes the unit, for example 512.20 MB or 31.40 GB.
So before we can do computaons with this gure, we need to normalize it to an integer
number of megabytes:
<% raw_memsize = @memorysize
if raw_memsize.include?("GB")
mem_in_mb = raw_memsize.to_f * 1024
else
mem_in_mb = raw_memsize.to_f
end
%>
export HADOOP_DATANODE_OPTS="-XX:MaxDirectMemorySize=<%= ( mem_in_mb *
0.25 ).to_i %>M ${HADOOP_DATANODE_OPTS}"
Having copied this code (of mine) from a producon system, I see that it isn't really very
good. It assumes the only units returned will be MB or GB, so it will fail on systems with
memory measured in terabytes (TB), for example. But you get the idea, and your code
will be beer.
eBooks-IT.org
Chapter 6
[ 103 ]
Summary
A quick rundown of what we've learned in this chapter.
Exec resources
Anything you can do on the command line, Puppet can do with an exec resource.
Specify the command to run using the command aribute:
exec { 'Run my arbitrary command':
command => '/bin/echo I ran this command on `/bin/date` >/tmp/
command.output.txt',
}
By default, an exec resource will always be applied, every me you run Puppet. There are
several ways to control whether or when an exec will be applied:
creates runs the exec only if a given le doesn't exist
onlyif runs the exec only if a given command succeeds
unless runs the exec only if a given command fails
To run the command in a specied directory, use the cwd aribute:
exec { 'Download public key for John':
cwd => '/tmp',
command => '/usr/bin/wget http://bitfieldconsulting.com/files/john.
pub',
creates => '/tmp/john.pub',
}
To apply the command only when triggered by some other resource, use the
refreshonly aribute:
exec { 'icinga-config-check':
command => '/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/
sbin/service icinga restart',
refreshonly => true,
subscribe => File['/etc/icinga/icinga.cfg'],
}
This will apply the exec only when the resource it subscribes to (/etc/icinga/icinga.
cfg) is changed. You could have the other resource notify the exec instead, which has the
same eect.
eBooks-IT.org
Tasks and templates
[ 104 ]
For short sequences of commands, you can chain them in a single exec using
the & shell operator:
/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/sbin/service icinga
restart
For longer sequences using mulple exec resources, you can specify the necessary ordering
using require:
exec { 'command-1':
command => '/bin/echo Step 1',
}
exec { 'command-2':
command => '/bin/echo Step 2',
require => Exec['command-1'],
}
Puppet requires you to specify the full path to each command you run in an exec, unless
you specify a list of paths to search for commands using the path aribute:
exec { 'Run my arbitrary command':
command => 'echo I ran this command on `date` >/tmp/command.output.
txt',
path => ['/bin', '/usr/bin'],
}
You can set a default list of paths for all exec resources in your site.pp le:
Exec {
path => ['/bin', '/usr/bin'],
}
Scheduled jobs
To run commands at a specied me of day, or at regular intervals, you can use a cron
resource:
cron { 'Back up cat-pictures':
command => '/usr/bin/rsync -az /var/www/cat-pictures/ /cat-pictures-
backup/',
hour => '04',
minute => '00',
}
You can use any combinaon of these aributes to set the scheduled me for the job: hour,
minute, day, weekday, monthday, month.
eBooks-IT.org
Chapter 6
[ 105 ]
You can run a job at regular intervals (every 5 minutes, for example) with a seng like this:
minute => '*/5',
Cron jobs default to running as root. To make a job execute as a parcular user, specify the
user aribute:
user => 'www-data',
Recursive le resources
To have Puppet copy a whole tree of les, use the recurse aribute on a file resource:
file { '/var/www/cat-pictures/img':
source => 'puppet:///modules/cat-pictures/img',
recurse => true,
require => File['/var/www/cat-pictures'],
}
Templates
Templates can be used wherever you need to insert informaon into a le based on Puppet
variables or Facter facts. You can also use Ruby code in templates to do math or string
computaons, or read and write les, anything, in fact, that Ruby can do. Just specify a
template le using the template funcon:
file { '/etc/nginx/sites-enabled/cat-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
The most common use for templates is simply inserng the value of a variable:
server_name <%= @site_domain %>;
But you can use any valid Ruby code in a template:
The time is <%= Time.now %>
Inline templates don't require a separate template le; you just supply the template to
Puppet as a string in your manifest and call the inline_template funcon to evaluate it:
file { '/tmp/the_answer.txt':
content => inline_template("What do you get if you multiply six by
nine? <%= 6 * 7 %>.\n")
}
eBooks-IT.org
Denitions and Classes
There are basically two types of people. People who accomplish things, and
people who claim to have accomplished things. The first group is less crowded.
— Mark Twain
In this chapter, you'll learn how to group resources into reusable clumps that you can refer
to by name, making it easy to create lots of similar resources at once. You can also make your
Puppet manifests shorter, neater, and more readable by eliminang duplicated code.
node 'web' {
include memcache
include rails
include nginx
include my_app
}
This is so
awesome !
Employee of
the Month
GOLD
STAR
7
eBooks-IT.org
Denions and Classes
[ 108 ]
Grouping resources into arrays
Suppose you have several instances of the same resource, as follows:
package { 'php5-cli':
ensure => installed,
}
package { 'php5-fpm':
ensure => installed,
}
package { 'php-pear':
ensure => installed,
}
You can make your code shorter and simpler by grouping them into a single resource
declaraon with a list of names, as follows:
package { [ 'php5-cli',
'php5-fpm',
'php-pear' ]:
ensure => installed,
}
A comma-separated list in square brackets, shown in the following code line, is called
an array:
[ 'php5-cli', 'php5-fpm', 'php-pear' ]
I've split it over mulple lines to make it more readable, but it's all the same to Puppet.
Arrays are acceptable in many places where otherwise you might use a single value:
require => [ Package['ntp'], File['/etc/ntp.conf'] ],
And they are especially useful when declaring lots of instances of the same resource type,
which only dier in their names:
file { [ '/var/www/myapp',
'/var/www/myapp/releases',
'/var/www/myapp/shared',
'/var/www/myapp/shared/config',
'/var/www/myapp/shared/log',
'/var/www/myapp/shared/pids',
'/var/www/myapp/shared/system' ]:
ensure => directory,
}
eBooks-IT.org
Chapter 7
[ 109 ]
Any aributes you add (le ownership or mode, for example) will be the same for every le
in the array. This is a great way to set aributes for a large number of resources all at once.
Denitions
Grouping resources into arrays is very helpful, but it only works with instances of a single
resource type. What if you want to group resources of dierent types? Let's take an example:
creang scheduled jobs that run a script at a parcular me. For each job, we need to have
Puppet deploy the script le itself to the server:
file { '/usr/local/bin/backup_database':
source => 'puppet:///modules/scripts/backup_database',
mode => '0755',
}
We also need to create a cron resource to run the script:
cron { 'Run backup_database':
command => '/usr/local/bin/backup_database',
hour => '00',
minute => '00',
}
So far, so good. But when you have ten jobs to run, all this typing gets a lile repeve:
file { '/usr/local/bin/job1':
source => 'puppet:///modules/scripts/job1',
mode => '0755',
}
cron { 'Run job1':
command => '/usr/local/bin/job1',
hour => '00',
minute => '00',
}
file { '/usr/local/bin/job2':
source => 'puppet:///modules/scripts/job2',
mode => '0755',
}
cron { 'Run job2':
command => '/usr/local/bin/job2',
hour => '00',
minute => '00',
}
...
# and so on
eBooks-IT.org
Denions and Classes
[ 110 ]
Worse, when you have lots of duplicated code like this, it becomes very dicult to maintain.
If you want to change a parameter for all your jobs (say, to run them all at 1 a.m. instead of
midnight) you have to track down every job in your code and make the same modicaon.
That's tedious and error-prone.
A beer way is to group this pair of resources (the le and the cron job) and give them
a name using the define keyword:
# Manages a script plus the cron job to run it
define script_job() {
file { "/usr/local/bin/${name}":
source => "puppet:///modules/scripts/${name}",
mode => '0755',
}
cron { "Run ${name}":
command => "/usr/local/bin/${name}",
hour => '00',
minute => '00',
}
}
You can see that this is exactly the same as the resources we had before, except that the
name of the job has been replaced with ${name}, and the whole thing is wrapped inside
these lines:
define script_job() {
...
}
The resource that you create using the define keyword is called a denion. A denion
can be used just like a regular resource type:
script_job { 'backup_database':
}
When Puppet sees this, it eecvely replaces it with the following:
file { '/usr/local/bin/backup_database':
source => 'puppet:///modules/scripts/backup_database',
mode => '0755',
}
cron { 'Run backup_database':
command => '/usr/local/bin/backup_database',
hour => '00',
minute => '00',
}
Wherever ${name} occurred in the denion, it's been replaced with backup_database.
eBooks-IT.org
Chapter 7
[ 111 ]
Passing parameters to denitions
So a denion can encapsulate a bunch of dierent resources and each of them has access
to the $name variable. What if you want to add another variable? For example, in the
preceding script_job example, you might want to make the hour a parameter rather
than running all your jobs at midnight.
To do this, add the name of the parameter in round brackets following the name of
the dene:
define script_job( $hour ) {
...
}
You can then refer to $hour anywhere inside the denion and get its value:
cron { "Run ${name}":
command => "/usr/local/bin/${name}",
hour => $hour,
minute => '00',
}
Quotes
If you put double quotes around a string, Puppet will process it for variable
references (replacing ${name} with backup_database, for example).
It will also interpret the special escape sequences such as \n for new line.
If you use single quotes, Puppet will leave the string just as it is. So Puppet
Labs ocial style guidelines say:
All strings that do not contain variables should be enclosed in single quotes.
Double quotes should be used when variable interpolaon is required.
When you declare an instance of script_job, you now have to pass in a value for hour just
like any other resource aribute:
script_job { 'backup_database':
hour => '05',
}
You can pass more than one parameter using a comma-separated list:
define script_job( $hour, $minute ) {
file { "/usr/local/bin/${name}":
source => "puppet:///modules/scripts/${name}",
mode => '0755',
}
eBooks-IT.org
Denions and Classes
[ 112 ]
cron { "Run ${name}":
command => "/usr/local/bin/${name}",
hour => $hour,
minute => $minute,
}
}
And passing mulple parameters to a denion is just like seng mulple aributes on
a regular resource:
script_job { 'backup_database':
hour => '05',
minute => '30',
}
Optional parameters
We don't always care what me a job runs, so it would be nice to have the hour and minute
parameters take some default value (00, say). You can do this by specifying the default value
in the parameter list:
define script_job( $hour = '00', $minute = '00' ) {
...
}
Now if we don't specify an hour or minute for a script_job, they will get the default
values. Here is an instance of script_job declared this way, with no parameters:
script_job { 'backup_database':
}
This results in a job that runs at midnight. However, if you pass in values for hour or minute,
they will override the defaults, and a script_job like this will run every hour:
script_job { 'download_tweets':
hour => "*",
}
Time for action – creating a denition for Nginx websites
Previously we set up an Nginx for the cat-pictures.com site, and created a virtual host
template so we could create many websites that dier only in a couple of parameters. Let's
extend that a lile further, and create a denion that includes everything required for an
Nginx website.
eBooks-IT.org
Chapter 7
[ 113 ]
Following the success of cat-pictures.com and its sister site dog-pictures.com,
the creave department is building a new site where users can upload cute pictures
of all kinds of animals. Your job is to use Puppet to set up a server for the new
adorable-animals.com site.
1. In your Puppet repo, create the le modules/nginx/manifests/website.pp
with the following contents:
# Manage an Nginx virtual host
define nginx::website( $site_domain ) {
include nginx
$site_name = $name
file { "/etc/nginx/sites-enabled/${site_name}.conf":
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
}
2. Modify your manifests/nodes.pp le as follows:
node 'demo' {
nginx::website { 'adorable-animals':
site_domain => 'adorable-animals.com',
}
}
3. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Nginx::Website[adorable-animals]/
File[/etc/nginx/sites-enabled/adorable-animals.conf]/ensure:
defined content as '{md5}53febc966302b52afc5346803606ced3'
Notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh'
from 1 events
Notice: Finished catalog run in 0.35 seconds
What just happened?
When you include this on your node:
nginx::website { 'adorable-animals':
site_domain => 'adorable-animals.com',
}
eBooks-IT.org
Denions and Classes
[ 114 ]
Puppet looks up the denion of nginx::website and nds this:
define nginx::website( $site_domain ) {
The rst step in this denion is:
include nginx
This pulls in the nginx class, which we set up in earlier chapters to manage the Nginx server.
The next line sets up the $site_name variable that we're going to use in the template:
$site_name = $name
You might remember that $name is a special parameter that Puppet sets implicitly for you.
When you declare a resource, you give it a name in quotes aer the resource type:
package { 'nginx':
...
}
So inside that package denion, $name will have the value nginx. Similarly, we declared
this instance of nginx::website with the name adorable-animals:
nginx::website { 'adorable-animals':
So here, $name will have the value adorable-animals. We assign this value to the variable
$site_name.
Next, we declare a file resource for the Nginx virtual host le:
file { "/etc/nginx/sites-enabled/${site_name}.conf":
We know that $site_name has the value adorable-animals, so the actual le Puppet
creates will be /etc/nginx/sites-enabled/adorable-animals.conf.
The contents of this le will be read from a template:
content => template('nginx/vhost.conf.erb'),
The template le, which we created previously, contains:
server {
listen 80;
root /var/www/<%= site_name %>;
server_name <%= site_domain %>;
}
eBooks-IT.org
Chapter 7
[ 115 ]
Puppet will interpolate the values for site_name and site_domain into this template,
as we saw in the previous chapter. So the actual contents of the le will be:
server {
listen 80;
root /var/www/adorable-animals;
server_name adorable-animals.com;
}
Multiple instances of denitions
Of course, now that we've made it so easy to set up websites in just a couple of lines of
Puppet code, we can make a few more of them:
nginx::website { 'adorable-animals-staging':
site_domain => 'staging.adorable-animals.com',
}
nginx::website { 'amusing-animals':
site_domain => 'funny.adorable-animals.com',
}
Exercise
Extend the nginx::website denion so that it restarts or reloads the nginx service
whenever a virtual host le changes.
Classes
We've seen classes before, when we used the class keyword to group together the Puppet
resources that implement some parcular service, such as Nginx:
# Manage nginx webserver
class nginx {
package { 'nginx':
ensure => installed,
}
}
Dening classes
The class keyword introduces a new class denion:
class nginx {
...
}
eBooks-IT.org
Denions and Classes
[ 116 ]
You can also specify some parameters that the class accepts:
class appserver($domain,$database) {
...
}
The parameters can take default values, as with a denion:
class hadoop($role = 'node') {
...
}
Putting classes inside modules
It's a good idea to organize your classes into modules, just as we did with the nginx class.
Each class should be stored in the modules/MODULE_NAME/manifests directory, in a le
named aer the class, with each le containing just one class.
So if we create an nginx::loadbalancer class, the denion should look like this:
class nginx::loadbalancer {
...
}
It should go in the le modules/nginx/manifests/loadbalancer.pp.
The excepon is the class named aer the module (for example, nginx). This should be in
the le modules/nginx/manifests/init.pp.
Declaring classes
There are dierent ways to declare a class (that is, to create an instance of it and apply it to
the current node) once you've dened it. If you don't need to give the class parameters, the
simplest way is to use include, as we did before:
include nginx
Alternavely, you can use require. This behaves just like include, except it species that
everything in the required class must be applied immediately, before Puppet moves on to
the next part of the code:
require nginx
If the class does need parameters, declare it like this (a bit like a resource):
class { 'cluster_node':
role => 'master',
}
eBooks-IT.org
Chapter 7
[ 117 ]
You can include the same class from several dierent places, and Puppet won't mind. But
you can only use a resource-like declaraon once (because resources have to be unique).
What's the difference between a class and a denition?
So far, a class looks much like a denion. What's the dierence? Why would you use a class
instead of a denion, or vice versa?
Well, there is some overlap between them. Both classes and denions bundle a group of
dierent resources into a single named enty that you can create instances of, with some
oponal parameters. In older versions of Puppet, classes didn't take parameters, which made
the two types more disnct.
However, there are important dierences. Classes are singletons; that is, Puppet only allows
one instance of a class to exist on a node at a me.
This can be very useful when the class has system-wide eects (installing Nginx, for example)
and you want to prevent it from being used mulple mes. If you had two Nginx classes,
each specifying a dierent version of Nginx, that could cause problems.
Denions, by contrast, can have as many instances as you like. We saw this earlier when we
created mulple websites on the same machine using the nginx::website denion.
So if you're wondering which to use, consider:
Will you need to have mulple instances of this on the same node (for example,
a website)? If so, use a denion.
Could this cause conicts with other instances of the same thing on this node
(for example, a web server)? If so, use a class.
Time for action – creating an NTP class
Let's build an example class that manages the NTP me service. The class will take an
oponal parameter specifying an NTP server to sync from.
1. First, create the directories for an ntp module:
ubuntu@demo:~/puppet$ mkdir modules/ntp
ubuntu@demo:~/puppet$ mkdir modules/ntp/manifests
ubuntu@demo:~/puppet$ mkdir modules/ntp/templates
eBooks-IT.org
Denions and Classes
[ 118 ]
2. Create the le modules/ntp/manifests/init.pp with the following contents:
# Manage NTP server
class ntp($server='UNSET') {
package { 'ntp':
ensure => installed,
}
file { '/etc/ntp.conf':
content => template('ntp/ntp.conf.erb'),
notify => Service['ntp'],
}
service { 'ntp':
ensure => running,
enable => true,
require => [ Package['ntp'], File['/etc/ntp.conf'] ],
}
}
3. Create the le modules/ntp/templates/ntp.conf.erb with the following
contents:
driftfile /var/lib/ntp/ntp.drift
<% if server != 'UNSET' -%>
server <%= server %> prefer
<% end -%>
server 0.ubuntu.pool.ntp.org
server 1.ubuntu.pool.ntp.org
server 2.ubuntu.pool.ntp.org
server 3.ubuntu.pool.ntp.org
server ntp.ubuntu.com
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery
restrict 127.0.0.1
restrict ::1
4. Modify your manifests/nodes.pp as follows:
node 'demo' {
class { 'ntp':
server => 'us.pool.ntp.org',
}
}
eBooks-IT.org
Chapter 7
[ 119 ]
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Ntp/Package[ntp]/ensure: created
Notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/ensure: defined
content as '{md5}65e3b66fbf63d0c6c667179b5d0c5216'
Notice: /Stage[main]/Ntp/Service[ntp]: Triggered 'refresh' from 1
events
Notice: Finished catalog run in 4.99 seconds
What just happened?
Let's take a detailed look at the ntp class denion. First, we give the class name and its
parameters:
class ntp($server='UNSET') {
...
}
The class takes one parameter, server, with a default value of UNSET (so the parameter is
oponal). It's a good idea to set your default values to something like UNSET, which makes it
very obvious that a value hasn't been provided, rather than using an empty string.
The class will install the ntp package:
package { 'ntp':
ensure => installed,
}
We will now set up the conguraon le /etc/ntp.conf, using a template:
file { '/etc/ntp.conf':
content => template('ntp/ntp.conf.erb'),
notify => Service['ntp'],
}
The template contains the following logic:
<% if server != 'UNSET' -%>
server <%= server %> prefer
<% end -%>
This means that if the value of $server is UNSET, everything between the <% if -%> and
<% end -%> tags will be ignored, and the le will contain only the default NTP sengs.
eBooks-IT.org
Denions and Classes
[ 120 ]
If $server is anything other than UNSET, a line like this will be added to the le:
server us.pool.ntp.org prefer
Here us.pool.ntp.org is the value of $server that we passed in to the class.
Finally, we manage the ntp service itself:
service { 'ntp':
ensure => running,
enable => true,
require => [ Package['ntp'], File['/etc/ntp.conf'] ],
}
Note that this depends on both the ntp package (we can hardly start the service unl the
soware's installed) and the ntp.conf le. As we saw in Chapter 3, Packages, Files, and
Services, require implies notify, so if the ntp.conf le is changed later on, the service
will be restarted to pick up the changes.
Summary
A quick rundown of what we've learned in this chapter.
Arrays
You can refer to or declare a number of idencal resources concisely by giving them
as an array:
package { [ 'php5-cli', 'php5-fpm', 'php-pear' ]:
ensure => installed,
}
Denitions
You can group together resources of any type by using the define keyword to
create a denion:
define script_job() {
RESOURCE1
RESOURCE2
...
}
eBooks-IT.org
Chapter 7
[ 121 ]
You create an instance of a denion by declaring it just as though it were a built-in resource:
script_job { 'backup_database': }
Denions can take parameters, if you specify them in () aer the denion name:
dene script_job( $hour, $minute ) {
...
}
You can make these parameters oponal by giving default values for them:
define script_job( $hour = '00', $minute = '00' ) {
...
}
To pass parameters to the denion, specify them just like normal resource aributes:
script_job { 'backup_database':
hour => '05',
minute => '30',
}
Classes
Classes are like denions, and you introduce them with the class keyword:
class nginx::loadbalancer {
If the class takes no parameters, you can use the include or require keywords to create
an instance of the class on a node:
include postfix
require loadbalancer::nginx
If the class takes parameters, you use the class keyword to instanate it, but in a
resource-like way:
class { 'cluster_node':
role => 'master',
}
You can include or require the same class in many dierent places without a problem,
but if the class takes parameters this isn't the case. There can only be one instance of a
parameterized class on each node. This makes parameterized classes more suitable for
things that make system-wide changes that could potenally conict with other instances
of the same class.
eBooks-IT.org
Expressions and Logic
A young man should read five hours in a day, and so may acquire a great deal
of knowledge.
— Samuel Johnson
In this chapter, you'll learn how to make choices in your Puppet manifests, how to do
arithmec, logic, and string operaons in the Puppet language, and how to use regular
expressions to match paerns in strings. You'll also nd out about some useful Puppet
data types: arrays and hashes.
$#!?
$eggs = 61
$bacon = 1.80
$eggs
+$bacon = ?
Conditionals
It's useful to be able to do dierent things in a manifest depending on the value of some
variable or expression. Puppet provides several ways to do this. The rst is the if statement.
8
eBooks-IT.org
Expressions and Logic
[ 124 ]
If statements
An if statement has the following form:
if EXPRESSION {
OPTIONAL_SOMETHING
}
The part of the manifest represented by OPTIONAL_SOMETHING will only be applied if the
value of EXPRESSION is true. We'll learn more about expressions later in the chapter, but for
now let's take a simple example:
if $eggs == 61 {
notify { 'Glory be, eggs have just gone up to 61¢ a dozen!': }
}
Here the EXPRESSION is:
$eggs == 61
The == operator means "is equal to".
Note the dierence between $eggs == 61 and $eggs = 61
$eggs = 61 has a dierent meaning to Puppet. The single = operator has
the eect of assigning the value 61 to the variable $eggs, while the double
== operator tests equality. So in condional expressions—expressions in an
if statement, for example—we always use ==, not =.
Puppet reads the expression $eggs == 61, and decides whether it evaluates to true or
false. If the variable $eggs does have the value 61, the expression will be true, and if it
doesn't, it will be false.
If the expression is true, Puppet will apply everything inside the braces:
notify { 'Glory be, eggs have just gone up to 61¢ a dozen!': }
If the expression is false, Puppet will simply skip the contents of the braces and proceed to
the next part of the manifest. So if is called a condional statement; it makes part of the
manifest condional on some expression being true.
else and elsif
You can extend the if statement by using else:
if $::operatingsystem == 'zx81' {
notify { 'Enabling experimental Puppet ZX81 support': }
} else {
notify { 'ZX81 not detected': }
}
eBooks-IT.org
Chapter 8
[ 125 ]
The contents of the else branch will only be applied if the condion is not true.
To build up more complex condional statements, you can use elsif to add more tests.
Puppet will try them in sequence:
if $::processorcount >= 16 {
include cpu_intensive_application
} elsif $::processorcount >= 4 {
include medium_application
} else {
include lightweight_application
}
As you can see, the extended form of the if statement looks like this:
if EXPRESSION {
OPTIONAL_SOMETHING
} elsif ANOTHER_EXPRESSION {
OPTIONAL_SOMETHING_ELSE
} else {
OPTIONAL_OTHER_THING
}
You can have as many elsif branches as you want; Puppet will test each of the condions in
order, and if none of them matches the else branch (if there is one) will be applied instead.
Unless statements
As you might imagine, unless is like if, but with the opposite sense. The block is not
applied if the expression is true. An unless statement has this form:
unless EXPRESSION {
OPTIONAL_SOMETHING
}
Again, EXPRESSION is a logical expression (one that can evaluate to true or false).
This me, the OPTIONAL_SOMETHING is only applied if EXPRESSION is false.
You can't use elsif or else with unless; Puppet treats this as a syntax error.
Case statements
If you just have one or two choices to make, the if statement is ideal. However, if you need
to choose between several alternaves, it becomes awkward to write:
if $::operatingsystem == 'Ubuntu' {
include os_specific::ubuntu
eBooks-IT.org
Expressions and Logic
[ 126 ]
} elsif $::operatingsystem == 'Debian' {
include os_specific::debian
} elsif $::operatingsystem == 'RedHat' {
include os_specific::redhat
} else {
include os_specific::default
}
For situaons like this, Puppet provides the case statement:
case $::operatingsystem {
'Ubuntu': { include os_specific::ubuntu }
'Debian': { include os_specific::debian }
'RedHat': { include os_specific::redhat }
default : { include os_specific::default }
}
case takes an expression and tries to match it against a list of values (the cases). If one
matches, Puppet will apply the corresponding code block. If there is a default case, this
will be applied if none of the other cases match.
The general form of a case statement is:
case EXPRESSION {
CASE1 { BLOCK1 }
CASE2 { BLOCK2 }
CASE3 { BLOCK3 }
...
default : { ... }
}
Note that EXPRESSION can be any expression; it's not restricted to logical expressions as the
if and unless statements are.
The code blocks BLOCK1, BLOCK2, and BLOCK3 can be any Puppet code, though it's a good
idea to keep the blocks short enough so that you can see the whole case statement at once.
If you need to have a lot of code in the blocks, you can use include to apply classes you've
dened somewhere else.
Puppet will apply only the rst case that matches, and ignore any subsequent ones, so if it's
possible for there to be mulple matches you should list them in order of preference. The
default case must always come at the end.
eBooks-IT.org
Chapter 8
[ 127 ]
The default case
It's good pracce to always have a default case. If your case statement is supposed to
always match something, then you can have the default case signal an error using the
fail funcon:
default: { fail('This should never happen') }
This will halt Puppet with the error message you specify.
Alternavely, you can provide an empty code block to default:
default: { }
This makes it clear to anyone reading your code that no acon is needed if none of the cases
match. It's a good principle of programming that "explicit is beer than implicit."
Matching multiple cases
You can specify two or more cases that will trigger the same code block by separang the
values with commas as shown in the following code snippet:
case $::operatingsystem {
'Debian', 'Ubuntu': {
include os_specific::debianlike
}
}
Selectors
Somemes you want to choose between a number of dierent values depending on the
result of some expression. You could do it with a case statement that sets a variable:
case $::operatingsystem {
'Ubuntu': {
$os_type = 'Debianlike'
}
'RedHat': {
$os_type = 'Redhatlike'
}
'Darwin': {
$os_type = 'Mac OS'
}
default: {
$os_type = 'UNKNOWN'
}
}
notify { "You're running a ${os_type} system": }
eBooks-IT.org
Expressions and Logic
[ 128 ]
But this is a bit tedious and repeve. For situaons like this, Puppet provides the selector,
which is like a case statement, but instead of matching a case and applying a code block, it
matches a case and returns a value.
$os_type = $::operatingsystem ? {
'Ubuntu' => 'Debianlike',
'RedHat' => 'Redhatlike',
'Darwin' => 'Mac OS',
default => 'UNKNOWN',
}
notify { "You're running a ${os_type} system": }
As with a case statement, Puppet goes through the list in order, returning the rst match it
nds. If nothing matches, the value for default is returned.
You can use a selector anywhere that expects a value, but it's good style to assign the value
of a selector to a variable, and then use that variable, as in the $os_type example.
Expressions
It's me to look at expressions in a lile more detail, and see what kind of expressions
Puppet allows us to construct.
Comparisons
An important kind of expression is the comparison expression. This compares two values,
and the expression is true or false depending on the result of the comparison.
Equality
We've already seen an expression involving a comparison of two values:
$eggs == '61'
And we know the == operator means "is equal to." Its opposite is the != operator
(not equal to):
$username != 'FOTHERINGTON-THOMAS'
Comparison expressions like these are logical expressions; their value is either true or false.
By the way, true and false are reserved words in Puppet that stand for these logical
values. You can use them like any other literal values:
$raining = true
if $raining {
include umbrella
}
eBooks-IT.org
Chapter 8
[ 129 ]
Magnitude
You can also compare values using the following operators:
> (greater than)
< (less than)
>= (greater than or equal to)
<= (less than or equal to)
Expressions with these operators are also logical expressions:
if $eggs >= 61 {
notify { 'YOU KNOW I GOT NO SENSE OF EGGS': }
}
However, their operands (the values they work on) can only be numbers. If you try to say
something as follows:
if $eggs > 'TALBOT?' {
...
}
Puppet will not self-destruct like a computer in a bad sci- movie, but it will complain:
Error: comparison of Fixnum with String failed
Substrings
Similarly, there is another comparison operator that only works with string operands, in.
if 'eggs' in 'Can you believe the price of eggs?' {
...
}
in tests whether the rst operand is a substring of the other. For example, this expression
is true:
'spring' in 'springfield'
But this expression is false:
'Paris' in 'the spring'
eBooks-IT.org
Expressions and Logic
[ 130 ]
Boolean operators
Boolean, or logical, operators work on logical values (things that evaluate to true or false).
You can use them to build up more complex expressions from simpler components. For
example, the and operator takes two logical expressions as operands:
$eggs > 61 and $eggs < 100
This expression is true if both operands are true. So the expression will be true if $eggs is
both greater than 61 and less than 100.
If one or both of the operands is false, the and expression will also be false.
$eggs > 61 and $eggs < 100
The preceding expression will be false if $eggs is 120. Although $eggs > 61 is true, $eggs
< 100 is false, so the and expression evaluates to false.
The or operator is a lile more forgiving. It is true if either (or both) of its operands is true:
$eggs > 61 or $today == 'Thursday'
The ! (not) operator takes only one operand, and ips its value. If $raining is true, then !
$raining is false, and vice versa.
Combining Boolean operators
You can combine Boolean operators, but it's helpful to use parentheses to group dierent
subexpressions together. This makes it clear to you, to Puppet, and to anyone else reading
the code what's intended. For example:
$today == 'Thursday' and ($eggs < 61 or $eggs > 100)
The preceding expression is not the same as this:
($today == 'Thursday' and $eggs < 61) or $eggs > 100
Arithmetic operators
Puppet's arithmec operators all work with numeric operands, and the value of an
arithmec expression is always a number (so you can't use it as a test in a condional
statement, for example). You can use the following familiar operators:
+ (addion)
- (subtracon)
* (mulplicaon)
/ (division)
eBooks-IT.org
Chapter 8
[ 131 ]
You can combine these operators in any way you like:
$celsius = ($fahrenheit - 32) * 5 / 9
There are also two bitwise shi operators, << and >>, which mulply and divide integers by
powers of 2.
$x << $y
The preceding expression mulplies $x by 2 to the power of $y. So $x = 1 << 3 evaluates
to 1 mes 2 cubed, which is 8.
Regular expressions
We've seen a couple of dierent ways of tesng string values already. You can compare
strings for equality:
if $role == 'webserver' {
...
}
You can also test whether one string is a substring of another:
if 'dunk' in 'doughnuts' {
...
}
But what if you want to test for paerns of characters? Say, app followed by any characters,
followed by staging. Puppet has a special paern-matching language you can use for this:
if $::hostname =~ /app.*staging/ {
...
}
This expression will be true if $::hostname is any of the following, and many more:
app_staging
app-1-staging
application_staging
appstaging
my_app_staging_server
eBooks-IT.org
Expressions and Logic
[ 132 ]
Note the slash characters surrounding the paern:
/app.*staging/
This kind of paern is called a regular expression, or regex for short, and Puppet uses the
slash character (/) to mark the start and end of regular expressions.
Operators
The operator which tests whether a string matches a regex, as in the previous example,
is the regex match operator, =~:
VALUE =~ /REGEX/
The operator with the opposite sense is the regex non-match operator, !~:
VALUE !~ /REGEX/
Syntax
The simplest regular expression is just the literal string that you want to match:
$animal =~ /cat/
Wildcards (which match any single character) are represented by a dot:
/c.t/
The preceding expression matches cat, cot, cut, crt, and so on. To match from zero to any
number of wildcard characters, use .*:
/c.*t/
The preceding expression matches ct, cat, count, constitutionalist, and so on.
To match digits only, use the sequence \d:
/app\d*/
The preceding expression matches app, app1, app200, app99999, and so on.
The parcular avor of regular expression language that Puppet recognizes is
the same as that implemented by Ruby, so any valid Ruby regex is just ne with
Puppet. You can nd a good introducon to Ruby regular expression syntax here:
http://www.tutorialspoint.com/ruby/ruby_regular_
expressions.htm
eBooks-IT.org
Chapter 8
[ 133 ]
Conditionals
As we've seen, expressions involving regexes are very useful as the test in a
condional statement:
if VALUE =~ /REGEX/ {
DO_SOMETHING
}
So you can use regular expressions with if and unless statements, but you can also use
them as cases in case statements:
case $::ec2_placement_availability_zone {
/us-.*/: { notify { 'In United States': } }
/eu-.*/: { notify { 'In Europe': } }
default: { notify { 'Some other region': } }
}
A regular expression can also be a case in a selector:
$ec2_family = $::ec2_instance_type ? {
/t1/ => 'micro',
/m1/ => 'first generation',
/m2/ => 'high-memory',
/m3/ => 'second generation',
/c1/ => 'high-cpu',
default => 'other',
}
Capture variables
If you need to refer to the actual text that was matched, it will be available in the special
variable called $0. This is called a capture variable. Within the scope of the condional
statement, you can refer to $0 to get the string that the regex successfully matched, if any:
$uname = generate('/bin/uname','-a')
if $uname =~ /\d+\.\d+\.\d+/ {
notify { "I have kernel version ${0}": }
}
The output of the command uname -a on a Linux server usually looks something like this:
Linux demo 3.2.0-29-virtual #46-Ubuntu SMP Fri Jul 27 17:23:50 UTC 2012
x86_64 x86_64 x86_64 GNU/Linux
eBooks-IT.org
Expressions and Logic
[ 134 ]
So the code above looks for a string that matches the regular expression:
/\d+\.\d+\.\d+/
This expression matches three numbers separated by periods. In this case the match
text will be:
3.2.0
So the output is:
Notice: I have kernel version 3.2.0
You can also capture smaller parts of the regular expression, by pung it in parentheses,
like this:
/abc(def)ghi/
The value of anything matched by the characters in parentheses will be available as $1:
$uname = generate('/bin/uname','-a')
if $uname =~ /(\d+)\.\d+\.\d+/ {
notify { "I have kernel version ${0}, major version ${1}": }
}
Notice: I have kernel version 3.2.0, major version 3
You can use more than one set of parentheses, and the values for each will be available as
$1, $2, and so on.
These capture variables are only good within the block of the condional expression, so use
them or lose them. If you need to preserve one of these values for later, you can assign it to
a regular variable:
if $uname =~ /(\d+)\.\d+\.\d+/ {
$major_version = $1
}
Substitutions
Somemes it's handy to be able to search and replace text within strings. Puppet gives you
this capability with the regsubst funcon, which matches text with a regular expression
and replaces it with the value you specify:
regsubst(STRING, REGEX, REPLACEMENT)
eBooks-IT.org
Chapter 8
[ 135 ]
The STRING argument is the input. The REGEX is what you want to match in the input.
The REPLACEMENT is what you want to replace any matched text with. For example:
$output = regsubst('Look at my cat picture', 'cat', 'dog')
notify { $output: }
The output from the preceding code snippet will be:
Notice: Look at my dog picture
The regex can simply be a literal string, as in this example, or it can be more complicated:
$output = regsubst('Look at my cat picture','my .* picture','something
more interesting')
notify { $output: }
The output from the preceding code snippet will be:
Notice: Look at something more interesting
You can also use capture variables, as in condional statements. Here, the contents of
successive capture variables are named \1, \2, and so on.
$output = regsubst('Look at my cat picture','my (.*) picture','this
adorable \1')
notify { $output: }
The output from the preceding code snippet will be:
Notice: Look at this adorable cat
There are a few syntax dierences when using regular expressions with
regsubst. Instead of pung the regular expression within slashes (/
REGEX/) you use quotes ('REGEX'). And as we just saw, the capture
variables are named \1, \2, \3 instead of $1, $2, $3. The makers of Puppet
put these lile dierences in to make sure you're paying aenon.
Node denitions
A handy place to use regular expressions is in node denions. You can apply a node
denion not merely to a hostname:
node 'demo' {
...
}
eBooks-IT.org
Expressions and Logic
[ 136 ]
Or to a list of hostnames:
node 'demo1', 'demo2', 'demo3' {
...
}
You can also apply a node denion to hostnames matching a regular expression:
node /demo.*/ {
...
}
This is very useful when you have a number of otherwise idencal servers whose hostnames
match some paern:
node /web.*/ {
include webserver
}
node /app.*/ {
include appserver
}
node /db.*/ {
include dbserver
}
Node denions don't support capture variables, so you can't capture the
matched text and use it inside the node denion as you can in a condional
statement. If you want to capture some part of the hostname, you can do
this with regsubst and the $::hostname fact.
Arrays and hashes
So far we've dealt mostly with strings and numbers, but Puppet has a couple of other
data types you can use, which are ways of grouping values together: arrays and hashes.
Grouping resources with arrays
We've encountered arrays before, when we used them to concisely declare several
similar resources:
package { [ 'php5-cli', 'php5-fpm', 'php-pear' ]:
ensure => installed,
}
eBooks-IT.org
Chapter 8
[ 137 ]
To make an array, all you need to do is put square brackets round it:
['jerry', 'george', 'elaine']
If you use an array in the context where a resource name is expected, this has the eect of
declaring a resource for each member of the array:
$developers = ['jerry', 'george', 'elaine']
notify { $developers: }
The output from the preceding code snippet will be:
Notice: george
Notice: jerry
Notice: elaine
This is why the trick of declaring an array of package names works: it declares a package
resource for each member of the array.
However, this doesn't work if the array is interpolated into a string. In that case, the
members of the array are all simply clumped together in the string:
$developers = ['jerry', 'george', 'elaine']
notify { "The developers are: $developers": }
The output from the preceding code snippet will be:
Notice: The developers are: jerrygeorgeelaine
Getting values out of arrays
To retrieve a specic element of an array (for example, the rst element), put the element
number in square brackets aer the array name:
$developers[0]
The elements are numbered from 0 upwards, with 0 being the rst element, 1 the second,
and so on. If this seems odd to you, you can always refer to element 0 as the zeroth element
instead. Computer sciensts and mathemacians will understand you perfectly.
You can also number elements backwards from the end of the array. For example, the last
element of an array is element [-1]:
$developers = ['jerry', 'george', 'elaine']
notify { "The last developer is: ${developers[-1]}": }
eBooks-IT.org
Expressions and Logic
[ 138 ]
The output from the preceding code snippet will be:
Notice: The last developer is: elaine
The second-to-last element is [-2], and so on.
You can also use the in operator to test if some value is a member of an array:
if $crewmember in ['Frank', 'Dave'] {
notify { "I'm sorry, ${crewmember}. I'm afraid I can't do that.": }
}
Hashes
A hash is a set of pairs of elements. The rst member of each pair is called the key, and the
second is the value. Here's an example:
$interfaces = {
'lo0' => '127.0.0.1',
'eth0' => '192.168.0.1',
}
You can think of a hash as being like an array, but instead of looking up elements by number,
you look them up by name (the key):
$address = $interfaces['eth0']
notify { "Interface eth0 has address ${address}": }
The output from the preceding code snippet will be:
Notice: Interface eth0 has address 192.168.0.1
The key must be a string, but the value can be any data type:
$contrived_example = {
'fish' => 'babel',
'answer' => 42,
'crew' => ['Ford Prefect', 'Arthur Dent'],
'hash' => { 'Warning' => 'Beware of the leopard' }
}
Multilevel hashes
As you can see, the value can be a string, a number, an array, or even another hash. This
means you can construct mullevel hashes, where you use a series of increasingly specic
keys to get what you want. For example:
eBooks-IT.org
Chapter 8
[ 139 ]
$interfaces = {
'lo0' => {
'address' => '127.0.0.1',
'netmask' => '255.0.0.0',
},
'eth0' => {
'address' => '192.168.0.1',
'netmask' => '255.255.255.0',
}
}
$eth0_netmask = $interfaces['eth0']['netmask']
notify { "eth0 has netmask ${eth0_netmask}": }
The output from the preceding code snippet will be:
Notice: eth0 has netmask 255.255.255.0
Note the syntax for looking up keys in a mullevel hash:
$interfaces['eth0']['netmask']
Testing hash keys
The in operator also works with hashes, and tests whether the hash has a certain key:
if 'eth0' in $interfaces {
...
}
Summary
A quick rundown of what we've learned in this chapter.
Conditionals
You can condionally apply a block of Puppet code using an if statement:
if EXPRESSION {
OPTIONAL_SOMETHING
}
You can add extra elsif clauses and an oponal else clause:
if EXPRESSION {
OPTIONAL_SOMETHING
} elsif ANOTHER_EXPRESSION {
eBooks-IT.org
Expressions and Logic
[ 140 ]
OPTIONAL_SOMETHING_ELSE
} else {
OPTIONAL_OTHER_THING
}
The else clause, if present, will be applied if none of the condions match.
The case statement lets you condionally apply code if any of a number of possible cases
are matched:
case EXPRESSION {
CASE1 { BLOCK1 }
CASE2 { BLOCK2 }
CASE3 { BLOCK3 }
...
default : { ... }
}
With a selector, you can test a number of cases and return a value:
$result = EXPRESSION ? {
CASE1 => VALUE1,
CASE2 => VALUE2,
CASE3 => VALUE3,
default => DEFAULT_VALUE,
}
Operators
You can build expressions using dierent kinds of operators:
Comparison operators (==, !=, <, <=, >, >=)
Boolean operators (and, or, !)
String, array, or hash membership operators (in)
Arithmec operators (+, -, *, /, <<, >>)
Regular expressions
You can use regular expressions to match paerns of characters:
if $::hostname =~ /app.*staging/ {
eBooks-IT.org
Chapter 8
[ 141 ]
Regular expressions can also be the cases for selectors and case statements:
case $::ec2_placement_availability_zone {
/us-.*/: { notify { 'In United States': } }
...
}
$ec2_family = $::ec2_instance_type ? {
/t1/ => 'micro',
...
}
The text matched by a regular expression, or part of a regular expression grouped with
parentheses, is available in the capture variables $0, $1, $2, and so on.
if $uname =~ /(\d+)\.\d+\.\d+/ {
notify { "I have kernel version ${0}, major version ${1}": }
}
Text substitution
To substute text in strings, use the regsubst funcon with a suitable regular expression:
$output = regsubst('Look at my cat picture','my (.*) picture','this
adorable \1')
notify { $output: }
The output from the preceding code snippet will be:
Notice: Look at this adorable cat
Regular expressions can also be used to match node denions:
node /web.*/ {
include webserver
}
Arrays
Arrays are sets of values surrounded by square brackets:
['jerry', 'george', 'elaine']
You can oen use an array in place of a single value:
package { [ 'php5-cli', 'php5-fpm', 'php-pear' ]:
ensure => installed,
}
eBooks-IT.org
Expressions and Logic
[ 142 ]
To look up an element in an array by number (starng from zero), use square brackets aer
the array name:
$developers[0]
Hashes
A hash is a set of key/value pairs grouped inside curly braces:
$interfaces = {
'lo0' => '127.0.0.1',
'eth0' => '192.168.0.1',
}
You look up hash values with a string key in square brackets aer the hash name:
$address = $interfaces['eth0']
Hash keys must be strings, but hash values can be strings, numbers, arrays, or other hashes:
$interfaces = {
'lo0' => {
'address' => '127.0.0.1',
'netmask' => '255.0.0.0',
},
'eth0' => {
'address' => '192.168.0.1',
'netmask' => '255.255.255.0',
}
}
To look up a value in a mullevel hash, use consecuve keys in square brackets:
$interfaces['eth0']['netmask']
eBooks-IT.org
Reporting and troubleshooting
Often, the most important piece of information is that something has
gone wrong.
— Frank Herbert, "God Emperor of Dune"
In this chapter, you'll learn how to get informaon on what Puppet's doing, when it runs,
the changes it makes, how to monitor Puppet, and what to do about many common errors
you may encounter.
BASIC SOLDERING
ELECTRONICS
FOR DUMMIES
UH-OH...
9
eBooks-IT.org
Reporng and troubleshoong
[ 144 ]
Reporting
Most of the me you'll probably be happy for Puppet to just run and do its job. In some
situaons, however, it can be very useful to have Puppet record informaon about exactly
what it did and when it did it. This facility in Puppet is called reporng.
For example, if something is not working as you expected, you can look at Puppet's reports
and get a very detailed picture of what's going on. Or you might want to monitor what
Puppet is doing across your whole network and record performance informaon over me.
You can also see if Puppet runs are failing, and diagnose the reason.
Summary reports
You can get a quick overview of what Puppet is doing on a given run by using the
--summarize ag to puppet apply. It will report some overall stascs on ming and
resources changed:
ubuntu@demo:~/puppet$ papply --summarize
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: defined content
as '{md5}5d41402abc4b2a76b9719d911017c592'
Notice: Finished catalog run in 0.06 seconds
Changes:
Total: 1
Events:
Success: 1
Total: 1
Resources:
Changed: 1
Out of sync: 1
Skipped: 6
Total: 9
Time:
Filebucket: 0.00
File: 0.00
Config retrieval: 0.15
Total: 0.16
Last run: 1360157807
Version:
Config: 1360157805
Puppet: 3.0.2
eBooks-IT.org
Chapter 9
[ 145 ]
This can be helpful if you want to make sure that Puppet is doing what you think it should.
However, if you need more informaon, especially about changes to specic resources, you'll
need to enable full reports. We'll see how to do this in the next secon.
Enabling reports
Reporng is enabled in the Ubuntu Puppet package by default, but if you're using another
distribuon or installing Puppet from another source, this may not be the case. To check
your seng, run the following command:
ubuntu@demo:~/puppet$ sudo puppet config print report
true
If the seng is false, and you want to enable reporng, edit the le /etc/puppet/
puppet.conf and add the following seng:
[main]
report=true
What's in a report?
Puppet produces detailed reports every me it runs, recording the following:
The date and me of the run
How long the run took
The version of Puppet
Whether the run failed, changed resources, or le them unchanged
How many resources were changed (if any)
Every resource in the catalog (the set of resources that apply to this node), with the
following informaon:
The name of the resource
The resource type
Whether or not the resource was out of sync (didn't match the manifest)
Whether or not the resource was changed
The number of properties (attribute values) that were out of sync
The number of properties that were changed
If any properties of a resource were changed, the report includes:
The name of the changed property
The previous value
The new value
eBooks-IT.org
Reporng and troubleshoong
[ 146 ]
Let's look at an example. We'll have Puppet make a change to a resource, and then examine
the resulng report.
Time for action – generating a report
1. Check whether reporng is enabled:
ubuntu@demo:~/puppet$ sudo puppet config print report
true
2. If the result is false, follow the instrucons in the Enabling reports secon.
3. Edit your manifests/nodes.pp le as follows:
node 'demo' {
file { '/tmp/test':
content => 'Zaphod Beeblebrox, this is a very large drink',
}
}
4. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/content: content
changed '{md5}e705c4d685bf03258eb5ba0dc767905b' to '{md5}
aea5a3708af83f6e53b4b391b469ae44'
Notice: Finished catalog run in 0.11 seconds
5. Find the report le generated by Puppet. First, check where Puppet is congured to
write its reports (the default locaon on Ubuntu is /var/lib/puppet/reports):
ubuntu@demo:~/puppet$ sudo puppet config print reportdir
/var/lib/puppet/reports
6. You will need root privileges to read the report:
ubuntu@demo:~/puppet$ sudo su -
root@demo:~#
7. Change to the report directory:
root@demo:~# cd /var/lib/puppet/reports
8. You should see a directory with the same name as the hostname of your machine:
root@demo:/var/lib/puppet/reports# ls
demo.compute-1.internal
eBooks-IT.org
Chapter 9
[ 147 ]
9. Change to this directory:
root@demo:/var/lib/puppet/reports# cd demo.compute-1.internal/
10. Check for the most recently created le:
root@demo:/var/lib/puppet/reports/demo.compute-1.internal# ls -lt
|head -2
total 1084
-rw-r----- 1 root root 6742 Feb 1 13:43 201302011343.yaml
11. The le's name is generated from the date and me of the Puppet run, so the name
of your le will be dierent. Display the contents of the le:
root@demo:/var/lib/puppet/reports/demo.compute-1.internal# less
201302011343.yaml
--- !ruby/object:Puppet::Transaction::Report
status: changed
kind: apply
host: demo.compute-1.internal
configuration_version: 1359726210
...
12. Look for the secon relang to the file resource you created:
File[/tmp/test]: !ruby/object:Puppet::Resource::Status
resource: File[/tmp/test]
file: /home/ubuntu/puppet/manifests/nodes.pp
line: 4
evaluation_time: 0.016333
change_count: 1
out_of_sync_count: 1
tags:
- file
- node
- demo
- class
time: 2013-02-01 13:43:32.529725 +00:00
events:
- !ruby/object:Puppet::Transaction::Event
audited: false
eBooks-IT.org
Reporng and troubleshoong
[ 148 ]
property: ensure
previous_value: !ruby/sym absent
desired_value: !ruby/sym file
historical_value:
message: "defined content as '{md5}
aea5a3708af83f6e53b4b391b469ae44'"
name: !ruby/sym file_created
status: success
time: 2013-02-01 13:43:32.530130 +00:00
out_of_sync: true
changed: true
resource_type: File
title: /tmp/test
skipped: false
failed: false
What just happened?
We'll examine this report in detail to see what informaon Puppet has recorded about what
it did.
There will be lots of informaon in the report, about all the other resources on the machine,
as well as some of Puppet's internal data. However, the part we're interested in at the
moment is the Puppet::Resource::Status secon relang to the /tmp/test le:
File[/tmp/test]: !ruby/object:Puppet::Resource::Status
resource: File[/tmp/test]
file: /home/ubuntu/puppet/manifests/nodes.pp
line: 4
evaluation_time: 0.016333
change_count: 1
out_of_sync_count: 1
This secon gives the name and type of the resource:
resource: File[/tmp/test]
The manifest le and line number where it's dened:
file: /home/ubuntu/puppet/manifests/nodes.pp
line: 4
eBooks-IT.org
Chapter 9
[ 149 ]
The me it took to compile the resource denion:
evaluation_time: 0.016333
The number of properes of the resource that were changed:
change_count: 1
The number of properes that were found to be out of sync with the manifest:
out_of_sync_count: 1
There's a Puppet::Transaction::Event secon for each property that was changed,
in this case, only one.
- !ruby/object:Puppet::Transaction::Event
audited: false
property: ensure
previous_value: !ruby/sym absent
desired_value: !ruby/sym file
historical_value:
message: "defined content as '{md5}
aea5a3708af83f6e53b4b391b469ae44'"
name: !ruby/sym file_created
status: success
time: 2013-02-01 13:43:32.530130 +00:00
This secon tells us which property was changed:
property: ensure
Its previous value:
previous_value: !ruby/sym absent
The value requested by the manifest (although we didn't specify ensure => file,
this is implicit for a file resource):
desired_value: !ruby/sym file
Whether the property change was successful:
status: success
The me the change was made:
time: 2013-02-01 13:43:32.530130 +00:00
eBooks-IT.org
Reporng and troubleshoong
[ 150 ]
Finally, the Puppet::Transaction::Report secon provides general data about the
Puppet run:
--- !ruby/object:Puppet::Transaction::Report
status: changed
kind: apply
host: demo.compute-1.internal
configuration_version: 1359726210
The status eld indicates that the conguraon of the machine was changed on this
Puppet run. If the Puppet run was successful, but no resources were changed, the status
would be unchanged. If there was an error, the status would be failed.
Using reports
Although you don't oen need to see this level of detail about what Puppet's doing, it can be
useful when something's not working right and you need to gure out why.
For example, if you think Puppet should be making a parcular change, and it's not
happening, you can use the report to help troubleshoot the problem. Turn on reporng,
run Puppet, and inspect the report as we did in the previous example. Find the resource in
queson and you'll be able to see what Puppet thinks it should be, whether it's in sync with
the manifest, and whether there were any failures.
For larger-scale reporng on a whole network of Puppet-managed machines, you can set
up a report server where Puppet will send reports from each machine. These can then
be aggregated and processed, and you can see graphs and results using a tool like Puppet
Dashboard. This is beyond the scope of this book, but you can nd out more at:
https://puppetlabs.com/puppet/related-projects/dashboard/
Debug runs
Running Puppet with the --debug ag will not produce as much detail as a report, but sll
gives you much more informaon than a normal Puppet run. For example:
ubuntu@demo:~/puppet$ papply --debug
Debug: importing '/home/ubuntu/puppet/manifests/nodes.pp' in environment
production
Debug: Failed to load library 'selinux' for feature 'selinux'
Debug: Creating default schedules
Debug: Using settings: adding file resource 'graphdir': 'File[/var/lib/
puppet/state/graphs]
:links=>:follow, :backup=>false, :ensure=>:directory, :loglevel=>:debug,
:path=>"/var/lib/puppet/state/graphs"}'
eBooks-IT.org
Chapter 9
[ 151 ]
...
Notice: Finished catalog run in 0.08 seconds
Debug: Using settings: adding file resource 'rrddir': 'File[/var/
lib/puppet/rrd]{:links=>:follow, :group=>"puppet", :backup=>false,
:ensure=>:directory, :owner=>"puppet", :mode=>"750", :loglevel=>:debug,
:path=>"/var/lib/puppet/rrd"}'
Debug: Finishing transaction 69968312591020
Debug: Received report to process from demo.compute-1.internal
Debug: Processing report from demo.compute-1.internal with processor
Puppet::Reports::Store
Because the --debug ag tells Puppet to output everything it does, this usually produces a
lot of informaon that isn't interesng, but it may help you in some situaons to gure out
why Puppet is doing something it shouldn't, or not doing something it should.
Noop runs
By its very nature, Puppet can produce big changes on a machine in a single run. Depending
on the manifest, it can change or delete les, restart services, drop databases, or do many
other potenally destrucve things. So it would be nice to have Puppet tell us what it's going
to do before it does it.
The --noop ag does exactly this. Noop stands for no-operaon; in other words, do
everything except actually make changes to the system. This is also somemes known as
dry-run mode. Let's see an example:
ubuntu@demo:~/puppet$ papply --noop
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: current_value
absent, should be file (noop)
Notice: Node[demo]: Would have triggered 'refresh' from 1 events
Notice: Class[Main]: Would have triggered 'refresh' from 1 events
Notice: Stage[main]: Would have triggered 'refresh' from 1 events
Notice: Finished catalog run in 0.06 seconds
This is telling us that Puppet has found one resource out of sync:
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: current_value
absent, should be file (noop)
eBooks-IT.org
Reporng and troubleshoong
[ 152 ]
The ensure property for the le /tmp/test should be file, but instead it is absent.
In other words, the manifest says there should be a le /tmp/test, but there isn't.
Puppet will x this by creang the le, when you run Puppet without the --noop ag.
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: defined content
as '{md5}aea5a3708af83f6e53b4b391b469ae44'
Notice: Finished catalog run in 0.06 seconds
So dry-run mode is very useful for making sure that Puppet will only make the changes you
expected to see. If you're not sure what would change, or you want to make sure that your
changes won't aect a running service, for example, you can use dry-run mode to nd out.
Be warned: dry-run mode doesn't come with any guarantees. It's quite
possible to do a dry run with no errors, but then encounter a problem
running Puppet for real. For example, if the manifest tries to install a
package that doesn't exist in the repository, this will succeed in dry-run
mode, because there's no way for Puppet to know in advance that it won't
work. Similarly, exec resources won't actually be run, so dry-run mode
can't tell you whether they will succeed or fail. Test your crical changes in a
staging environment, rather than relying solely on dry-run mode.
Syntax checking
If you want to make sure there are no syntax errors in your manifest, you can use Puppet's
parser validate command to check this:
ubuntu@demo:~/puppet$ puppet parser validate manifests/nodes.pp
Error: Could not parse for environment production: Syntax error at
'server'; expected '}' at /home/ubuntu/puppet/manifests/nodes.pp:3
Validaon mode only aempts to compile the manifest and validate the syntax. It won't
actually apply anything, so you can safely run this command anywhere.
You could run this check manually or via a Git hook, for example, to validate the manifest
before comming it to your repository.
Debug output
When Puppet isn't doing what you expect, it can be very dicult to work out why.
A me-honored debugging technique used by many programmers is to print out
informaon at dierent points to show you what's going on.
eBooks-IT.org
Chapter 9
[ 153 ]
Notify resources
A handy way to do this is to use a notify resource. We've sneaked these into the book
several mes so far without explaining what they are. A notify resource simply prints out
its name to the console when you run Puppet:
notify { 'Got this far!': }
The preceding manifest produces:
ubuntu@demo:~/puppet$ papply
Notice: Got this far!
Notice: /Stage[main]//Node[demo]/Notify[Got this far!]/message: defined
'message' as 'Got this far!'
Notice: Finished catalog run in 0.07 seconds
A simple message like this can help you gure out whether Puppet is even loading or
applying a parcular bit of code. If you want to nd out the value of a variable at a certain
point in the manifest, you can interpolate it into a string, like this:
notify { "I think my hostname is ${::hostname}": }
Note that you need double quotes ("like this") around the string or Puppet won't
process it for variables. You'll see an output like this:
ubuntu@demo:~/puppet$ papply
Notice: I think my hostname is demo
Notice: /Stage[main]//Node[demo]/Notify[I think my hostname is demo]/
message: defined 'message' as 'I think my hostname is demo'
Notice: Finished catalog run in 0.06 seconds
Exec output
If you use an exec resource to run a command, and the command fails, Puppet will give you
an error message including the output from the command. For example, if you have an exec
like this:
exec { 'this-will-fail':
command => '/bin/cat /tmp/doesntexist',
}
You'll see this output:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Exec[this-will-fail]/returns: /bin/cat:
/tmp/doesntexist: No such file or directory
eBooks-IT.org
Reporng and troubleshoong
[ 154 ]
Error: /bin/cat /tmp/doesntexist returned 1 instead of one of [0]
Error: /Stage[main]//Node[demo]/Exec[this-will-fail]/returns: change from
notrun to 0 failed: /bin/cat /tmp/doesntexist returned 1 instead of one
of [0]
Notice: Finished catalog run in 0.13 seconds
As you can see, Puppet not only reports that the command returned a failed exit status:
Error: /bin/cat /tmp/doesntexist returned 1 instead of one of [0]
But also, the actual output from running the command:
Notice: /Stage[main]//Node[demo]/Exec[this-will-fail]/returns: /bin/cat:
/tmp/doesntexist: No such file or directory
Very useful! But somemes the command can succeed and yet whatever was supposed to
happen doesn't happen. We'd like to be able to see the output of the command even though
it didn't return an error. To do this, set the logoutput aribute of the exec to true:
exec { 'this-will-succeed-but-give-us-output-anyway':
command => '/bin/cat /etc/hostname',
logoutput => true,
}
This will produce output such as the following:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Exec[this-will-succeed]/returns: demo
Notice: /Stage[main]//Node[demo]/Exec[this-will-succeed]/returns:
executed successfully
Notice: Finished catalog run in 0.14 seconds
The default value of logoutput is on_failure, which means "only show the command
output if it fails." Seng it to true will always show the command output. If you set it to
false, you'll never see command output even in the case of a failure.
In older versions of Puppet, logoutput defaulted to false, so you needed
to explicitly set it to on_failure if you wanted to see failed command
output. In Puppet 3.0 and later, on_failure is the default.
eBooks-IT.org
Chapter 9
[ 155 ]
Specifying expected exit status
How does Puppet know whether a command succeeded or failed? UNIX-like systems use a
numeric value called the exit status to indicate this. The convenon is to return an exit status
of 0 if all is well, and some non-zero value if there was a problem. Some commands return
dierent non-zero values depending on the specic error. As you can see in the example, if
you try to use cat on a le that doesn't exist, it returns an exit status of 1.
Puppet interprets a non-zero exit status as failure, and raises an error. If you want to run a
command that returns a non-zero exit status, but you're happy for Puppet to ignore this, you
can specify the returns aribute for the exec, to tell Puppet what exit status to expect:
exec { 'this-will-fail-but-that-is-ok':
command => '/bin/cat /tmp/doesntexist',
returns => 1,
}
In this case, Puppet will only raise an error if the exit status is something other than 1.
Monitoring
Devops people like to say, "If it's not monitored, it's not in producon." By "monitored,"
what we really mean is that some automated system is checking whatever it is, and alerng
you if there's a problem. If your customers know the system is down before you do, then you
don't have eecve monitoring.
Managing monitoring with Puppet
Puppet can be a big help with monitoring, as it can be with all other aspects of automaon
and control. At the least, you can use Puppet to help you set up a monitoring server (using
Nagios, Icinga, Zabbix, or one of the many other freely-available monitoring tools).
Puppet has some built-in support for Nagios in parcular, and can automacally generate
monitoring checks for hosts and services that you manage in your Puppet manifest. This
requires PuppetDB, a central database that stores informaon about your nodes. We haven't
space here to go into the details of PuppetDB and stored conguraon, but you can nd out
more at:
https://puppetlabs.com/blog/introducing-puppetdb-put-your-data-to-
work/
eBooks-IT.org
Reporng and troubleshoong
[ 156 ]
What to monitor
However you manage your monitoring infrastructure, there are some basic things you will
want to monitor:
Hosts being alive
Web sites responding to HTTP requests
Processes running
Memory and disk space being within limits
You can also monitor Puppet itself. This is especially useful if you are running Puppet
automacally from cron, perhaps using a similar setup to that described in Chapter 4,
Managing Puppet with Git. We'd like to know at least:
Whether Puppet has run recently
Whether the run succeeded or failed
We'll see how to do this in the next secon.
Monitoring Puppet status
You can do this very simply by having Puppet write a le on each server when it runs.
For example:
file { '/tmp/puppet.lastrun':
content => inline_template('<%= Time.now %>'),
backup => false,
}
This will write the current date and me to the le /tmp/puppet.lastrun, and you can
check this le with your monitoring system. If you run Puppet every 10 minutes, say, then
the mestamp le should be no more than 10 minutes old. Allowing a lile me for the
Puppet run itself, which could take up to a few minutes, you might set your monitoring
system to alert you if the le is, say, 15 minutes old.
Did you noce that we've specied backup => false for the puppet.
lastrun le? Normally, Puppet creates a backup copy of any le it changes,
and stores it on the machine in a place called the clientbucket. This can be handy
if you ever accidentally overwrite an important le, and want to retrieve its
original contents. In this case, however, Puppet will be changing the le every
me it runs, and we don't want to waste space storing lots of useless backup
copies. backup => false tells Puppet never to back up this le.
eBooks-IT.org
Chapter 9
[ 157 ]
Problems with Puppet
There are many possible reasons for an alert to be triggered by the puppet.lastrun le
becoming too old:
The cron job that runs Puppet didn't re. Maybe it was disabled by someone making
local changes to the machine, who then forgot to re-enable it.
Git wasn't able to pull changes. Maybe the Git server is down, or inaccessible, or
the SSH authencaon got messed up. Maybe someone made local changes to the
Puppet repo but then didn't commit them, causing git pull to complain.
Puppet wasn't able to run. Maybe there's a typo in the manifest, or another error
that means the manifest doesn't compile properly.
Whatever the reason, you'll be able to go in and invesgate why Puppet isn't running,
and you'll know which machines are potenally out of sync with the manifest.
Staying in sync
Some people don't like to run Puppet regularly on their machines because they worry that
it might change something unexpectedly. In fact, the best way to avoid this is to run Puppet
all the me. Why? Because if you don't do this, when you eventually do run Puppet on a
machine, there will be lots of changes all happening at once, which makes it dicult to
diagnose any problems you may have.
Also, one of the main benets of using Puppet is that you know your machines are all in
sync with each other and the manifest. If you make a change to the manifest that could
potenally break something on a machine, it's beer for you to nd out now so you can x
it. Running Puppet with --noop can help you make sure that your latest changes haven't
caused problems.
It's a good idea, if your budget allows, to set up some staging servers, and make them as
similar as possible to your producon systems. You can then test any changes to Puppet,
package versions, conguraon, or soware releases on the staging servers and eliminate
any problems before rolling out to producon.
If you're very risk-averse, you could run Puppet automacally on the staging servers but only
run it manually on the live servers when you need to push out a change.
Errors
The two main kinds of error you're likely to encounter when running Puppet are compilaon
errors—errors in the manifest itself, or in template les—and errors from commands
executed by Puppet when applying the manifest. We'll look at these in turn.
eBooks-IT.org
Reporng and troubleshoong
[ 158 ]
Compilation errors
If you make a typo in the manifest, or some other kind of error, Puppet will usually alert
you when you run puppet apply (or puppet parser validate). It will tell you:
What the error was
What source le, and line number, the error occurred in
Diagnosing errors
Let's take an example. If we apply a manifest containing a deliberate typo, like this
(can you spot it?):
file { '/tmp/test':
contents => 'Hello, world'
}
Puppet will complain with an error message:
ubuntu@demo:~/puppet$ papply
Error: Invalid parameter contents at /home/ubuntu/puppet/manifests/nodes.
pp:4 on node demo.compute-1.internal
We actually should have said content, not contents, and Puppet is quite helpful about
poinng out exactly where the problem is.
Here are some other common errors you might come across, with some hints on what might
cause them.
Missing le sources
A common typo is to specify a le source as:
puppet://modules/sudoers/sudoers
Instead of:
puppet:///modules/sudoers/sudoers
That is, to put a double slash (//) instead of a triple slash (///) before modules.
We're all used to typing web URLs, which typically have a double slash.
The format of the source URI is actually:
puppet://[HOSTNAME]/modules/...
eBooks-IT.org
Chapter 9
[ 159 ]
The oponal HOSTNAME is usually omied unless you're using a Puppet le server, so the URI
just looks like this:
puppet:///modules/...
If you miss out the third slash, Puppet will think you're trying to specify a HOSTNAME where it
can nd the le, and complain:
Error: /Stage[main]//Node[demo]/File[/tmp/test]: Could not evaluate:
getaddrinfo: Name or service not known
Could not retrieve file metadata for puppet://modules/sudoers/sudoers:
getaddrinfo: Name or service not known
If the source URI is correctly formaed, but the source le just doesn't exist (maybe you
forgot to create it), Puppet will say instead:
Error: /Stage[main]//Node[demo]/File[/tmp/test]: Could not evaluate:
Could not retrieve information from environment production source(s)
puppet:///modules/sudoers/sudoers
Missing parent directory
If you specify a file resource with a path like this:
file { '/tmp/testdir/test':
content => 'Hello, world',
}
Puppet requires that the directory /tmp/testdir exist before it can create the le test in
it. If it doesn't, you'll see an error message similar to:
Error: Could not set 'file' on ensure: No such file or directory - /tmp/
testdir/test.puppettmp_236 at 4:/home/ubuntu/puppet/manifests/nodes.pp
You might expect that Puppet would simply create any missing path components for you.
Alas! there are limits to what even a robot butler can do. You have to create the parent
directory as a separate resource:
file { '/tmp/testdir':
ensure => directory,
}
file { '/tmp/testdir/test':
content => 'Hello, world',
}
Puppet is, however, smart enough to gure out the le /tmp/testdir/test depends
on the directory /tmp/testdir being created rst, so you don't have to add an explicit
require for this.
eBooks-IT.org
Reporng and troubleshoong
[ 160 ]
Mistyped command line options
If you mistype an opon name on the command line, for example, pung -debug instead of
--debug, Puppet gives a very puzzling error:
ubuntu@demo:~/puppet$ papply -debug
Error: Could not parse for environment production: Syntax error at end of
file at line 1 on node demo.compute-1.internal
If you see this error, check your command line!
Summary
A quick rundown of what we've learned in this chapter.
Reporting
You can get a summary report of what Puppet did on its run by using the --summarize ag
with puppet apply. For more detailed reporng, enable reports by seng report=true
in /etc/puppet/puppet.conf.
Puppet will write report les to (by default, but you can change this) /var/lib/puppet/
reports, in a directory named aer the machine's hostname. Each report le will be named
according to the date and me of the Puppet run it covers.
Puppet's report les include some summary data about the run itself, and how many
resources were found to be out of sync with the manifest. This is followed by a detailed list
of all the resources on the server and the number of properes that were changed or out of
sync for each resource.
If any resource was changed, the report will include details of each property that was
changed, with its previous value and updated value.
Debug and dry-run modes
When you don't need a full report, but you do want some more detailed informaon on
Puppet's acvity, you can use the --debug ag with puppet apply. You can also see a
dry-run output of what Puppet thinks is out of sync, and what it would change, by using the
--noop ag.
You can check your Puppet manifest for compilaon errors using the puppet parser
validate command.
eBooks-IT.org
Chapter 9
[ 161 ]
Printing messages
To print out debugging messages, or other informaon, use a notify resource, which simply
prints out its name to the console during Puppet's run:
notify { "I think my hostname is ${::hostname}": }
Commands run via exec will print their output if the command returns a failed (non-zero)
exit status. To see the output even if the command succeeds, set the logoutput aribute
to true:
exec { 'this-will-succeed-but-give-us-output-anyway':
command => '/bin/cat /etc/hostname',
logoutput => true,
}
If a command rounely returns a failed exit status, but you're happy for Puppet to ignore
it and carry on, you can specify the exit status that should be expected using the returns
aribute:
exec { 'this-will-fail-but-that-is-ok':
command => '/bin/cat /tmp/doesntexist',
returns => 1,
}
Monitoring Puppet
If you want to be able to monitor whether Puppet is running successfully on a number of
machines, without having to check each one, you can have Puppet write a mestamp le
every me it runs, and check this le with your monitoring system. If the le is not updated
regularly, there may be a problem running Puppet on the system.
Common Puppet errors
When Puppet does encounter a problem, it will usually print out a (more or less) helpful
message, including details of the error and the source le and line number where it
occurred. Some common errors that you may encounter are as follows:
Could not retrieve le metadata for XXX: getaddrinfo: Name or service not known
You may have accidentally typed puppet://modules... in a le source instead
of puppet:///modules....
eBooks-IT.org
Reporng and troubleshoong
[ 162 ]
Could not evaluate: Could not retrieve informaon from environment producon
source(s) XXX
The source le may not be present or in the right locaon in the Puppet repo.
Error: Could not set 'le' on ensure: No such le or directory XXX
The le path may specify a parent directory (or directories) that doesn't exist. You can use
separate le resources in Puppet to create these.
Could not parse for environment production: Syntax error at end of file
at line 1
You may have mistyped some command line opons (parcularly, using a single hyphen
instead of a double hyphen).
eBooks-IT.org
10
Moving on Up
There are only two mistakes one can make on the road to truth: not going all
the way and not starting.
— Buddha
In this chapter, you'll learn some simple principles for wring beer Puppet manifests, nd
some resources for learning more about Puppet, and get some ideas for praccal projects
that will help you start pung your Puppet skills to work.
ADVANCED
PUPPET
MANAGING
HUMANS
eBooks-IT.org
Moving on Up
[ 164 ]
Puppet style
Just like everyone else, I want to be a nonconformist, too. But when it comes to programming,
conformity is a virtue. When your code looks the same as everybody else's, it's easy to read,
easy to understand, and easy to maintain. Here are some simple Puppet style ps you can
adopt now to help those who work on your code in the future, including yourself.
Break out code into modules
Logical separaon of your manifest into modules is a big help when it comes to
understanding and maintaining your code. Although you can structure your modules any way
you want—it makes no dierence to Puppet—I nd the best strategy is to have each module
control some more or less independent chunk of funconality.
For example, if you're wring code that manages a parcular customer-facing service, such
as a website or an API, that could be a module. Similarly, code that manages a specic piece
of soware such as Apache, MySQL, or Hadoop should have its own module.
Modules can then be connected together to do useful things; for example, a module to
manage Drupal might use the Apache module, the PHP module, the MySQL module, and
so on. If your modules are well-structured, there should be very lile duplicaon of code.
Refactor common code into denitions
If you nd yourself repeang very similar code several mes, it's a good idea to refactor the
common code into a denion. For example, the following code has a lot of duplicaon:
file{ '/etc/init/foo_worker.conf':
source => 'puppet:///modules/admin/foo_worker.upstart',
mode => '0755',
}
service { 'foo_worker':
ensure => running,
enable => true,
provider => upstart,
require => File['/etc/init/foo_worker.conf'],
}
file{ '/etc/init/bar_worker.conf':
source => 'puppet:///modules/admin/bar_worker.upstart',
mode => '0755',
}
eBooks-IT.org
Chapter 10
[ 165 ]
service { 'bar_worker':
ensure => running,
enable => true,
provider => upstart,
require => File['/etc/init/bar_worker.conf'],
}
file{ '/etc/init/baz_worker.conf':
source => 'puppet:///modules/admin/baz_worker.upstart',
mode => '0755',
}
service { 'baz_worker':
ensure => running,
enable => true,
provider => upstart,
require => File['/etc/init/baz_worker.conf'],
}
It denes three services, foo_worker, bar_worker, and baz_worker, each with an
Upstart script to manage it. The aributes are exactly the same for each of the scripts and
services, so you can make this code much simpler, shorter, and clearer by refactoring it using
a denion like this:
# Manage worker services
define worker_service() {
file{ "/etc/init/${name}_worker.conf":
source => "puppet:///modules/admin/${name}_worker.upstart",
mode => '0755',
}
service { "${name}_worker":
ensure => running,
enable => true,
provider => upstart,
require => File["/etc/init/${name}_worker.conf"],
}
}
worker_service { ['foo', 'bar', 'baz']: }
In other words, idenfy what is common to several secons of code, and extract that part
into a denion. This not only makes the code easier to understand, but if you need to
modify it later, you only need to change it in one place. It's also more scalable because it's
easy to add another worker_service (or a hundred).
eBooks-IT.org
Moving on Up
[ 166 ]
Don't take refactoring too far, though; it can overcomplicate your code. Beer to have
slightly repeve code that's easy to understand and extend, than code that's elegant
but dicult to follow.
Keep node declarations simple
One of the benets of having your infrastructure managed by Puppet is that (in theory) you
can look at the manifest and see what each machine does. To help with this, keep your node
declaraons short, clear, and descripve. For example:
node 'web1' {
include webserver
}
You can look at this manifest and say, "Ah! web1 is a web server." All the individual resources,
modules, parameters, and other cluer are pushed down into the web server module so that
the node declaraon simply says what the box is for.
Another example:
node 'base-server' {
include admin::basics
include user::sysops
include monitoring::target
}
node 'cluster660' inherits 'base-server' {
class { 'hadoop::node':
master => 'cluster1',
}
}
Here, we have a bunch of stu that is common to all (or most) servers:
include admin::basics
include user::sysops
include monitoring::target
We've extracted this out into a base-server declaraon. There's no actual server named
base-server; however, actual servers can inherit from this node declaraon and get
everything in it:
node 'cluster660' inherits 'base-server' {
We know what role this machine has because it includes this class:
class { 'hadoop::node':
eBooks-IT.org
Chapter 10
[ 167 ]
It takes a parameter to idenfy the cluster master:
master => 'cluster1',
This node declaraon contains only the key informaon that the node needs to do its job,
and tells us at a glance what that job is.
If your node declaraons contain business logic, or individual resources, think about
refactoring these into a class or module that the node can include.
Use puppet-lint
puppet-lint is a useful tool that checks your manifest to make sure it conforms to
the Puppet Labs ocial style guidelines, and catches a number of common problems.
For example, code like this:
node 'demo' {
file { "/tmp/test":
content => 'Hello, world',
mode => 644,
}
}
The preceding code will produce the following output from puppet-lint:
ubuntu@demo:~/puppet$ puppet-lint manifests/nodes.pp
ERROR: trailing whitespace found on line 2
WARNING: unquoted file mode on line 4
WARNING: double quoted string containing no variables on line 2
WARNING: mode should be represented as a 4 digit octal value or symbolic
mode on line 4
WARNING: indentation of => is not properly aligned on line 4
When we clean it up:
node 'demo' {
file { '/tmp/test':
content => 'Hello, world',
mode => '0644',
}
}
puppet-lint maintains an approving silence:
ubuntu@demo:~/puppet$ puppet-lint manifests/nodes.pp
ubuntu@demo:~/puppet$
eBooks-IT.org
Moving on Up
[ 168 ]
You can install puppet-lint with the following command:
ubuntu@demo:~/puppet$ sudo gem install puppet-lint
Successfully installed puppet-lint-0.3.2
1 gem installed
Installing ri documentation for puppet-lint-0.3.2...
Installing RDoc documentation for puppet-lint-0.3.2...
To nd out more about puppet-lint and to see what tests it runs on your code, visit the
site https://github.com/rodjek/puppet-lint
If you keep your code lint-clean (which is to say, it passes puppet-lint with no errors or
warnings), you can be reasonably condent that it conforms to style guidelines and doesn't
contain any dangerous or deprecated syntax. This will make it easier and safer to upgrade to
new versions of Puppet as they're released.
It also means your code will be easier for others to understand and work on.
Make comments superuous
Good code is its own best documentaon.
—Steve McConnell, 'Code Complete'
There is a tendency to sprinkle comments liberally throughout code, oen because it's not
clear what the code is doing or why it's there. Instead, rewrite the code so that no comment
is needed. You can do this by using a simple, logical structure for your code and choosing
descripve names for things.
Assume that anyone reading your code is familiar with Puppet (or at least as familiar as you
are), so you don't need to explain how the language works:
# This will run a command
exec { 'do-the-stuff':
...
}
If part of your code works by complicated magic, which you feel needs explanaon in
comments, simply remove the magic, and rewrite the code in a simple, obvious way.
Similarly, comments like this are a sign of problems:
# Not sure exactly why this works - DO NOT TOUCH!!
eBooks-IT.org
Chapter 10
[ 169 ]
Cleverness, in general, is not a characterisc of robust, reliable code. Samuel Johnson
advised writers, "Read over your composions and where ever you meet with a passage
which you think is parcularly ne, strike it out." He would have been an early proponent
of refactoring.
There are useful comments, however. A good rule of thumb for comments, as with commit
messages, is "Not what, but why." Why is this piece of code necessary?
# apache2-utils gives us rotatelogs
package { "apache2-utils": ensure => installed }
Puppet learning resources
There are several helpful web and print resources that you should keep handy when working
with Puppet. This is a small selecon of those that I nd most useful.
Reference
It might seem obvious, but one of the best sources of reference documentaon for Puppet
is the Puppet Labs site itself. To save you a lot of clicking around, here are the links you'll
probably use the most.
Resource types
One link that I keep bookmarked at all mes is the Puppet Type Reference:
http://docs.puppetlabs.com/references/latest/type.html
This lists each of the types of Puppet resources—file, exec, user, and so on—with a
complete descripon of all the aributes of each resource and what they do. Each resource
also has a breakdown of the features supported by its providers or plaorms.
Puppet also has some built-in help on resource types, available via the puppet describe
command. For example:
ubuntu@demo:~/puppet$ puppet describe --list
These are the types known to puppet:
augeas - Apply a change or an array of changes to the ...
computer - Computer object management using DirectorySer ...
cron - Installs and manages cron jobs
exec - Executes external commands
file - Manages files, including their content, owner ...
...
eBooks-IT.org
Moving on Up
[ 170 ]
ubuntu@demo:~/puppet$ puppet describe file
file
====
Manages files, including their content, ownership, and permissions.
...
Parameters
----------
- **backup**
Whether files should be backed up before
being replaced.
Language and syntax
Also very important is the Puppet Language Reference:
http://docs.puppetlabs.com/puppet/3/reference/lang_summary.html
This describes every part of the Puppet language and syntax: variables, classes, data types,
and so on. If you need to check how a parcular Puppet construct works, or nd out what's
available, this is an excellent place to look.
Facts
For working with Facter, use the Core Facts Reference:
http://docs.puppetlabs.com/facter/latest/core_facts.html
This lists all of the standard facts that you can use to get informaon about machines,
such as fqdn, memorysize, operatingsystem, and so on.
Style
The ocial Puppet Labs style guidelines (as implemented by puppet-lint, for example)
are here:
http://docs.puppetlabs.com/guides/style_guide.html
You may not agree with all of the style rules (I'm not crazy about some of them), but there
are advantages to using standard coding style. If your organizaon's coding style is dierent,
or you need to break the rules for some other reason, go ahead, but it's always good to know
what rules you're breaking.
eBooks-IT.org
Chapter 10
[ 171 ]
Modules and code
One of the best ways to learn to write code is to look at other people's code, at least, if it's
any good.
Puppet Forge
The Puppet Forge is a community repository of Puppet code:
http://forge.puppetlabs.com/
There you can nd open source modules for managing things such as Apache, MySQL,
MongoDB, Ganglia, Sphinx, and many others. These can be very useful to look at and get
ideas from. In some cases you may be able to download and use the module directly in your
infrastructure as is; most of the me, you will need to adapt and modify the code a lile to
work in your environment.
Be warned that the code on Puppet Forge is of variable quality. Some modules are excellent,
mature, highly portable, well-documented, and up to date. Others aren't. Oen it's quickest,
easiest, and best to simply write your own code. This has the addional advantage that you
learn more about Puppet while you're doing it.
A quick way to nd out whether there is any Puppet Forge code relevant to what you're
working on is to use the puppet module search command:
ubuntu@demo:~/puppet$ puppet module search memcached
Notice: Searching https://forge.puppetlabs.com ...
NAME DESCRIPTION AUTHOR KEYWORDS
saz-memcached UNKNOWN @saz debian redhat fedora ubuntu memcached
The Puppet Cookbook
If you've enjoyed this book, you might consider The Puppet Cookbook, by the same author:
http://bitfieldconsulting.com/cookbook
The book is aimed at those who have some familiarity with Puppet (perhaps those who've
worked their way through the Puppet Beginner's Guide) and outlines a number of specic
techniques and recipes for doing things with Puppet:
Managing virtual machines with Vagrant
Building a Nagios monitoring server
Using Augeas to edit cong les
Managing users with virtual resources
eBooks-IT.org
Moving on Up
[ 172 ]
Managing Rails applicaons
Managing package and gem repositories
Managing rewalls with iptables
Building high-availability servers with Heartbeat
Using HAProxy for load balancing
Using tools such as MCollecve, Dashboard, and Foreman
The book contains lots of complete, working code to do all the things above and many more.
As in this book, each piece of code is explained line-by-line so that you can see how it works,
and use the same ideas in your own Puppet code.
It also shows you many of the ps, tricks, ideas, and advanced techniques that I've picked up
over many years of working with Puppet, and that there wasn't room to cover in this book,
such as:
Using Rake to manage Puppet workows
Producing HTML documentaon for your manifests
Using tags, run stages, and environments
Using class inheritance and overriding
Imporng data from commands and comma-separated values (CSV) les
Creang custom facts
Creang custom types and providers
Generang manifests automacally
Using external node classiers
Projects
The best way to learn is by doing, so here are some things you might like to try to do with
Puppet that will improve your skills and your infrastructure at the same me. Most of these
projects are fairly small—a few hours of work, maybe—but each will give you a valuable win
and make your life easier. They provide a series of stepping-stones from your rst use of
Puppet to a completely automated environment.
eBooks-IT.org
Chapter 10
[ 173 ]
Puppet everywhere
Project: First, install Puppet on all the machines you're responsible for. Set up a central Git
repo as described in Chapter 4, Managing Puppet with Git, and have each of the machines
pull from the repo and run Puppet automacally. For now, Puppet won't actually manage
anything, so all your node declaraons will look like this:
node 'kermit' {
}
That's ne. Once you've got Puppet everywhere, you can start adding things to it.
Win: It's now easy to add conguraon to any machine, simply by pung something in its
node declaraon.
User accounts
Project: Create a base node denion that which every machine inherits, as described earlier
in this chapter in the Keep node declaraons simple secon. To this base node, add your own
user account and SSH key as described in Chapter 5, Managing Users. You probably want to
give yourself full sudo privileges as well. Add any other users who need login access
to machines.
Win: You can now easily log in to every machine using your own named account and key,
and run commands with root privileges using sudo.
System toolbox
Project: Add a set of packages to your base node containing soware that you nd useful for
system administraon: for example, htop, dstat, iptraf, tmux, mosh, vim, and so on. If
you have custom conguraons for any of these, add the cong les to Puppet.
Win: You now have a well-equipped sysadmin environment on every machine, congured
the way you want it.
Time sync
Project: Use Puppet to add the NTP service to all of your machines and set them to the
UTC me zone. If you have a central NTP server, or your ISP does, congure ntp.conf
to use this.
Win: All server clocks are now in sync and in the same me zone, which prevents a variety
of obscure problems, and makes troubleshoong much easier (you can cross-reference
mestamps in logles, for example).
eBooks-IT.org
Moving on Up
[ 174 ]
Monitoring server
Project: If you don't already have a monitoring server such as Icinga, set one up to monitor
your machines as described in Chapter 9, Reporng and Troubleshoong. You don't have to
automate the install of Icinga for now, but have Puppet manage the list of hosts to monitor
(hosts.cfg for Icinga) and the list of services to check (services.cfg).
Win: You now have automated monitoring and you can see the state of your network at a
glance, including whether any hosts are down. In the future, it'll be easy to add new hosts
and services to your monitoring system.
Puppetize your key services
Project: Use Puppet to manage the most important service provided by your machines.
Your priories for bringing services under Puppet management should be driven by business
consideraons. What service or facility is most crical or earns the most money for your
business? Or, if you're a non-prot, the most business for your business?
Once you've decided on the most important thing to Puppeze, make a list of exactly what
needs to be managed. For example, if it's a website, you might list the following things to be
managed by Puppet:
Web server installed
Virtual host le to serve website
Directory where site is deployed
Database for website
The list should contain everything you would have to do manually to set up a new server to
serve the website (not including, for example, installing the operang system, since Puppet
requires that, too). You probably also won't include deploying the website itself, unless it
consists of just a few stac les. What Puppet needs do to is make the machine ready to
have the site deployed to it, whatever the deployment process is (FTP, Capistrano, shell
scripts, and so on).
Don't aempt to have Puppet manage these things on the exisng live server. Instead, set
up a new server and build up the conguraon, checking against the live machine as you go
to make sure you have included everything. Then it's easy to know when you're done; when
you can deploy the site to the new machine and it works idencally to the live version,
you're done.
You can turn down or repurpose the old machine (keep it around for a lile while, though,
just in case there's something you missed).
eBooks-IT.org
Chapter 10
[ 175 ]
Win: Your key service is under Puppet management, and that service can now be easily and
quickly built on a new server if anything happens to the live one. Also, you have complete
documentaon for what's required to run it.
Automate backups
Project: Use Puppet to distribute backup scripts to each machine and run backup jobs
automacally via cron. You should have a local copy of all important data (that is, in a backup
directory on the machine) and an osite copy of anything that can't easily be reconstructed.
This should be o the machine, o your infrastructure, and out of your ISP's data center
(Amazon S3 is one opon).
Monitor the backup jobs with your monitoring server (have the job write a logle, and you
can monitor that the logle has been touched, and doesn't contain any error messages).
Use Puppet to build copies of your machines and test restoring the data to them. Write
down the procedure you follow to do this, so that someone else could follow it, and put
the procedure document where it can be easily found. Knowledge of the restore procedure
shouldn't die with you.
Win: Your data (and thus your business) is no longer a hostage to fortune. You needn't just
hope that your hard disks won't fail, or that your ISP won't lose connecvity. In fact, no
sentence that contains the word "hope" is part of a viable operaons strategy.
Set up staging servers
Project: Once you've fully Puppezed a server, create a "staging" version that is idencal to the
live version. When you need to test upgrades, new releases, or changes to the setup, you can
try them out on the staging server rst and avoid any unexpected problems in producon.
Win: You have a staging environment where you can try out changes (no more comming
and hoping). Also, it's easy to create copies of your live server, for redundancy, load
balancing, or development VMs.
Automate everything
Project: Extend Puppet management to any remaining parts of your infrastructure that sll
require manual setup. For any parcular machine or service, ask yourself this queson:
If I wipe and reinstall this machine, then run Puppet, will it be in producon?
If the answer is No, then you sll have some work to do. If the answer is Yes, then do the test
to make sure. (It might be wise to use a replacement server rather than wiping the live one
and nding you can't rebuild it.)
eBooks-IT.org
Moving on Up
[ 176 ]
If there are manual steps that you can't automate or do without (restoring data from a
backup, say), write down a detailed procedure for what has to be done, so that someone
else could follow it. Write down what you need to type, what you'll see, error messages you
might encounter, and so on.
Your wrien procedures are business-crical soware just like your applicaon source code.
Procedures are just soware that runs on humans. Write, test, and maintain procedures with
as much care and pride as you do your computer soware.
Win: You can spend less me on day-to-day operaons maers, such as building and
conguring servers, and you can concentrate on really valuable tasks, such as making your
systems faster, more resilient, and more cost-eecve.
You have more me to communicate with your colleagues, instead of computers.
You can make infrastructure changes quickly and safely, making the business more agile.
You have me for training, learning, research, experimentaon, and innovaon.
You can share your knowledge with others by helping them use Puppet to achieve what
they need to do. In the process, you'll learn experse from them about their own domains
and speciales.
Last word
System administraon can be a rather conservave profession. ("If it ain't broke, don't x it.")
Worse, some system administrators suer from an atude problem. Perhaps they perceive
themselves as undervalued by colleagues, like a kind of digital janitor. Perhaps they're reluctant
to share what they know, for fear of making themselves dispensable. Perhaps they're simply so
overloaded with me-consuming work that their default response is "Go away!"
This can lead to "BOFH": the system administrator as remote, unfriendly, inaccessible,
enforcing unhelpful and bureaucrac policies, rejecng new ideas. The last person, in fact,
you'd want to ask for help with a problem.
Automaon tools such as Puppet are a threat to this kind of sysadmin, because she sees
herself as the guardian of the secret technical informaon about how the systems work.
"Why, if all that informaon was in Puppet, everyone would be able to see and understand
it, and they could build and manage their own servers! Then I wouldn't be needed!"
eBooks-IT.org
Chapter 10
[ 177 ]
Obviously, this isn't you, or you wouldn't be reading this book (unless someone bought it for
you and le it on your desk, pointedly highlighng this secon). But if you know someone
who ts that descripon, share this with them:
The more you automate the tedious parts of your job, the more me you have for
the excing and challenging parts. You know, the ones that need a brain
The more opportunity you have to use your brain, the more you can learn about
and explore new technologies and ideas
The more automated your systems, the more quickly you can deliver new things,
and the more you can be known as the person who solves problems, instead of
creang them
The more you can innovate, improve the status quo, and add value for the business,
the more indispensable you'll be to the business
The more you share your knowledge, teach, and inspire others, the more your
colleagues will value you, and the higher the opinion they'll have of your skills
and experse. And that won't go unnoced by whoever signs your paycheck
So go to it! And have fun.
eBooks-IT.org
Index
Symbols
! $raining 130
-m switch 56
--noop ag 151
! (not) operator 130
A
Advanced Package Tool. See APT
and expression 130
APT 34
arbitrary command
running 88
architecture 101
arithmec operators 130, 131
arrays
about 120, 136, 141
denions 120
resources, grouping into 108, 109
resources, grouping with 136, 137
values, geng out 137
arsan server craing 9
aributes 17
automac pull-and-apply script 67, 68
B
backup
scheduling 92, 93
base-server declaraon 166
bitwise shi operators 131
BOFH syndrome 176
boolean operators
combining 130
branching 60
C
capture variable 133, 134
case statement
about 125, 126
default case 127
cat-pictures.com 8
changes
automac pulling 67
pushing, to master repo 65, 66
repeang, across servers 10
classes
about 115, 121
and denion, dierenang 117
declaring 116
dening 115
pung, into modules 116
class keyword 39, 115, 117
cloud scaling 16
code
breaking, into modules 164
refactoring, into denions 164-166
command line opons 160
commands
arbitrary command, running 88, 89
chaining 90
running selecvely 89, 90
running, with exec resources 88
search paths 91
trigerring 90
comment aribute 74
comments 168, 169
commit 60
comparison expression 128
eBooks-IT.org
[ 180 ]
comparisons
equality 128
magnitude 129
substrings 129
compilaon errors 158
condionals
about 123, 133, 139
capture variable 133, 134
case statements 125, 126
If statements 124
selectors 127, 128
conguraon management 8, 18
conguraon management system (CM system)
tools 11, 12, 13
Core Facts Reference
URL 170
creates aribute 89
cron resource 104
current working directory. See cwd
cwd 89
D
daemons 42
debug output
about 152
nofy resource 153
debug runs 150
declarave programming language 19
declarave style 17
default case, case statement 127
denion
about 110, 117
common code, refactoring into 164, 165, 166
creang, for Nginx websites 113, 114
mulple instances 115
parameters, passing 111, 112
directory structure
creang 29
DMI images
installing, from Puppet Labs website 25
documentaon
self-updang 10
dry-run mode 151, 160
E
else branch 125
elseif statement 124
else statement 124
equality, comparisons 128
ERB 102
errors
about 157
command line opons, mistyped 160
compilaon errors 158
diagnosing 158
le sources, missing 158, 159
parent directory, missing 159
exec output 153, 154
exec resources
about 103
commands, running with 88
exit status 155
expression
about 128
arithmec operators 130, 131
boolean operators 130
F
Facter 101, 170
facts, puppet manifest
architecture 101
fqdn 101
hostname 101
ipaddress 101
memorysize 101
operangsystem 101
les
about 46, 52
virtual host, deploying 46-48
le sources
missing 158, 159
fqdn 101
fully-qualied domain name. See fqdn
G
Git
about 68
manifest, imporng 55, 56
git whatchanged command 59
eBooks-IT.org
[ 181 ]
H
hashes
about 138, 142
hash keys, tesng 139
mullevel hashes 138
hasstatus 45
home aribute 74
hostname 101
I
if statements 124
infrastructure as code 13
inline_template funcon 101
installing, Puppet
prerequisites 22
steps for 23, 24, 25
ipaddress 101
issues
about 8
solving 11
J
jen user 17
jobs
running, as specied user 94
running, at regular intervals 94
scheduled 104
K
key 138
L
logical expression 128
M
magnitude, comparisons 129
manifest
about 18, 26, 31
applying 27
changing 56-59
directory structure, creang 29
distribung 61
exisng les, modifying 28
imporng, in Git 55, 56
nodes.pp le, creang 29, 30
organizing 28
Puppet, applying 26
reliability 61
scalability 61
simplicity 61
master Git repo
creang 62, 63
master repo
changes, pushing to 65, 66
cloning, to new machine 63-65
memorysize 101
messages
prinng 161
modules
about 38, 50
code, breaking into 164
Nginx module, creang 38, 39
monitoring
about 155
managing, with puppet 155
puppet 161
puppet status 156
MSI images
installing, from Puppet Labs website 25
mullevel hash 138, 142
N
Nginx
installing 34
nginx class 116
Nginx module
creang 38, 39
Nginx service
adding 41
Nginx websites
denion, creang for 113, 114
node
adding 65
declaraons 166, 167
node declaraon
about 29, 32
creang 30
node denions 135, 136
eBooks-IT.org
[ 182 ]
nodes.pp le
creang 29, 30
no-operaon 151
noop runs 151
nofy 49
nofy resource 153
NTP class
creang 117-120
O
onlyif aribute 89
operands 129
operangsystem 101
operators 140
OPTIONAL_SOMETHING 124
or operator 130
P
packages
about 34
Nginx, installing 34
removing 37, 50
specic versions, installing 36
updang 37
parameters
about 121
oponal 112
passing, to denion 111, 112
parent directory 159
private key 75
procedural style 17
projects
about 172
automate everything 175, 176
backups, automang 175
key services, puppezing 174
monitoring server 174
Puppet everywhere 173
staging servers, seng up 175
system toolbox 173
me sync 173
user accounts 173
public key 75
pull-updates script 68
puppet
about 7, 16
advantages 14, 19
code, breaking into modules 164
exisng les, modifying 28
features 72, 73
installing, prerequisites for 22
installing, steps for 23-25
issues 157, 161
language 16-19
learning, resources 169
manifest le, applying 27
manifest le, creang 26
manifests, organizing 28
packages 34
monitoring, managing 155
resources 17, 18
scaling 19
style 164
user, creang 73, 74
uses 18
puppet apply command
creang 40
Puppet Cookbook
URL 171
PuppetDB 155
Puppet Forge
URL 171
Puppet Labs style guidelines
URL 170
Puppet Language Reference
URL 170
puppet learning, resources
facts 170
language and syntax 170
modules and code 171
Puppet Cookbook 171, 172
Puppet Forge 171
reference 169
types 169
puppet-lint 168
Puppetmaster 48, 69
Puppet status
monitoring 156
Puppet Type Reference
URL 169
eBooks-IT.org
[ 183 ]
R
recursive le resource
using 95, 96
refactoring
about 39
common code, into denion 164-166
refreshonly aribute 90, 103
regex. See regular expression
regsubst funcon 141
regular expression
about 131, 140
condionals 133
node denions 135, 136
operators 132
substuons 134, 135
syntax 132
reporng 144, 160
reports
about 144
enabling 145
generang 146-148
summary reports 144
using 150
repository 55
require aribute 42, 43
resource-like way 121
resources
about 17, 18, 43, 44
dependencies 51
for learning puppet 169, 170
grouping 109, 110
grouping, into arrays 108, 109
grouping, with arrays 136, 137
root account 72
Ruby regular expression syntax
URL 132
Run my arbitrary command 88
S
scheduling
about 92
backup 92
opons 94
security and access control 72
selectors 127, 128
service paern 49
services
about 41, 44, 51
control commands 51
Nginx service, adding 41, 42
restarng 46
starng 46, 51
starng, at boot me 44
status, opons 51
stopping 46
with no support status 45
singletons 117
SSH
about 75
authorized key, adding 76, 77
conguraon le, deploying 79, 80
conguraon, managing 79
conguring 84
keys, generang 78
keys, managing 75, 84
special-purpose keys 78
SSH authencaon 74
ssh module 79
STRING argument 135
substuons 134, 135
substrings, comparisons 129
sudo
privileges, managing with 85
sudo command 81
sudoers le
deploying 81
syntax 81
syntax
checking 152
sysadmin
job sasfacon 14
tasks 8, 9
system administraon. See sysadmin
T
tasks
scheduling 92
template
about 105
inline templates 101
eBooks-IT.org
[ 184 ]
Nginx virtual host 97, 98
using 97
template funcon 100
text substuon 141
U
unless aribute 89
unless command 90
unless statement 125
user
about 72
accounts, locking 78, 84
accounts, removing 74
creang 73, 74
removing 84
resources 83
security and access control 72
user privileges
about 80
sudo command 81
sudoers le, deploying 81
user resource 74
V
value 138
version control 11, 54, 69
visudo command 82
eBooks-IT.org
Thank you for buying
Puppet 3 Beginner’s Guide
About Packt Publishing
Packt, pronounced 'packed', published its rst book "Mastering phpMyAdmin for Eecve
MySQL Management" in April 2004 and subsequently connued to specialize in publishing
highly focused books on specic technologies and soluons.
Our books and publicaons share the experiences of your fellow IT professionals in adapng
and customizing today's systems, applicaons, and frameworks. Our soluon based books
give you the knowledge and power to customize the soware and technologies you're
using to get the job done. Packt books are more specic and less general than the IT books
you have seen in the past. Our unique business model allows us to bring you more focused
informaon, giving you more of what you need to know, and less of what you don't.
Packt is a modern, yet unique publishing company, which focuses on producing quality,
cung-edge books for communies of developers, administrators, and newbies alike. For
more informaon, please visit our website: www.packtpub.com.
About Packt Open Source
In 2010, Packt launched two new brands, Packt Open Source and Packt Enterprise, in order
to connue its focus on specializaon. This book is part of the Packt Open Source brand,
home to books published on soware built around Open Source licences, and oering
informaon to anybody from advanced developers to budding web designers. The Open
Source brand also runs Packt's Open Source Royalty Scheme, by which Packt gives a royalty
to each Open Source project about whose soware a book is sold.
Writing for Packt
We welcome all inquiries from people who are interested in authoring. Book proposals
should be sent to author@packtpub.com. If your book idea is sll at an early stage and you
would like to discuss it rst before wring a formal book proposal, contact us; one of our
commissioning editors will get in touch with you.
We're not just looking for published authors; if you have strong technical skills but no wring
experience, our experienced editors can help you develop a wring career, or simply get
some addional reward for your experse.
eBooks-IT.org
Puppet 2.7 Cookbook
ISBN: 978-1-84951-538-2 Paperback: 300 pages
Build reliable, scalable, secure, high-performance
systems to fully ulize the power of cloud compung
1. Shows you how to use 100 powerful advanced
features of Puppet, with detailed step-by-step
instrucons
2. Covers all the popular tools and frameworks used
with Puppet: Dashboard, Foreman, MCollecve,
and more
3. Includes the latest features and updates in Puppet
2.7
Instant Puppet 3 Starter
ISBN: 978-1-78216-174-5 Paperback: 50 pages
Gain complete consistency from your systems with
minimal eort using Instant Puppet 3 Starter
1. Learn something new in an Instant! A short, fast,
focused guide delivering immediate results.
2. Learn how determinisc results can vastly reduce
your workload
3. Deploy Puppet Server as a Ruby-on-Rails applicaon
to handle thousands of clients
Please check www.PacktPub.com for information on our titles
eBooks-IT.org
OpenStack Cloud Computing Cookbook
ISBN: 978-1-84951-732-4 Paperback: 318 pages
Over 100 recipes to successfully set up and manage
your OpenStack cloud environments with complete
coverage of Nova, Swi, Keystone, Glance, and
Horizon.
1. Learn how to install and congure all the core
components of OpenStack to run an environment
that can be managed and operated just like AWS or
Rackspace
2. Master the complete private cloud stack from
scaling out compute resources to managing swi
services for highly redundant, highly available
storage
OpenNebula 3 Cloud Computing
ISBN: 978-1-84951-746-1 Paperback: 314 pages
Set-up, manage, and maintain your Cloud and learn
soluons for datacenter virtualizaon with this
step-by-step praccal guide
1. Take advantage of open source distributed
le-systems for storage scalability and
high-availability
2. Build-up, manage and maintain your Cloud without
previous knowledge of virtualizaon and cloud
compung
3. Install and congure every supported hypervisor:
KVM, Xen, VMware
Please check www.PacktPub.com for information on our titles
eBooks-IT.org