Puppet: System Administration Automated

~Puppet


Tim O'Reilly's post on operations has got me even more obsessed with why it is that operating system administration is left to the professionals while development is considered acceptable for anyone.

Or rather, why people don't realize that they're maintaining operating systems all the time.

I'll be doing my best to post about this more as time goes on, but I figured I'd start out with how I use Puppet in my daily life, largely unrelated to being a sysadmin.

I have a very complex shell profile, and I try to keep all of my other important configurations in Subversion, since I have so many accounts in so many places and I'm basically retarded without syntax highlighting and all of my cool little aliases and functions. So, I created a Puppet manifest that manages these things for me, and I'm going to walk through what it does and how it works.

My homedir manifest, which I'll call ~puppet, is mostly responsible for two things: Link my configuration files to where they're supposed to be, and create any necessary cron jobs.

Most of my configuration files are in ~/etc/<application>, and I link them back to the appropriate location; e.g.:

lrwxrwxrwx  1 luke luke        19 Feb 13 14:20 .gaim -> /home/luke/etc/gaim
lrwxrwxrwx  1 luke luke        26 Feb 13 14:20 .procmailrc -> /home/luke/etc/procmail/rc
lrwxrwxrwx  1 luke luke        27 Feb 13 14:20 .profile -> /home/luke/etc/profile/init
lrwxrwxrwx  1 luke luke        18 Feb 13 14:20 .ssh -> /home/luke/etc/ssh
lrwxrwxrwx  1 luke luke        15 Feb  6 19:37 .subversion -> etc/subversion/
lrwxrwxrwx  1 luke luke        22 Feb 13 14:20 .vim -> /home/luke/etc/vim/vim
lrwxrwxrwx  1 luke luke        24 Feb 13 14:20 .vimrc -> /home/luke/etc/vim/vimrc

This makes it easy to control everything in subversion and use the same configurations everywhere.

There's a twist in making the links, though; not all of my home directories have the same path. So, to make these links work, I have to create a Facter fact that points to my home directory. The long term solution to this is that I have ~/lib/ruby in my $RUBYLIB environment variable and the following code at ~/lib/ruby/facter/home.rb:

require 'facter'

Facter.add("home") do
    setcode do
        ENV['HOME']
    end
end

That's set in my profile, though, which hasn't been linked through yet, so for setup I usually just set 'FACTER_HOME=$HOME', which Facter picks up and sets for me in Puppet. Because I create a bunch of these links in my ~puppet manifest, I create a reusable component to do so:

define homelink(ensure) {
    case $home {
        "": {
            exec { "nohome-$name":
                command => "/bin/echo No home variable",
                logoutput => true
            }
        }
        default: {
            file {
                "$home/$name": ensure => "$ensure"
            }
        }
    }
}

If I've forgetton to set $home, then this warns me; otherwise, it just saves a very small amount of typing. Here's how I use it:

class profile {
    file {
        "$home/.bashrc": ensure => ".profile"
    }

    homelink {
        ".gaim":        ensure => "etc/gaim";
        ".procmailrc":  ensure => "etc/procmail/rc";
        ".profile":     ensure => "etc/profile/init";
        ".ssh":         ensure => "etc/ssh";
        ".vim":         ensure => "etc/vim/vim";
        ".vimrc":       ensure => "etc/vim/vimrc";
    }
}

As you can see, I stuck them into a profile class to simplify referring to them. I just include this class on every host, since I always want these links. The first file element just creates a link from .bashrc to .profile, so I don't have to care whether I'm a login shell or not. The homelink elements create each of the links above.

The only other work I currently do in my ~puppet manifest is create spam processing cron jobs, which are only used on my mail server, and a signature generation cron job, which is only used machines that I use as a mail client. Here's what my mailserver class looks like:

class mailserver {
    cron { spamyup:
        minute => [0, 30],
        user => luke,
        command => "/home/luke/bin/spamproc"
    }

    cron { spamclean:
        hour => 2,
        minute => 15,
        user => luke,
        command => "/home/luke/bin/spamproc -a"
    }
}

Every half an hour I want spam processed, and once a day I want spam deleted. Done, portably.

class mailclient {
    cron { "joyent-sig":
        command => "$home/bin/signature --write",
        minute => [0,5,10,15,20,25,30,35,40,45,50,55]
    }
}

I have a large collect of quotes and such that I like to use for random signatures, and some stupid mail clients coughThunderbirdcoughEvolution* aren't smart enough to consistently be able to use scripts for signature sources, so I change the signature every five minutes. I only run this on my desktop and laptop, not on all the machines I have server accounts on.

Lastly, I decide what happens where:

include profile

case $hostname {
    culain: {
        include mailserver, mailclient
    }

    phage: { include mailclient }
}

You can see that everyone gets the profile class, but only specific machines get the mail-related classes.

So, you might say to yourself, "Well, Luke's a sysadmin; I don't have that kind of problem". I would instead say that you've probably structured your computer usage in a way that allows you to avoid the problems that I'm solving, and I would guess that there are things you do that could be significantly simplified through this kind of application of Puppet to your personal computing uses.

Just for posterity, here's the full configuration:

define homelink(ensure) {
    case $home {
        "": {
            exec { "nohome":
                command => "/bin/echo No home variable",
                logoutput => true
            }
        }
        default: {
            file {
                "$home/$name": ensure => "$ensure"
            }
        }
    }
}

class mailserver {
    cron { spamyup:
        minute => [0, 30],
        user => luke,
        command => "/home/luke/bin/spamproc"
    }

    cron { spamclean:
        hour => 2,
        minute => 15,
        user => luke,
        command => "/home/luke/bin/spamproc -a"
    }
}

class profile {
    file {
        "$home/.bashrc": ensure => ".profile"
    }

    homelink {
        ".gaim":        ensure => "etc/gaim";
        ".procmailrc":  ensure => "etc/procmail/rc";
        ".profile":     ensure => "etc/profile/init";
        ".ssh":         ensure => "etc/ssh";
        ".vim":         ensure => "etc/vim/vim";
        ".vimrc":       ensure => "etc/vim/vimrc";
    }
}

class mailclient {
    cron { "joyent-sig":
        command => "$home/bin/signature --write",
        minute => [0,5,10,15,20,25,30,35,40,45,50,55]
    }
}

include profile

case $hostname {
    culain: {
        include mailserver, mailclient
    }

    phage: { include mailclient }
}

Updated to fix library path

add to del.icio.us Add to Blinkslist add to furl Digg it add to ma.gnolia Stumble It! add to simpy seed the vine TailRank post to facebook

Fri, 21 Jul 2006 | Tags:


Posted by jeanlouis.pouget@orange-ftgroup.com at Mon Jul 16 16:45:34 2007
PLEASE SPAM ME

Name:


E-mail:


URL:


Comment: