Wednesday, June 14, 2006

SubVersion for Linux Administrators

Updated: 26 Aug 2006

I *think* I have this figured out. After banging my head against the wall for a few months, I finally figured out how to put my /etc configuration folder (and config files) into SubVersion so that I have version control over them.

Assumptions:

  1. You need to have SubVersion 1.2 or 1.3 (or later) installed
  2. You have a folder called /var/svn where you keep your repositories
  3. I'm assuming that you've su'd to the root account
  4. Make sure you have a clean system (run etc-update first)

I think that's the only requirements.

Step #1 - Create the repository

The name of the repository can be anything you want. I tend to name it after the machine name (but you could use the name "system", "config", "admin" or anything else). In this particular case, I'm setting it up on a machine called "nogitsune" which is my Gentoo AMD64 system.

Creating the repository is easy:

# svnadmin create /var/svn/nogitsune

Replace "nogitsune" with the preferred name of your repository. I'd suggest keeping the name as short as possible in case you have to type it by hand later (i.e. "nogitsune", "copper", "alpha", "san1pri", "xen-athens").

Step #2 - Checkout the repository to the root folder

nogitsune etc # cd /
nogitsune / # svn co file:///var/svn/nogitsune .
Checked out revision 0.
nogitsune / # svn status
? media
? lib64
? tgh
? root
? home
? var
? lost+found
? software
? sbin
? mnt
? tmp
? opt
? boot
? proc
? backup
? lib
? bin
? usr
? lib32
? etc
? dev
? sys
nogitsune / #


As long as the "svn status" command returns something like the above, we know that we've connected properly to the SubVersion repository. You can also look for the ".svn" directory in the root.

nogitsune / # ls -la .svn
total 40
drwxr-xr-x 7 root root 4096 Jun 14 21:28 .
drwxr-xr-x 24 root root 4096 Jun 14 21:28 ..
-r--r--r-- 1 root root 118 Jun 14 21:28 README.txt
-r--r--r-- 1 root root 0 Jun 14 21:28 empty-file
-r--r--r-- 1 root root 283 Jun 14 21:28 entries
-r--r--r-- 1 root root 2 Jun 14 21:28 format
drwxr-xr-x 2 root root 4096 Jun 14 21:28 prop-base
drwxr-xr-x 2 root root 4096 Jun 14 21:28 props
drwxr-xr-x 2 root root 4096 Jun 14 21:28 text-base
drwxr-xr-x 6 root root 4096 Jun 14 21:28 tmp
drwxr-xr-x 2 root root 4096 Jun 14 21:28 wcprops
nogitsune / # cat .svn/entries

xmlns="svn:">
committed-rev="0"
name=""
committed-date="2006-06-15T01:26:51.238552Z"
url="file:///var/svn/nogitsune"
kind="dir"
uuid="21f0cb31-3916-0410-ae38-e44852334012"
revision="0"/>

nogitsune / #


Step #3 - Adding directories and files to SubVersion

It's important to understand a little bit how "svn add" and "svn commit" work hand-in-hand. Just because we've issued the "svn add" command does not mean that our changes have been pushed to the repository. That requires using the "svn commit" command.

For the first example, I'm going to push the contents of /boot into the Subversion repository.

