Makefile | 9
Workshops/PuppetWorkshop/en-US/Appendix.xml | 612 ++++
Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png |binary
Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png |binary
Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png |binary
Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml | 1432
++++++++--
Workshops/PuppetWorkshop/en-US/environment_staging.png |binary
Workshops/PuppetWorkshop/en-US/images/environment_staging.png |binary
en-US/Courses.xml | 3
9 files changed, 1864 insertions(+), 192 deletions(-)
New commits:
commit a2022d718e70e9ea45909f9ef2cb89e993662244
Author: Jeroen van Meeuwen (Fedora Unity) <kanarip(a)fedoraunity.org>
Date: Fri Oct 17 20:50:09 2008 +0200
Updates
diff --git a/Makefile b/Makefile
index 5b3c298..a395170 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,14 @@ TRANSLATIONS = $(XML_LANG) $(OTHER_LANGS)
COMMON_CONFIG = /usr/share/publican
include $(COMMON_CONFIG)/make/Makefile.common
-html: html-en-US
+int-workshop:
+ rm -rf en-US/Books/Workshops
+ mkdir -p en-US/Books/Workshops/PuppetWorkshop/
+ cp -a Workshops/PuppetWorkshop/en-US/* en-US/Books/Workshops/PuppetWorkshop/
+ cp -a en-US/Common_Content/Legal_Notice.xml
en-US/Books/Workshops/PuppetWorkshop/Common_Content/Legal_Notice.xml
+ cp -a en-US/Common_Content/Conventions.xml
en-US/Books/Workshops/PuppetWorkshop/Common_Content/Conventions.xml
+
+html: clean int-workshop html-en-US
fpeople: html
rsync -aHvz --delete --progress --rsh=ssh tmp/en-US/html/
fedorapeople.org:./public_html/Courses/
diff --git a/Workshops/PuppetWorkshop/en-US/Appendix.xml
b/Workshops/PuppetWorkshop/en-US/Appendix.xml
index d268653..8710799 100644
--- a/Workshops/PuppetWorkshop/en-US/Appendix.xml
+++ b/Workshops/PuppetWorkshop/en-US/Appendix.xml
@@ -52,7 +52,7 @@
<formalpara
id="PuppetWorkshop-Appendix-Terminology-nativetype">
<title>native type</title>
<para>
- definition
+ A type provided by puppet
</para>
</formalpara>
</listitem>
@@ -159,6 +159,9 @@
Deny from all
</Directory>
+Listen 8140
+NameVirtualHost *:8140
+
<Proxy
balancer://<replaceable>master.puppetmanaged.org</replaceable>>
BalancerMember
http://127.0.0.1:8141 keepalive=on retry=30
</Proxy>
@@ -210,11 +213,616 @@
<section id="PuppetWorkshop-Appendix-Examples-DefinedType">
<title>Example Defined Type</title>
<para>
- para
+ A defined type is a custom type you can define yourself. This can be a
shortcut to a collection of resources given a set of parameters, or just a single
resource, as shown in the following examples.
+ </para>
+ <formalpara>
+ <title>Defined type for a single resource</title>
+ <para>
+ This is an example defined type that creates a single resource, with
just one parameter controlling the source of where the file comes from.
+ <screen>define yum::repository ($enable = true) {
+ file { "/etc/yum/repos.d/$name.repo":
+ mode => 644,
+ owner => "root",
+ group => "root",
+ backup => false,
+ links => follow,
+ source => $enable ? {
+ true => "puppet:///yum/$os/$osver/repos/$name.repo",
+ default => "puppet:///yum/$os/$osver/repos/$name.repo.disabled"
+ }
+ }
+}</screen>
+ </para>
+ </formalpara>
+ <para>
+ You can use this defined type to manage the files in
<filename>/etc/yum/repos.d/</filename>, and thus the repositories available to
the YUM package manager, by calling (for example):
+ <screen>yum::repository { "custom":
+ enable => true
+}</screen>
+ </para>
+ <para>
+ This will create a
<code>File["/etc/yum/repos.d/custom.repo"]</code> and grab if from a
location on the puppetmaster's fileserver where it has
<code>enabled=1</code> in the repository configuration file. Should
<code>$enable</code> have been set to false in the
<code>Yum::Repository["custom"]</code> resource, then the file would
have been grabbed from the location on the puppetmaster's fileserver where the
contents would have <code>enabled=0</code>.
+ </para>
+
+ <formalpara>
+ <title>Multiple resources in a defined type</title>
+ <para>
+ This is an example
+ </para>
+ </formalpara>
+ <para>
+ <screen>define yum::plugin($enable = false) {
+ file { "/etc/yum/pluginconf.d/$name.conf":
+ ensure => $enable ? {
+ true => file,
+ default => absent
+ },
+ owner => "root",
+ group => "root",
+ mode => 644,
+ source => "puppet:///yum/plugins/$name.conf"
+ }
+
+ package { "yum-$name":
+ ensure => $enable ? {
+ true => installed,
+ default => absent
+ }
+ }
+
+ case $name {
+ "versionlock": {
+ file { "/etc/yum/pluginconf.d/versionlock.list":
+ source => "puppet:///yum/plugins/versionlock.list"
+ }
+ }
+ }
+}</screen>
</para>
</section>
</appendix>
+ <appendix id="PuppetWorkshop-Appendix-GITCommitHooks">
+ <title>GIT Commit Hooks</title>
+ <para>
+ The following GIT commit hooks allow you to check the manifests you commit
before they are actually committed (pre-commit), and sync the puppetmasster after the
changes are applied to the repo (post-commit), including posting the changes or diff to a
mailing list.
+ </para>
+ <formalpara>
+ <title>pre-commit</title>
+ <para>
+ There is no valid pre-commit hook for GIT repositories yet.
+ </para>
+ </formalpara>
+ <formalpara>
+ <title>post-commit</title>
+ <para>
+ <screen>#!/bin/sh
+#
+# An hook script to mail out commit update information.
+# Called by git-receive-pack with arguments: refname sha1-old sha1-new
+#
+# To enable this hook:
+# (1) change the recipient e-mail address
+# (2) make this file executable by "chmod +x update".
+#
+
+project=$(cat $GIT_DIR/description)
+[ -f $GIT_DIR/commit-list ] && \
+ recipients=$(cat $GIT_DIR/commit-list)
+[ -n "$recipients" ] || exit 0
+
+ref_type=$(git cat-file -t "$3")
+
+# Only allow annotated tags in a shared repo
+# Remove this code to treat dumb tags the same as everything else
+# case "$1","$ref_type" in
+# refs/tags/*,commit)
+# echo "*** Un-annotated tags are not allowed in this repo" >&2
+# echo "*** Use 'git tag [ -a | -s ]' for tags you want to
propagate."
+# exit 1;;
+# refs/tags/*,tag)
+# echo "### Pushing version '${1##refs/tags/}' to the masses"
>&2
+# # recipients="release-announce(a)somwehere.com
+# announce(a)somewhereelse.com"
+# ;;
+# esac
+
+# set this to 'cat' to get a very detailed listing.
+# short only kicks in when an annotated tag is added
+short='git shortlog'
+
+# see 'date --help' for info on how to write this
+# The default is a human-readable iso8601-like format with minute
+# precision ('2006-01-25 15:58 +0100' for example)
+date_format="%F %R %z"
+
+# Set to the number of pathname components you want in the subject line
+# to indicate which components of a project changed.
+num_path_components=2
+
+# Set subject
+(if expr "$2" : '0*$' >/dev/null ; then
+ subject="Changes to '${1##refs/heads/}'"
+ echo "Subject: $subject"
+else
+ base=$(git-merge-base "$2" "$3")
+ subject=$(git-diff-tree -r --name-only "$base" "$3" |
+ cut -d/ -f-$num_path_components |
+ sort -u |
+ xargs echo -n)
+
+ commits=$(git-rev-list "$3" "^$base" | wc -l)
+
+ if [ "$commits" -ne 1 ] ; then
+ subject="$commits commits - $subject"
+ fi
+
+ branch="${1##refs/heads/}"
+
+ if [ "$branch" != "master" ] ; then
+ subject="Branch '$branch' - $subject"
+ fi
+
+ echo "Subject: $subject"
+fi
+
+echo "To: $recipients"
+echo "X-Project: $project"
+
+module=$(basename `readlink -f $GIT_DIR`)
+
+echo "X-Git-Module: $module"
+echo ""
+
+if expr "$2" : '0*$' >/dev/null
+then
+ # new ref
+ case "$1" in
+ refs/tags/*)
+ # a pushed and annotated tag (usually) means a new version
+ tag="${1##refs/tags/}"
+ if [ "$ref_type" = tag ]; then
+ eval $(git cat-file tag $3 | \
+ sed -n '4s/tagger
\([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
+ date=$(date --date="1970-01-01 00:00:00 $ts seconds"
+"$date_format")
+ echo "Tag '$tag' created by $tagger at $date"
+ git cat-file tag $3 | sed -n '5,$p'
+ echo
+ fi
+
+ prev=$(git describe "$3^" | sed 's/-g.*//')
+ # the first tag in a repo will yield no $prev
+ if [ -z "$prev" ]; then
+ echo "Changes since the dawn of time:"
+ git rev-list --pretty $3 | $short
+ else
+ echo "Changes since $prev:"
+ git rev-list --pretty $prev..$3 | $short
+ echo ---
+ git diff $prev..$3 | diffstat -p1
+ echo ---
+ fi
+ ;;
+
+ refs/heads/*)
+ branch="${1##refs/heads/}"
+ echo "New branch '$branch' available with the following
commits:"
+ git-rev-list --pretty "$3" $(git-rev-parse --not --all)
+ ;;
+ esac
+else
+ case "$base" in
+ "$2")
+ git diff "$3" "^$base" | diffstat -p1
+ echo
+ echo "New commits:"
+ ;;
+ *)
+ echo "Rebased ref, commits from common ancestor:"
+ ;;
+ esac
+ git-rev-list "$3" "^$base" | while read rev; do git-show $rev;
echo ""; echo ""; done
+fi) | /usr/local/bin/send-unicode-email.py $recipients
+exit 0</screen>
+ </para>
+ </formalpara>
+ <note>
+ <para>
+ The post-commit GIT hook requires
<filename>/usr/local/bin/send-unicode-email.py</filename>
+ </para>
+ </note>
+ </appendix>
+
+ <appendix id="PuppetWorkshop-Appendix-SVNCommitHooks">
+ <title>SVN Commit Hooks</title>
+ <para>
+ The following SVN commit hooks allow you to check the manifests you commit
before they are actually committed (pre-commit), and sync the puppetmaster after the
changes are applied to the repo (post-commit), including posting the changes or diff to a
mailing list.
+ </para>
+ <formalpara>
+ <title>pre-commit</title>
+ <para>
+ <screen>#!/bin/sh
+# SVN pre-commit hook to check Puppet syntax for .pp files
+# Modified from
http://mail.madstop.com/pipermail/puppet-users/2007-March/002034.html
+REPOS="$1"
+TXN="$2"
+tmpfile=`mktemp`
+export HOME=/tmp
+SVNLOOK=/usr/bin/svnlook
+$SVNLOOK changed -t "$TXN" "$REPOS" | awk '{print $2}' | grep
'\.pp$' | while read line
+do
+ $SVNLOOK cat -t "$TXN" "$REPOS" "$line" > $tmpfile
+ if [ $? -ne 0 ]; then
+ echo "Warning: Failed to checkout $line" >&2
+ fi
+ puppet --color=false --confdir=/tmp --vardir=/tmp --parseonly --ignoreimport $tmpfile
>&2
+ if [ $? -ne 0 ]; then
+ echo "Puppet syntax error in $line." >&2
+ exit 2
+ fi
+done
+res=$?
+rm -f $tmpfile
+if [ $res -ne 0 ]; then
+ exit $res
+fi</screen>
+ </para>
+ </formalpara>
+ <formalpara>
+ <title>post-commit</title>
+ <para>
+ <screen>#!/bin/sh
+REPOS="$1"
+REV="$2"
+PATH=/usr/bin:/bin
+PROJECT=puppet
+TO=<replaceable>to(a)domain.tld</replaceable>
+FROM=<replaceable>from(a)domain.tld</replaceable>
+
+svn up /etc/puppet/
+svn up /var/lib/puppet/manifests/development/
+svn up /var/lib/puppet/manifests/testing/
+svn up /var/lib/puppet/manifests/production/
+svn up /var/lib/puppet/modules/development/
+svn up /var/lib/puppet/modules/testing/
+svn up /var/lib/puppet/modules/production/
+
+# Note that --from is optional and if omitted the from address
+# will be $COMMITTER@$hostname
+#
+# If no --from is used, the committer may need to be subscribed
+# to the mailing list used in order for this message to be
+# accepted to the mailing list
+#
+
+svnnotify --repos-path "$REPOS" \
+ --revision "$REV" \
+ --smtp localhost \
+ --svnlook /usr/bin/svnlook \
+ --to $TO \
+ <replaceable>--from $FROM \</replaceable>
+ --subject-prefix "Puppet SVN Commit: " \
+ --with-diff --subject-cx --handler HTML::ColorDiff</screen>
+ </para>
+ </formalpara>
+ </appendix>
+
+ <appendix id="PuppetWorkshop-Appendix-ModuleConventions">
+ <title>Module Conventions</title>
+ <para>
+ This section lists a number of conventions used by all modules on <ulink
url="http://puppetmanaged.org" />, that you can use (f desireable) and modify
as needed.
+ </para>
+
+ <section
id="PuppetWorkshop-Appendix-ModuleConventions-CodeLayout">
+ <title>Code Layout</title>
+ <para>
+ A consistent layout of the code is important within a collaborative
environment. This section describes a number of conventions used in the modules on
puppetmanaged.org.
+ </para>
+
+ <section
id="PuppetWorkshop-Appendix-ModuleConventions-CodeLayout-Indentation">
+ <title>Indentation</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <formalpara>
+ <title>4 spaces to a tab</title>
+ <para>
+ No tabs should be used, and instead 4 space
characters should be inserted to increase the indentation level. This applies to the
manifests, custom facts, the configuration files (if compatible) as well as the DocBook
documentation.
+ </para>
+ </formalpara>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Aggregated resource
definitions</title>
+ <para>
+ Aggregated resource definitions, such as
<code>file { [ "foo", "bar" ]: ensure => absent
}</code> are indented like this:<screen>file { [
+ "foo",
+ "bar"
+ ]:
+ ensure => absent
+}</screen>
+ </para>
+ </formalpara>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </section>
+
+ </section>
+
+ <section id="PuppetWorkshop-Appendix-ModuleConventions-Sources">
+ <title>Sources</title>
+ <para>
+ The location a file is sourced from is subject to the following
conditions:
+ <orderedlist>
+ <listitem>
+ <para>
+ A file should first be sourced from a domain-specific
configuration tree as to allow domain administrators to maintain their own files. The
exact location again is subject to the following conditions:
+ <orderedlist inheritnum="inherit">
+ <listitem>
+ <para>
+ If the file is part of a directory structure that
is going to be pulled from the source recursively, and the configuration files in that
directory structure is host specific, files for such a host go into a sub-directory
<code>$hostname</code>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the file is is a single host-specific file to
be pulled from the source, the file goes into the same directoryas the original file
appended with <code>.$hostname</code>.
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A file should then be sourced from the common, shared
<filename>files/</filename> file server share and is organization specific,
but not necessarily domain-specific. The <filename>files/</filename> file
server share allows an organization to maintain the configuration in a single source
controlled tree. Again, the following conditions apply to a file location:
+ <orderedlist inheritnum="inherit">
+ <listitem>
+ <para>
+ If the file is part of a directory structure that
is going to be pulled from the source recursively, and the configuration files in that
directory structure is host specific, files for such a host go into a sub-directory
<code>$hostname</code>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the file is is a single host-specific file to
be pulled from the source, the file goes into the same directoryas the original file
appended with <code>.$hostname</code>.
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The source is the default configuration available with the
puppet module.
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+ <para>
+ Such a set of conditions can maybe be shown best in an example
configuration, in this case a single file:
+ </para>
+ <para>
+<screen>class sudo {
+ file { "/etc/sudoers":
+ source => [
+ # Organization-wide, host specific, single file
+ "puppet:///files/sudo/sudoers.$hostname",
+
+ # Organization-wide
+ "puppet:///files/sudo/sudoers",
+
+ # Module default
+ "puppet:///sudo/sudoers"
+ ]
+ }
+}</screen>
+ </para>
+ <para>
+ If, however, a host is configured to recursively pull a directory from
source, this is a better approach:
+ </para>
+ <para>
+<screen>class foo {
+ file { "/path/to/foo/":
+ source => [
+ # Organization-wide, host specific, directory tree
+ "puppet:///files/foo/$hostname/",
+
+ # Organization-wide
+ "puppet:///files/foo/common/",
+
+ # Module default
+ "puppet:///foo/common/"
+ ],
+ recurse => true
+ }
+}</screen>
+ </para>
+ <para>
+ For common files shared between the common and host specific tree you use
symbolic links from the host specific tree to the common tree. If, however, there is a
million files in the common directory tree with thousands of changes every day, which
makes using symbolic links not particularly sustainable, you can also specify an
additional File resource in the manifest applied to the host and manage the host-specific
file separate from the recursively managed directory. An example:
+ </para>
+ <para>
+<screen>file { "/path/to/foo/":
+ source => "puppet:///foo/common/",
+ recurse => true
+}
+
+file { "/path/to/foo/bar":
+ source => "puppet:///foo/$hostname/bar"
+}</screen>
+ </para>
+ <para>
+ For host specific files and directory trees shared amongst a group of
hosts, you use a category specific file name or directory name appendix (such as
<code>.admins-only</code>) and use symbolic links between the host specific
file or directory and the category specific file or directory.
+ </para>
+
+ <section
id="PuppetWorkshop-Appendix-ModuleConventions-Sources-Scalability">
+ <title>Scalability Issues</title>
+ <para>
+ Suppose you are a large ISP (
example-isp.com) running a million
websites which may or may not be managed by puppet. Putting all virtualhost specific
configuration files in your domain specific tree under
<filename>webserver/sites/</filename> doesn't scale very well because of
the large amount of files in one single directory which could be inconvenient when trying
to edit those files. We've set up a domain-specific tree working around that problem
by adding a <filename>webserver/sites.d/</filename> tree dividing the files
into a hierarchy with makes tab-completion and finding the files needing to be edited
somewhat easier.
+ </para>
+ <para>
+ The <filename>webserver/</filename> tree in GIT =>
<ulink
url="http://git.puppetmanaged.org/?p=domains/example-isp.com/.git;a=...
/>
+ </para>
+ </section>
+
+ </section>
+
+ <section
id="PuppetWorkshop-Appendix-ModuleConventions-ModuleTreeLayout">
+ <title>Module Tree Layout</title>
+ <para>
+ Like described on <ulink
url="http://reductivelabs.com/trac/puppet/wiki">Puppet's
wiki</ulink>, a module tree looks as follows:
+ </para>
+ <para>
+ <screen>MODULE_PATH
+ `- module_name/
+ `- README
+ `- depends/
+ `- files/
+ `- manifests/
+ `- plugins/
+ `- facter/
+ `- puppet/type/
+ `- templates/</screen>
+ See also: <ulink
url="http://reductivelabs.com/trac/puppet/wiki/ModuleOrganisation" />
+ </para>
+ <para>
+ This is a segmentedlist:
+ <segmentedlist>
+ <title>Module Layout</title>
+ <segtitle>Directory</segtitle>
+ <segtitle>Description</segtitle>
+ <seglistitem>
+ <seg><code>MODULE_PATH</code></seg>
+ <seg>The path that holds the modules (see
<code>modulepath</code> in
<filename>/etc/puppet/puppet.conf</filename>)</seg>
+ </seglistitem>
+ <seglistitem>
+ <seg><filename>`-
module_name/depends/</filename></seg>
+ <seg>Dependencies for the module</seg>
+ </seglistitem>
+ <seglistitem>
+ <seg><filename>`-
module_name/files/</filename></seg>
+ <seg>Files distributed with the module. Files in this
directory can be sourced by using the <code>puppet:///module_name/</code>
URI</seg>
+ </seglistitem>
+ <seglistitem>
+ <seg><filename>`-
module_name/manifests/</filename></seg>
+ <seg>The manifests directory for this module. Should at
least hold a file called <filename>init.pp</filename></seg>
+ </seglistitem>
+ <seglistitem>
+ <seg><filename>`-
module_name/plugins/facter/</filename></seg>
+ <seg>Holds custom facts for this module</seg>
+ </seglistitem>
+ <seglistitem>
+ <seg><filename>`-
module_name/plugins/puppet/type/</filename></seg>
+ <seg>Holds custom puppet types for this module</seg>
+ </seglistitem>
+ </segmentedlist>
+ </para>
+ <para>
+ Additionally,
puppetmanaged.org modules define a tree layout for some of
the files needed by these modules. For example, Red Hat Enterprise Linux 3 cannot have the
<code>UsePAM</code> configuration directive in
<filename>/etc/ssh/sshd_config</filename>. For the SSH module, the
<filename>files/</filename> directory has the following layout:
+ </para>
+ <para>
+ <screen>ssh/files
+ `- CentOS -> RedHat
+ `- RedHat/
+ `- 3/
+ `- sshd_config
+ `- sshd_config</screen>
+ </para>
+ <para>
+ whereas the module sources the
<filename>/etc/ssh/sshd_config</filename> as follows:
+ </para>
+ <para><screen>file { "/etc/ssh/sshd_config":
+ owner => "root",
+ group => "root",
+ mode => 600,
+ replace => true,
+ source => [
+ # Organization specific, OS version specific, host specific
+ "puppet:///files/ssh/$os/$osver/sshd_config.$hostname",
+ # Organization specific, OS version specific
+ "puppet:///files/ssh/$os/$osver/sshd_config",
+
+ # Organization specific, OS specific, host specific
+ "puppet:///files/ssh/$os/sshd_config.$hostname",
+ # Organization specific, OS specific
+ "puppet:///files/ssh/$os/sshd_config",
+
+ # Organization specific, host specific
+ "puppet:///files/ssh/sshd_config.$hostname",
+ # Organization specific
+ "puppet:///files/ssh/sshd_config",
+
+ # Module default, OS version specific
+ "puppet:///ssh/$os/$osver/etc/ssh/sshd_config",
+ # Module default, OS specific
+ "puppet:///ssh/$os/etc/ssh/sshd_config",
+ # Module default
+ "puppet:///ssh/etc/ssh/sshd_config"
+ ],
+ notify => Service["sshd"],
+ require => Package["openssh-server"]
+}</screen>
+ </para>
+ </section>
+
+ <section
id="PuppetWorkshop-Appendix-ModuleConventions-FileAndDirectoryPaths">
+ <title>File And Directory Paths</title>
+ <para>
+ To be able to clearly distinct files from directories (and vice-versa of
course), the modules use the following naming convention:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Directory names end with a forward slash.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Filenames do not (however obvious).
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ And here's why it's important. There's a few utilities that
do not work if a trailing slash to a fully qualified directory name is omitted. One of
these situations is shown as an example (and is not the actual code in the git module):
+ </para>
+ <para>
+<screen>git::pull { [
+ "foo",
+ "bar"
+ ]:
+ base_source => "/srv/git",
+ localtree => "/var/lib/puppet/modules/development"
+}
+
+define git::pull($localtree, $base_source, $branch = 'master') {
+ exec { "git_pull_$name":
+ command => "git pull ${base_source}${name} $branch",
+ cwd => "${localtree}${name}"
+ }
+}</screen>
+ </para>
+ <para>
+ where the <code>cwd</code> has become
<filename>/var/lib/puppet/modules/developmentfoo</filename> instead of the
intended <filename>/var/lib/puppet/modules/development/foo/</filename>. Also,
the <code>$base_source</code> in this example is, unintentionally,
<filename>/srv/gitfoo</filename>.
+ </para>
+ <para>
+ If the modules do not use the standard directory naming schema as set
forth in this section, some modules might start omitting the forward slash (between
<code>${localtree}</code> and <code>$name</code>, or between
<code>${base_source}</code> and <code>${name}</code>), and others
might not, which in the end leads to confusion and malfunctioning modules.
+ </para>
+ </section>
+
+ </appendix>
+
+<!-- <appendix id="PuppetWorkshop-Appendix-Modules">
+ <title>Modules</title>
+ <para>
+
+ </para>
+
+ <section id="PuppetWorkshop-Appendix-Modules-Puppet">
+ <title>Puppet</title>
+ <para>
+
+ </para>
+ </section>
+
+ </appendix>-->
+
<xi:include href="Revision_History.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
</part>
diff --git a/Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png
b/Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png
new file mode 100644
index 0000000..e2e7218
Binary files /dev/null and
b/Workshops/PuppetWorkshop/en-US/Common_Content/environment_staging.png differ
diff --git a/Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png
b/Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png
new file mode 100644
index 0000000..e2e7218
Binary files /dev/null and
b/Workshops/PuppetWorkshop/en-US/Common_Content/images/environment_staging.png differ
diff --git a/Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png
b/Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png
new file mode 100644
index 0000000..b611f79
Binary files /dev/null and
b/Workshops/PuppetWorkshop/en-US/Common_Content/images/title_logo.png differ
diff --git a/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml
b/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml
index a8ccafd..853e1f1 100644
--- a/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml
+++ b/Workshops/PuppetWorkshop/en-US/Puppet_Workshop.xml
@@ -419,7 +419,7 @@ ensure => present
}
file { "/etc/yp.conf":
- source => "puppet://$server/files/yp.conf",
+ source => "puppet:///files/yp.conf",
notify => Service["ypbind"],
require => Package["ypbind"]
}
@@ -438,7 +438,7 @@ service { "ypbind":
The above example is called a <emphasis>manifest</emphasis>,
built out of <emphasis>types</emphasis> (package, file, service), which, once
defined in a manifest, are referred to as <emphasis>resources</emphasis>. See
also <xref linkend="PuppetWorkshop-Appendix-Terminology" />
</para>
<para>
- The configuration file for this YP client is pulled from
<code>puppet://$server/files/yp.conf</code>, the puppetmaster's
fileserver, which is where you put the file, possibly from within a Version Control
System.
+ The configuration file for this YP client is pulled from
<code>puppet:///files/yp.conf</code>, the puppetmaster's fileserver, which
is where you put the file, possibly from within a Version Control System.
</para>
</section>
@@ -447,6 +447,9 @@ service { "ypbind":
<para>
As you can see in the <xref
linkend="PuppetWorkshop-IntroductionToPuppet-WhatDoesPuppetDo-YpClientExample"
/>, Puppet does not just manage resources and hopes for the best, but allows one
resource to require another resource (<code>File["/etc/yp.conf"] { require
=> Package["ypbind"] }</code>), and for one resource to notify another
resource on changes (<code>File["/etc/yp.conf"] { notify =>
Service["ypbind"] }</code>).
</para>
+ <para>
+ This capability ensures that files are placed in the right location after
the appropriate package is installed, and before the service starts. This also allows
changes to configuration files to notify the service so that it can reload or restart.
+ </para>
</section>
<section
id="PuppetWorkshop-IntroductionToPuppet-WriteOnceApplyManyTimes">
@@ -461,7 +464,7 @@ service { "ypbind":
}
file { "/etc/yp.conf":
- source => "puppet://$server/files/yp.conf",
+ source => "puppet:///files/yp.conf",
notify => Service["ypbind"],
require => Package["ypbind"]
}
@@ -477,13 +480,12 @@ service { "ypbind":
}
node '<replaceable>node2.example.com</replaceable>' {
-
package { "ypbind":
ensure => latest
}
file { "/etc/yp.conf":
- source => "puppet://$server/files/yp.conf",
+ source => "puppet:///files/yp.conf",
notify => Service["ypbind"],
require => Package["ypbind"]
}
@@ -499,7 +501,7 @@ node
'<replaceable>node2.example.com</replaceable>' {
}</screen>
</para>
<para>
- This of course is not very efficient; One change would need to be applied
many times still, and errors are easily made. Besides, it doesn't scale well. Why not
group the YP Client relevant resources into a class, and then include the class with each
node? Here's how:
+ This of course is not very efficient; One change would need to be applied
many times still, and errors are easily made. Besides, it doesn't scale well. Group
the YP Client relevant resources into a <xref
linkend="PuppetWorkshop-PuppetTerminology-class" /> instead, and then include
the class with each node:
</para>
<para>
<screen># Example /etc/puppet/manifests/classes/yp.pp
@@ -510,7 +512,7 @@ class yp::client {
}
file { "/etc/yp.conf":
- source => "puppet://$server/files/yp.conf",
+ source => "puppet:///files/yp.conf",
notify => Service["ypbind"],
require => Package["ypbind"]
}
@@ -524,9 +526,7 @@ class yp::client {
]
}
}</screen>
-<screen># Example /etc/puppet/manifests/site.pp
-
-$server = "<replaceable>master.example.com</replaceable>"
+ <screen># Example /etc/puppet/manifests/site.pp
import "classes/*.pp"
@@ -548,8 +548,6 @@ node
'<replaceable>node3.example.com</replaceable>' {
<para>
<screen># Example /etc/puppet/manifests/site.pp
-$server = "<replaceable>master.example.com</replaceable>"
-
import "classes/*.pp"
node '<replaceable>node1.example.com</replaceable>' {
@@ -598,8 +596,6 @@ node
'<replaceable>node3.example.com</replaceable>' {
<para>
<screen># Example /etc/puppet/manifests/site.pp
-$server = "<replaceable>master.example.com</replaceable>"
-
import "classes/*.pp"
node '<replaceable>node1.example.com</replaceable>' {
@@ -614,8 +610,26 @@ node
'<replaceable>node3.example.com</replaceable>' {
include desktop
}</screen>
</para>
+ <para>
+ Note that nodes, like classes, allow a simple form of inheritance, but
can only inherit from one other node. As such, you can also use:
+ </para>
+ <para>
+ <screen>node desktop {
+ include yp::client
+}
+
+node '<replaceable>node1.example.com</replaceable>' inherits desktop
{
+}
+
+node '<replaceable>node1.example.com</replaceable>' inherits desktop
{
+}
+
+node '<replaceable>node1.example.com</replaceable>' inherits desktop
{
+}</screen>
+ </para>
</section>
+
</chapter>
<chapter id="PuppetWorkshop-PuppetTerminology">
@@ -624,7 +638,7 @@ node
'<replaceable>node3.example.com</replaceable>' {
First, some clarification on the terminology used in this documentation. See
also <xref linkend="PuppetWorkshop-Appendix-Terminology" />
<itemizedlist>
<listitem>
- <formalpara>
+ <formalpara
id="PuppetWorkshop-PuppetTerminology-class">
<title>class</title>
<para>
A class is a collection of resources applied to a node with a
single include statement. It groups together a comprehensible set of resources. A class
<emphasis>ypclient</emphasis> would manage the
<code>File["/etc/nsswitch.conf"]</code>,
<code>File["/etc/yp.conf"]</code>,
<code>Package["ypbind"]</code>, and
<code>Service["ypbind"]</code> resources.
@@ -714,6 +728,9 @@ FIXME:
</listitem>
</itemizedlist>
</para>
+ <para>
+ More definition of terms are in <xref
linkend="PuppetWorkshop-Appendix-Terminology" />
+ </para>
</chapter>
<chapter id="PuppetWorkshop-HowPuppetWorks">
@@ -760,7 +777,13 @@ FIXME:
</para>
</formalpara>
<para>
- <screen># <userinput>puppetca --sign
<fqdn></userinput></screen>
+ <screen># <userinput>puppetca --sign
<replaceable><Certificate-CN></replaceable></userinput></screen>
+ </para>
+ <para>
+ To refuse a certificate, or clean a signed certificate, use:
+ </para>
+ <para>
+ <screen># <userinput>puppetca --clean
<replaceable><Certificate-CN></replaceable></userinput></screen>
</para>
</listitem>
<listitem>
@@ -969,7 +992,7 @@ debug: Calling fileserver.describe</screen>
A webserver capable of performing as a frontend SSL
reverse proxy load balancer, such as the Apache HTTPd webserver.
</para>
<para>
- <screen># <userinput>yum install
httpd</userinput></screen>
+ <screen># <userinput>yum install httpd
mod_ssl</userinput></screen>
</para>
</listitem>
<listitem>
@@ -1019,6 +1042,8 @@ debug: Calling fileserver.describe</screen>
<title>Configuring the Puppetmaster</title>
<para>
The configuration file for puppet and puppetmaster is
<filename>/etc/puppet/puppet.conf</filename>. It is a file in INI-like format
with sections, keys and values. There's 4 sections of interest,
+ </para>
+ <para>
<itemizedlist>
<listitem>
<formalpara>
@@ -1061,140 +1086,146 @@ debug: Calling fileserver.describe</screen>
<title>Relevant Settings For The First Run</title>
<para>
For the first run of the puppetmaster, the following settings
require configuration:
- <itemizedlist>
- <listitem>
- <formalpara>
- <title>[main]</title>
- <para>
- The locations where puppet seeks it's
configuration and puts it's transitional data. The most important setting is
<literal>vardir</literal>, which should be set to
<filename>/var/lib/puppet/</filename>. Further settings include:
- <itemizedlist>
- <listitem>
- <para>
- <code>logdir =
/var/log/puppet/</code>
- </para>
- </listitem>
- <listitem>
+ </para>
+ </formalpara>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <formalpara>
+ <title>[main]</title>
+ <para>
+ The locations where puppet seeks it's
configuration and puts it's transitional data. The most important setting is
<literal>vardir</literal>, which should be set to
<filename>/var/lib/puppet/</filename>. Further settings include:
+ <itemizedlist>
+ <listitem>
+ <para>
+ <code>logdir =
/var/log/puppet/</code>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <code>rundir =
/var/run/puppet/</code>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <code>ssldir =
$vardir/ssl/</code>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </formalpara>
+ <note>
+ <para>
+ If you used a package to install puppet, the
defaults should work, but may not comply with your backup strategy. It is the upstream
puppet package that cannot cater to each and every distribution or operating system it is
available for, and therefore has a set of defaults that will work, but will need to be
changed on most platforms.
+ </para>
+ </note>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>[puppetmasterd]</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <formalpara>
+ <title>certname</title>
<para>
- <code>rundir =
/var/run/puppet/</code>
+ The puppetmaster
certificate's Common Name (CN), for which by default the system's hostname is
used. The hostname of the system is a pretty reasonable value.
</para>
- </listitem>
- <listitem>
+ </formalpara>
+ </listitem>
+ <listitem>
+ <formalpara>
+
<title>certdnsnames</title>
<para>
- <code>ssldir =
$vardir/ssl/</code>
+ A colon
(<literal>:</literal>) seperated list of DNS names resolving to the
puppetmaster. Include here:
</para>
- </listitem>
- </itemizedlist>
- </para>
- </formalpara>
- <note>
- <para>
- If you used a package to install puppet, the
defaults should work, but may not comply with your backup strategy. It is the upstream
puppet package that cannot cater to each and every distribution or operating system it is
available for, and therefore has a set of defaults that will work, but will need to be
changed on most platforms.
- </para>
- </note>
- </listitem>
- <listitem>
- <formalpara>
- <title>[puppetmasterd]</title>
- <para>
- <itemizedlist>
- <listitem>
- <formalpara>
-
<title>certname</title>
- <para>
- The puppetmaster
certificate's Common Name (CN), for which by default the system's hostname is
used. The hostname of the system is a pretty reasonable value.
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <formalpara>
-
<title>certdnsnames</title>
- <para>
- A colon
(<literal>:</literal>) seperated list of DNS names resolving to the
puppetmaster. Include here:
- <orderedlist>
- <listitem>
- <para>
- The short
hostname of the system, using the output of: <screen># <userinput>hostname
-s</userinput></screen>
- </para>
- </listitem>
- <listitem>
- <para>
-
<literal>puppet</literal>
- </para>
- </listitem>
- <listitem>
- <para>
-
<literal>puppet</literal>, followed by the DNS domain name of the system,
using the output of <screen>#
<userinput>dnsdomainname</userinput></screen>
- </para>
- </listitem>
- <listitem>
- <para>
- Any other
hostname or fully qualified domain name you want to use for the puppetmaster.
- </para>
- </listitem>
- </orderedlist>
- </para>
- </formalpara>
- </listitem>
- </itemizedlist>
- </para>
- </formalpara>
- </listitem>
- <listitem>
- <para>
- Another setting to check is whether or not this
puppetmaster is going to be the Certificate Authority <screen>[puppetmasterd]
- ca = <replaceable>true</replaceable></screen>
- The default is often set to
<code>true</code>.
+ </formalpara>
+ <para>
+ <orderedlist>
+ <listitem>
+ <para>
+ The short hostname of the
system, using the output of: <screen># <userinput>hostname
-s</userinput></screen>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
<literal>puppet</literal>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+
<literal>puppet</literal>, followed by the DNS domain name of the system,
using the output of <screen>#
<userinput>dnsdomainname</userinput></screen>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Any other hostname or
fully qualified domain name you want to use for the puppetmaster.
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+ </listitem>
+ </itemizedlist>
</para>
- </listitem>
- <listitem>
- <para>
- Whether or not to use autosigning of
certificates, using
- <screen>[puppetca]
+ </formalpara>
+ </listitem>
+ <listitem>
+ <para>
+ Another setting to check is whether or not this
puppetmaster is going to be the Certificate Authority <screen>[puppetmasterd]
+ ca = <replaceable>true</replaceable></screen>
+ The default is often set to
<code>true</code>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Whether or not to use autosigning of certificates,
using
+ <screen>[puppetca]
autosign = <replaceable>false</replaceable></screen>
- The default is to
<emphasis>not</emphasis> use autosigning. Only applicable if
<code>puppetca</code> is set to <code>true</code>.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ The default is to
<emphasis>not</emphasis> use autosigning. Only applicable if
<code>puppetca</code> is set to <code>true</code>.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
<formalpara>
<title>Other Relevant Settings</title>
<para>
The following settings require review before the puppetmaster
is going in production.
- <itemizedlist>
- <listitem>
- <para>
- A list of environments using a comma seperated
list, in
- <screen>[puppetmasterd]
+ </para>
+ </formalpara>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ A list of environments using a comma seperated list,
in
+ <screen>[puppetmasterd]
environments =
<replaceable>development,testing,production</replaceable></screen>
- </para>
- <para>
- <emphasis>See also</emphasis>:
<xref linkend="PuppetWorkshop-HowToUsePuppet-Environments" />
- </para>
- </listitem>
- <listitem>
- <para>
- Whether or not to use reporting, and what
reporting to use (tagmail, store, rrdgraph). To configure the types or reports that should
be used by the puppetmaster, use a comma separated list without spaces, in:
- <screen>[puppetmasterd]
+ </para>
+ <para>
+ <emphasis>See also</emphasis>: <xref
linkend="PuppetWorkshop-HowToUsePuppet-Environments" />
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Whether or not to use reporting, and what reporting
to use (tagmail, store, rrdgraph). To configure the types or reports that should be used
by the puppetmaster, use a comma separated list without spaces, in:
+ <screen>[puppetmasterd]
reports =
<replaceable>tagmail,store,rrdgraph</replaceable></screen>
- </para>
- <para>
- <emphasis>See also</emphasis>:
<xref linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting"
/>
- </para>
- </listitem>
- <listitem>
- <para>
- The location of tagmail.conf, in order to map
tags you give to resources to email addresses the reports should be sent to;
<screen>[main]
+ </para>
+ <para>
+ <emphasis>See also</emphasis>: <xref
linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting" />
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The location of tagmail.conf, in order to map tags
you give to resources to email addresses the reports should be sent to;
<screen>[main]
tagmap = <replaceable>/path/to/tagmail.conf</replaceable></screen>
- for reporting changes applied to puppets, via
email.
- </para>
- <para>
- <emphasis>See also</emphasis>:
<xref linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting"
/>
- </para>
- </listitem>
- </itemizedlist>
- </para>
- </formalpara>
+ for reporting changes applied to puppets, via email.
+ </para>
+ <para>
+ <emphasis>See also</emphasis>: <xref
linkend="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting" />
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
</section>
<section
id="PuppetWorkshop-SettingUpPuppet-Configuration-Puppetmaster-Sitepp">
@@ -1203,11 +1234,9 @@ debug: Calling fileserver.describe</screen>
Create a minimal <code>site.pp</code> in
<filename>/etc/puppet/manifests/site.pp</filename> for the puppetmaster to
parse on it's initial startup. Below is an example.
</para>
<screen>#
-# site.pp for any domain
+# Example site.pp
#
-$server = "<replaceable>master.puppetmanaged.org</replaceable>"
-
# The default node
node default {
@@ -1231,51 +1260,343 @@ node default {
<para>
The webserver is going to listen on port 8140, the default port for
the puppetmaster to listen for clients. It is going to forward traffic (after being
decrypted) to the puppetmaster on 127.0.0.1:8141.
</para>
+ <para>
+ Setting up the webserver requires you install
<application>httpd</application> and
<application>mod_ssl</application>. If these are not installed already, use:
+ </para>
+ <para>
+ <screen># <userinput>yum install httpd
mod_ssl</userinput></screen>
+ </para>
+ <para>
+ Refer to <xref
linkend="PuppetWorkshop-Appendix-ExampleSSLFrontendReverseProxyLoadBalancerConfiguration"
/> for more an example VirtualHost configuration for an SSL frontend reverse proxy load
balancer.
+ </para>
</section>
- <section
id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer">
- <title>Configuring the Database Server</title>
+ </section>
+
+ </chapter>
+
+ <chapter id="PuppetWorkshop-LanguageTutorial">
+ <title>Language Tutorial</title>
+ <para>
+ The language puppet uses has a strong focus on making the specification of
types as easy as possible within a heterogeneous set of resources shared over all the
systems you are managing.
+ </para>
+
+ <section id="PuppetWorkshop-LanguageTutorial-Resources">
+ <title>Resources</title>
+ <para>
+ Resource are built by using a type and specifying a list of attributes to
that type. Each type has a specific list of supported attributes. You can find the
complete list of native types and their attributes on <ulink
url="http://reductivelabs.com/trac/puppet/wiki/TypeReference" />
+ </para>
+ <para>
+ A simple example resource is:
+ </para>
+ <para>
+ <screen>file { "/etc/passwd":
+ owner => "root",
+ group => "root",
+ mode => 644
+}</screen>
+ </para>
+ <para>
+ Any system on which this resource is managed will use it to verify that
the <filename>/etc/passwd</filename> is owned by the root user and group, and
has permissions 644. The field before the colon, in this case "/etc/passwd", is
the resource's title, which can be used to refer to the resource later in the
manifest.
+ </para>
+ <para>
+ For simple resources that don't vary much between systems, a single
title to refer to is sufficient. Many system resources though vary from system to system.
The OpenSSH Client package for example is called
<application>openssh-client</application> on Debian systems, while it is
<application>openssh-clients</application> on Fedora systems. To create the
package resource to get the OpenSSH Client package installed through a manifest, Puppet
allows you to specify:
+ </para>
+ <para>
+ <screen>package { "openssh-clients":
+ ensure => installed,
+ name => $operatingsystem ? {
+ "Debian" => "openssh-client",
+ default => "openssh-clients"
+ }
+}</screen>
+ </para>
+ <important>
+ <para>
+ Note that the name of this resource is now conditional, and thus
virtually unpredictable from within the rest of the manifests, but still if you wanted to
require the OpenSSH Client package you do not need to conditionally require the resource,
but instead can use the title of the resource, "openssh-clients".
+ </para>
+ </important>
+ <para>
+ Even more complex resources are file resources that are located in
different paths on different operating systems, such as the configuration file for the
OpenSSH Client package.
+ </para>
+ <para>
+ <screen>file { "/etc/ssh/ssh_config":
+ path => $operatingsystem ? {
+ "Solaris" => "/usr/local/etc/ssh/ssh_config",
+ "Darwin" => "/etc/ssh_config",
+ "Debian" => "/etc/openssh/ssh_config",
+ default => "/etc/ssh/ssh_config"
+ },
+ require => Package["openssh-clients"]
+}</screen>
+ </para>
+ <para>
+ The <emphasis>title</emphasis> for this resource is
"/etc/ssh/ssh_config", while the path of the file managed is conditional to the
value of the <code>$operatingsystem</code> variable. The
<code>$operatingsystem</code> variable in this case is a fact describing the
operating system name. Note that the title of the resource is used as a path if the path
parameter had not been seperately configured.
+ </para>
+ <para>
+ Also note that it is the title of the
<code>Package["openssh-clients"]</code> used in the reference to the
OpenSSH Client package resource.
+ </para>
+ <note>
+ <para>
+ To refer to a resource, you can use it's title, or specify the
magic attribute "alias".
+ </para>
+ </note>
+
+ <section
id="PuppetWorkshop-LanguageTutorial-ResourceDefaults">
+ <title>Resource Defaults</title>
+ <para>
+ You can specify resource defaults so that the default parameter is
always used when you make a resource by capitalizing the resource and not specifying a
title. With the exec type for example, the command either must use a fully qualified path,
or the path parameter to the exec type must be included.
+ </para>
+ <para>
+ <screen>Exec {
+ path => [
+ "/bin/",
+ "/usr/bin/",
+ "/usr/local/bin/",
+ "/sbin/",
+ "/usr/sbin/",
+ "/usr/local/sbin/"
+ ]
+}</screen>
+ </para>
+ <para>
+ This makes the following work
+ </para>
+ <para>
+ <screen>exec { "echo this works": }</screen>
+ </para>
+ <para>
+ While normally you would have to specify (in each exec resource):
+ </para>
+ <para>
+ <screen>exec { "/bin/echo this works"
}</screen>
+ </para>
+ <para>
+ Needless to say, <application>echo</application> does not
exist in <filename>/bin/</filename> on all operating sytems you may want to
manage.
+ </para>
<para>
- A database can be configured to store the configuration distributed
by the puppetmaster, and applied by the puppet. This is optional, and creates some
overhead to the original purpose of configuring the puppets, but provides the opportunity
to create overviews of applied classes to nodes, and a complete inventory of
<emphasis>facts</emphasis> for all nodes. similarities / exemptions.
+ This type of defaults you would normally store in a file named after
the type you specify the defaults for, in the directory
<filename>utils/</filename> beneath the
<filename>manifests/</filename> directory.
</para>
- <section
id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-SQLite3">
- <title>SQLite3</title>
+ <section
id="PuppetWorkshop-LanguageTutorial-ResourceDefaults-ConditionalResourceDefaults">
+ <title>Conditional Resource Defaults</title>
<para>
- SQLite(3) is a file based, light SQL database which is suitable
for small databases. Depending on the size of the organization or priority you give to
storing configs, generally speaking using SQLite(3) is not very suitable. In addition,
SQLite3 isn't easily queried either manually or automatically. To setup SQLite3,
provide the following settings in
<filename>/etc/puppet/puppet.conf</filename>:
+ To facilitate the use of different defaults per operating system
for example, you can set the resource defaults conditionally, using either
<code>if-then-else</code> statements, or using <code>case</code>
statements.
</para>
<para>
- <screen>[puppetmasterd]
- reports = store[,tagmail,rrdgraph]
- storeconfigs = true
- dbadapter = sqlite3
- dblocation = /var/lib/puppet/storeconfigs.sqlite</screen>
+ Case statements let you set the resource defaults as follows:
+ </para>
+ <para>
+ <screen>case $operatingsystem {
+ "Darwin": {
+ Exec {
+ path => [
+ "/foo/bin/",
+ "/bar/sbin/",
+ (...etc...)
+ ]
+ }
+ }
+ "Solaris": {
+ Exec {
+ path => [
+ "/baz/bin/",
+ "/baz/sbin/",
+ (...etc...)
+ ]
+ }
+ }
+ default: {
+ Exec {
+ path => [
+ "/bin/",
+ "/usr/bin/",
+ (...etc...)
+ ]
+ }
+ }
+}</screen>
</para>
+ <important>
+ <para>
+ Resource defaults are not global, but apply to every resource
in the current scope. If you define the resource defaults within a class, then the
resources within that class and all sub-classes will have defaults set. The only way you
can currently specify global defaults is to define them outside of any classes.
+ </para>
+ </important>
+
</section>
- <section
id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-MySQL">
- <title>MySQL</title>
+ </section>
+
+ <section
id="PuppetWorkshop-LanguageTutorial-ResourceCollections">
+ <title>Resource Collections</title>
+ <para>
+ There is two ways you can combine multiple resources: Classes and
definitions. Classes are only interpreted once per node. Definitions on the other hand can
be used as custom types and are meant to be interpreted more then once, each time with
different parameters.
+ </para>
+ <section
id="PuppetWorkshop-LanguageTutorial-ResourceCollections-Classes">
+ <title>Classes</title>
+ <para>
+ Classes are introduced by using the
<code>class</code> keyword, and support a simple form of inheritence.
Subclasses are allowed to override resources defined in parent classes.
+ </para>
+ <para>
+ <screen>class unix {
+ file { [
+ "/etc/passwd",
+ "/etc/group"
+ ]:
+ owner => root,
+ group => root,
+ mode => 644
+ }
+}
+
+class freebsd inherits unix {
+ File["/etc/passwd"] {
+ group => "wheel"
+ }
+ File["/etc/group"] {
+ group => "wheel"
+ }
+}</screen>
+ </para>
<para>
- MySQL of course is much more scalable, and you can query it
manually or automatically.
+ You can use the <code>undef</code> keyword when
overriding a resource to make the child class act as if the value had never been set in
the parent:
</para>
<para>
- <screen>[puppetmasterd]
- reports = store[,tagmail,rrdgraph]
- storeconfigs = true
- dbadapter = mysql
- dbserver = 127.0.0.1
- dbuser = <replaceable>puppet</replaceable>
- dbpassword = <replaceable>puppet</replaceable>
- [dbsocket = /var/lib/mysql/mysql.sock]
- </screen>
+ <screen>class freebsd inherits unix {
+ File["/etc/passwd"] {
+ group => undef
+ }
+}</screen>
+ </para>
+ <para>
+ In this example, nodes which include the unix class will have the
password file forced to group root, while nodes including freebsd would have the password
file group ownership left unmodified.
+ </para>
+ <para>
+ It is also possible to add additional values to resource
parameters using the <literal>+></literal> operator:
+ </para>
+ <para>
+ <screen>class apache {
+ service { "apache":
+ require => Package["httpd"]
+ }
+}
+
+class apache-ssl inherits apache {
+ # host certificate is required for SSL to function
+ Service["apache"] {
+ require +> File["apache.pem"]
+ }
+}</screen>
+ </para>
+ <para>
+ The above effectively makes the <code>require</code>
parameter for the <code>Service["apache"]</code> resource in the
<code>apache-ssl</code> class equal to <code>[
Package["httpd"], File["apache.pem"] ]</code>.
+ </para>
+ <para>
+ You can add multiple values by separating each value with
commas:
+ </para>
+ <para>
+ <screen>class apache {
+ service { "apache":
+ require => Package["httpd"]
+ }
+}
+
+class apache-ssl inherits apache {
+ Service["apache"] {
+ require +> [
+ File["apache.pem"],
+ File["/etc/httpd/conf/httpd.conf"]
+ ]
+ }
+}</screen>
+ </para>
+ <para>
+ The above would make the require parameter in the apache-ssl
class equal to <code>[ Package["httpd"], File["apache.pem"],
File["/etc/httpd/conf/httpd.conf"] ]</code>.
+ </para>
+ <para>
+ Like resources, you can also require a class, like so:
+ </para>
+ <para>
+ <screen>class apache {
+ service { "apache":
+ require => Class["squid"]
+ }
+}</screen>
+ </para>
+ <para>
+ In this case, the
<code>Class["squid"]</code> will need to be applied successfully
before the <code>Service["apache"]</code> resource is applied on the
puppet.
+ </para>
+ <formalpara>
+ <title>Namespacing</title>
+ <para>
+ Classes such as the <code>apache</code> class in
one of the forementioned examples, sub-classed by the <code>apache-ssl</code>
class, can also be defined within another class:
+ </para>
+ </formalpara>
+ <para>
+ <screen>class apache {
+ service { "apache":
+ require => Package["httpd"]
+ }
+
+ class ssl inherits apache {
+ Service["apache"] {
+ require +> [
+ File["apache.pem"],
+ File["/etc/httpd/conf/httpd.conf"]
+ ]
+ }
+ }
+}</screen>
+ </para>
+ <para>
+ In this case, using the ssl subclass of apache would become
<literal>include apache::ssl</literal>.
</para>
</section>
- <section
id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-Postgresql">
- <title>PostgreSQL</title>
+ <section
id="PuppetWorkshop-LanguageTutorial-ResourceCollections-Definitions">
+ <title>Definitions</title>
+ <para>
+ Definitions are very similar to classes, but are introduced with
the <code>define</code> keyword, and do not allow inheritance. Definitions
also take parameters, which classes do not.
+ </para>
+ <para>
+ <screen>class yum {
+ define repository($enable = true) {
+ file { "/etc/yum.repos.d/$name.repo":
+ mode => 644,
+ owner => "root",
+ group => "root",
+ backup => false,
+ links => follow,
+ source => $enable ? {
+ true => [
+ "puppet:///yum/$os/$osver/repos/$name.repo"
+ ],
+ default => [
+ "puppet:///yum/$os/$osver/repos/$name.repo.disabled"
+ ]
+ }
+ }
+ }
+}</screen>
+ </para>
<para>
- PostgreSQL is even more scalable then MySQL
+ This definition can be used as follows:
</para>
+ <para>
+ <screen>node
'<replaceable>node1.example.com</replaceable>' {
+ yum::repository { "<replaceable>custom</replaceable>":
+ enable => true
+ }
+}</screen>
+ </para>
+ <para>
+ Now, <code>node1.example.com</code> gets a file
<filename>/etc/yum.repos.d/custom.repo</filename> from
<filename>puppet:///yum/$os/$osver/repos/custom.repo</filename>.
+ </para>
+ <note>
+ <para>
+ The above example makes use of <code>$os</code>
and <code>$osver</code> variables you have to set first. See <xref
linkend="PuppetWorkshop-BestPractices-SettingOsAndOsver" /> for more
details.
+ </para>
+ </note>
</section>
</section>
@@ -1287,35 +1608,470 @@ node default {
<chapter id="PuppetWorkshop-HowToUsePuppet">
<title>How To Use Puppet</title>
<para>
- This is a first section
+ Now that we've set up Puppet, let's see how we can use puppet.
</para>
<section id="PuppetWorkshop-HowToUsePuppet-Modules">
<title>Using Modules</title>
<para>
- About using modules
+ Modules are collections of manifests, files, templates, plugins and
possibly documentation on how to use the module, and have a special position in Puppet
that allows them to be loaded automatically. Modules live in
<filename><replaceable>$confdir</replaceable>/modules/</filename>
by default, so you will need to change that using the <code>modulepath</code>
setting in <filename>/etc/puppet/puppet.conf</filename>:
</para>
+ <para>
+ <screen>[puppetmasterd]
+ modulepath = /var/lib/modules/</screen>
+ </para>
+ <para>
+ If you are to have multiple environments (development, testing and
production for example), you would move modules to
<filename>/var/lib/puppet/modules/<replaceable>environment</replaceable>/</filename>.
For more information on configuring environments see <xref
linkend="PuppetWorkshop-HowToUsePuppet-Environments-SettingUpEnvironments"
/>. Modules are comprised of the following:
+ </para>
+ <formalpara>
+
<title><filename><replaceable>module_name</replaceable>/manifests/init.pp</filename></title>
+ <para>
+ This file is mandatory and defines the manifest for the module. The
<filename><replaceable>module_name</replaceable>/manifests/init.pp</filename>
should at least define 1 class with the same name as the module to allow automatically
loading the module.
+ </para>
+ </formalpara>
+ <formalpara>
+
<title><filename><replaceable>module_name</replaceable>/files/</filename></title>
+ <para>
+ The tree of files this module may distribute to clients. Note that
this tree should only contain the modules default, which would represent the best default
for all of the systems in the organization. If the manifest uses files from the module,
address the files as follows from the manifest:
+ </para>
+ </formalpara>
+ <para>
+ <screen>source =>
"puppet:///<replaceable>module_name</replaceable>/path/to/file"</screen>
+ </para>
+ <para>
+ So, for example, if the file to distribute is
<filename><replaceable>module_name</replaceable>/files/foo</filename>,
the <code>source</code> value would be:
+ </para>
+ <para>
+ <screen>source =>
"puppet:///<replaceable>module_name</replaceable>/foo</screen>
+ </para>
+ <para>
+ For more information on using multiple sources, see <xref
linkend="PuppetWorkshop-BestPractices-UsingMultipleSources" />. For more
information on fileserver operations, see <xref
linkend="PuppetWorkshop-HowToUsePuppet-Fileserver" />.
+ </para>
+ <formalpara>
+
<title><filename><replaceable>module_name</replaceable>/templates/</filename></title>
+ <para>
+ Templates are basically files with conditional content, or little
programs spitting out what the actual file should look like given a set of variables,
conditions and logical statements. Templates should go into the
<filename><replaceable>module_name</replaceable>/templates/</filename>
directory, and can therefrom used as follows:
+ </para>
+ </formalpara>
+ <para>
+ <screen>content =>
template('<replaceable>module_name</replaceable>/<replaceable>template_filename</replaceable>')</screen>
+ </para>
+ <para>
+ Example with
<filename>webserver/templates/virtualhost.conf.erb</filename>:
+ </para>
+ <para>
+ <screen>content =>
template('webserver/virtualhost.conf.erb')</screen>
+ </para>
+ <formalpara>
+
<title><filename><replaceable>module_name</replaceable>/plugins/puppet/type/</filename></title>
+ <para>
+ The location to put custom types to be distributed with this module.
Requires <code>pluginsync</code> to be set to <code>true</code> in
the puppet configuration.
+ </para>
+ </formalpara>
+ <formalpara>
+
<title><filename><replaceable>module_name</replaceable>/plugins/puppet/provider/</filename></title>
+ <para>
+ The location to put custom providers to be distributed with this
module. Requires <code>pluginsync</code> to be set to
<code>true</code> in the puppet configuration.
+ </para>
+ </formalpara>
+ <formalpara>
+
<title><filename><replaceable>module_name</replaceable>/plugins/facter/</filename></title>
+ <para>
+ The location to put custom facts to be distributed with this module.
Requires <code>pluginsync</code> to be set to <code>true</code> in
the puppet configuration.
+ </para>
+ </formalpara>
+ <formalpara>
+
<title><filename><replaceable>module_name</replaceable>/documentation/</filename></title>
+ <para>
+ Just an optional placeholder for documentation you may want to
distribute along with the module, if you are to share the module with the community.
+ </para>
+ </formalpara>
+ </section>
+
+ <section id="PuppetWorkshop-HowToUsePuppet-Environments">
+ <title>Environments</title>
+ <para>
+ Environments aid in staging developments and not breaking the production.
There's three environments you would want to consider using:
+ </para>
+ <formalpara>
+ <title>development</title>
+ <para>
+ The development environment is where you stage developments in your
manifests, modules and plugins. You would apply the development environment to a small
number of non-critical systems, that maybe are dedicated to the sole purpose of puppet
development. These development systems would not necessarily be managed extensively with
puppet, and may just apply only the classes you are making changes to. There is no use in
applying the <code>foo</code> class to a development system if you are making
changes to the <code>bar</code> module or class.
+ </para>
+ </formalpara>
+ <formalpara>
+ <title>testing</title>
+ <para>
+ The testing stage would be the stage where your basic development is
finished, and you test the changes in a broader scope. Again, these systems should be
non-critical systems, but these systems may also not be dedicated to puppet testing. In
the testing stage, you would want to make sure all puppet classes and modules are applied
correctly when used in combination with one another. For example, conflicts between
modules and/or classes could be caused by duplicate resource definitions, or incorrect
inter-module or inter-class dependencies (<code>require =></code>,
<code>notify =></code>, <code>before =></code> and
<code>subscribe =></code> statements). Although most of the dependencies
are used in the development stage as well, using the testing stage makes sure N+X modules
cooperate before you destroy your production.
+ </para>
+ </formalpara>
+ <para>
+ Obviously, the testing stage is entirely optional. One could decide to
not use the testing stage the way it is subscribed here, but instead assign a couple of
(power-)user desktops and have these users provide feedback on the changes applied, or not
use the testing stage at all.
+ </para>
+ <formalpara>
+ <title>production</title>
+ <para>
+ The production stage is the most important stage in the entire
process of staging changes. The production environment is where most of the systems within
your orgnization are managed in, and as such, changes applied to the production
environment need to be proven stable (or system and services may be interrupted).
+ </para>
+ </formalpara>
+ <para>
+ <inlinemediaobject>
+ <objectinfo>
+ <title>An overview of how changes are applied and
staged</title>
+ </objectinfo>
+ <imageobject>
+ <imagedata format='PNG'
fileref="images/environment_staging.png" />
+ </imageobject>
+ </inlinemediaobject>
+ </para>
+
+ <section
id="PuppetWorkshop-HowToUsePuppet-Environments-SettingUpEnvironments">
+ <title>Setting Up Environments</title>
+ <para>
+ Setting up the environments requires you to configure the available
environments in <filename>/etc/puppet/puppet.conf</filename>. The relevant
settings are:
+ </para>
+ <para>
+ <screen>[puppetmasterd]
+ environments =
<replaceable>development,testing,production</replaceable></screen>
+ </para>
+ <para>
+ Per environment, create a section similar to the following:
+ </para>
+ <para>
+ <screen>[development]
+ manifest = /var/lib/puppet/manifests/development/site.pp
+ modulepath = /var/lib/puppet/modules/development
+
+[testing]
+ manifest = /var/lib/puppet/manifests/testing/site.pp
+ modulepath = /var/lib/puppet/modules/testing
+
+[production]
+ manifest = /var/lib/puppet/manifests/production/site.pp
+ modulepath = /var/lib/puppet/modules/production</screen>
+ </para>
+ <para>
+ Make sure you put the files and directories in place before
restarting the puppetmaster service.
+ </para>
+ </section>
+
</section>
<section id="PuppetWorkshop-HowToUsePuppet-VirtualResources">
<title>Virtual Resources</title>
<para>
- paragraph
+ Using virtual resources is a way to avoid duplicate definitions in your
manifests if you define a virtual resource in parent classes, inherit the parent classes
in sub-classes, and realize() the virtual resource in the sub-class.
+ </para>
+ <para>
+ An example where you can use virtual resources is a webserver class:
+ </para>
+ <para>
+ <screen>class webserver {
+ package { "httpd":
+ ensure => installed
+ }
+
+ class ssl {
+ package { [
+ "httpd",
+ "mod_ssl"
+ ]:
+ ensure => installed
+ }
+ }
+}</screen>
+ </para>
+ <para>
+ Now, if you were to <code>include webserver</code> on a
system, everything is well. However, some other class may require the
<code>webserver::ssl</code> class to be applied to the system as well:
+ </para>
+ <para>
+ <screen>class mail {
+ class server {
+ # Include webserver::ssl for secure webmail capacity
+ include webserver::ssl
+
+ webserver::virtualhost { "mail.$domain":
+ enable => true,
+ certificate => true
+ }
+
+ package { "squirrelmail":
+ ensure => installed
+ }
+ }
+}</screen>
+ </para>
+ <para>
+ This would result in a duplicate definition of the
<code>Package["httpd"]</code> resource. Such is easily prevented by
making sure the <code>Package["httpd"]</code> resource is only
defined once:
+ </para>
+ <para>
+ <screen>class webserver {
+ @package { [
+ "httpd",
+ "mod_ssl"
+ ]:
+ ensure => installed
+ }
+
+ realize(Package["httpd"])
+
+ class ssl inherits webserver {
+ realize( Package["httpd"],
+ Package["mod_ssl"])
+ }
+}</screen>
+ </para>
+ <para>
+ A more complex but also more appropriate example can be found at
<ulink
url="http://git.puppetmanaged.org/?p=puppet;a=blob;f=manifests/init.... />.
Note that in the puppet module linked from
puppetmanaged.org, the definition and use of
virtual resources is a necessity.
</para>
</section>
<section id="PuppetWorkshop-HowToUsePuppet-Plugins">
<title>Using Plugins</title>
<para>
- About the use of plugins
+ Using plugins (e.g. custom types, facts, functions and providers) gives
you even more control over the behaviour of puppet and extend what it can do. An example
custom fact that exposes the version of Python installed is:
+ </para>
+ <para>
+ <screen># From <ulink
url="http://reductivelabs.com/trac/puppet/wiki/Recipes/PythonVersion... />
+require 'facter'
+
+pythonversion = nil
+if FileTest.exists?("/usr/bin/python")
+ pythonversion = %x{python -V 2>&1}.split(" ")[1]
+end
+
+Facter.add("pythonversion") do
+ setcode do
+ pythonversion
+ end
+end
+
+Facter.add("pythonmmversion") do
+ pythonmmversion = nil
+ if pythonversion != nil
+ pythonversionsplit = pythonversion.split(".")
+ pythonmmversion = pythonversionsplit[0] + "." + pythonversionsplit[1]
+ end
+ setcode do
+ pythonmmversion
+ end
+end</screen>
</para>
+ <para>
+ Using this new custom fact you can create conditional statements in
manifests or templates using <code>$pythonverion</code> or
<code>$pythonmmversion</code>.
+ </para>
+
</section>
- <section id="PuppetWorkshop-HowToUsePuppet-Environments">
- <title>Environments</title>
+ <section
id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM">
+ <title>Using Manifests from a SCM</title>
<para>
- paragraph
+ Using manifests from a Source Control Management system.
</para>
+
+ <section
id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-SingleTree">
+ <title>Using a Single Tree</title>
+ <para>
+ Using a single tree in case you want to use environment staging and
module support is considered a bad idea, but can still be done. The layout of such a
repository can be one of the following:
+ </para>
+ <section
id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-SingleTree-UsingSVN">
+ <title>Using SVN</title>
+ <para>
+ Using SVN allows Windows users to contribute to the repository
and/or make edits to files in the repository, which of course has it's advantages.
+ </para>
+ <para>
+ The layout of an SVN repository would be as follows:
+ </para>
+ <para>
+
<screen><replaceable>/path/to/repository/</replaceable>
+ `- trunk/
+ `- branches/
+ `- testing/
+ `- production/</screen>
+ </para>
+ <para>
+ <filename>trunk/</filename> would represent the
development branch in this case. Continuing with <filename>trunk/</filename>,
the repository layout could look as follows:
+ </para>
+ <para>
+ <screen>trunk/
+ `- conf/
+ `- files/<footnote><para>Used for files distributed without them being
integrated into a module</para></footnote>
+ `- modules/
+ `- module1/
+ `- files/
+ `- manifests/
+ `- plugins/
+ `- templates/
+ `- module2/
+ `- (etc)
+ `- manifests/
+ `- site.pp
+ `- classes/
+ `- nodes/
+ `- utils/
+ `- (etc)</screen>
+ </para>
+ <para>
+ Checking out the SVN repository (sub-directories) in each various
location on the puppetmaster would look like
+ </para>
+ <para>
+ <screen># svn co
http://server/svn/puppet/trunk/conf/
/etc/puppet/<footnote><para>Note that checking out the puppet configuration
from <code>trunk/</code> like this almost certainly results in you having to
manage the puppetmaster with puppet; See <ulink
url="http://puppetmanaged.org/documentation/puppet-module/" /> for more
information.</para></footnote>
+# svn co
http://server/svn/puppet/trunk/modules/ \
+ /var/lib/puppet/modules/development/
+# svn co
http://server/svn/puppet/trunk/manifests/ \
+ /var/lib/puppet/manifests/development/
+# svn co
http://server/svn/puppet/branches/testing/modules/ \
+ /var/lib/puppet/modules/testing/
+# svn co
http://server/svn/puppet/branches/testing/manifests/ \
+ /var/lib/puppet/manifests/testing/
+# svn co
http://server/svn/puppet/branches/production/modules/ \
+ /var/lib/puppet/modules/production/
+# svn co
http://server/svn/puppet/branches/production/manifests/ \
+ /var/lib/puppet/manifests/production/
+ </screen>
+ </para>
+ <para>
+ See <xref
linkend="PuppetWorkshop-Appendix-SVNCommitHooks" /> for how to make the
puppetmaster update it's sources when you commit changes to the SVN repository.
+ </para>
+ </section>
+
+ <section
id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-SingleTree-UsingGIT">
+ <title>Using GIT</title>
+ <para>
+ It is currently not possible to use a single GIT tree to
configure all of your puppet environment without requiring manual (or automatic) copying
of files and directories from within a copy of the source repository into the location
where puppet expects them to be.
+ </para>
+ </section>
+
+ </section>
+
+ <section
id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-MultipleTrees">
+ <title>Multiple Trees</title>
+ <para>
+ Multiple trees is more efficient because you can merge changes from
one stage to another on a per-module basis, rather then merging entire branches with all
configuration, manifests, modules and so forth. Additionally, using multiple trees allows
you to upstream modules integrated into your own set of modules.
+ </para>
+ <para>
+ Using one SCM tree for each module allows you to set access control
accordingly, and stage changes per module instead of globally.
+ </para>
+ </section>
+
+ <section
id="PuppetWorkshop-HowToUsePuppet-UsingManifestFromASCM-ModulesFromUpstream">
+ <title>Modules From Upstream</title>
+ <para>
+ You can use one or more modules from an upstream provider such as:
+ <itemizedlist>
+ <listitem>
+ <para>
+ <ulink
url="http://puppetmanaged.org/"
/>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <ulink url="http://git.black.co.at/" />
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <ulink
url="http://reductivelabs.com/trac/puppet/wiki/Recipes" />
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Using either a single tree or multiple trees as described above; the
environment staging though does not apply to most of the modules available from upstream
providers. If the upstream provider does provide branches for each of your environments,
you would still only want to use the production or equivalent branch.
+ </para>
+ <para>
+ If you want to use a module from an upstream provider without the use
of the branches for staging, you can specify a colon-seperated list to the
<code>modulepath</code> setting for your environments to have puppet search
through the list of paths. Such would look like:
+ </para>
+ <para>
+ <screen>[puppetmasterd]
+ environments =
<replaceable>development,testing,production</replaceable>
+
+[development]
+ manifest = /var/lib/puppet/manifests/development/site.pp
+ modulepath = /var/lib/puppet/modules/upstream/:/var/lib/puppet/modules/development/
+
+[testing]
+ manifest = /var/lib/puppet/manifests/testing/site.pp
+ modulepath = /var/lib/puppet/modules/upstream/:/var/lib/puppet/modules/testing/
+
+[production]
+ manifest = /var/lib/puppet/manifests/production/site.pp
+ modulepath =
/var/lib/puppet/modules/upstream/:/var/lib/puppet/modules/production/</screen>
+ </para>
+ <para>
+ In the example configuration, you would clone, checkout and pull the
modules you use from an upstream provider to
<filename>/var/lib/puppet/modules/upstream/<replaceable>module_name</replaceable>/</filename>.
+ </para>
+
+ </section>
+
+ </section>
+
+ <section id="PuppetWorkshop-HowToUsePuppet-Fileserver">
+ <title>Fileserver</title>
+ <para>
+ Puppet's fileserver, again a core component in being able to apply
configuration management to your systems, needs to be configured appropriately to allow
files, templates, plugins and facts to be distributed to your clients.
+ </para>
+
+ <section
id="PuppetWorshop-HowToUsePuppet-Fileserver-Operations">
+ <title>Fileserver Operations</title>
+ <para>
+ Using <filename>/etc/puppet/fileserver.conf</filename>,
you can define fileserver <emphasis>mounts</emphasis>.
+ </para>
+ <para>
+ Each fileserver mount has a <code>path</code> attribute
and <code>allow</code> and/or <code>deny</code> statements. The
<code>path</code> attribute describes where the files are on the local
filesystem, and <code>allow</code> and <code>deny</code>
statements allow you to apply access control to these fileserver mounts. Note that
<code>deny</code> always has precedence over <code>allow</code>,
and that the order of <code>allow</code> and <code>deny</code>
does therefor not matter.
+ </para>
+ <para>
+ Also note, that the wildcard matches used in
<code>allow</code> and <code>deny</code> match DNS, or IP
addresses. You can also use CIDR notations for the latter.
+ </para>
+ <para>
+ Additionally, the special <code>[modules]</code> mount
does not use the <code>path</code> attribute but instead figures out the
filesystem path based on the <code>modulepath</code> setting in
<filename>/etc/puppet/puppet.conf</filename>, taking the environment used into
account as well.
+ </para>
+ <para>
+ <screen># This file consists of arbitrarily named
sections/modules
+# defining where files are served from and to whom
+
+# Define a section 'files'
+# Adapt the allow/deny settings to your needs. Order
+# for allow/deny does not matter, allow always takes precedence
+# over deny
+
+[facts]
+ path /var/lib/puppet/facts
+ allow *
+
+[files]
+ path /var/lib/puppet/files
+ allow *.example.com
+ deny *.evil.example.com
+
+[modules]
+ allow *</screen>
+ </para>
+ <para>
+ The <code>source =></code> parameter in resources now
let's you use the following sources:
+ <itemizedlist>
+ <listitem>
+ <formalpara>
+
<title><filename>puppet:///files/<replaceable>path/to/file</replaceable></filename></title>
+ <para>
+ Resulting in the file being looked for in
<filename>/var/lib/puppet/files/<replaceable>path/to/file</replaceable></filename>.
+ </para>
+ </formalpara>
+ </listitem>
+ <listitem>
+ <formalpara>
+
<title><filename>puppet:///<replaceable>module_name</replaceable>/<replaceable>path/to/file</replaceable></filename></title>
+ <para>
+ Resulting in the file being looked for in the path
for module <replaceable>module_name</replaceable>, subdirectory
<filename>files/</filename>,
<filename><replaceable>path/to/file</replaceable></filename>.
+ </para>
+ </formalpara>
+ <para>
+ So,
<filename>puppet:///sudo/sudoers</filename> would result in
<filename>/var/lib/puppet/modules/<replaceable>$environment</replaceable>/sudo/files/sudoers</filename>.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </section>
</section>
</chapter>
@@ -1323,7 +2079,7 @@ node default {
<chapter id="PuppetWorkshop-TroubleshootingPuppet">
<title>Troubleshooting Puppet</title>
<para>
- This section is about troubleshooting the manifests, puppetmaster and
puppet.
+ This section is about troubleshooting the manifests, the puppetmaster and the
puppet.
</para>
<section id="PuppetWorkshop-TroubleshootingPuppet-Manifests">
@@ -1411,17 +2167,20 @@ node default {
<section
id="PuppetWorkshop-TroubleshootingPuppet-ThePuppetmaster">
<title>The Puppetmaster</title>
<para>
- The puppetmaster
+ The puppetmaster service is configured using
<filename>/etc/sysconfig/puppetmaster</filename>, which is also where you can
specify what the log destination is, and whether the puppetmaster service should run in
verbose or debug mode. Both would be using the
<code>PUPPETMASTER_EXTRAOPTS</code> variable.
</para>
<section
id="PuppetWorkshop-TroubleshootingPuppet-ThePuppetmaster-DebuggingThePuppetmaster">
<title>Debugging The Puppetmaster</title>
<para>
- Running the puppetmaster in debug mode generates a lot of output. To
run the puppetmaster in debug mode, stop the puppetmaster service and type:
+ Running the puppetmaster in debug mode generates a lot of output. You
may want to redirect the output to a file and read the file to prevent the available
scrollback buffer from filling up too fast. To run the puppetmaster in debug mode, stop
the puppetmaster service and type:
</para>
<para>
<screen># <userinput>puppetmasterd --debug --no-deamonize
[--servertype mongrel --masterport 8141]</userinput></screen>
</para>
+ <para>
+ Or run the service in verbose or debug mode by editing
<filename>/etc/sysconfig/puppetmaster</filename> and adding
<code>--verbose</code> or <code>--debug</code> to
<code>PUPPETMASTER_EXTRAOPTS</code>.
+ </para>
</section>
</section>
@@ -1429,7 +2188,7 @@ node default {
<section id="PuppetWorkshop-TroubleshootingPuppet-ThePuppet">
<title>The Puppet</title>
<para>
- para
+ Debugging the puppet can be tricky, because it is the puppetmaster that
parses the manifests first, and so if the puppetmaster fails parsing the manifests, the
puppet client won't get any changes to it's manifests. If files or templates have
changed on the fileserver, these will obviously be changed on the puppet as well, but if a
template uses a new variable from the manifest, the template will fail to parse and the
error message will occur on the puppetmaster.
</para>
<section
id="PuppetWorkshop-TroubleshootingPuppet-ThePuppet-DebuggingThePuppet">
@@ -1446,41 +2205,193 @@ node default {
<chapter id="PuppetWorkshop-OtherThingsToDoWithPuppet">
<title>Other Things To Do With Puppet</title>
<para>
- This is a first section
+ There's way more you can do with puppet. Actually there's so much you
can do with puppet it does not fit in a workshop type of course.
</para>
+
+ <section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-StoreConfigurationsInADatabase">
+ <title>Store Configurations In A Database</title>
+ <para>
+ A database can be configured to store the configuration distributed by
the puppetmaster, and applied by the puppet. This is optional, and creates some overhead
to the original purpose of configuring the puppets, but provides the opportunity to create
overviews of applied classes to nodes, and a complete inventory of
<emphasis>facts</emphasis> for all nodes. similarities / exemptions.
+ </para>
+
+ <section
id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-SQLite3">
+ <title>SQLite3</title>
+ <para>
+ SQLite(3) is a file based, light SQL database which is suitable for
small databases. Depending on the size of the organization or priority you give to storing
configs, generally speaking using SQLite(3) is not very suitable. In addition, SQLite3
isn't easily queried either manually or automatically. To setup SQLite3, provide the
following settings in <filename>/etc/puppet/puppet.conf</filename>:
+ </para>
+ <para>
+ <screen>[puppetmasterd]
+ reports = store[,tagmail,rrdgraph]
+ storeconfigs = true
+ dbadapter = sqlite3
+ dblocation =
<replaceable>/var/lib/puppet/storeconfigs.sqlite</replaceable></screen>
+ </para>
+ </section>
+
+ <section
id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-MySQL">
+ <title>MySQL</title>
+ <para>
+ MySQL of course is much more scalable, and you can query it manually
or automatically. A simple query can give you all webservers in the organization:
+ </para>
+ <para>
+ <screen>$ mysql -p puppet -e 'SELECT hosts.name
+ FROM resources INNER JOIN hosts
+ ON resources.host_id = hosts.id
+ WHERE resources.title = "webserver"
+ GROUP BY name;'
+
++---------------------------+
+| name |
++---------------------------+
+| app1.genomicscenter.nl |
+|
elwood.kanarip.com |
+|
master.puppetmanaged.org |
+| open.the-cave-of-steef.nl |
+|
pinky.kanarip.com |
+| server.ogd.nl |
+|
vito.kanarip.com |
++---------------------------+
+7 rows in set (0.02 sec)
+
+</screen>
+ </para>
+ <para>
+ To configure storing the configurations, use the following settings
in <filename>/etc/puppet/puppet.conf</filename>:
+ </para>
+ <para>
+ <screen>[puppetmasterd]
+ storeconfigs = true
+ dbadapter = mysql
+ dbserver = <replaceable>127.0.0.1</replaceable>
+ dbuser = <replaceable>puppet</replaceable>
+ dbpassword = <replaceable>puppet</replaceable>
+ [dbsocket = /var/lib/mysql/mysql.sock]
+ </screen>
+ </para>
+ </section>
+
+ <section
id="PuppetWorkshop-SettingUpPuppet-Configuration-DatabaseServer-Postgresql">
+ <title>PostgreSQL</title>
+ <para>
+ PostgreSQL is even more scalable then MySQL as it's multi-master,
but most importantly you may already have PostgreSQL running in your organization, and may
just want to add puppet's configuration store to that infrastructure.
+ </para>
+ <para>
+ To add the configuration store to PostgreSQL, use the following
settings in <filename>/etc/puppet/puppet.conf</filename>:
+ </para>
+ <para>
+ <screen>[puppetmasterd]
+ storeconfigs = true
+ dbadapter = postgresql
+ dbuser = <replaceable>puppet</replaceable>
+ dbpassword = <replaceable>password</replaceable>
+ dbserver = <replaceable>localhost</replaceable>
+ dbname = <replaceable>puppet</replaceable></screen>
+ </para>
+ <para>
+ And create the database:
+ </para>
+ <para>
+ <screen># <userinput>su - postgres</userinput>
+$ <userinput>psql template1</userinput>
+template1=# <userinput>create database
<replaceable>puppet</replaceable>;</userinput>
+CREATE DATABASE
+<userinput>create user <replaceable>puppetuser</replaceable> with
unencrypted password
'<replaceable>password</replaceable>';</userinput>
+CREATE ROLE
+template1=# <userinput>grant create on database
<replaceable>puppet</replaceable> to
<replaceable>puppetuser</replaceable>;</userinput>
+</screen>
+ </para>
+ </section>
+
+ </section>
+
<section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-TweakingReporting">
<title>Tweaking Reporting</title>
<para>
- paragraph
+ Reports can be sent out to various email addresses as a notification on
errors or changes applied to the systems. This not only helps in keeping track of changes
being applied, but can also help you keep your manifests clean, attending to each error
that may otherwise have passed by unnoticed.
</para>
- </section>
+ <para>
+ To enable reporting, use the following settings in
<filename>/etc/puppet/puppet.conf</filename>:
+ </para>
+ <para>
+ <screen>[main]
+ tagmap = /etc/puppet/tagmail.conf
- <section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomTypes">
- <title>Writing Custom Types</title>
+[puppetmasterd]
+ reports = tagmail
+
+[puppet]
+ reportfrom =
puppet-reports(a)<replaceable>domain.tld</replaceable></screen>
+ </para>
+ <para>
+ This will cause the puppetmaster to look at
<filename>/etc/puppet/tagmail.conf</filename>, which maps tags to email
addresses that need to be notified on changes applied to systems tagged with the
appropriate keywords.
+ </para>
+ <formalpara>
+ <title>Using tags in your manifests</title>
+ <para>
+ You can use tags in your manifests like so:
+ </para>
+ </formalpara>
+ <para>
+ <screen>class ssh {
+ tag("security")
+ file { "...":
+ (...)
+ }
+}</screen>
+ </para>
+ <para>
+ To have notifications on changes to systems including the ssh class sent
to security-admins(a)domain.tld, use the following tagmap entry:
+ </para>
+ <para>
+ <screen>ssh: ssh-admins(a)domain.tld
+security: security-admins(a)domain.tld</screen>
+ </para>
+ <para>
+ A catch-all can be specified to have notifications on all changes sent to
an emailaddress or multiple email addresses:
+ </para>
<para>
- paragraph
+ <screen>all: system-admins(a)domain.tld</screen>
</para>
</section>
<section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomFacts">
<title>Writing Custom Facts</title>
<para>
- paragraph
+ Writing custom facts, like writing custom types, functions or providers,
is done in Ruby, using the interfaces and objects offered by Puppet.
+ </para>
+ </section>
+
+ <section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomTypes">
+ <title>Writing Custom Types</title>
+ <para>
+ <ulink
url="http://reductivelabs.com/trac/puppet/wiki/CreatingCustomTypes&q... />
</para>
</section>
<section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomFunctions">
<title>Writing Custom Functions</title>
<para>
- paragraph
+ <ulink
url="http://reductivelabs.com/trac/puppet/wiki/WritingYourOwnFunctio... />
+ </para>
+ </section>
+
+ <section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-WritingCustomProviders">
+ <title>Writing Custom Providers</title>
+ <para>
+ <ulink
url="http://reductivelabs.com/trac/puppet/wiki/ProviderDevelopment&q... />
</para>
</section>
<section
id="PuppetWorkshop-OtherThingsToDoWithPuppet-StoreconfigsReportingAndPuppetview">
<title>Storeconfigs, Reporting and Puppetview</title>
<para>
- paragraph
+ Using Puppet's storeconfig capability and standard reporting, in
combination with rrdgraphing, you can use applications such as
<application>Puppetview</application> to create nice overviews of results of
your configuration management as applicable to your systems.
+<!--
+ FIXME
+
+ Elaborate
+//-->
</para>
</section>
@@ -1489,8 +2400,153 @@ node default {
<chapter id="PuppetWorkshop-BestPractices">
<title>Best Practices</title>
<para>
- This is a first section
+ Using Puppet starting in a small(er) scale, scaling up to more and more
systems in your organization to become managed with puppet, you might encounter some
challenges and to prevent you end up with a giant mess of manifests, files, templates,
needing to search (with <code>grep</code> and <code>find</code>
for example) where it is exactly you need to configure the next change, or have you use
configuration items that need to be changed as soon as your organization introduces the
next generation operating systems or distribution version, here's a set of
<emphasis>Best Practices</emphasis>, or actually Tips & Tricks.
</para>
+
+ <section id="PuppetWorkshop-BestPractices-SettingOsAndOsver">
+ <title>Setting $os and $osver</title>
+ <para>
+ The <code>$operatingsystem</code> and
<code>$operatingsystemrelease</code>,
<code>$operatingsystemversion</code> or
<code>$lsbdistrelease</code> variables relate directly to specific operating
system resources, configuration file locations, package names and most importantly,
settings you can or cannot use in the configuration files for specific
applications<footnote><para>Enterprise Linux 3 distributions for example have
OpenSSH Server versions that do not use, and are incompatible with, the
<code>UsePAM</code> setting in
<filename>/etc/ssh/sshd_config</filename>. In these cases, you would want the
configuration file for EL-3 to come from a different location then for the EL-4 and EL-5
distributions that do take <code>UsePAM</code> because of the OpenSSH Server
package versions they ship.</para></footnote>. Using the
<code>$operatingsystem</code>,
<code>$operatingsystemrelease</code>,
<code>$operatingsystemversion</code> and/or
<code>$lsbdistrelease</code> variables (set from Facter) help you determine
the Operating System (Distribution) and Version.
+ </para>
+ <para>
+ However, some distributions have Facter set
<code>$operatingsystemrelease</code>, while others set
<code>$lsbdistrelease</code>. If these variables need to be used in manifests,
you really do not want to use these both because in your manifests all you are interested
in is the Operating System Version. Using the following snippet of code in
<filename>site.pp</filename> enables you to use <code>$os</code>
and <code>$osver</code> throughout your manifests.
+ </para>
+ <para>
+ <screen># Get facts and give them a good, good name
+$os = $operatingsystem
+
+case $os {
+ "Fedora", "CentOS", "RedHat": {
+ $osver = $lsbdistrelease
+ }
+ "Debian", "SuSE', "OpenSuSE": {
+ $osver = $operatingsystemrelease
+ }
+ "Darwin": {
+ $osver = $operatingsystemrelease
+ }
+}</screen>
+ </para>
+ </section>
+
+ <section id="PuppetWorkshop-BestPractices-UsingMultipleSources">
+ <title>Using Multiple Sources</title>
+ <para>
+ para
+ </para>
+ </section>
+
+ <section id="PuppetWorkshop-BestPractices-GroupProfiles">
+ <title>Group Profiles</title>
+ <para>
+ Grouping <emphasis>profiles</emphasis> that define certain
resources to be applied to the systems you manage can help you in giving and keeping
structure in the ever-growing configuration tree you use.
+ </para>
+ <para>
+ We know of organizations that run the following deterministic set of
profiles:
+ </para>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <formalpara>
+ <title>Classes</title>
+ <para>
+ Organization specific classes not eligible to become a
module. Note that these are mostly very simple classes, managing a single resource, or
merely defining a set of variables per-class to be used throughout the manifests.
+ </para>
+ </formalpara>
+ <para>
+ Using classes to manage multiple resources is strongly
discouraged as these are commonly eligible to become modules. However, when using a module
from upstream changing that module to fit your needs introduces the risk of conflicts when
trying to update the module from upstream. Therefor, classes can be used to override
and/or extend classes from modules.
+ </para>
+ <para>
+ Classes are stored in
<filename>classes/*.pp</filename>, where the name of the actual file
represents the class defined in the file.
+ </para>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Domains</title>
+ <para>
+ Domains are most commonly used within organizations that
manage branch offices, or merge with other organizations, or Puppet setups that manage
more then one organization at a time.
+ </para>
+ </formalpara>
+ <para>
+
puppetmanaged.org runs multiple organizations with each
organization maintaining their own domain specific configuration tree and manifests.
+ </para>
+ <para>
+ CustomerA has 3 branch offices and uses domains to define
branch office specific settings.
+ </para>
+ <para>
+ Defined domains should go in
<filename>domains/*.pp</filename> where the actual filename is the most
descriptive common denominator between all systems in a domain.
+ </para>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Groups</title>
+ <para>
+ Grouping systems allow you to control a lot more with a
lot less. Less is more, sort of. Groups can include
<emphasis>servers</emphasis>, <emphasis>desktops</emphasis>,
<emphasis>laptops</emphasis>.
+ </para>
+ </formalpara>
+ <para>
+ Given a group of servers you can imagine SELinux needs to be
enabled on all of these, and the firewall needs to be up and running. Create a group
servers in <filename>groups/server.pp</filename> and include the class defined
or inherit the node used.
+ </para>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Modules</title>
+ <para>
+ Modules that need settings or modules that need to be
pulled (updated) from upstream. For older versions of Puppet (< 0.24.4), you can
state "imports" here as well.
+ </para>
+ </formalpara>
+ <para>
+ explanation
+ </para>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Nodes</title>
+ <para>
+ The nodes you manage, one per file. These oughta be
actual nodes, not abstraction levels where you inherit from.
+ </para>
+ </formalpara>
+ <para>
+ explanation
+ </para>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Services</title>
+ <para>
+ para
+ </para>
+ </formalpara>
+ <para>
+ explanation
+ </para>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Utils</title>
+ <para>
+ para
+ </para>
+ </formalpara>
+ <para>
+ explanation
+ </para>
+ </listitem>
+ <listitem>
+ <formalpara>
+ <title>Users</title>
+ <para>
+ para
+ </para>
+ </formalpara>
+ <para>
+ explanation
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </section>
+
</chapter>
<xi:include href="Appendix.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
diff --git a/Workshops/PuppetWorkshop/en-US/environment_staging.png
b/Workshops/PuppetWorkshop/en-US/environment_staging.png
new file mode 100644
index 0000000..e2e7218
Binary files /dev/null and b/Workshops/PuppetWorkshop/en-US/environment_staging.png
differ
diff --git a/Workshops/PuppetWorkshop/en-US/images/environment_staging.png
b/Workshops/PuppetWorkshop/en-US/images/environment_staging.png
new file mode 100644
index 0000000..e2e7218
Binary files /dev/null and b/Workshops/PuppetWorkshop/en-US/images/environment_staging.png
differ
diff --git a/en-US/Courses.xml b/en-US/Courses.xml
index 00287a7..b250580 100644
--- a/en-US/Courses.xml
+++ b/en-US/Courses.xml
@@ -94,7 +94,6 @@
</listitem>
//-->
-
</itemizedlist>
</para>
</formalpara>
@@ -182,4 +181,6 @@
<xi:include href="Books/Linux/RHCETraining/Course.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Books/Linux/RHCETraining/Manual.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="Books/Workshops/PuppetWorkshop/Puppet_Workshop.xml"
xmlns:xi="http://www.w3.org/2001/XInclude" />
+
</set>