When writing system integration tests it often happens that I want to mount some tmpfses over directories like /etc/postgresql/ or /home, and run the whole script with an unshared mount namespace so that (1) it does not interfere with the real system, and (2) is guaranteed to clean up after itself (unmounting etc.) after it ends in any possible way (including SIGKILL, which breaks usual cleanup methods like “trap”, “finally”, “def tearDown()”, “atexit()” and so on).
In gvfs’ and postgresql-common’s tests, which both have been around for a while, I prepare a set of shell commands in a variable and pipe that into
unshare -m sh, but that has some major problems: It doesn’t scale well to large programs, looks rather ugly, breaks syntax highlighting in editors, and it destroys the real stdin, so you cannot e. g. call a “bash -i” in your test for interactively debugging a failed test.
I just changed postgresql-common’s test runner to use unshare/tmpfses as well, and needed a better approach. What I eventually figured out preserves stdin, $0, and $@, and still looks like a normal script (i. e. not just a single big string). It still looks a bit hackish, but I can live with that:
#!/bin/sh set -e # call ourselves through unshare in a way that keeps normal stdin, $0, and CLI args unshare -uim sh -- -c "`tail -n +7 $0`" "$0" "$@" exit $? # unshared program starts here set -e echo "args: $@" echo "mounting tmpfs" mount -n -t tmpfs tmpfs /etc grep /etc /proc/mounts echo "done"
As Unix/Linux’ shebang parsing is rather limited, I didn’t find a way to do something like
#!/usr/bin/env unshare -m sh
If anyone knows a trick which avoids the “tail -n +7″ hack and having to pay attention to passing around “$@”, I’d appreciate a comment how to simplify this.Read more