nogitsune / # mount /boot
nogitsune / # ls -la /boot
total 41261
drwxr-xr-x 4 root root 2048 Nov 29 2005 .
drwxr-xr-x 24 root root 4096 Jun 14 21:28 ..
-rw-r--r-- 1 root root 0 Jul 27 2005 .keep
-rw-r--r-- 1 root root 917680 Nov 12 2005 System.map-2.6.13-12Nov2005
-rw-r--r-- 1 root root 910027 Nov 12 2005 System.map-2.6.13-12Nov2005-2300
-rw-r--r-- 1 root root 910027 Nov 12 2005 System.map-2.6.13-12Nov2005-2330
-rw-r--r-- 1 root root 898088 Nov 12 2005 System.map-2.6.13-13Nov2005
-rw-r--r-- 1 root root 898098 Nov 13 2005 System.map-2.6.13-13Nov2005-1700
-rw-r--r-- 1 root root 917799 Nov 13 2005 System.map-2.6.13-13Nov2005-1810
-rw-r--r-- 1 root root 917372 Nov 13 2005 System.map-2.6.13-13Nov2005-1948
-rw-r--r-- 1 root root 837348 Nov 14 2005 System.map-2.6.13-14Nov2005-1500
-rw-r--r-- 1 root root 918716 Nov 14 2005 System.map-2.6.13-14Nov2005-1600
-rw-r--r-- 1 root root 846033 Nov 21 2005 System.map-2.6.13-21Nov2005-2300
-rw-r--r-- 1 root root 888601 Nov 22 2005 System.map-2.6.13-22Nov2005-0030
-rw-r--r-- 1 root root 890145 Nov 29 2005 System.map-2.6.13-29Nov2005-2148
-rw-r--r-- 1 root root 916779 Nov 8 2005 System.map-2.6.13-8Nov2005
-rw-r--r-- 1 root root 919480 Nov 9 2005 System.map-2.6.13-9Nov2005
lrwxrwxrwx 1 root root 1 Nov 8 2005 boot -> .
-rw-r--r-- 1 root root 23975 Nov 12 2005 config-2.6.13-12Nov2005
-rw-r--r-- 1 root root 23749 Nov 12 2005 config-2.6.13-12Nov2005-2300
-rw-r--r-- 1 root root 23738 Nov 12 2005 config-2.6.13-12Nov2005-2330
-rw-r--r-- 1 root root 23782 Nov 12 2005 config-2.6.13-13Nov2005
-rw-r--r-- 1 root root 23197 Nov 13 2005 config-2.6.13-13Nov2005-1700
-rw-r--r-- 1 root root 24091 Nov 13 2005 config-2.6.13-13Nov2005-1810
-rw-r--r-- 1 root root 24106 Nov 13 2005 config-2.6.13-13Nov2005-1948
-rw-r--r-- 1 root root 22579 Nov 14 2005 config-2.6.13-14Nov2005-1500
-rw-r--r-- 1 root root 23986 Nov 14 2005 config-2.6.13-14Nov2005-1600
-rw-r--r-- 1 root root 23084 Nov 21 2005 config-2.6.13-21Nov2005-2300
-rw-r--r-- 1 root root 23427 Nov 22 2005 config-2.6.13-22Nov2005-0030
-rw-r--r-- 1 root root 23416 Nov 29 2005 config-2.6.13-29Nov2005-2148
-rw-r--r-- 1 root root 23986 Nov 8 2005 config-2.6.13-8Nov2005
-rw-r--r-- 1 root root 23964 Nov 9 2005 config-2.6.13-9Nov2005
drwxr-xr-x 2 root root 1024 Nov 9 2005 grub
-rw-r--r-- 1 root root 2139460 Nov 12 2005 kernel-2.6.13-12Nov2005
-rw-r--r-- 1 root root 2081829 Nov 12 2005 kernel-2.6.13-12Nov2005-2300
-rw-r--r-- 1 root root 2081802 Nov 12 2005 kernel-2.6.13-12Nov2005-2330
-rw-r--r-- 1 root root 2056584 Nov 12 2005 kernel-2.6.13-13Nov2005
-rw-r--r-- 1 root root 2064792 Nov 13 2005 kernel-2.6.13-13Nov2005-1700
-rw-r--r-- 1 root root 2105823 Nov 13 2005 kernel-2.6.13-13Nov2005-1810
-rw-r--r-- 1 root root 2099454 Nov 13 2005 kernel-2.6.13-13Nov2005-1948
-rw-r--r-- 1 root root 1958868 Nov 14 2005 kernel-2.6.13-14Nov2005-1500
-rw-r--r-- 1 root root 2142469 Nov 14 2005 kernel-2.6.13-14Nov2005-1600
-rw-r--r-- 1 root root 2008155 Nov 21 2005 kernel-2.6.13-21Nov2005-2300
-rw-r--r-- 1 root root 2017013 Nov 22 2005 kernel-2.6.13-22Nov2005-0030
-rw-r--r-- 1 root root 2024425 Nov 29 2005 kernel-2.6.13-29Nov2005-2148
-rw-r--r-- 1 root root 2139807 Nov 8 2005 kernel-2.6.13-8Nov2005
-rw-r--r-- 1 root root 2151371 Nov 9 2005 kernel-2.6.13-9Nov2005
drwx------ 2 root root 12288 Nov 8 2005 lost+found
nogitsune / # svn add -N boot
A boot
nogitsune / # cd boot
nogitsune boot # ls -la .svn
total 11
drwxr-xr-x 7 root root 1024 Jun 14 21:33 .
drwxr-xr-x 5 root root 2048 Jun 14 21:33 ..
-r--r--r-- 1 root root 118 Jun 14 21:33 README.txt
-r--r--r-- 1 root root 0 Jun 14 21:33 empty-file
-r--r--r-- 1 root root 190 Jun 14 21:33 entries
-r--r--r-- 1 root root 2 Jun 14 21:33 format
drwxr-xr-x 2 root root 1024 Jun 14 21:33 prop-base
drwxr-xr-x 2 root root 1024 Jun 14 21:33 props
drwxr-xr-x 2 root root 1024 Jun 14 21:33 text-base
drwxr-xr-x 6 root root 1024 Jun 14 21:33 tmp
drwxr-xr-x 2 root root 1024 Jun 14 21:33 wcprops
nogitsune boot # svn add .keep System* config* kernel* grub boot
A .keep
A System.map-2.6.17-25Aug2006-2300
A config-2.6.17-25Aug2006-2300
A (bin) kernel-2.6.17-25Aug2006-2300
A grub
A grub/menu.lst
A (bin) grub/splash.xpm.gz
A grub/grub.conf.sample
A (bin) grub/e2fs_stage1_5
A (bin) grub/fat_stage1_5
A (bin) grub/ffs_stage1_5
A (bin) grub/iso9660_stage1_5
A (bin) grub/jfs_stage1_5
A (bin) grub/minix_stage1_5
A (bin) grub/reiserfs_stage1_5
A (bin) grub/stage1
A (bin) grub/stage2
A (bin) grub/stage2_eltorito
A (bin) grub/ufs2_stage1_5
A (bin) grub/vstafs_stage1_5
A (bin) grub/xfs_stage1_5
A grub/grub.conf
A boot
nogitsune boot # svn status
? boot
? lost+found
A .
A grub
A grub/grub.conf
A grub/stage1
A grub/stage2
A grub/e2fs_stage1_5
A grub/xfs_stage1_5
A grub/vstafs_stage1_5
A grub/fat_stage1_5
A grub/grub.conf.sample
A grub/menu.lst
A grub/ffs_stage1_5
A grub/stage2_eltorito
A grub/iso9660_stage1_5
A grub/ufs2_stage1_5
A grub/jfs_stage1_5
A grub/reiserfs_stage1_5
A grub/minix_stage1_5
A grub/splash.xpm.gz
A .keep
A System.map-2.6.17-25Aug2006-2300
A kernel-2.6.17-25Aug2006-2300
A config-2.6.17-25Aug2006-2300
nogitsune boot # cd /
nogitsune / # svn commit -m "Initial snapshot of /boot"
Adding boot
Adding boot/.keep
Adding boot/System.map-2.6.17-25Aug2006-2300
Adding boot/boot
Adding boot/config-2.6.17-25Aug2006-2300
Adding boot/grub
Adding (bin) boot/grub/e2fs_stage1_5
Adding (bin) boot/grub/fat_stage1_5
Adding (bin) boot/grub/ffs_stage1_5
Adding boot/grub/grub.conf
Adding boot/grub/grub.conf.sample
Adding (bin) boot/grub/iso9660_stage1_5
Adding (bin) boot/grub/jfs_stage1_5
Adding boot/grub/menu.lst
Adding (bin) boot/grub/minix_stage1_5
Adding (bin) boot/grub/reiserfs_stage1_5
Adding (bin) boot/grub/splash.xpm.gz
Adding (bin) boot/grub/stage1
Adding (bin) boot/grub/stage2
Adding (bin) boot/grub/stage2_eltorito
Adding (bin) boot/grub/ufs2_stage1_5
Adding (bin) boot/grub/vstafs_stage1_5
Adding (bin) boot/grub/xfs_stage1_5
Adding (bin) boot/kernel-2.6.17-25Aug2006-2300
Transmitting file data ......................
Committed revision 1.
nogitsune / #


