
An overview of Puppet's modules
A module can be seen as a higher-order organizational unit. It bundles up classes and defined types that contribute to a common management goal (specific system aspects or a piece of software, for example). These manifests are not all that is organized through modules; most modules also bundle files and file templates. There can also be several kinds of Puppet plugins in a module. This section will explain these different parts of a module and show you where they are located. You will also learn about the means of module documentation and how to obtain existing modules for your own use.
Parts of a module
For most modules, manifests form the most important part - the core functionality. The manifests consist of classes and defined types, which all share a namespace, rooted at the module name. For example, an ntp
module will contain only classes and defines whose names start with the ntp::
prefix.
Many modules contain files that can be synced to the agent's filesystem. This is often used for configuration files or snippets. You have seen examples of this, but let's repeat them. A frequent occurrence in many manifests is file
resources such as the following:
file { '/etc/ntp.conf': source => 'puppet:///modules/ntp/ntp.conf', }
The above resource references a file that ships with a hypothetical ntp
module. It has been prepared to provide generally suitable configuration data. However, there is often a need to tweak some parameters inside such a file, so that the node manifests can declare customized config settings for the respective agent. The tool of choice for this is templates, which will be discussed in the next chapter.
Another possible component of a module that you have already read about is Custom Facts—code that gets synchronized to the agent and runs before a catalog is requested, so that the output becomes available as facts about the agent system.
These facts are not the only Puppet plugins that can be shipped with modules. There are also parser functions (also called custom functions), for one. These are actual functions that you can use in your manifests. In many situations, they are the most convenient way, if not the only way, to build some specific implementations.
The final plugin type that has also been hinted at in an earlier chapter is the custom native types and providers, which are conveniently placed in modules as well.
Module structure
All the mentioned components need to be located in specific filesystem locations for the master to pick them up. Each module forms a directory tree. Its root is named after the module itself. For example, the ntp
module is stored in a directory called ntp/
.
All manifests are stored in a subdirectory called manifests/
. Each class and defined type has its own respective file. The ntp::package
class will be found in manifests/package.pp
, and the defined type called ntp::monitoring::nagios
will be found in manifests/monitoring/nagios.pp
. The first particle of the container name (ntp
) is always the module name, and the rest describes the location under manifests/
. You can refer to the module tree in the following paragraphs for more examples.
The manifests/init.pp
file is special. It can be thought of as a default manifest location, because it is looked up for any definition from the module in question. Both the examples that were just mentioned can be put into init.pp
and will still work. Doing this makes it harder to locate the definitions, though.
In practice, init.pp
should only hold one class, which is named after the module (such as the ntp
class), if your module implements such a class. This is a common practice, as it allows the manifests to use a simple statement to tap the core functionality of the module:
include ntp
You can refer to the Modules' best practices section for some more notes on this subject.
The files and templates that a module serves to the agents are not this strictly sorted into specific locations. It is only important that they be placed in the files/
and templates/
subdirectories, respectively. The contents of these subtrees can be structured to the module author's liking, and the manifest must reference them correctly. Static files should always be addressed through URLs, such as these:
puppet:///modules/ntp/ntp.conf puppet:///modules/my_app/opt/scripts/find_my_app.sh
These files are found in the corresponding subdirectories of files/
:
.../modules/ntp/files/ntp.conf .../modules/my_app/files/opt/scripts/find_my_app.sh
The modules
prefix in the URI is mandatory and is always followed by the module name. The rest of the path translates directly to the contents of the files/
directory. There are similar rules for templates. You can refer to Chapter 6, Leveraging the Full Toolset of the Language, for the details.
Finally, all plugins are located in the lib/
subtree. Custom facts are Ruby files in lib/facter/
. Parser functions are stored in lib/puppet/parser/functions/
, and for custom resource types and providers, there is lib/puppet/type/
and lib/puppet/provider/
, respectively. This is not a coincidence; these Ruby libraries are looked up by the master and the agent in the according namespaces. There are examples for all these components later in this chapter.
In short, following are the contents of a possible module in a tree view:
/opt/puppetlabs/code/environments/production/modules/my_app templates # templates are covered in the next chapter files subdir1 # puppet:///modules/my_app/subdir1/<filename> subdir2 # puppet:///modules/my_app/subdir2/<filename> subsubdir # puppet:///modules/my_app/subdir2/subsubdir/... manifests init.pp # class my_app is defined here params.pp # class my_app::params is defined here config detail.pp # my_app::config::detail is defined here basics.pp # my_app::config::basics is defined here lib facter # contains .rb files with custom facts puppet functions # contains .rb files with Puppet 4 functions parser functions # contains .rb files with parser functions type # contains .rb files with custom types provider # contains .rb files with custom providers
Documentation in modules
A module can and should include documentation. The Puppet master does not process any module documentation by itself. As such, it is largely up to the authors to decide how to structure the documentation of the modules that are created for their specific site only. That being said, there are some common practices and it's a good idea to adhere to them. Besides, if a module should end up being published on the Forge, appropriate documentation should be considered mandatory.
Note
The process of publishing modules is beyond the scope of this book. You can find a guide at https://docs.puppetlabs.com/puppet/latest/reference/modules_publishing.html.
For many modules, the main focus of the documentation is centered on the README
file, which is located right in the module's root directory. It is customarily formatted in Markdown as README.md
or README.markdown
. The README
file should contain explanations, and often, there is a reference documentation as well.
Puppet DSL interfaces can also be documented right in the manifest, in the rdoc
and YARD
format. This applies to classes and defined types:
# Class: my_app::firewall # # This class adds firewall rules to allow access to my_app. # # Parameters: none class my_app::firewall { # class code here }
You can generate HTML documentation (including navigation) for all your modules using the puppet doc
subcommand. This practice is somewhat obscure, so it won't be discussed here in great detail. However, if this option is attractive to you, we encourage you to peruse the documentation.
The following command is a good starting point:
puppet help doc
Another useful resource is the puppetlabs-strings
module (https://forge.puppetlabs.com/puppetlabs/strings), which will eventually supersede puppet doc
.
Plugins are documented right in their Ruby code. There are examples for this in the following sections.