Example 42 Modules
Customization Hints
Introduction
In this document are outlined some techniques useful to customize and adapt to custom necessities the modules provided with the Example 42 Puppet Modules.
In order to properly follow these advices and put them into a practical working solution you should:
- Have a basic idea on how to use Puppet and how to write manifests. The best point to start from is this page on Puppet's wiki.
- Read the Example 42 HowTo, in order to see how to import the Example42 module set up your custom project.
- Read the InfrastructureDesignGuidelines in order to get a grasp on the logic and design choices followed in Example42.
Define your project(s)
Before running down to Puppet configuration you need to define what are your scopes and start to consider the single elements and peculiarities of your infrastructure. Ask yourself the following questions:
- Are you going to plan a single Puppet project or manage different projects?
- How many people will work on puppet configurations? Will they be "at close contact"? Do you need to delegate modifications in some parts of your project?
- How many servers will you manage with Puppet? Will they be able to communicate to the same puppet master? Do you need different puppet masters? Will they share the same set of modules?
- What are the provisioning needs you forecast? Few additions after initial setup? Constant need of new servers to deploy?
Various questions generate a multitude of answers, we can't face all the possible alternatives here but we can underline some basic points:
- If you need to work on different projects you can decide to keep the same set of modules and manage differences in separated manifests layouts, or make the projects completely independent, each of them with its own set of modules (by the way, with the use of Puppet's environments you can have, if you want, a single puppet master for all the projects, even if they don't share any module. Hint: "puppetd -t --environment=myproject1")
- If different people are working on the puppet manifests you should definitively consider using a SCM to manage them. Git is a typical, well established solution, but of course you can use alternatives such as Subversion, CVS and so on. A SCM is reccomended also if you are the only one managing puppet configurations.
- For large installations with multiple puppet-masters sharing the same manifests, you MUST define a clear deleopment/testing/distribution workflow. You can do this with git (puppet masters pull, developers push on a main git repository). A good alternative is provided by the Puppet-Team tool.
- In some cases a fully automatic configuration via Puppet for certain software can be particularly complicated. When a software can't be installed only via command line and requires a procedure that can hardly been made automatically, consider the possibility of configuring via Puppet only the prerequisites and do the legacy software installation by hand (this makes sense especially when you don't have frequent deployment needs for this kind of setup).
Customize existing modules
Example42 modules share a common logic. There's a module for each application, the module name is also the name of the primary class that installs the software and, if it provides a services, starts it. By default a base class (can be placed in a file called init.pp or base.pp accoring to the complexity of the module) does not changes configuration files, it just defines them , in order to leave easy modifications according to custom needs. Let's consider, for example, the apache class, even if Apache is porbably not the most suitable example since it generally has more than a single configuration file splattered abour the filesystem accoring to erratic Operating Systems logics:
Here you find the httpd.conf file with a simple "ensure present", in order to modify it you have different alternatives:
Source static file(s)
Insert the custom definitions in the same apache class inserting in the file "httpd.conf" object something like:
source => "puppet://$servername/httpd/httpd.conf",
and place your file in $MODULESDIR/apache/files/httpd.conf
You may need to provide different static files for different hosts so you might modify the above line in a similar way:
source => [ "puppet://$servername/httpd/httpd.conf-$hostname" , "puppet://$servername/httpd/httpd.conf" ],
in this case if there's a file specific for a host (for example $MODULESDIR/apache/files/httpd.conf-webmail ), that file is sourced, otherwise Puppet provides the default httpd.conf
Templates
Instead of a static file you can use a template with a syntax like:
content => template("apache/httpd.conf.erb"),
that point to a template placed in $MODULESDIR/apache/templates/httpd.conf.erb.
Note that you may either use source => to provide a static file or content=> to provide a file based on a template.
Inline modifications
You may want to change existing settings in an already existing file without providing the whole file.
Puppet currently can use the Type Augeas for simila atomic changes, but Augeas in not supported on every Operating Systems.
In Example42 it has been created a general Type, called Config that can change line or parameters in configuration files using different "engines" (methos to change lines, based on Augeas, sed or other commands).
Many modules in the Example42 provide a custom define to change the main configuration file for the relevant application. They are called modulename::conf, so if you want to change only some parameters on httpd.conf you can use the custom define httpd::conf:
Where you can make changes
You can decide where and how to change the behaviour of the Example42 Puppet modules in different ways.
Modify existing classes
You can simply modify the existing classes adding the configuration settings you need.
In the above examples the source => or content => settings can simply be inserted
- Define a custom subclass that inherits the main class and redefines the file "httpd.conf" properties:
class apache::my_project inherits apache {
File ["httpd.conf"]
{
content => template("apache/httpd.conf.erb"),
}
}
This solution is better if you want to keep unmodified the native Apache class, in order to reuse it in other projects or be able to easily import an updated version of it. The first choice, instead, is preferred if you have one project and no needs to share modules.
It's important to underline that the positions of the files you provide (in the examples before they are placed in the same apache module directory tree) can be changed according to your needs:
class apache::my_project inherits apache {
File ["httpd.conf"] {
content => template("my_project/apache/httpd.conf.erb"),
}
}
this looks for the file $MODULESDIR/my_project/templates/apache/httpd.conf.erb.
Another important and useful thing to note is that you can provide different files for different hosts following the logic that better suits your environment. For example you may want a different httpd.conf file according to specific hostnames or roles or zones (if thery are defined and used as in Infrastructure Design Guidelines). For example:
class apache::my_project inherits apache {
File ["httpd.conf"] {
content => [ template("apache/httpd.conf.erb-$hostname"),
template("apache/$my_role/httpd.conf.erb"),
template("apache/httpd.conf.erb") ]
}
}
Here, if you have a node called nagios.example42.com, including the "monitor" role, Puppet looks for the template in the following locations (the first template found, in this order, is the one actually served:
$MODULESDIR/apache/templates/httpd.conf.erb-nagios.
$MODULESDIR/apache/templates/monitor/httpd.conf.erb.
$MODULESDIR/apache/templates/httpd.conf.erb.
Consider this last $MODULESDIR/apache/templates/httpd.conf.erb as a general fallback common template and the previous locations as more specific places where to look to a custom templates.
Of course you can use any kind of variable (both custom or provided by facter) to differentiate file paths, as usual according to your own infrastructure and the logic behind it.
Before defining an array of file paths think about your needs: how that specific configuration file changes among your servers? What variable can identify and match these differences? Is it better to use a template or a static file?
As usual there's not a predefined solution, but the best one that suits your infrastructure and, somehow, you own way of organizing things.

