Puppet: System Administration Automated

Support

Operating System Release Management

Problem: different OS release versions are subtly different. Between two releases of Debian (and other distributions too) there are subtle and not so subtle differences which have to be taken in account when creating manifests. This can lead to unwieldy manifests with much duplicated code.

Solutions

Value based

A very direct method is the usage of a variable to hold the current release and use various techniques to use this value. This value could be retrieved by Facter if release changes are not done via Puppet.

Setting a Release variable

The release variable has to be set on the node level, to affect all included classes.

# the old server is still sarge
node oldserver { $dv = 'sarge'
   include apt_conf
}

# the new server is already etch
node newserver { $dv = 'etch'
   include apt_conf
}

Direct Usage

In many cases it is enough to use a template keyed to the $dv variable or to use a different file. Both methods are demonstrated in this manifest:

# for demonstration usage only
class apt_conf {
   file {
      # see template below
      "/etc/apt/sources.list":
         content => template("apt/sources.list.erb");
      # use different sources for different releases
      "/etc/apt/preferences":
         source => "puppet://puppet/files/$dv/apt/preferences";
   }
}

Template apt/sources.list.erb:

deb http://ftp.at.debian.org/debian <%= dv %> main
deb http://security.debian.org/ <%= dv %>/updates main

Conditionals

For details see Language Tutorial#conditionals

Detected via lsb-release

LSB compatible systems provide the lsb_release command, which reports detailed version information about the running release. This information is passed via Facter to Puppet and can be used to make decisions:

class base-host {
    file {
        "/etc/bash.bashrc":
            source => [ "puppet://$pserver/files/apps/bash/bash.bashrc.$lsbdistcodename",
                        "puppet://$pserver/files/apps/bash/bash.bashrc" ]
        ;
    }
}

This installs a specific bashrc, if one exists for this $lsbdistcodename, else it takes the generic version. Alternatively a template can be used.

Debian installations need to have the lsb-release package installed. Ubuntu installs this by default.

Inheritance and Overriding

In more involved cases or to give bigger manifests more structure, the common parts can be re-factored into a base class, with the specific changes for the releases collected in separate subclasses:

class webserver_base {
   package { "apache": ...
   file { "/etc/apache/..."
}

class webserver_sarge inherits webserver_base {
   # this fictional apache package needs some symlinks in sarge
   file { "/etc/apache/...": ensure => "/etc/apache/muh...", }
}

class webserver_etch inherits webserver_base {
   # this fictional apache package needs some debconf answers in etch
   Package["apache"] { responsefile => "/var/cache/debconf/..." }
}

Of course, this technique can be combined with conditional application and the release variable.

Problem

We got a lot of Debian servers and it's impractical to issue an 'apt-get dist-upgrade' on all of them separately. Also, we do not want unsupervised upgrades, since that could mess stuff up. We want to test before telling all the machines to upgrade.

Solution

We create a file on our puppetmaster. Where it resides or what the content is, doesn't matter. Let's call the file 'update_initiator'. Now we add the following recipe to the manifest we use for each server:

file { "/etc/update_initiator":
  source => "puppet://puppet/config/update_initiator",
}

exec { "/usr/bin/apt-get -y dist-upgrade":
  refreshonly => true,
  subscribe => File["/etc/update_initiator"],
}

Et voila, when we've tested all the available upgrades on our test server, we simply change the content of 'update-initiator' on the puppetmaster, the file registers as changed and the machines will initiate the dist-upgrade. This of course requires you to run a "apt-get update" once in a while on all the servers (we use cron-apt for that). A good way to change the file is to have the date of the last upgrade in there. That way it's a reference point.