Most of that should be self explanatory. You can see that I use "svn add -N boot" from the / (root) directory to add the /boot directory, then I move into the /boot folder and issue a selective "svn add". Pay attention to the "-N" option which prevents the add from recursing down through subdirectories. You should also take care to only add files that you control as an administrator to the svn repository (avoid adding things like "lost+found" or "._cfg*" files).

If you want to version control something that is 3 levels deep, you need to "svn add -N foldername" for each level in the tree until you get deep enough to add the file. It might be possible to do it faster in one command, but I'm still learning SubVersion.

For the second example, I'm going to add everything in /etc to SubVersion.

# cd /
# svn add -N etc
# cd etc
# svn add *
# svn commit -m "Initial snapshot of /etc"


That's the basics. For the third example, I'll show how to add custom scripts stored in /usr/local/sbin.

# cd /
# svn add -N usr ; cd usr
# svn add -N local ; cd local
# svn add -N sbin ; cd sbin
# svn add *
# cd /
# svn commit -m "Initial snapshot of /usr/local/sbin"


Step #4 - Creating a cron job to backup your SubVersion repositories

On my systems, I create a folder called /backup which is a separate set of spindles that I mount for quick backups. Under that folder, I create a sub-folder called "subversion".

# ls -l /backup/subversion
total 96
-rw-r--r-- 1 root root 20 Nov 30 2005 dev.svnadmin.dump.2005.11.gz
-rw-r--r-- 1 root root 20 Dec 31 02:00 dev.svnadmin.dump.2005.12.gz
-rw-r--r-- 1 root root 20 Jan 31 02:00 dev.svnadmin.dump.2006.01.gz
-rw-r--r-- 1 root root 20 Feb 28 02:00 dev.svnadmin.dump.2006.02.gz
-rw-r--r-- 1 root root 20 Mar 31 02:00 dev.svnadmin.dump.2006.03.gz
-rw-r--r-- 1 root root 20 Apr 30 02:00 dev.svnadmin.dump.2006.04.gz
-rw-r--r-- 1 root root 20 May 31 02:00 dev.svnadmin.dump.2006.05.gz
-rw-r--r-- 1 root root 20 Jun 14 02:00 dev.svnadmin.dump.2006.06.gz
-rw-r--r-- 1 root root 20 Nov 30 2005 photo.svnadmin.dump.2005.11.gz
-rw-r--r-- 1 root root 20 Dec 31 02:00 photo.svnadmin.dump.2005.12.gz
-rw-r--r-- 1 root root 20 Jan 31 02:00 photo.svnadmin.dump.2006.01.gz
-rw-r--r-- 1 root root 20 Feb 28 02:00 photo.svnadmin.dump.2006.02.gz
-rw-r--r-- 1 root root 20 Mar 31 02:00 photo.svnadmin.dump.2006.03.gz
-rw-r--r-- 1 root root 20 Apr 30 02:00 photo.svnadmin.dump.2006.04.gz
-rw-r--r-- 1 root root 20 May 31 02:00 photo.svnadmin.dump.2006.05.gz
-rw-r--r-- 1 root root 20 Jun 14 02:00 photo.svnadmin.dump.2006.06.gz
-rw-r--r-- 1 root root 20 Nov 30 2005 web.svnadmin.dump.2005.11.gz
-rw-r--r-- 1 root root 20 Dec 31 02:00 web.svnadmin.dump.2005.12.gz
-rw-r--r-- 1 root root 20 Jan 31 02:00 web.svnadmin.dump.2006.01.gz
-rw-r--r-- 1 root root 20 Feb 28 02:00 web.svnadmin.dump.2006.02.gz
-rw-r--r-- 1 root root 20 Mar 31 02:00 web.svnadmin.dump.2006.03.gz
-rw-r--r-- 1 root root 20 Apr 30 02:00 web.svnadmin.dump.2006.04.gz
-rw-r--r-- 1 root root 20 May 31 02:00 web.svnadmin.dump.2006.05.gz
-rw-r--r-- 1 root root 20 Jun 14 02:00 web.svnadmin.dump.2006.06.gz


