4. Quick-n-dirty Puppet modules

The goal of a quick-n-dirty Puppet module is to reduce the common “package, file, service” pattern to a bare minimum by..

  1. describing classes in an admin’s configuration:

    [qnd]
    ntp.config.default  = mysite:ntp:0644:ntp_conf:/etc/ntp.conf:stratnum:servers
    ntp.config.solaris  = mysite:ntp:0644:ntp_conf:/etc/inet/ntp.conf
    ntp.service.default = ntp:ntpd
    ntp.service.solaris = ntp:ntp
    ntp.install.redhat  = ntp:ntp
    ntp.install.solaris = ntp:CSWntp:pkgutil
  2. ..and generating a quick-n-dirty (here, qnd) module:

    puppet
    +-- modules
        +-- qnd
            +-- ntp
                +-- default.pp      # class qnd::ntp::default
                    +-- config.pp   # class qnd::ntp::config
                    +-- install.pp  # class qnd::ntp::install
                    +-- service.pp  # class qnd::ntp::service
  3. ..that supports these kinds of declarations:

    class {'qnd::ntp::install': }
    class {'qnd::ntp::config': $ntp_conf_stratnum => '10' }
    class {'qnd::ntp::service': $ntp_subs => File['ntp_conf']}

The idea is that the descriptions in the admin’s configuration provide a quick reference for common and concise declarations, and important configuration information is separated from the class implementations (which can be regenerated at will).

4.1. Defining values

Start by adding a [qnd] section to your admin configuration that follows this convention:

[qnd]
ntp.config.default  = mysite:ntp:0644:ntp_conf:/etc/ntp.conf:stratnum:servers
ntp.config.solaris  = mysite:ntp:0644:ntp_conf:/etc/inet/ntp.conf
ntp.service.default = ntp:ntpd
ntp.service.solaris = ntp:ntp
ntp.install.redhat  = ntp:ntp
ntp.install.solaris = ntp:CSWntp:pkgutil

Options follow a submodule.classname.platform pattern. submodule is a custom, administrative name for the target service across all platforms. classname is one of config, service, or install depending on which class you’re configuring. platform is a string that Puppet should be able to apply to a conditional $::operatingsystem switch (default is acceptable).

Values are colon-separated parameters that are passed to the underlying class generators’ add() method (see minimin.fabqnd). The first parameter is a unique identifier for the class ( type, and following parameters are values specific to the class appropriate for the corresponding platform.

A line-by-line explanation:

  1. ntp.config.default  = mysite:ntp:0644:ntp_conf:/etc/ntp.conf:stratnum:servers

    The default ntp configuration (ntp.config.default) uses one configuration template found in the mysite module named ntp_conf; this template should exist at mysite/templates/ntp_conf. The user and mode of the file are ntp and 0644. Content will be written to /etc/ntp.conf. The names stratnum and servers will create two template variables named ntp_conf_stratnum and ntp_conf_servers. The template determines whether these variables are required (or even used).

    The point of having a template home outside the quick-n-dirty module is to keep actual configuration in a module that is actively maintained; the quick-n-dirty module will need to be re-created if the admin’s [qnd] options change.

    For the purposes of our configuration, we’re tailoring our default values to a Red Hat/Fedora-based platform.

  2. ntp.config.solaris  = mysite:ntp_conf:/etc/inet/ntp.conf

As above, with the destination of the ntp_conf content on Solaris being written to /etc/inet/ntp.conf.

No template variables are provided, since all but the first set of variables defined for a unique template identifier (ntp_conf) are ignored; we are trying to enforce uniform configuration behavior.

  1. ntp.service.default = ntp:ntpd
The ntp portion of ntp:ntpd sets a service identifier, the default value of which is ntpd. This means that the platform’s native service management facility should know how to start and stop something named ntpd. ntpd satisfies Red Hat and Fedora’s service facility.
  1. ntp.service.solaris = ntp:ntp
Again, we override the service name for Solaris (the second ntp in ntp:ntp), since svcadm recognizes ntp instead of ntpd.
  1. ntp.install.redhat  = ntp:ntp
For Red Hat, we define a native (probably yum) package installation for the package named ntp.
  1. ntp.install.solaris = ntp:CSWntp:pkgutil
For Solaris, we specify a third-party package installer pkgutil that will understand how to fetch CSWntp.

4.2. Generating the module

To create our quick-n-dirty module in the common directory documented in Layout, we’ll generate and run a script that will create the module in /proj/common/puppet/modules/qnd:

fab --hide=running,status qndgen:/proj/common/puppet/modules/qnd > /tmp/qnd.sh
sh /tmp/qnd.sh

Note that the qndgen command looks for the [qnd] section in our administrative configuration and creates submodules for all the options in this section. In the example above, we only defined values for ntp-prefixed options, so we only expect the qnd/ntp/ submodule directory to be created.

4.2.1. Submodule files

The following files will be created after the script is run:

/
+-- proj
    +-- common
        +-- puppet
            +-- modules
                +-- qnd
                    +-- ntp
                        +-- default.pp  # class qnd::ntp::default
                        +-- config.pp   # class qnd::ntp::config
                        +-- install.pp  # class qnd::ntp::install
                        +-- service.pp  # class qnd::ntp::service

Managed components are organized as ‘submodules’. Each submodule provides classes for installation, configuration, and service management independently via parameterized classes. ‘Independently’ means that a service class does not implicitly require the configuration class, the configuration class does not implicitly require the installation class, etc..

default.pp defines operating system-specific values for internal use by install.pp, config.pp, and service.pp.

4.3. Enable the module

So far we’ve only used our admin config to define quick-n-dirty NTP behavior and generate a Puppet module with classes that implement the behavior, but we haven’t enabled the ability to use those classes in our site configuration.

Enable the quick-n-dirty module by..

  1. ..verifying that the parent path leading to the module is in the admin’s .fabricrc (in this case, the parent path is /proj/common/puppet/modules):

    admin_puppet_path = /proj/common/puppet/modules
  2. ..and including the qnd module by name in the [role] section (see Project configuration):

    [role]
    common.nodes = websvr mailsvr
    common.puppet.mods = mysite qnd
    common.puppet.modpath = /etc/puppet/modules
    common.puppet.site = /etc/puppet/modules/mysite/manifests/site.pp

When we finally attempt to apply a Puppet configuration (see Applying Puppet configuration), fab apply will look in /proj/common/puppet/modules for mysite and qnd directories and copy both to the target node’s /etc/puppet/modules before running /etc/puppet/modules/mysite/manifests/site.pp. Given the Puppet path and required modules for the common role, /proj/common/puppet/modules/mysite/manifests/site.pp must exist locally.

4.4. Use the classes

  • ${svc}::config - Config files, templates, and parameters. The default config file should provide a reasonable configuration, perhaps using a template that accomodates differences between operating systems. Since we don’t expect a default template to accept enough custom parameters to satisfy every site, all files will accept a string to use as file content. The intention is for the site to use it’s own config file templates (if necessary) with reasonable custom parameters and pass the resulting string representation (using template('{sitemod}/{configfile}').
  • ${svc}::service - On the site level, the only (configurable) concerns are whether to enable or disable services (default running) and whether they should refresh themselves after a configuration change (default ${svc}_subscribe = [], ). The idea is that we probably want to run a service, but we can’t take for granted that automatic restarts are appropriate.
  • ${svc}::install - This is only useful when a package manager is capable of verifying and installing a package by name.

Assuming all the above is intact, we can include qnd::ntp classes in the site configuration (site.pp). Here we invoke the default behavior defined above (see Defining values):

class {'qnd::ntp::install': }
class {'qnd::ntp::config': }
class {'qnd::ntp::service': }

Installation, configuration, and service management are independent - include only those classes that you want to use. To skip installation (perhaps you installed a package manually) and have a service restart after a change is made to a config file:

class {'qnd::ntp::config':  ntp_conf_stratnum => '4'}
class {'qnd::ntp::service': ntp_subs => File['ntp_conf']}

See the minimin.fabqnd for details on config, service, and install class parameters.