Puppet Best Practices 1 (Early Release) Edition Chris Barbour 2024 scribd download
Puppet Best Practices 1 (Early Release) Edition Chris Barbour 2024 scribd download
https://ebookultra.com
https://ebookultra.com/download/puppet-best-
practices-1-early-release-edition-chris-barbour/
https://ebookultra.com/download/github-1-early-release-edition-chris-
dawson/
ebookultra.com
https://ebookultra.com/download/two-scoops-of-django-best-practices-
for-django-1-5-1st-edition-daniel-greenfeld/
ebookultra.com
https://ebookultra.com/download/android-best-practices-1st-edition-
godfrey-nolan/
ebookultra.com
https://ebookultra.com/download/the-regulation-of-unfair-commercial-
practices-under-ec-directive-2005-29-stephen-weatherill/
ebookultra.com
Accounting best practices 3rd ed Edition Steven M Bragg
https://ebookultra.com/download/accounting-best-practices-3rd-ed-
edition-steven-m-bragg/
ebookultra.com
https://ebookultra.com/download/healthcare-information-systems-second-
edition-best-practices-kevin-beaver/
ebookultra.com
https://ebookultra.com/download/ruby-best-practices-1st-ed-edition-
gregory-t-brown/
ebookultra.com
https://ebookultra.com/download/best-practices-for-effective-
boards-1st-edition-e-lebron-fairbanks/
ebookultra.com
https://ebookultra.com/download/puppet-cookbook-3rd-edition-thomas-
uphill/
ebookultra.com
Puppet Best Practices 1 (Early Release) Edition Chris
Barbour Digital Instant Download
Author(s): Chris Barbour
ISBN(s): 9781491923009, 1491923008
Edition: 1 (Early Release)
File Details: PDF, 1.56 MB
Year: 2015
Language: english
Puppet Best Practices
Chris Barbour
Puppet Best Practices
by Chris Barbour
Copyright © 2010 Chris Barbour. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are
also available for most titles (http://safaribooksonline.com). For more information, contact our corporate/
institutional sales department: 800-998-9938 or corporate@oreilly.com.
Editor: Brian Anderson Indexer: FIX ME!
Production Editor: FIX ME! Cover Designer: Karen Montgomery
Copyeditor: FIX ME! Interior Designer: David Futato
Proofreader: FIX ME! Illustrator: Rebecca Demarest
Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of O’Reilly
Media, Inc. !!FILL THIS IN!! and related trade dress are trademarks of O’Reilly Media, Inc.
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as
trademarks. Where those designations appear in this book, and O’Reilly Media, Inc. was aware of a trademark
claim, the designations have been printed in caps or initial caps.
While every precaution has been taken in the preparation of this book, the publisher and authors assume
no responsibility for errors or omissions, or for damages resulting from the use of the information contained
herein.
ISBN: 063-6-920-03852-8
[?]
Table of Contents
1. Forward. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
What’s new? 1
About best practices 2
About this pre-release 2
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
iii
Business logic with Roles and Profiles 36
Hiera & Site Specific Data 39
Node Classification 40
Exported Resources & Service Discovery 41
Summary 42
4. Coding Practices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
The Style Guide 45
Coding Principles 46
KISS 46
The Single Responsibility Principle 48
Seperation of Concerns 49
Interface Driven Design 51
Don’t Repeat Yourself (the DRY principle) 52
General Coding Recommendations 55
The balance of Code and Resources 55
Conditional Logic 57
Selectors 59
Variables 61
Variable Naming 61
Referencing Variables 61
Other Variable Use Cases 66
Function Calls 66
Functions for logging and Debugging 67
String manipulation functions 68
Path Manipulation 68
Input validation functions 68
Catalog tests 70
Iteration 72
Iteration with Puppet 3 72
Iteration with Puppet 4 and the Future Parser 73
Generating lists 75
Data Transformation 76
Templates 78
ERB Templates 78
EPP Templates 79
EPP vs. ERB 80
Template abuse 80
The puppet::file defined type 80
Other Language Features 81
Summary 81
iv | Table of Contents
5. Puppet Module Design. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Design modules for public consumption 84
Using public modules 84
Picking good modules 84
Module checklist 85
Module Applicability 85
Contributing Modules 86
Planning and scoping your module 87
Basic Module Layout 87
manifests/init.pp; the module entry point 88
An example init class 89
Parameterizing your module 91
Input validation 95
params.pp pattern 96
Module data sources; the alternative to params.pp 98
Subclasses 99
Subclass relationships 99
Subclass containment 101
Interfacing with subclasses. 105
Defined Types 108
Iteration and DRY with defined types 108
Module Interfaces with defined types 108
Providing services with defined types 109
Defined types for simplifying complex tasks 111
Interacting with the rest of the module 112
Documentation 113
Markdown 113
In-line documentation 115
Rake tasks 116
Testing 116
Rspec 117
Acceptance testing 120
Module testing best practices 121
Continuous Integration 122
Dependencies 122
Summary 123
Table of Contents | v
CHAPTER 1
Forward
First of all, I’d like to thank you for taking the time to read this early preview of Puppet
Best Practices. The reception to the book has been amazing, and we’ve received great
feedback about our initial update.
What’s new?
This months update includes two new chapters. Chapter 4 focusing on coding best
practices and the features of the Puppet DSL. This chapter explores iterators, variables,
and conditional logic among other language features. It also discusses coding principles
as they apply to Puppet.
Chapter 5 focuses on the development of Puppet modules. In this chapter we explore
module structure, scoping, the use of defined types, and module testing.
Puppet 4.0 was officially launched following our previous pre-release. We had already
been discussing practices relating to Puppet 4. This update significantly extends our
Puppet 4 coverage.
Puppet 4 is our target platform for this book, however we expect that a lot of sites will
continue to use Puppet 3 for the foreseeable future. We attempt to address practices for
both major releases of Puppet, directly exploring differences between the two releases
where applicable. This should both help you handle your current requirements, and
plan your approach for the future.
The introduction of the future parser and the release of Hiera 2 are both fairly significant
changes for Puppet 4. We are excited about the introduction of iterators to the Puppet
DSL, as well as module data sources, and the improvements for data handling. We have
however, found that most best practices apply to both releases of Puppet.
1
Best practices will become even more important to this release; the new features of
Puppet 4 are powerful, but carry a lot of potential for mis-use. This book should prove
to be invaluable as you upgrade, grow, and improve your environment.
2 | Chapter 1: Forward
Preface
This book is a work in progress – new chapters will be added as they are written. We
welcome feedback – if you spot any errors or would like to suggest improvements, please
email the author at puppet.best.practices@gmail.com.
In this book, we discuss on how to build and deploy highly maintainable Puppet code,
with a focus on avoiding and eliminating technical debt.
Puppet is by far the most popular configuration management and automation platform
available today, and is quickly being adopted by companies of all sizes, from single
platform startups to established enterprises that may be using it to manage half a dozen
different operating systems.
This book does not attempt to explain the basics of using Puppet; instead we will be
looking at how to use Puppet most effectively. This will include discussions about the
design decisions behind Puppet, an exploration of how to organize code and data, a
look at many common features of Puppet, and discussions about common pitfalls and
traps when deploying Puppet infrastructure.
iii
The information contained in this book will be invaluable to both green and brown field
deployments of Puppet. If you are building a new environment, we will discuss how to
lay a solid foundation that provides flexibility to grow and change. If you already have
an exiting environment or are inheriting an environment, we will explore useful strate‐
gies to eliminate many pain points in your code base and improve upon what you have.
iv | Preface
Navigating This Book
This book is organized roughly as follows:
• Chapters 1 and 2 discuss design concepts that drive the recommendations made
throughout this book.
• Chapters 3 through 8 explore major features of Puppet such as Hiera in depth and
provide many concrete recommendations for each component.
• Chapter 9 and 10 discusses release management practices and development tools.
• Chapter 11 and 13 discuss advanced topics relating to extending Puppet and other
infrastructure management tools that may be useful to your site.
This book is organized so that it can be read front to back, however most of the chapters
in this book are fairly self contained and will provide references to other topics where
appropriate.
I strongly encourage you to start with chapters 2 and 3. From there, feel free to skip
around if you’re the impatient type, or continue reading through to gain a wholistic
understanding of the Puppet infrastructure. Once you’ve read through this book, it is
my sincere hope that you will return to individual sections when needed to address a
difficult problem, to refresh your knowledge, or to pickup strategies that may have been
missed the first time through.
Online Resources
• https://docs.puppetlabs.com/
• https://ask.puppetlabs.com
Preface | v
Constant width italic
Shows text that should be replaced with user-supplied values or by values deter‐
mined by context.
vi | Preface
Technology professionals, software developers, web designers, and business and crea‐
tive professionals use Safari Books Online as their primary resource for research, prob‐
lem solving, learning, and certification training.
Safari Books Online offers a range of product mixes and pricing programs for organi‐
zations, government agencies, and individuals. Subscribers have access to thousands of
books, training videos, and prepublication manuscripts in one fully searchable database
from publishers like O’Reilly Media, Prentice Hall Professional, Addison-Wesley Pro‐
fessional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, John
Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FT
Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Technol‐
ogy, and dozens more. For more information about Safari Books Online, please visit us
online.
How to Contact Us
Please address comments and questions concerning this book to the publisher:
We have a web page for this book, where we list errata, examples, and any additional
information. You can access this page at http://www.oreilly.com/catalog/<catalog page>.
To comment or ask technical questions about this book, send email to bookques
tions@oreilly.com.
For more information about our books, courses, conferences, and news, see our website
at http://www.oreilly.com.
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia
Acknowledgments
This book would not have been possible without the patience and support of my wife
and son.
Preface | vii
CHAPTER 2
The Puppet Design Philosophy
Before we begin to explore practical best practices with Puppet, it’s valuable to under‐
stand the reasoning behind these recommendations.
Puppet can be somewhat alien to technologists who have a background in automation
scripting. Where most of our scripts scripts are procedural, Puppet is declarative. While
a declarative language has many major advantages for configuration management, it
does impose some interesting restrictions on the approaches we use to solve common
problems.
Although Puppet’s design philosophy may not be the most exciting topic to begin this
book, it drives many of the practices in the coming chapters. Understanding that phi‐
losophy will help contextualize many of the recommendations covered in this book.
Declarative Code
The Puppet Domain Specific Language (DSL) is a declarative language, as opposed to
the imperative or procedural languages that system administrators tend to be most
comfortable and familiar with.
9
In theory, a declarative language ideal for configuration base-lining tasks. With the
Puppet DSL, we describe the desired state of our systems, and Puppet handles all re‐
sponsibility for making sure the system conforms to this desired state. Unfortunately,
most of us are used to a procedural approach to system administration. The vast majority
of the bad Puppet code I’ve seen has been the result of trying to write procedural code
in Puppet, rather than adapting existing procedures to Puppet’s declarative model.
Of course, it’s easy to simply say “be declarative.” In the real world, we are often tasked
to deploy software that isn’t designed for a declarative installation process. A large part
of this book will attempt to address how to handle many uncommon tasks in a declar‐
ative way. As a general rule, if your infrastructure is based around packaged open source
software, writing declarative Puppet code will be relatively straight forward. Puppet’s
built in types and providers will provide a declarative way to handle most of your op‐
erational tasks. If you’re infrastructure includes Windows clients and a lot of Enterprise
software writing declarative Puppet code may be significantly more challenging.
Another major challenge system administrators face when working within the con‐
straints of a declarative model is that we tend to operate using an imperative work-flow.
How often have you manipulated files using regular expression substitution? How often
do we massage data using a series of temp files and piped commands? While Puppet
offers many ways to accomplish the same tasks, most of our procedural approaches do
not map well into Puppet’s declarative language. We will explore some examples of this
common problem, and discuss alternative approaches to solving it.
The declarative approach allows you to chose the best way to reach the destination based
on your current situation, and it relies on your ability to find your way to the destination
given.
Declarative languages aren’t magic. Much like an address relies on you understanding
how to read a map or use a GPS device, Puppet’s declarative model relies on it’s own
procedural code to turn your declarative request into a set of instructions that can ach‐
ieve the declared state. Puppet’s model uses a Resource type to model an object, and a
provider implementing procedural code to produce the state the model describes.
The major limitation imposed by Puppet’s declarative model might be somewhat obvi‐
ous. If a native resource type doesn’t exist for the resource you’re trying to model, you
can’t manage that resource in a declarative way. Declaring that I want a red two story
house with 4 bedrooms might empower you to build the house out of straw or wood or
Declarative Code | 11
brick, but it probably won’t actually accomplish anything if you don’t happen to be a
general contractor.
There is some good news on this front, however. Puppet already includes native types
and providers for most common objects, the Puppet community has supplied additional
native models, and if you absolutely have to accomplish something procedurally you
can almost always fall back to the exec resource type.
A practical example
Let’s examine a practical example of procedural code for user management. We will
discuss how to make the code can be made robust, it’s declarative equivalent in Puppet,
and the benefits of using Puppet rather than a shell script for this task.
What if we decide this user should also be a member of the wheel group?
Example 2-2. Imperative user modification with BASH
useradd -g examplegroup alice
usermod -G wheel alice
Of course, this example only covers the use case of creating and managing a few basic
properties about a user. If our policy changed, we would need to write a completely
different script to manage this user. Even fairly simple changes, such as revoking this
user’s wheel access could require somewhat significant changes to this script.
This approach has one other major disadvantage; it will only work on platforms that
implement the same commands and arguments of our reference platform. This example
will fail on FreeBSD (implements adduser, not useradd) Mac OSX, and Windows.
Declarative Code
Let’s look at our user management example using Puppet’s declarative DSL.
Creating a user and group:
Declarative Code | 13
Example 2-5. Declarative user creation with Puppet
$ssh_key = "AAAAB3NzaC1yc2EAAAABIwAAAIEAm3TAgMF/2RY+r7KIeUoNbQb1TP6ApOtgJPNV0T\
Y6teCjbxm7fjzBxDrHXBS1vr+fe6xa67G5ef4sRLl0kkTZisnIguXqXOaeQTJ4Idy4LZEVVbngkd2R\
9rA0vQ7Qx/XrZ0hgGpBA99AkxEnMSuFrD/E5TunvRHIczaI9Hy0IMXc="
group { 'examplegroup':
ensure => 'present',
}
user { 'alice':
ensure => 'present',
gid => 'examplegroup',
managehome => true,
}
ssh_authorized_key { 'alice@localhost':
ensure => 'present',
user => 'alice',
type => 'ssh-rsa',
key => $ssh_key,
}
group { 'examplegroup':
ensure => 'present',
}
user { 'alice':
ensure => 'present',
gid => 'examplegroup',
groups => 'wheel', #
managehome => true,
}
ssh_authorized_key { 'alice@localhost':
ensure => 'present',
user => 'alice',
type => 'ssh-rsa',
key => $ssh_key,
}
Note that the only change between this example and the previous example is the
addition of the groups parameter for the alice resource.
group { 'examplegroup':
ensure => 'absent', #
}
user { 'alice':
ensure => 'absent', #
gid => 'examplegroup',
groups => 'wheel',
managehome => true,
}
ssh_authorized_key { 'alice@localhost':
ensure => 'absent', #
user => 'alice',
type => 'ssh-rsa',
key => $ssh_key,
}
Ssh_authorized_key['alice@localhost'] -> #
User['alice'] -> #
Group['examplegroup']
Ensure values are changed from Present to Absent.
Resource ordering is added to ensure groups are removed after users. Normally,
the correct order is implied due to the Autorequire feature discussed in Chapter 6
Declarative Code | 15
In this example, we are able to remove the user by changing the ensure state from present
to absent on the user’s resources. Although we could remove other parameters such as
gid, groups, and the users key, in most cases it’s better to simply leave the values in place,
just in case we ever decide to restore this user.
It’s usually best to disable accounts rather than remove them. This
helps preserve file ownership information and helps avoid UID reuse.
In our procedural examples, we saw a script that would bring several divergent systems
into conformity. For each step of that example script, we had to analyze the current state
of the system, and perform an action based on state. With a declarative model, all of
that work is abstracted away. If we wanted to have a user who was a member of 2 groups,
we would simply declare that user as such, as in Example 2-6.
1. Exec resources have a set timeout. This example may work well over a relatively fast
corporate netowkr connection, and then fail completely from a home DSL line. The
solution would be to set the timeout parameter of the exec resources to a reasonably
high value.
2. This example does not validate the checksum of the downloaded file, which could
produce some odd results upon extraction. An additional exec resource might be
used to test and correct for this case automatically.
This is a relatively clean example of non-declarative Puppet code, and tends to be seen
when working with software that is not available in a native packaging format. Had this
application been distributed as an RPM, dpkg, or MSI, we could have simply used a
package resource for improved portability, flexibility, and reporting. While this example
is not best practices, there are situations where is unavoidable, often for business or
support reasons.
Another common pattern is the use of conditional logic and custom facts to test for the
presence of software. Please don’t do this unless it’s absolutely unavoidable:
Facter.add(:example_app_version) do
confine :kernel => 'Linux'
setcode do
Facter::Core::Execution.exec('/usr/local/app/example_app --version')
end
end
$app_source = 'http://www.example.com/app-1.2.3.tar.gz'
$app_target = '/tmp/app-1.2.3.tar.gz'
if $example_app_version != '1.2.3' {
exec { 'download application':
command => "/usr/bin/wget -q ${app_source} -O ${app_target}",
before => exec['extract application'],
}
Declarative Code | 17
This particular example has many of the same problems of the previous example, and
introduces one new problem: it breaks Puppet’s reporting and auditing model. The
conditional logic in this example causes the download and extraction resources not to
appear in the catalog sent to the client following initial installation. We won’t be able to
audit our run reports to see whether or not the download and extraction commands
are in a consistent state. Of course, we could check the example_application_ver
sion fact if it happens to be available, but this approach becomes increasingly useless
as more resources are embedded in conditional logic.
This approach is also sensitive to facter and pluginsync related issues, and would defi‐
nitely produce some unwanted results with cached catalogs.
Using facts to exclude parts of the catalog does have one benefit: it can be used to
obfuscate parts of the catalog so that sensitive resources do not exist in future Puppet
runs. This can be handy if, for example, your wget command embeds a pass-phrase,
and you wish to limit how often it appears in your catalogs and reports. Obviously, there
are better solutions to that particular problem, but in some cases there is also benefit to
security in depth.
Idempotency
In computer science, an idempotent function is a function that will return the same
value each time it’s called, whether it’s only called once, or called 100 times. For example:
X = 1 is an idempotent operation. X = X + 1 is a non-idempotent,recursive operation.
Puppet as a language is designed to be inherently idempotent. As a system, Puppet
designed to be used in an idempotent way. A large part of this idempotency owed to it’s
declarative resource management model, however Puppet also enforces a number of
rules on it’s variable handling, iterators, and conditional logic to maintain it’s idempo‐
tency.
Idempotence has major benefits for a configuration management language:
For example, if for some reason Puppet fails part way through a configuration run, re-
invoking Puppet will complete the run and repair any configurations that were left in
an inconsistent state by the previous run.
Side Effects
In computer science, a side effect is a change of system or program state that is outside
the defined scope of the original operation. Declarative and idempotent languages usu‐
ally attempt to manage, reduce, and eliminate side effects. With that said, it is entirely
possible for an idempotent operation to have side effects.
Puppet attempts to limit side effects, but does not eliminate them by any means; doing
so would be nearly impossible given Puppet’s role as a system management tool.
Some side effects are designed into the system. For example, every resource will generate
a notification upon changing a resource state that may be consumed by other resources.
The notification is used to restart services in order to ensure that the running state of
the system reflects the configured state. File bucketing is another obvious intended side-
effect designed into Puppet.
Idempotency | 19
Some side effects are unavoidable. Every access to a file on disk will cause that file’s atime
to be incremented unless the entire file-system is mounted with the noatime attribute.
This is of course true whether or not Puppet is being invoked in noop mode.
The following code is not idempotent, because it will add undesirable duplicate host
entries each time it’s invoked:
Example 2-9. A non-idempotent operation that will create duplicate records
echo '127.0.0.1 example.localdomin' >> /etc/hosts
The following code is idempotent, but will probably have undesirable results:
Example 2-10. An idempotent operation that will destroy data
echo '127.0.0.1 example.localdomin' > /etc/hosts
To make our example idempotent without clobbering /etc/hosts, we can add a simple
check before modifying the file:
Example 2-11. An imperative idempotent operation
grep -q '^127.0.0.1 example.localdomin$' /etc/hosts \
|| echo '127.0.0.1 example.localdomin' >> /etc/hosts
The same example is simple to write in a declarative and idempotent way using the
native Puppet host resource type:
Example 2-12. Declarative Idempotence with Puppet
host { 'example.localdomain':
ip => '127.0.0.1',
}
Alternatively, we could implement this example using the file_line resource type from
the optional stdlib Puppet module:
Example 2-13. Idempotent host entry using the File_line resource type
file_line { 'example.localdomin host':
path => '/etc/hosts',
In both cases, the resource is modeled in a declarative way and is idempotent by it’s very
nature. Under the hood, Puppet handles the complexity of determining whether the
line already exists, and how it should be inserted into the underlying file. Using the
native host resource type, Puppet also determines what file should be modified and
where that file is located. Example 2-12 will work on Windows.
The idempotent examples are safe to run as many times as you like. This is a huge benefit
across large environments; when trying to apply a change to thousands of hosts, it’s
relatively common for failures to occur on a small subset of the hosts being managed.
Perhaps the host is down during deployment? Perhaps you experienced some sort of
transmission loss or timeout when deploying a change? If you are using an idempotent
language or process to manage your systems, it’s possible to handle these exceptional
cases simply by performing a second configuration run against the affected hosts (or
even against the entire infrastructure.)
When working with native resource types, you typically don’t have to worry about
idempotence; most resources handle idempotence natively. A couple of notable excep‐
tions to this statement are the exec and augeas resource types. We’ll explore those in
depth in Chapter 6.
Puppet does however attempt to track whether or not a resource has changed state. This
is used as part of Puppet’s reporting mechanism and used to determine whether or not
a signal should be send to resources with a notify relationship. Because Puppet tracks
whether or not a resource has made a change, it’s entirely possible to write code that is
functionally idempotent, without meeting the criteria of idempotent from Puppet’s re‐
source model.
For example, the following code is functionally idempotent, but will report as having
changed state with every Puppet run.
Example 2-14. Puppet code that will report as non-idempotent
exec { 'grep -q /bin/bash /etc/shells || echo /bin/bash >> /etc/shells':
path => '/bin',
provider => 'shell',
}
Puppet’s idempotence model relies on a special aspect of it’s resource model. For every
resource, Puppet first determines that resource’s current state. If the current state does
not match the defined state of that resource, Puppet invokes the appropriate methods
on the resources native provider to bring the resource into conformity with the desired
state. In most cases, this is handled transparently, however there are a few exceptions
that we will discuss in their respective chapters. Understanding these cases will be critical
in order to avoid breaking Puppet’s simulation and reporting models.
Idempotency | 21
This example will report correctly:
Example 2-15. Improved code that will report as Idempotent
exec { 'echo /bin/bash >> /etc/shells':
path => '/bin',
unless => 'grep -q /bin/bash /etc/shells',
}
In this case, unless provides a condition Puppet can use to determine whether or not a
change actually needs to take place.
Using condition such as unless and onlyif properly will help pro‐
duce safe and robust exec resources. We will explore this in depth in
Chapter 6
A final surprising example is the notify resource, which is often used to produce de‐
bugging information and log entries.
Example 2-16. The Notify resource type
notify { 'example':
message => 'Danger, Will Robinson!'
}
The notify resource generates an alert every time its invoked, and will always report as
a change in system state.
Run level idempotence is a place where Puppet’s idea of change is just as important as
whether or not the resources are functionally idempotent. Remember that before per‐
forming any configuration change, Puppet must first determine whether or not the
resource currently conforms to policy. Puppet will only make a change if resources are
file { '/etc/httpd/conf/httpd.conf':
ensure => 'file',
content => template('apache/httpd.conf.erb'),
}
Package['httpd'] ->
File['/etc/httpd/conf/httpd.conf']
The file resource will not create paths recursively. In Example 2-17, the httpd package
must be installed before the httpd.conf file resource is enforced; and it depends on the
existence of /etc/httpd/conf/httpd.conf, which is only present after the httpd pack‐
age has been installed. If this dependency is not managed, the file resource becomes
non-idempotent; upon first invocation of Puppet it may throw an error, and only enforce
the state of httpd.conf upon subsequent invocations of Puppet.
Such issues will render Puppet convergent. Because Puppet typically runs on a 30 minute
interval, convergent infrastructures can take a very long time to reach a converged state.
There are a few other issues that can render Puppet non-idempotent
Non-deterministic code
As a general rule, the Puppet DSL is deterministic, meaning that a given set of inputs
(manifests, facts, exported resources, etc) will always produce the same output with no
variance.
For example, the language does not implement a random() function; instead a
fqdn_rand() function is provided that returns random values based on a static seed
(the host’s fully qualified domain name.) This function is by it’s very nature not cryp‐
tographically secure, and not actually random at all. It is however useful for in cases
where true randomness is not needed, such as distributing the start times of load in‐
tensive tasks across the infrastructure.
Idempotency | 23
Non-deterministic code can pop up in strange places with Puppet. A notorious example
is Ruby 1.8.7’s handling of hash iteration. The following code is non-deterministic with
Ruby 1.8.7; the output will not preserve the original order and will change between runs:
Example 2-18. Non-deterministic hash ordering with Ruby 1.8.x
$example = {
'a' => '1',
'b' => '2',
'c' => '3',
}
Another common cause of non-deterministic code pops up when our code is dependent
on a transient state.
Environment induced non-determinism.
file { '/tmp/example.txt':
ensure => 'file',
content => "${::servername}\n",
}
Environment induced non-determinism will not be idempotent if you have a load bal‐
anced cluster of Puppet Masters. The value of $::servername changes depending on
which master compiles the catalog for a particular run.
With non-deterministic code, Puppet loses run level idempotence. For each invocation
of Puppet, some resources will change shape. Puppet will converge, but it will always
report your systems as having been brought into conformity with it’s policy, rather than
being conformant. As a result, it’s virtually impossible to determine whether or not
changes are actually pending for a host. It’s also more difficult to track what changes
were made to the configuration, and when they were made.
Non deterministic code also has the side effect that it can cause services to restart due
to Puppet’s notify behavior. This can cause unintended service disruption.
Stateless
Puppet’s client / server API is stateless, and with a few major (but optional) exceptions,
catalog compilation is a completely stateless process.
A stateless system is a system that does not preserve state between requests; each request
is completely independent from previous request, and the compiler does not need to
consult data from previous request in order to produce a new catalog for a node.
Puppet uses a RESTful API over HTTPS for client server communications.
It is worth noting that there are a few stateful features of Puppet. It’s important to weigh
the value of these features against the cost of making your Puppet infrastructure stateful,
and to design your infrastructure to provide an acceptable level of availability and fault
tolerance. We will discuss how to approach each of these technologies in upcoming
chapters, but a quick overview is provided here.
Stateless | 25
Sources of State
In the beginning of this section, I mentioned that there are a few features and design
patterns that can impose state on Puppet catalog compilation. Let’s look at some of these
features in a bit more depth.
Filebucketing
File-bucketing is an interesting and perhaps under-appreciated feature of the File re‐
source type. If a file-bucket is configured, the file provider will create a backup copy of
any file before overwriting the original file on disk. The backup may be bucketed locally,
or it can be submitted to the Puppetmaster.
Bucketing your files is useful for keeping backups, auditing, reporting, and disaster
recovery. It’s immensely useful if you happen to blast away a configuration you needed
to keep, or if you discover a bug and would like to see how the file is changed. The
Puppet enterprise console can use file-bucketing to display the contents of managed
files.
File-buckets can also be used for content distribution, however using a file-bucket this
way creates state. Files are only present in a bucket when placed there; either as a backup
from a previous run, or by the static_compiler terminus. Placing a file in the bucket only
happens during a Puppet run, and Puppet has no internal facility to synchronize buckets
between masters. Reliance upon file buckets for content distribution can create prob‐
lems if not applied cautiously. It can create problems when migrating hosts between
data-centers, when rebuilding masters. Use of file bucketing in your modules can also
create problems during local testing with puppet apply.
Exported Resources
Exported resources provide a simple service discovery mechanism for Puppet. When a
puppetmaster or agent compiles a catalog, resources can be marked as exported by the
compiler. Once the resources are marked as exported, they are recorded in a SQL da‐
tabase. Other nodes may then collect the exported resources, and apply those resources
locally. Exported resources persist until they are overwritten or purged.
As you might imagine, exported resources are, by definition stateful and will affect your
catalog if used.
We will take an in depth look at PuppetDB and exported resources in (to come). For
the time being, just be aware that exported resources introduce a source of state into
your infrastructure.
In this example, a pool of web-servers export their pool membership information to a
haproxy load balancer, using the puppetlabs/haproxy module and exported resources.
haproxy::listen { 'web':
ipaddress => $::ipaddress,
ports => '80',
}
Exported resources rely on PuppetDB, and are typically stored in a PostgreSQL database.
While the PuppetDB service is fault tolerant and can scale horizontally, the PostgreSQL
itself scales Vertically and introduces a potential single point of failure into the infra‐
structure. We will discuss approaches to scale and mitigate risk in (to come)
Hiera
Hiera is by design a pluggable system. By default is provides JSON and YAML back-
ends, both of which are completely stateless. However, it is possible to attach Hiera to
a database or inventory service, including PuppetDB. If you use this approach, it can
introduce a source of state into your Puppet Infrastructure. We will explore Hiera in
depth in [Link to Come].
Stateless | 27
There are plug-ins to Puppet that allow inventory information to be used during catalog
compilation, however these are not core to Puppet.
Custom Facts
Facts themselves do not inherently add state to your Puppet manifests, however they
can be used to communicate state to the Puppetmaster, which can then be used to
compile conditional catalogs. We saw an example of this in [Link to Come] when dis‐
cussing non-declarative code. Using facts in this way does not create the scaling and
availability problems inherent in server site state, but it does create problems if you
intend to use cached catalogs, and it does reduce the effectiveness of your reporting
infrastructure.
Summary
In this chapter, we reviewed the major design features of Puppet’s language, both in
terms of the benefits provided by Puppet’s language, and the restrictions it’s design places
on us. Future chapters will provide more concrete recommendations for the usage of
Puppet’s language, overall architecture of Puppet, and usage of Puppet’s native types
and providers. Building code that leverages Puppet’s design will be a major driving force
behind may of the considerations in future chapters.
Takeaways from this chapter:
In this chapter, we will look at the primary structures of a Puppet code-base, and discuss
how the design of your site will impact module maintenance, code reuse, debugging,
and scaling.
Here, we introduce the major systems of Puppet, including Hiera and Puppet Modules
as well as several common design patterns. We also attempt to broadly classify the com‐
mon kinds of code and data-sources seen with puppet into categories, so that we can
discuss the most appropriate way of handling each concern.
Organization of your Puppet infrastructure becomes critical as your environment
grows; correct placement of your data will improve the flexibility of your code-base and
control the disruption caused by environmental changes. Correct separation of your
code will help to create small problem domains that are much easier to debug and
improve. Correct organization and design will help promote code re-use as you bring
up new applications, and will reduce the impact of changes to business logic and ap‐
plication stack design driven by ever changing business requirements.
As with Chapter 2 this document focuses primarily on “why” rather than “how,” and
will help lay a foundation for many of the recommendations in coming chapters.
1. Application logic
2. Business logic (site specific logic)
3. Site specific data
4. Node specific data (node classification)
29
5. Service discovery data (optional)
These terms are not universally accepted, but are useful for discussing the organization
of your code, and how it maps to the various features and design patterns of Puppet.
These concepts loosely map to a number of Puppet features and design patterns:
Application Logic
Application logic is the logic to manage a single service, application, or subsystem. As
a general rule, application logic will manage a single named product and some of it’s
dependencies. E.g. Apache, Nginx, and IIS are applications. A web-server is a higher
level abstraction that will probably contain multiple applications, services, and their
dependencies.
The application logic for Apache will contain resources to deploy Apache’s packages,
manage Apache’s configuration files, and ensure that the Apache service is running. The
application logic may also contain code to setup virtual hosts that can be declared by
other modules or as part of an application stack.
Application logic may also contain conditional logic and data to handle platform specific
implementation details. For example, an Apache module should contain platform spe‐
cific logic to ensure that the correct package is installed (E.g. httpd on RedHat and
apache2 on Debian platforms) that the configuration file is installed to the correct lo‐
cation and contains the correct platform specific configurations, and that the correct
service name is used when managing the apache service state.
The Apache module contains a lot of data about Apache; package names, configuration
details, file locations, default port numbers, docroot, and file ownership information. It
may even include generic usernames, passwords, hostnames, and contact information
for the cases where the user simply wants to test the module or isn’t concerned about
such details.
Business Logic
Business logic also manages applications, but typically at a higher level. At this level, we
aren’t managing Apache, we are managing a web-server. At this level, we aren’t con‐
cerned with package names or the platform specific implementation details of our serv‐
ices; instead we’re concerned with what components are deployed to our web-server.
The implementation details of each components are abstracted away as application code.
Business logic contains code that ensures our web-servers have the appropriate HTTP
server installed with the necessary extensions, and that the module deploying our web‐
site is applied. Business logic may still contain conditional code; e.g. we might declare
that Windows hosts get the IIS and Linux hosts get the Apache Module. The database
names used by your applications are site specific data.
Business logic may pass higher level data on to your applications. For example, your
application may require that web-server listen on a nonstandard port.
Service Data
Service data is another subset of site specific data, and is related to node classification.
Service data is the record of what hosts provide a specific service as part of a specific
application stack. This kind of data is prevalent with horizontally scaled and multi-tier
applications.
For example, the classic 3-tier application stack consisting of Apache, Tomcat, and
MySQL would typically have 2 pools of service data that would need to be maintained
to configure every host in the stack.
One pool of application data would consist of service data for the Tomcat servers. This
service data would be consumed by the Apache servers in order to configure reverse
proxies. The Apache servers could use this data to load balance requests to the appli‐
cation servers. Service data about the Tomcat servers could also be consumed by the
MySQL servers in order to define database grants. Doing so would provide much more
granular control than authorizing access by user-name and password alone or by au‐
thorizing an entire subnet of hosts.
The other pool of application data would contain information about the MySQL servers.
This could allow the Tomcat servers to load balance read requests across a set of read-
only slaves. It might also identify the write master in a pool of database servers.
Service discovery is often handled as site-specific data and updated manually, however
this approach does not facilitate auto-scaling, or automatic fail-over, and adds extra
manual steps to the process of bringing up new nodes in a pool of hosts. For these and
other reasons, it’s typical to handle service data as a special class of data, managed using
a special set of tools.
More on service discovery tools below, and in (to come)
Breaking it down
Let’s take a look at how these concerns map to the features and common design patterns
of Puppet.
Breaking it down | 33
the number of useful functions it offers. As another example, it’s usually better to create
a dependency on nanliu/staging than to re-implement your own archive retrieval and
extraction code on a per-module basis.
Tightly scoped modules provide a lot of flexibility, but their greatest benefit is their ease
of maintenance. Writing test cases for small simple modules is much easier than writing
test cases for big complex modules. Debugging a problem is much simpler when you
can test each module in isolation and confirm that each module’s behavior is reasonable
and its test cases are correct.
Modules may contain Puppet resources, Puppet code, and data relating to the module.
Let’s take a look at the Puppetlabs/ntp module as an example.
• Package, File, and Service resources manage the basic components of the NTP ser‐
vice.
• Conditional logic provides platform specific defaults for NTP servers, service
names, and file locations
• A list of default, platform specific public NTP servers
• Parameters to change the behavior of the module or set your own NTP servers
• Input validation to ensure that data supplied to the module is sane (if not correct)
• Documentation describing the features use of the module
• Test cases for ensuring correctness and stability of the module
• Meta-data that can be consumed by the Puppet Forge and Puppet Module utility
The NTP module is fairly simple as far as Puppet modules are concerned. It contains
few resources, and a lot of data. However, all of this data is specific to the NTP module.
You can easily use this module as is in your site either by applying it with default pa‐
rameters, or you could overriding the the default parameters with your own site specific
data using site-specific logic or automatic parameter lookups.
From a business logic perspective, this module would probably be part of your baseline
system configuration, and would most likely be applied to every node in your infra‐
structure.
Although this module is concerned with service data, the list of NTP servers in your
site will tend to remain fairly static. In this case, service discovery can be handled as
generic site-specific data provided by Hiera.
When you start to notice that your code meets one or more of the above criteria, consider
how your module might be split into multiple modules and managed using the Roles
and Profiles design pattern.
Breaking it down | 35
By designing your module to be portable, you force your site specific data to be cen‐
tralized and stored outside of the module. By adhering to this simple pattern, you en‐
courage use of node classification and Hiera.
Conversely, it is a good idea to contain application specific data entirely inside your
module. If application data, such as the name of the packages your module installs or
the template used to configure a service are externalized, the module becomes unusable
in isolation; it can only be tested in the greater context of your infrastructure. It can’t be
reused without also supplying the data that lives outside the module, and it isn’t going
to be easily portable since that data is often stored or formulated in such a way to become
non portable. Storing application data outside the module also tends to increase de‐
pendencies; if your module depends on data stored in Hiera in order to perform a basic
smoke test, you must have hiera configured and your data deployed in order to simply
test the module.
One caution about this recommendation: Always try to provide sane defaults for site
specific data, even if those defaults aren’t terribly useful in the real world. This provides
significant benefits for anyone attempting to debug, test, or simply play with your mod‐
ule.
A major feature of Puppet is code reuse. Modules provide a way to model applications
and services in a portable and reusable way. Application stacks can be created by com‐
bining multiple modules together in interesting ways using the roles and profiles pat‐
tern.
For example, if you wanted to setup an instance of WordPress, we would need an ap‐
plication stack containing the following components:
Breaking it down | 37
node web inherits base {
include ::apache
include ::php
}
# etc.
This approach tended to be difficult to maintain, and had a few major limitations; node
definitions do not support multiple inheritance, so it’s impossible to build a development
box that contains both the db and web node classifications. Node inheritance also has
some very surprising behavior relating to variable inheritance and scope.
Trying to migrate this approach to an external node classifier creates some issues as well.
Many web console ENCs support the concept of groups and group inheritance, however
they often don’t support parameterized classes, and when they do they may only support
strings as a data-type.
Of course, conditional logic isn’t really an option with an off-the-shelf console; if you
want to install IIS on your Windows web nodes and Apache on your RHEL nodes, you
need to create two groups and assign them manually.
With roles and profiles, you move node classification logic to a set of conventional
puppet manifests/classes.
Example 3-2. Node Classification with Roles & Profiles
class profiles::base {
include ::accounts
include ::security
include ::repos
class { '::ntp':
servers => [ 'ntp1.corp.com', 'ntp2.corp.com' ],
}
}
class profiles:web {
include ::apache
include ::php
}
Language: English
PHILADELPHIA
HENRY LONGSTRETH, 740 SANSOM STREET
1891
CONTENTS.
CHAPTER PAGE
Introduction 1
I. Organization 6
II. The Inner Light 20
III. Worship 51
IV. Free Ministry 91
V. Special Testimonies 118
VI. Our Calling 157
Appendix 199
QUAKER STRONGHOLDS.
INTRODUCTION.
Whether Quakerism be, as some Friends believe, destined to any
considerable revival or not, it seems at least certain that any
important revival of religion must be the result of a fresh recognition
and acceptance of the very principles upon which the Society of
Friends is built. What these principles and the practices resulting
from them really are, is a subject on which there is a surprising
amount of ignorance amongst us, considering how widely spread is
the connection with and interest about Friends amongst the
members of other persuasions. One seldom meets any one who has
not some link with the Society, and yet it is rare to find any one not
belonging to it at all accurately informed as to its point of view or its
organization. The notorious disinclination of Friends to any attempts
at proselytizing, and perhaps some lingering effects of persecution,
probably account for the very common impression that Friends’ 2
meetings are essentially private—mysterious gatherings into
which it would be intrusive to seek admission. Many people, indeed,
probably suppose (if they think about it at all) that such meetings
are no longer held; that the Society is fast dying out, and the “silent
worship” of tradition is a thing of the past—impracticable, and hardly
to be seriously mentioned in these days of talk and of breathless
activity.
Mount Pleasant,
West Malvern, 1890.
6
CHAPTER I.
ORGANIZATION.
In like manner, upon any subject affecting the Society at large, the
Yearly Meeting communicates with the Quarterly Meetings, who in
their turn diffuse the impulse through their own Monthly and
particular meetings, till it reaches every individual member; and, in
return, information respecting every meeting for worship is from
time to time given to the Monthly Meetings, to be by them in a
condensed form reported to the Quarterly Meetings, and so
eventually presented to the Yearly Meeting in London. All these
ascending and descending processes are carried on with 11
minute accuracy and regularity, and are duly recorded at every
stage in the books of each meeting. There is thus a complete system
of circulation, as of veins and arteries, by which every individual
member is brought within reach of the Society at large, and through
which information, influence, and discipline are carried to and from
the centre and the extremities.
The London Yearly Meeting has two standing committees for the
transaction of such of its affairs as need attention more frequently
than once a year. One of these represents the Yearly Meeting at
large, and has charge of its money matters and other general
business; it bears the curious and suggestive title of the “Meeting for
Sufferings,” from having been originally occupied mainly in relieving
Friends under persecution. The other is a committee of the 14
Yearly Meeting on Ministry and Oversight, and is called the
“Morning Meeting.”
I have already referred to the peculiarity which lies at the root of all
the rest; namely, our views as to the nature of the true gospel
ministry, as a call bestowed on men and women, on old and young,
learned and unlearned; bestowed directly from above, and not to be
conferred by any human authority, or hired for money; to be
exercised under the sole and immediate direction of the one Master,
the only Head of the Church, Christ the Lord. As a consequence of
this view, Friends have, as is well known, refused as a matter of
conscience to pay tithes, or in any way to contribute to the
maintenance of a paid ministry, and of the services prescribed by the
Established Church.
20
CHAPTER II.
THE INNER LIGHT.
When questioned as to the reality and nature of the inner light, the
early Friends were accustomed in return to ask the 22
questioners whether they did not sometimes feel something
within them that showed them their sins; and to assure them that
this same power, which made manifest, and therefore was truly
light, would also, if yielded to, lead them out of sin. This assurance,
that the light which revealed was also the power which would heal
sin, was George Fox’s gospel. The power itself was described by him
in many ways. Christ within, the hope of glory; the light, life, Spirit,
and grace of Christ; the seed, the new birth, the power of God unto
salvation, and many other such expressions, flow forth in abundant
streams of heartfelt eloquence. To “turn people to the light within,”
to “direct them to Christ, their free Teacher,” was his daily business.
For this purpose he and his friends travelled continually up and down
the country, holding meetings everywhere, and finding a never-
failing response to their appeal, as is proved by the bare numbers of
those who, within a very few years, were ready to encounter
persecution, and to maintain their testimony through long years of
imprisonment and sufferings. In the earlier days of the Society the
doctrine of the inner light was clearly one readily understood and
accepted by the ordinary English mind. In our own day it is 23
usually spoken of as a mysterious tenet, springing up now and
again in the minds of isolated enthusiasts, but indigenous only in
Oriental countries, and naturally abhorrent to the practical common
sense of our own people.
The difference arises, I think, from the fact that there are circles
within circles, or spheres within spheres, and that the light to which
the early Friends bore witness was not confined to that innermost
sanctuary of whose very existence, perhaps, none but a few
“mystics” are conscious; but that, while proceeding from those
deepest depths, it was recognized as also lighting up conscience,
and conduct, and all the tangible outer framework of life; and that it
was called “within” not alone in the sense of lying nearer the centre
of our being than anything else, but also in the (to ordinary minds)
more intelligible sense of beginning at home—of being the reward of
each man’s own faithfulness, of being independent of priests and
ordinances. The religion they preached was one which enforced the
individual responsibility of each one for his own soul; it was a
portable and verifiable religion—a religion which required truth in
word and deed, plain dealing and kindness and self-control, and
which did not require ceremonial observances or priestly 24
guarantees; a religion in which practice went for more than
theory, and all were expected to take their stand on one level, and
their share in the worship and the business of “the Church.” It is
easy to see how such preaching as this would commend itself to
English independence. It surely commends itself to the unchanging
sense of truth in the human heart, and will be welcomed whenever it
is preached from first-hand experience of its power.
“That which you seek without you have already within you.” The
words which changed the life of Madame Guyon will never lose their
power while human nature is occupied with the struggle for a state
of stable equilibrium. The perennial justification of Quakerism lies in
its energetic assertion that the kingdom of heaven is within us; that
we are not made dependent upon any outward organization for our
spiritual welfare. Its perennial difficulty lies in the inveterate
disposition of human beings to look to each other for spiritual help,
in the feebleness of their perception of that Divine Voice which
speaks to each one in a language no other ear can hear, and in the
apathy which is content to go through life without the attempt at
any true individual communion with God.