Puppet: System Administration Automated

Support

Module Organisation

General stuff

The target of a module is a redistributable encapsulation of a certain aspect of a manifest. This can be a special configuration of a package manager, a define for configuring trac sites or similar things. In short, this will enable you to have the default site configuration under /etc/puppet, with modules shipped by puppet proper in /usr/share/puppet/ and a happy mix-and-match of VCS-checkouts in various states of development and production-readiness.

Modules as described on this page are available in Puppet 0.22.2 and later.

Sources of Modules

There were three major sources for manifest modules identified:

  • the puppet distribution
  • the in-house source control
  • external downloads (e.g., from PRM)

To accommodate different locations in the file system for the different use cases, there is a configuration variable modulepath which is a list of directories to scan in turn.

A reasonable default could be configured as /etc/puppet/modules:/usr/share/puppet:/var/lib/prm. Alternatively, the /etc/puppet directory could be established as a special anonymous module which is always searched first to retain backwards compatibility to today's layout.

For some environments it might be worthwhile to consider extending the modulepath configuration item to contain branches directly from VCS, e.g. by saying svn:file:///Volumes/svn/repos/management/master/puppet.testing/trunk to setup access for a testing puppetmaster as is done manually in BranchTesting.

Naming

Module names MUST be normal words, matching \w+ in Ruby, not containing the namespace separators :: or /. While it might be desirable to allow module hierarchies, for now modules cannot be nested.

The namespace site is reserved for local use and should not be used in modules meant for distribution.

Internal Organisation

A Puppet module contains manifests, distributable files and templates:

MODULE_PATH/
   module_name/
      README
      manifests/
         init.pp
         defaults.pp
      templates/
      files/
      depends/

With each subdirectory containing the expected deliverables.

Each module should contain a init.pp manifest file at the specified location to enable the simple import "module_name". This manifest file can contain all the classes associated with this module or additional .pp files can be added directly under the manifests folder. If adding additional .pp files, naming them after the class they define will allow auto lookup magic (explained further below in Module Lookup).

One of the things we want to accomplish with modules is code sharing. A module by nature should be self-contained: one should be able to get a module from somewhere and drop it into your module path and have it work. However, there are cases where the module depends on generic things that most people will already have defines or classes for in their regular manifests. Instead of adding these into the manifests of your module, add them to the depends folder (which is basically only documenting, it doesn't change how your module works) and mention these in your README, so people can at least see exactly what your module expects from these generic dependencies, and possibly integrate them into their own regular manifests.

(See PluginsInModules for info on how to put custom types and facts into modules)

Example

As an example, consider a autofs module that installs a fixed auto.homes map and generates the auto.master from a template. Its init.pp could look something like:

class autofs::client {
  package { autofs: ensure => latest }
  service { autofs: ensure => running }
  file { "/etc/auto.homes":
    source => "puppet://$servername/autofs/auto.homes"
  }
  file { "/etc/auto.master":
    content => template("autofs/auto.master.erb")
  }
}

and have these files in the file system:

MODULE_PATH/
  autofs/
    manifests/
      init.pp
    files/
      auto.homes
    templates/
      auto.master.erb

Note that you can still access files in modules when using puppet instead of puppetd; just leave off the server name and puppetd will fill in the server for you (using its configuration server as its file server) and puppet will use its module path:

file { "/etc/auto.homes":
    source => "puppet:///autofs/auto.homes"
}

Module Lookup

Since modules contain different subdirectories for different types of files, a little behind-the-scenes magic makes sure that the right file is accessed in the right context. All module searches are done within the modulepath, a colon-separated list of directories. In most cases, searching files in modules amounts to inserting one of manifest, files, or templates after the first component into a path, i.e. paths can be thought of as module_name/part_path where part_path is a path relative to one of the subdirectories of the module module_name.

For manifests, imports can either import a whole module (which amounts to importing the module's init.pp) or import a specific file from the module:

import "autofs"                # Looks for 'autofs/manifests/init.pp' on the module path
import "autofs/init.pp"        # Looks for the same file on the module path
import "autofs/util/stuff.pp"  # Looks for 'autofs/manifests/util/stuff.pp' on the module path

If nothing appropriate is found on the module path, Puppet will try to resolve the import relative to the directory containing the manifest doing the import.

As of 0.23.1, Puppet will attempt to auto-load classes and definitions from modules, so you don't have to explicitly import them. You can just include the module class or start using the definition. Note that the init.pp file will always be loaded first, so you can put all of your classes and definitions in there if you prefer. But with namespaces, some additional magic is available.

Let's say your autofs module has a class defined in init.pp but you want an additional class called craziness. If you define that class as autofs::craziness and store it in file craziness.pp under the manifests directory, then simply using something like include autofs::craziness will trigger puppet to search for a class called craziness in a file named craziness.pp in a module named autofs in the specified module paths. Hooray!

If you prefer to keep class autofs in a file named autofs.pp, create an init.pp file containing simply:

import "*"

and you will then be able to reference the class by using include autofs just as if it were defined in init.pp.

For file references on the fileserver, a similar lookup is used so that a reference to puppet://$servername/autofs/auto.homes resolves to the file autofs/files/auto.homes in the module's path.

Warning: This will only work if you do not have an explicit, for example [autofs] mount already declared in your fileserver.conf.

You can apply some access controls to files in your modules by creating a [modules] file mount, which should be specified without a path statement, in the filserver.conf configuration file:

[modules]
allow *.domain.com
deny *.wireless.domain.com

Unfortunately, you cannot apply more granular access controls, for example at the per module level as yet.

To make a module usable with both the command line client and a puppetmaster, you can use a URL of the form puppet:///path, i.e. a URL without an explicit server name. Such URL's are treated slightly differently by puppet and puppetd: puppet searches for a serverless URL in the local filesystem, and puppetd retrieves such files from the fileserver on the puppetmaster. This makes it possible to use the same module in a standalone puppet script by running puppet --modulepath path script.pp and as part of a site manifest on a puppetmaster, without any changes to the module.

Finally, template files are searched in a manner similar to manifests and files: a mention of template("autofs/auto.master.erb") will make the puppetmaster look for a file autofs/templates/auto.master.erb on the module path. If no such file exists, the puppetmaster looks for a file autofs/auto.master.erb on the templatepath.

Configuration

There are only two items that can be configured for modules:

  1. The search path for modules is configured as a colon-separated list of directories in the puppetmaster section of puppetmaster's config file with the modulepath parameter:

    [puppetmasterd]
    ...
    modulepath = /var/lib/puppet/modules:/data/puppet/modules
    

The search path can be added to at runtime by setting the PUPPETLIB environment variable, which must also be a colon-separated list of directories.

Documentation

If you decide to make your modules available to others (and please do!), then please also make sure you document your module so others can understand and use them. Most importantly, make sure the dependencies on other defines and classes not in your module are clear.

From Puppet version 0.24.7 you can generate automated documentation from resources, classes and modules using the puppetdoc tool. You can find more detail at the Puppet Manifest Documentation page.

See Also

Distributing custom facts and types via modules: PluginsInModules

This page originates from a discussion kicked off by Bradley Baetz about how to organize manifests and the related files in modules for easier maintenance. This discussion is held in a google.com corporate webapplication that requires a password.