It is very handy to have a chroot jail for testing code snippets, packages, releases. This is on your (Debian) dev box, where you may have multiple installed versions of related libs. Before promoting your new code to Test or Staging, it can be a time saver to first test it in a pristine environment that can’t be affected by files outside the ‘jail’. For example, I have several versions of Perl installed on my dev box and several versions of EV around the place. When testing something against EV I want to be sure I haven’t omitted any dependencies and to be sure I’m testing against exactly the version I’m expecting. You should treat the jail as throw-away; keep in mind that you can (and should) delete and build a fresh one whenever the mood takes you. There are many online notes about creating one, but it is still hard to find any that tell you how to get it ironed out easily, eg to avoid confusing sessions that are inside/outside the jail.

Install packages

mkdir -p /opt/jail/mojo
debootstrap wheezy /opt/jail/mojo

(and then wait a few mins while it downloads and installs). Then install locales.

chroot /opt/jail/mojo
dpkg-reconfigure debconf  # setting priority to 'medium'
vi /etc/apt/sources
apt-get update
apt-get install locales less vim rsync
locale-gen en_GB.UTF-8

That last line is to generate any locales that the previous line reported as missing; if you don’t see any such warnings (on the commandline) then none are needed.

Make it clear you are in jail

vi /etc/debian_chroot

giving it the jail name as content, in this example ‘Mojo’.

vi /etc/skel/.bashrc
vi /etc/bash.bashrc

removing all instances of “@\h“, eg by doing “:%s/@\\h//gc” with repeated presses of y

adduser --ingroup users --disabled-password --gecos 'test user,,,' tester
su - tester

and check that your commandline prompt shows the jail name and not the (parent) hostname. If you’re using an xterm you should also see its title change to something similar when you ‘sub user’ to ‘tester’. The remaining step for this section is to do likewise for ‘root’. The simplest way to do that is to copy the xterm lines from /etc/skel/.bashrc so that /root/.bashrc now has

case "$TERM" in
xterm*|rxvt*)
  PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u: \w\a\]$PS1"
  ;;
*)
  ;;
esac

Now open a new xterm and test both user envs

chroot /opt/jail/mojo
su - tester

checking that in both cases the xterm title and the commandline prompt are clearly different from those of the (parent) host.

(The rest of these notes are standard fodder for getting processes to work nicely.)

Mount Parts of Host System

Before installing more packages or running processes, you’ll need to integrate /proc
In /etc/fstab of the host box, I have a section for each jail

/dev/pts /opt/jail/mojo/dev/pts none bind 0 4
proc     /opt/jail/mojo/proc    proc defaults 0 4
sysfs    /opt/jail/mojo/sys     sysfs defaults 0 4

and then mount them manually (again as ‘root’ in the host box)

mount /opt/jail/mojo/dev/pts
mount /opt/jail/mojo/proc
mount /opt/jail/mojo/sys

Fake your mtab

You’ll find that df fails because the chroot has no /etc/mtab. Some people are advocating cat /proc/mounts >/etc/mtab but that is wrong, the partitions inside the chroot are in general completely different to those outside. In my case I give the missing file just one line

rootfs / rootfs rw 0 0

which does all that I want.

Set your timezone

You can manipulate /etc/timezone and /etc/localtime yourself, but the easiest way is to

dpkg-reconfigure tzdata
Advertisements

Test driven development is nothing new, but recently I wanted to try out a stricter version of it in which the granularity is much finer — it became essential that I ran my unit tests for a file every time I hit <save>.

My current dev env is vim + bash + standard perl tests. For each area I’m working on, I define a brief script to declare the files of interest. In the below example I’m working on the Authen.pm plugin module and want the tests in cari_fn_authen.t to run each time the source is saved.

src/test/bin/run_authen:

#!/bin/bash
declare -x   TESTS="src/test/Cari/Fn/Authen/authen_unit.t"
declare -x   SOURCES="lib/Cari/Fn/Authen.pm"
${0/\/run*/\/run_tests.sh}

src/test/bin/run_tests.sh:

#!/bin/bash
declare -r   PROGNAME=${0##*/}
declare -x   PATH=/bin:/usr/bin
declare -i   DEBUG=0
declare      TESTS=${TESTS:?}
declare      SOURCES=${SOURCES:?}
declare      PERL="perl -Imod -Ilib -Isrc/test"

cache=$(mktemp -t ${PROGNAME}.XXXXXX)
function clean_up { rm -f ${cache:-/fictitious_path}; }
trap clean_up SIGHUP SIGINT SIGTERM

declare -i first_time=1
while [ $first_time -gt 0 ] \
        || inotifywait -qqe MOVE_SELF $TESTS $SOURCES; do
    echo -e "\n\n\n"$(date '+%F %R') $TESTS
    for t in "$TESTS"; do
        $PERL -T $t \
            | grep -vF ' skip ' \
            >$cache 2>&1
        result=$(tail -n 1 $cache)
        if [[ $result == 1..+([0-9]) ]]; then
            echo $result
        else
            cat $cache
        fi
    done

    echo -ne \\'033[0;43m'
    $PERL -wc $SOURCES 2>&1 \
        | grep -vF ' syntax OK'
    echo -ne \\'033[0m'
    first_time=0
done

At the beginning the script checks I’ve remembered to declare TESTS and SOURCES. Then it sets up a temporary file for cacheing test output. Once it’s running, the only way to stop it is to interrupt it, so clean_up is required when interrupted.

Since we’re monitoring vim saves, the loop guard has inotifywait look for the MOVE_SELF signal. Upon each iteration, it starts by spitting out a timestamp and the tests being invoked. (That’s just to give me confidence the monitoring is active.) Then it runs each test, putting its output into the cache, checking the result (last line), and then spitting out the full output only if there was a problem.

The next part just checks the syntax of the source files. If a problem is detected is spits out the errors and warnings with a yucky muddy yellow background. [See here for other colours.]

The end result is I can do quite major refactoring and rewriting at a quicker pace, knowing that any problems I create will be spat onto the neighbouring screen.