As you can see from my backup folder, I have 3 repositories being backed up (dev, photo, web) and I rotate to a new backup filename every month. It's not ideal because a bad backup could cause me to lose up to 30 days of work, but it meets my needs. More risk-adverse admins may want to switch to a new backup file on a daily basis.

To create this backup, I use the following script:

nogitsune / # cd /usr/local/sbin
nogitsune sbin # ls -l svndaily.sh
-rwxr-xr-x 1 root root 646 Nov 27 2005 svndaily.sh
nogitsune sbin # cat svndaily.sh
#!/bin/sh
# backup subversion repositories to /backup/subversion/filename.year.month.gz
# notice the use of the backtick character (`) instead of single-quote character (')
# overwrites the backup file every day

BACKUPDATE=`date +%Y.%m`
#echo $BACKUPDATE

# svnadmin dump /var/svn/reponame | gzip -c > /backup/subversion/reponame.svnadmin.dump.${BACKUPDATE}.gz
svnadmin dump /var/svn/dev | gzip -c > /backup/subversion/dev.svnadmin.dump.${BACKUPDATE}.gz
svnadmin dump /var/svn/photo | gzip -c > /backup/subversion/photo.svnadmin.dump.${BACKUPDATE}.gz
svnadmin dump /var/svn/web | gzip -c > /backup/subversion/web.svnadmin.dump.${BACKUPDATE}.gz

nogitsune sbin #


Note: Each of the "svnadmin dump" lines should be all on one line and not split across two lines.

As far as I know, svndump is adequate to the task of backing up SubVersion repositories.

No comments: