~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