System Configurations

This page is about how to manage configuration files (like /etc/*) on your machine(s) using subversion.

= Simple case =

Let's start off simple. I assume you are root, as you are the one modifying the configuration files. You first create a repository: svnadmin create /svn/configs

Then you can make your root directory a working copy of this (so far empty) repository: svn checkout file:///svn/configs /

Now you can start adding things you want to keep under revision control. When adding directories, make sure to use the -N (= --non-recursive) flag to add only the directory itself and not all its contents. And add directories before any files therein, although you may add both in the same command. svn add -N /etc /etc/fstab

Now we want to have a look at what we modified so far. A simple svn stat will tell you all the things that are not under revision control so far. When working with configuration files where you are not interested in many of them this is usually not what you want. Using the -q (= --quiet) flag suppresses those unmanaged files. svn stat -q /

Now we are ready to commit our configurations to the repository. It is usually a good idea to add and commit files before you modify them for the first time. This way you can always compare any revision of the file to its "vigin" form supplied by your distribution. And always write informative messages so you can later figure out not only what was changed but also why you changed it. Remember to set EDITOR for the root account as well. svn commit /

You may want to have a list of all the files under version control. Luckily you can combine the -q flag with -v</tt> (= --verbose</tt>) to get just that: svn stat -qv /

The basic work cycle probably is like this:
 * 1) if you modify file for the first time:
 * 2) add all parent dirs and the file itself
 * 3) commit
 * 4) edit the file
 * 5) check your modification works as expected, otherwise revert</tt>
 * 6) list all modifications
 * 7) commit modifications, including message about the reason for this change

= Basic concepts for multiple administrators or machines =

Maybe you are not the only one administrating the machine. Assume there are several people who can become root and tweak the config files. You would then want to know who changed what. If you are accessing the repository using the file://</tt> scheme you can simply provide --username </tt>yourName to provide this name for the record. It does not even have to correspond to an existing system account. svn --username john commit /

Maybe you decide to manage the configuration files for several machines in a single repository. For the moment I'll consider the machines rather independent from one another, so that you manage them in completely distinct trees of your repository. Having them in the same repository still helps when diffing them, applying changesets from one machine to another and so on, so it's preferable to rather have subtrees in a single repository than different repositories.

Now let's create a directory for a single machine in the above repository. Note that we create dir directly in the repository, not in any checked out working copy. svn mkdir file:///svn/configs/macchina

If you start off like this, you can simply check out the empty directory to the root directory of your machine, instead of checking out the repository root as described above. In case you started like described above and only later thought about adding other machines, you'll need to move your existing contents to the new location: svn move file:///svn/configs/etc file:///svn/configs/macchina/etc svn switch file:///svn/configs/macchina /

Make sure to first move the contents in the repository and only then switch your working copy, because otherwise you might switch to an empty tree and all your versioned files would be deleted.

You can then create additinal directories for other machines and check them out to their root directory. Let's suppose you are working on a machine called maschine</tt>. You could do the following: svn mkdir ssh+svn://root@macchina/svn/configs/maschine svn checkout ssh+svn://root@macchina/svn/configs/maschine /

= Advanced tricks for multiple administrators and machines =

Note that the trick for logging the user by passing --username</tt> to each command only works reasonably well with the file://</tt> scheme. It won't work with ssh access to another machine. And it is easy enough to forget to provide your name. We can solve all this and more by (ab)using the svn facility for defining tunnel applications.

On every machine where you want to manage the configuration using svn, first create the following file named /usr/local/bin/svn+sysadmin</tt> if -z $SYSADMIN  ; then SYSADMIN=$(    read -p "Your (non-root) login name: " NAME </dev/tty >/dev/tty && \     echo "$NAME"   ) fi if -z $SYSADMIN  ; then echo "You need to give your name!" >/dev/tty exit 1 fi exec /usr/bin/ssh -l sysadmin-svn "$@" --tunnel-user="${SYSADMIN}"
 * 1) !/bin/bash
 * 2) Taken from http://www.orcaware.com/svn/wiki/System_Configurations

Make it executable: chmod a+x /usr/local/bin/svn+sysadmin

Register it in the  [tunnels] </tt> section of your ~/.subversion/config</tt>: [tunnels] sysadmin = /usr/local/bin/svn+sysadmin

On the machine where the reporitory is, create a new user named sysadmin-svn</tt> and make it owner of the repository: chown -R sysadmin-svn /svn/configs

To allow access to the repository, it is probably most convenient to create a ssh public key for root on each machine (using ssh-keygen</tt>). I would consider it safe enough to not use a passphrase, as this key only protects access to the repository and if someone unauthorized managed to become root you are in trouble anyway. You can then register all those keys in ~sysadmin-svn/.ssh/authorized_keys</tt> on the machine with the repository to allow them all access to that account.

Now you can move your machine config to this new scheme: svn switch --relocate ssh+svn://root@macchina/svn/configs/maschine ssh+sysadmin://macchina/svn/configs/maschine /

Take care not to combine a switch --relocate</tt> (to change scheme) and a simple switch</tt> (to change repository subtree) into a single step, as I could imagine there would be trouble if you did so.

Because you changed ownership of the repository, it is probably easiest to access the repository through the <tt>ssh+sysadmin://</tt> scheme even on the machine where the repository lies. This way you get the same behaviour no matter what machine you are on.

Now whenever you access the configuration file repository, you will asked your login name unless you set the <tt>SYSADMIN</tt> environment variable. some commands like checkout make multiple connections, so you'll have to provide your name multiple times.

= Making use of the information =

So what can you do now your files are under revision control?

Suppose you are looking at some configuration file and want to know why some setting is the way it is. You'd svn blame the file authors to see who edited it (that's what the user name logging is for) and in which revision it was last modified. You can then use svn log to have a look at the message of that revision to get a clue why it was modified. You can use svn cat or svn diff to have a look at its previous setting. And of yourse you can do svn revert to undo changes you have not yet comitted and svn merge to undo changes already comitted to the repository.

= Sharing between machines =

Above we were assuming completely independently configured machines.

The opposite, completely identical machines, is almost as easy. You remove any existing local config files and then check out a new copy of the tree. You can then propagate configuration changes by comitting them on one machine and updating on all the others. Whenever you put a file under revision control that existed on the machines before you'll have to remove them before updating, which can be somewhat annoying. I don't know any nice solution for this yet.

How about mixed situations where some files are the same, but some others are not? I would think it best to have two trees, one shared and one independent, checked out on each machine. Of course only one of them can be the root. Usually I'd make this the independent tree unless you have very few differences. You can then add symlinks from one tree to the other to get each configuration file from the correct tree.

In case you made the independent tree your main working copy, you can graft the common tree using svn:externals. This way you model the tree composition in the repository as well, not only in your layout of checked out working copies. This has the advantage that many svn commands treat the whole thing as a single working copy.