Whups, I seem to have built a configuration management system this evening!
Propellor has similar goals to chef or puppet or ansible, but with an approach
much more like slaughter.
Except it's configured by writing Haskell code.
The name is because propellor ensures that a system is configured with the
desired PROPerties, and also because it kind of pulls system configuration
along after it. And you may not want to stand too close.
Disclaimer: I'm not really a sysadmin, except for on the scale of "diffuse
administration of every Debian machine on planet earth or nearby", and so I
don't really understand configuration management. (Well, I did write
debconf, which claims to be the "Debian Configuration Management system"..
But I didn't understand configuration management back then either.)
So, propellor makes some perhaps wacky choices. The least of these
is that it's built from a git repository
that any (theoretical) other users will fork and modify; a cron job can
re-make it from time to time and pull down configuration changes, or
something can be run to push changes.
A really simple configuration for a Tor bridge server
using propellor looks something like this:
main = ensureProperties
[ Apt.stdSourcesList Apt.Stable `onChange` Apt.upgrade
, Apt.removed ["exim4"] `onChange` Apt.autoRemove
, Hostname.set "bridget"
Since it's just haskell code, it's "easy" to refactor out common
configurations for classes of servers, etc. Or perhaps integrate
reclass? I don't know. I'm happy
with just pure functions and type-safe refactorings of my configs, I
Properties are also written in Haskell of course. This one ensures that
all the packages in a list are installed.
installed :: [Package] -> Property
installed ps = check (isInstallable ps) go
go = runApt $ [Param "-y", Param "install"] ++ map Param ps
Here's one that ensures the hostname is set to the desired value,
which shows how to specify content for a file, and also how to run
another action if a change needed to be made to satisfy a property.
set :: HostName -> Property
set hostname = fileHasContent "/etc/hostname" [hostname]
`onChange` cmdProperty "hostname" [Param hostname]
Here's part of a custom one that I use to check out a user's
home directory from git. Shows how to make a property require
that some other property is satisfied first, and how to test
if a property has already been satisfied.
installedFor :: UserName -> Property
installedFor user = check (not <$> hasGitDir user) $
IOProperty ("githome " ++ user) (go =<< homedir user)
`requires` Apt.installed ["git", "myrepos"]
go ... -- 12 lines elided
I'm about 37% happy with the overall approach to listing properties
and combining properties into larger properties etc. I think that some
unifying insight is missing -- perhaps there should be a Property monad?
But as long as it yields a list of properties, any smarter thing should
be able to be built on top of this.
Propellor is 564 lines of code, including 25 or so built-in properties like
the examples above. It took around 4 hours to build.
I'm pretty sure it was easier to write it than it would have been to look
into ansible and salt and slaughter (and also liw's human-readable configuration
language whose name I've forgotten) in enough detail to pick one, and learn
how its configuration worked, and warp it into something close to how I
wanted this to work.
I think that's interesting.. It's partly about NIH and
I-want-everything-in-Haskell, but it's also about a complicated system
that is a lot of things to a lot of people --
of the kind I see when I look at ansible -- vs the tools and experience
to build just the thing you want without the cruft. Nice to have the latter!
Syndicated 2014-03-30 07:51:59 from see shy jo