diff -Nru autopkgtest-2.8.1/debian/changelog autopkgtest-2.9/debian/changelog --- autopkgtest-2.8.1/debian/changelog 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/debian/changelog 2014-02-27 10:23:46.000000000 +0000 @@ -1,3 +1,58 @@ +autopkgtest (2.9) unstable; urgency=low + + New features: + * Add "downtmp-host=" testbed capability, for runners which can set up + a shared directory between host and testbed. This is used by copyupdown to + more efficiently copy files back and forth, and in many/most cases, don't + explicitly copy anything at all. + * Introduce a new testbed capability "downtmp-shared-fifo" that declares + that testbed and the host can use a FIFO in the shared downtmp for + communication. Use this to generalize live test output to all runners + which declare that, not just -null. + * Convert null, chroot, schroot, lxc, and qemu runners to use a shared + downtmp dir. Note that qemu does not support FIFO (through the 9p file + system), so that one does not get live test output. + * Don't define $TMPDIR for tests any more. Having $TMPDIR in the shared dir + isn't efficient and even breaks some tests (as they might not be able to + put sockets etc. into it), and it's an artificial difference to the + default system behaviour of just using /tmp. The documentation already was + changed a long time ago to only document $ADTTMP as a scratch directory + for tests. + * Add test restrictions "isolation-container" and "isolation-machine", and + corresponding testbed capabilities. Tests can use that to declare that + they want to start services or open ports (i. e. a simple chroot/schroot + is insufficient) or access hardware, reboot, and interact the kernel + (where even a container is insufficient), and will be skipped instead of + failing when they run in a virtualization server which does not provide + enough isolation. + * Add manpage for adt-build-lxc script. + * Ship adt-build-lxc script and its manpage in the package. + + Documentation updates: + * doc/README.package-tests: Some stylistic updates, and add section headers. + * adt-virt-lxc.1: Point to, and show how to use adt-build-lxc. + * Add doc/README.running-tests: Overview of how to run adt-run, most common + scenarios, and available virtualization servers with their pros/cons. + + Bug fixes: + * tests: Copy tests/home/ into a temporary dir and set $HOME to that, to + avoid cluttering the source dir. + * Don't produce empty stderr/stdout files with --output-dir. (LP: #1282866) + * adt-buildvm-ubuntu-cloud: Install linux-generic, to get the full kernel + (needed for e. g. udisks2 to get scsi_debug). + * tests: Add a broken symlink to testpkg, to ensure that copying trees + around gets along with those. + * adt-run: Always use fakeroot with adt-virt-qemu, to work around chown + failing on 9p. + * xen/initscript: Always source /lib/lsb/init-functions. (Thanks lintian) + * tests/adt-run: Increase timeout in NullRunner.test_timeout_long_build to + avoid failure on slow architectures. + * adt-build-lxc: Properly wait for finished container boot instead of a + static 10 second sleep. + * adt-build-lxc: Only flush the cache when updating an existing container. + + -- Martin Pitt Thu, 27 Feb 2014 11:23:32 +0100 + autopkgtest (2.8.1) unstable; urgency=medium * Use the install timeout instead of the short one for diff -Nru autopkgtest-2.8.1/doc/README.package-tests autopkgtest-2.9/doc/README.package-tests --- autopkgtest-2.8.1/doc/README.package-tests 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/doc/README.package-tests 2014-02-27 10:23:46.000000000 +0000 @@ -1,11 +1,11 @@ - AUTOPKGTEST - DEFINING TESTS FOR PACKAGES - ========================================= +AUTOPKGTEST - DEFINING TESTS FOR PACKAGES +========================================= This document describes how the autopkgtest tester core (the program `adt-run') interprets and executes tests found in source packages. - - +Overview +-------- The source package provides a test metadata file debian/tests/control. This is a file containing zero or more RFC822-style stanzas, along these lines: @@ -42,7 +42,9 @@ Tests must declare all applicable Restrictions - see below. -The fields which may appear in the RFC822-style stanza are: +Control fields +-------------- +The fields which may appear in debian/tests/control stanzas are: Tests: [ ...] @@ -108,9 +110,8 @@ Any unknown fields will cause the whole stanza to be skipped. - -The defined Restrictions are: - +Defined restrictions +-------------------- rw-build-tree The test(s) needs write access to the built source tree (so it may @@ -148,21 +149,39 @@ Output to stderr is not considered a failure. This is useful for tests which write e. g. lots of logging to stderr. + isolation-container + + The test wants to start services or open network TCP ports. This + commonly fails in a simple chroot/schroot, so tests need to be run + in their own container (e. g. adt-virt-lxc) or their own machine/VM + (e. g. adt-virt-qemu or adt-virt-null). When running the test in a + virtualization server which does not provide this (like + adt-virt-schroot) it will be skipped. + + isolation-machine + + The test wants to interact with the kernel, reboot the machine, or + other things which fail in a simple schroot and even a container. + Those tests need to be run in their own machine/VM (e. g. + adt-virt-qemu or adt-virt-null). When running the test in a + virtualization server which does not provide this it will be + skipped. +Defined features +---------------- There are no currently defined Features. Source package header --------------------- - -To allow test execution environments to discover packages which provide tests, -their source packages should have a `Testsuite:` header containing -`autopkgtest` (which is currently the only defined value). Multiple values get -comma separated, as usual in control files. +To allow test execution environments to discover packages which provide +tests, their source packages should have a `Testsuite:` header +containing `autopkgtest` (which is currently the only defined value). +Multiple values get comma separated, as usual in control files. This tag can be set manually in debian/control by adding XS-Testsuite: autopkgtest -in the `Source:` paragraph. Future versions of dpkg-source might add this +in the `Source:' paragraph. Future versions of dpkg-source might add this automatically when a debian/tests/control file is present. diff -Nru autopkgtest-2.8.1/doc/README.running-tests autopkgtest-2.9/doc/README.running-tests --- autopkgtest-2.8.1/doc/README.running-tests 1970-01-01 00:00:00.000000000 +0000 +++ autopkgtest-2.9/doc/README.running-tests 2014-02-27 10:23:46.000000000 +0000 @@ -0,0 +1,187 @@ +AUTOPKGTEST - RUNNING TESTS +=========================== + +This document gives an overview how to run tests with autopkgtest. It +does not cover each detail, please consult the individual manpages like +adt-run(1), adt-virt-schroot(1), etc. for all available options. + +Ingredients +----------- +To run a test you need the following: + + - A source package which defines tests in debian/tests/. See + README.package-tests for how to define them. There are plenty of + existing package tests in Debian/Ubuntu which you can use as examples + and inspiration, just look for a source package with a "Testsuite: + autopkgtest" header, or the automatic test running services in Debian + (http://ci.debian.net/) and Ubuntu (e. g. for Ubuntu 14.04 LTS: + https://jenkins.qa.ubuntu.com/view/Trusty/view/AutoPkgTest/) + + - A location for the source/tests: This can be a local source tree, a + local .dsc, or "download with apt-get source". + + - Optionally some pre-built binary packages which should be tested. + + - A virtualization server: this creates the environment in which the + test runs. Depending on how intrusive the test is this can provide + various degrees of isolation, from "run on my local system" (fastest, + but unsafe) to "run in a temporary virtual machine" (slowest, but + highest possible isolation). + +These are described in detail below. + +The "adt-run" program is the main program to run tests which gets all +these ingredients as arguments, in the following form: + + adt-run [options] --- [] + +Specifying tests and packages +----------------------------- +All possible options are explained in the adt-run(1) manpage. This +section shows the most common scenarios, with using "mysrc" as source +package name. Note that specifying the virtualization server and its +options is handled in the following section, and it is independent of +specifying tests and packages, so it is merely abbreviated as + here. + + - Run tests from the source package in the distribution. This calls + "apt-get source mysrc" in the virt-server, thus will use whichever + distribution/release that /etc/apt/sources.list configures: + + adt-run mysrc --- + + - Run tests from a local unbuilt source tree, using the binary packages + from the distribution. This is useful if you are creating or fixing + tests, but the actual binary packages are fine: + + adt-run -B --unbuilt-tree=packages/mysrc --- + + Note that you can abbreviate --unbuilt-tree= with just the directory + with two(!) trailing slashes, i. e. + + adt-run -B packages/mysrc// --- + + - Run tests from a local built source tree, using the binary packages + from the distribution: + + adt-run -B --built-tree=packages/mysrc --- + + Note that you can abbreviate --built-tree= with just the directory + with one(!) trailing slash, i. e. + + adt-run -B packages/mysrc/ --- + + Built vs. unbuilt only makes a difference for tests which declare the + "build-needed" restriction (see README.package-tests), in which case + --built-tree avoids having to re-build the source in the virt-server. + + - Build a local source package in the virt-server, then run its tests + against the built binaries. This is useful if you need to fix a bug + in the actual packages to make the tests succeed: + + adt-run packages/mysrc// --- + + - Same as above, but with specifying a built source package instead of + a source tree: + + adt-run packages/mysrc_*.dsc --- + + - Test new built binaries with a new source and avoid rebuilding them + in virt-server. This is useful if you e. g. update a package to a new + version and now want to check whether its tests still succeed: + + adt-run -B packages/*.deb packages/mysrc_*.dsc --- + + +Output +------ +Unless you specify some options, adt-run just writes the logging, test +outputs, and test results to stdout/stderr and exits with code 0 on +success, or some non-zero code if there were skipped or failed tests or +problems with the virt-server. (See adt-run(1) for defined codes). + +For getting output files you have three choices: + + - If you just want the "testname: PASS/FAIL" results, use + --summary-file=/path/to/summary.txt. + + - If you want the complete output of adt-run in a file, use + -l /path/to/test.log (or the long option --log-file) + + - If you want the log file, the individual test stdout and stderr + output, and built binaries (if any) in a directory, use + -o /path/to/test-output/ (or the long option --output-dir). + +You can also combine these. + +Virtualization server +--------------------- +These virtualization servers are available: + + - adt-run ... --- schroot schroot-name + + Run tests in the specified schroot. You can use mk-sbuild(1) to + conveniently create schroots, and run this as normal user if you + configured schroot accordingly. + + This server is the fastest available that provides "proper" file + system isolation and revert, but it does not provide enough isolation + for tests that need to start services, reconfigure the network, or + open TCP ports which are already open at the host. If your test does + not need to do these things this is the recommended server, as + schroots are also useful for other tasks like building packages with + sbuild. + + See adt-virt-schroot(1) manpage. + + - adt-run ... --- lxc container-name + + Run tests in the specified LXC container. Containers provide full + service and network isolation, but tests or packages cannot change + the kernel or hardware configuration. If your test does not need + that, this is the recommended server as it is faster than QEMU and + works on all Linux architectures. + + container-name will be cloned or be called with a temporary overlay + file system if you specify the -e (--ephemeral) option, thus it will + never be modified and you can run several tests in parallel safely. + Unless your test or architecture or RAM availability doesn't work + with overlayfs, using -e is highly recommended for better + performance. + + If your user can get root privileges with sudo, you can call adt-run + as your normal user and specify -s (--sudo) so that the container can + be started as root. + + See adt-virt-lxc(1) manpage. This also explains how to build + containers. + + - adt-run ... --- qemu path/to/image + + Run tests with QEMU/KVM using the specified image. The image will be + run with a temporary overlay file system, thus it will never be + modified and you can run several tests in parallel safely. + + If your test needs a full machine including kernel/hardware access, + this is the recommended runner; it provides complete system + isolation, revert and breaks-testbed capabilities. But it is also the + one with the biggest overhead and only works well on architectures + with KVM acceleration (i. e. mostly x86). + + See adt-virt-qemu(1) manpage. This also explains how to build + suitable images, and the requirements of the guest. + + - adt-run ... --- null + + This does not do any virtualization, but runs tests straight on the + host. Beware that this will leave some clutter on your system + (installed test or build dependency packages, configuration changes + that the tests might make, etc.). It is not able to run tests with + the "breaks-testbed" restriction. See adt-virt-null(1) manpage. + + - adt-run ... --- chroot /path/to/chroot + + Run tests in the specified chroot. You need to call adt-run as root + for this. There is no automatic cleanup or revert for the chroot, so + unless you can provide this by some other means, don't use this. + diff -Nru autopkgtest-2.8.1/doc/README.virtualisation-server autopkgtest-2.9/doc/README.virtualisation-server --- autopkgtest-2.8.1/doc/README.virtualisation-server 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/doc/README.virtualisation-server 2014-02-27 10:23:46.000000000 +0000 @@ -79,6 +79,26 @@ (with distinct s) may be advertised in which case more than one such user is available. + + downtmp-host= + If the testbed has the ability of setting up a shared directory + with the host, this gives the host directory path of the "downtmp" + directory as reported by the virt server's hook_downtmp() function. + + + downtmp-shared-fifo + The shared downtmp-host directory supports FIFOs between the host and + testbed. Only relevant if downtmp-host is set. + + + isolation-container + The testbed runs in a Linux cgroup/container (nspawn, LXC, docker, + etc.) and thus tests have full control over starting services and + opening network ports. + + + isolation-machine + The testbed runs in a complete (virtual or real) machine on its own + (qemu or bare metal) and thus tests have full control over starting + services, opening network ports, interacting with the kernel (e. g. + modprobe), rebooting, and accessing hardware. + * Command open diff -Nru autopkgtest-2.8.1/lib/VirtSubproc.py autopkgtest-2.9/lib/VirtSubproc.py --- autopkgtest-2.8.1/lib/VirtSubproc.py 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/lib/VirtSubproc.py 2014-02-27 10:23:46.000000000 +0000 @@ -34,6 +34,7 @@ import re import pipes import socket +import shutil debuglevel = None progname = "" @@ -253,7 +254,7 @@ def downtmp_mktemp(): d = execute('mktemp -d /tmp/adt-run.XXXXXX', [], downp=True, outp=True) - execute('chmod 755', [d], downp=True) + execute('chmod 1777', [d], downp=True) return d @@ -390,6 +391,93 @@ return [str(status)] +def get_downtmp_host(): + '''Return host directory of the testbed's downtmp dir, if supported''' + + for cap in caller.hook_capabilities(): + if cap.startswith('downtmp-host='): + return cap.split('=', 1)[1] + return None + + +def copyup_shareddir(tb, host, is_dir, downtmp_host): + debug('copyup_shareddir: tb %s, host %s, is_dir %s, downtmp_host %s' % ( + tb, host, is_dir, downtmp_host)) + + timeout_start(copy_timeout) + try: + tb_tmp = None + if tb.startswith(downtmp): + # translate into host path + tb = downtmp_host + tb[len(downtmp):] + else: + tb_tmp = os.path.join(downtmp, os.path.basename(host)) + debug('copyup_shareddir: tb path %s is not already in downtmp, ' + 'copying to %s' % (tb, tb_tmp)) + cp = subprocess.Popen(downs['auxverb'] + ['cp', '-a', tb, tb_tmp], + preexec_fn=preexecfn) + cp.communicate() + if cp.returncode != 0: + bomb('copyup_shareddir: cp -a exited with code %i' % + cp.returncode) + # translate into host path + tb = os.path.join(downtmp_host, os.path.basename(host)) + + if tb == host: + tb_tmp = None + else: + debug('copyup_shareddir: tb(host) %s is not already at ' + 'destination %s, copying' % (tb, host)) + if is_dir: + shutil.copytree(tb, host, symlinks=True) + else: + shutil.copy(tb, host) + + if tb_tmp: + (is_dir and shutil.rmtree or os.unlink)(tb_tmp) + finally: + timeout_stop() + + +def copydown_shareddir(host, tb, is_dir, downtmp_host): + debug('copydown_shareddir: host %s, tb %s, is_dir %s, downtmp_host %s' % ( + host, tb, is_dir, downtmp_host)) + + host = os.path.normpath(host) + tb = os.path.normpath(tb) + + timeout_start(copy_timeout) + try: + host_tmp = None + if host.startswith(downtmp_host): + # translate into tb path + host = downtmp + host[len(downtmp_host):] + else: + host_tmp = os.path.join(downtmp_host, os.path.basename(tb)) + if is_dir: + shutil.rmtree(host_tmp, ignore_errors=True) + shutil.copytree(host, host_tmp, symlinks=True) + else: + shutil.copy(host, host_tmp) + # translate into tb path + host = os.path.join(downtmp, os.path.basename(tb)) + + if host == tb: + host_tmp = None + else: + cp = subprocess.Popen(downs['auxverb'] + ['cp', '-a', host, tb], + preexec_fn=preexecfn) + cp.communicate() + if cp.returncode != 0: + bomb('copydown_shareddir: cp -a exited with code %i' % + cp.returncode) + + if host_tmp: + (is_dir and shutil.rmtree or os.unlink)(host_tmp) + finally: + timeout_stop() + + def copyupdown(c, ce, upp): cmdnumargs(c, ce, 2) if not downtmp: @@ -406,6 +494,17 @@ if dirsp != (sd[1][-1] == '/'): bomb("% paths must agree about directoryness" " (presence or absence of trailing /)" % wh) + + # if we have a shared directory, we just need to copy it from/to there; in + # most cases, it's testbed end is already in the downtmp dir + downtmp_host = get_downtmp_host() + if downtmp_host: + if upp: + copyup_shareddir(sd[0], sd[1], dirsp, downtmp_host) + else: + copydown_shareddir(sd[0], sd[1], dirsp, downtmp_host) + return + deststdout = devnull_read srcstdin = devnull_read remfileq = pipes.quote(sd[iremote]) diff -Nru autopkgtest-2.8.1/Makefile autopkgtest-2.9/Makefile --- autopkgtest-2.8.1/Makefile 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/Makefile 2014-02-27 10:23:46.000000000 +0000 @@ -29,6 +29,7 @@ virt-subproc/adt-virt-lxc \ virt-subproc/adt-virt-qemu \ tools/adt-buildvm-ubuntu-cloud \ + tools/adt-build-lxc \ runner/adt-run pythonfiles = lib/VirtSubproc.py diff -Nru autopkgtest-2.8.1/runner/adt-run autopkgtest-2.9/runner/adt-run --- autopkgtest-2.8.1/runner/adt-run 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/runner/adt-run 2014-02-27 10:23:46.000000000 +0000 @@ -60,6 +60,7 @@ tmp = None # pathstring on host testbed = None # Testbed +shared_downtmp = None # testbed's downtmp on the host, if supported opts = None # optparse options errorcode = 0 # exit status that we are going to use timeouts = {'short': 100, 'copy': 300, 'install': 3000, 'test': 10000, 'build': 100000} @@ -552,6 +553,8 @@ if opts.gainroot is None: if (opts.user or + # FIXME: work around chown root not working in qemu's 9p mount + 'qemu' in opts.vserver[0] or 'root-on-testbed' not in testbed.caps): opts.gainroot = 'fakeroot' build_essential += ['fakeroot'] @@ -615,13 +618,18 @@ if self.scratch is not None: return pl = self.commandr('open') - self.caps = self.commandr('capabilities') - debug('testbed capabilities: %s' % self.caps, 1) self._opened(pl) def _opened(self, pl): + global shared_downtmp + self.scratch = pl[0] self.deps_processed = [] + self.caps = self.commandr('capabilities') + debug('testbed capabilities: %s' % self.caps, 1) + for c in self.caps: + if c.startswith('downtmp-host='): + shared_downtmp = c.split('=', 1)[1] self._auxverbscript_make() self.run_setup_commands() @@ -673,6 +681,8 @@ 'made to testbed apt configuration, exit code %d' % rc) def close(self): + global shared_downtmp + self._debug('close, scratch=%s' % self.scratch, 1) if self.ec_auxverbscript: os.unlink(self.ec_auxverbscript) @@ -683,6 +693,7 @@ if self.sp is None: return self.command('close') + shared_downtmp = None def run_setup_commands(self): if not opts.setup_commands and not opts.apt_pocket: @@ -946,8 +957,10 @@ class TempTestbedPath(TestbedPath): - '''Represent a file in the hosts'/testbed's temporary directories''' + '''Represent a file in the hosts'/testbed's temporary directories + These are only guaranteed to exit within one testbed run. + ''' def __init__(self, testbed, name, is_dir=False, autoclean=True): '''Create a temporary TestbedPath object. @@ -964,7 +977,13 @@ --output-dir which are useful for reporting results, like test stdout/err, log files, and binaries. ''' - TestbedPath.__init__(self, testbed, os.path.join(tmp, name), + # if the testbed supports a shared downtmp, use that to avoid + # unnecessary copying, unless we want to permanently keep the file + if shared_downtmp and (not opts.output_dir or autoclean): + host = shared_downtmp + else: + host = tmp + TestbedPath.__init__(self, testbed, os.path.join(host, name), os.path.join(testbed.scratch, name), is_dir) self.autoclean = autoclean @@ -1028,6 +1047,20 @@ pass +class Restriction_isolation_container(Restriction): + + def __init__(self, rname, base): + if 'isolation-container' not in testbed.caps and 'isolation-machine' not in testbed.caps: + raise Unsupported(-1, 'Test requires container-level isolation but testbed does not provide that') + + +class Restriction_isolation_machine(Restriction): + + def __init__(self, rname, base): + if 'isolation-machine' not in testbed.caps: + raise Unsupported(-1, 'Test requires machine-level isolation but testbed does not provide that') + + class Restriction_breaks_testbed(Restriction): def __init__(self, rname, base): @@ -1194,8 +1227,6 @@ testbed.prepare(dn) def run(self, tree): - self._debug('[----------------------------------------') - # ensure our tests are in the testbed tree.copydown() @@ -1227,10 +1258,6 @@ else: test_argv = [tb_test_path] - test_tmpdir = testtmp + '/tmpdir' - xenv.append('TMPDIR=%s' % test_tmpdir) - script += 'chmod 1777 "%s"\n' % test_tmpdir - test_adttmp = testtmp + '/adttmp' xenv.append('ADTTMP=%s' % test_adttmp) @@ -1240,30 +1267,34 @@ rc = testbed.execute('mktmpdir-' + self.what, ['sh', '-ec', script, 'x', tree.tb, - testtmp, test_tmpdir, test_adttmp, + testtmp, test_adttmp, ]) if rc: bomb("could not create test tmp dirs in `%s', exit code %d" % (testtmp, rc)) - # With the NULL runner the test and the controller both run on the same - # file system and can route stdout/stderr through a common FIFO to - # watch them in realtime. FIXME: Make this work for schroot/lxc/etc. if - # they use a bind-mounted /tmp. + # If we have a shared downtmp and can create a FIFO in it, + # route stdout/stderr through a common FIFO to watch them in realtime. # Note that we can't use the coreutils tee with pipes, as it aborts on # the first read error. - #show_realtime = os.path.isdir(testtmp) and os.access(testtmp, os.W_OK) - show_realtime = opts.vserver[0].endswith('-null') + show_realtime = False + if shared_downtmp and 'downtmp-shared-fifo' in testbed.caps: + fifo_out = TempTestbedPath(testbed, 'test_stdout') + fifo_err = TempTestbedPath(testbed, 'test_stderr') + os.mkfifo(fifo_out.host) + os.mkfifo(fifo_err.host) + show_realtime = True + debug('shared downtmp with FIFO supported, showing test output in realtime', 1) + else: + debug('shared_downtmp with FIFO not supported', 1) + if show_realtime: - fifo_out = os.path.join(testtmp, 'test_stdout') - fifo_err = os.path.join(testtmp, 'test_stderr') - os.mkfifo(fifo_out) - os.mkfifo(fifo_err) - debug('teeing to stdout: %s, stderr: %s' % (fifo_out, fifo_err), 2) + self._debug('[----------------------------------------') + debug('teeing to stdout: %s, stderr: %s' % (fifo_out.host, fifo_err.host), 2) tee_out = os.fork() if tee_out == 0: - fd_in = os.open(fifo_out, os.O_RDONLY) - fd_out = os.open(so.tb, os.O_CREAT | os.O_WRONLY) + fd_in = os.open(fifo_out.host, os.O_RDONLY) + fd_out = os.open(so.host, os.O_CREAT | os.O_WRONLY) while True: block = os.read(fd_in, 1024) @@ -1275,8 +1306,8 @@ tee_err = os.fork() if tee_err == 0: - fd_in = os.open(fifo_err, os.O_RDONLY) - fd_out = os.open(se.tb, os.O_CREAT | os.O_WRONLY) + fd_in = os.open(fifo_err.host, os.O_RDONLY) + fd_out = os.open(se.host, os.O_CREAT | os.O_WRONLY) while True: block = os.read(fd_in, 1024) @@ -1288,7 +1319,8 @@ try: rc = testbed.execute('test-' + self.what, test_argv, - so=fifo_out, se=fifo_err, cwd=tree.tb, + so=fifo_out.tb, se=fifo_err.tb, + cwd=tree.tb, xenv=xenv, kind='test') debug('testbed executing test finished with exit status %i' % rc, 1) finally: @@ -1298,23 +1330,21 @@ os.kill(tee_err, signal.SIGTERM) os.waitpid(tee_out, 0) os.waitpid(tee_err, 0) + self._debug('----------------------------------------]') - os.unlink(fifo_out) - os.unlink(fifo_err) - - # if testtmp is not accessible, we cannot show realtime output and need + # without a shared downtmp we cannot show realtime output and need # to show stdout afterwards else: + self._debug('running...') rc = testbed.execute('test-' + self.what, test_argv, so=so.tb, se=se.tb, cwd=tree.tb, xenv=xenv, kind='test') debug('testbed executing test finished with exit status %i' % rc, 1) - self._debug('----------------------------------------]') + # copy stdout/err files to host + so.copyup() + se.copyup() - # copy stdout/err files to host - so.copyup() - se.copyup() se_size = os.path.getsize(se.host) # avoid mixing up stdout (from report) and stderr (from _debug) in output @@ -1338,14 +1368,21 @@ sys.stderr.flush() time.sleep(0.1) - if os.path.getsize(so.host) != 0 and not show_realtime: - self._debug(' - - - - - - - - - - stdout - - - - - - - - - -') - log_file(so.host) - - if se_size != 0 and (not show_realtime or 'allow-stderr' - not in self.restriction_names): - self._debug(' - - - - - - - - - - stderr - - - - - - - - - -') - log_file(se.host) + if os.path.getsize(so.host) != 0: + if not show_realtime: + self._debug(' - - - - - - - - - - stdout - - - - - - - - - -') + log_file(so.host) + else: + # don't produce empty -stdout files in --output-dir + os.unlink(so.host) + + if se_size != 0: + if not show_realtime or 'allow-stderr' not in self.restriction_names: + self._debug(' - - - - - - - - - - stderr - - - - - - - - - -') + log_file(se.host) + else: + # don't produce empty -stderr files in --output-dir + os.unlink(se.host) # copy artifacts to host, if we have --output-dir if opts.output_dir: @@ -1531,7 +1568,12 @@ class Binaries: def __init__(self, tb): - self.dir = TempTestbedPath(testbed, 'binaries', is_dir=True, autoclean=False) + # the binary dir must exist across tb reopenings, so don't use a + # TempTestbedPath + self.dir = TestbedPath(tb, + os.path.join(tmp, 'binaries'), + os.path.join(tb.scratch, 'binaries'), + is_dir=True) os.mkdir(self.dir.host) # clean up an empty binaries output dir atexit.register(lambda: os.path.exists(self.dir.host) and ( @@ -1661,7 +1703,10 @@ if rc: bomb('apt-ftparchive or signature failed, code %d' % rc) - # copy binaries directory to testbed + # copy binaries directory to testbed; self.dir.tb might have changed + # since last time due to a reset, so update it + self.dir.tb = os.path.join(testbed.scratch, 'binaries') + testbed.execute('clean-binaries', ['rm', '-rf', self.dir.tb]) self.dir.copydown() aptkey_out = TempTestbedPath(testbed, 'apt-key.out') diff -Nru autopkgtest-2.8.1/runner/adt-run.1 autopkgtest-2.9/runner/adt-run.1 --- autopkgtest-2.8.1/runner/adt-run.1 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/runner/adt-run.1 2014-02-27 10:23:46.000000000 +0000 @@ -25,6 +25,9 @@ supplied) in the top level directory of the built source tree, on the host. The package should be installed on the testbed. +See /usr/share/doc/autopkgtest/README.running\-tests.gz for an +introduction about how to use adt\-run. + .SH PROCESSING INSTRUCTIONS .TP @@ -335,7 +338,9 @@ 20 other unexpected failures including bad usage .SH SEE ALSO -\fBadt-virt-chroot\fR(1), \fBadt-virt-xenlvm\fR(1) +\fB/usr/share/doc/autopkgtest/README.running-tests.gz\fR +.br +\fB/usr/share/doc/autopkgtest/README.package-tests.gz\fR .SH BUGS This tool still lacks some important features and is not very diff -Nru autopkgtest-2.8.1/tests/adt-run autopkgtest-2.9/tests/adt-run --- autopkgtest-2.8.1/tests/adt-run 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/tests/adt-run 2014-02-27 10:23:46.000000000 +0000 @@ -47,7 +47,6 @@ super(AdtTestCase, self).__init__(*args, **kwargs) self.adt_run_path = os.path.join(root_dir, 'run-from-checkout') self.virt = 'adt-virt-' + virt - os.environ['HOME'] = os.path.join(test_dir, 'home') def setUp(self): self.workdir = tempfile.mkdtemp() @@ -55,6 +54,10 @@ self.cwd = os.getcwd() self.addCleanup(os.chdir, self.cwd) + temp_home = os.path.join(self.workdir, 'home') + shutil.copytree(os.path.join(test_dir, 'home'), temp_home) + os.environ['HOME'] = temp_home + def build_src(self, test_control, test_scripts): '''Create source package tree with given tests. @@ -205,6 +208,21 @@ with open(summary, encoding='UTF-8') as f: self.assertEqual(f.read(), 'ubtree0t-se FAIL status: 0, stderr: fancy ‴ʎɔuɐɟ″!\n') + def test_isolation(self): + '''isolation restrictions''' + + p = self.build_src('Tests: ic\nDepends:\nRestrictions: isolation-container\n\n' + 'Tests: im\nDepends:\nRestrictions: isolation-machine\n', + {'ic': '#!/bin/sh\necho container ok', + 'im': '#!/bin/sh\necho machine ok'}) + + (code, out, err) = self.adt_run(['-B', '--built-tree=' + p]) + self.assertEqual(code, 0, out + err) + self.assertRegex(out, 'tree0t-ic\s+PASS', out) + self.assertRegex(out, 'tree0t-im\s+PASS', out) + self.assertIn('container ok\n', out) + self.assertIn('machine ok\n', out) + @unittest.skipIf(os.getuid() > 0, 'null runner tests need to run as root') @@ -346,7 +364,7 @@ self.addCleanup(os.umask, prev_mask) p = self.build_src('Tests: t\nDepends: coreutils\nRestrictions: needs-root\n', {'t': '''#!/bin/sh -e -echo hello > $TMPDIR/rootowned.txt +echo hello > ${TMPDIR:=/tmp}/rootowned.txt su -s /bin/sh -c "echo hello > $TMPDIR/world.txt" nobody if su -s /bin/sh -c "echo break > $TMPDIR/rootowned.txt" nobody 2>/dev/null; then exit 1 @@ -368,7 +386,7 @@ p = self.build_src('Tests: t\nDepends:\n', {'t': '''#!/bin/sh -e - echo world > $TMPDIR/hello.txt + echo world > ${TMPDIR:=/tmp}/hello.txt cat $TMPDIR/hello.txt stat -c %U . stat -c %U debian @@ -424,14 +442,14 @@ # check outdir test stdout/err with open(os.path.join(outdir, 'ubtree0t-sP-stdout')) as f: self.assertEqual(f.read(), 'static script OK\n') - with open(os.path.join(outdir, 'ubtree0t-sP-stderr')) as f: - self.assertEqual(f.read(), '') + self.assertFalse(os.path.exists( + os.path.join(outdir, 'ubtree0t-sP-stderr'))) with open(os.path.join(outdir, 'ubtree0t-bP-stdout')) as f: self.assertEqual(f.read(), 'built script OK\n') - with open(os.path.join(outdir, 'ubtree0t-bP-stderr')) as f: - self.assertEqual(f.read(), '') - with open(os.path.join(outdir, 'ubtree0t-sF-stdout')) as f: - self.assertEqual(f.read(), '') + self.assertFalse(os.path.exists( + os.path.join(outdir, 'ubtree0t-bP-stderr'))) + self.assertFalse(os.path.exists( + os.path.join(outdir, 'ubtree0t-sF-stdout'))) with open(os.path.join(outdir, 'ubtree0t-sF-stderr')) as f: self.assertEqual(f.read(), 'kaputt\n') @@ -508,7 +526,7 @@ subprocess.check_call(['sed', '-i', '/^build:/ s/$/\\n\\tsleep 4/', os.path.join(p, 'Makefile')]) - (code, out, err) = self.adt_run(['--timeout-test=1', '--timeout-build=15', + (code, out, err) = self.adt_run(['--timeout-test=1', '--timeout-build=30', '--timeout-install=3', '-B', '--unbuilt-tree', p]) # should build package @@ -744,7 +762,7 @@ self.assertRegex(out, 'tree0t-pass\s+PASS', out) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nI am fine\n') + self.assertRegex(out, '^I am fine\n') # should not have any test stderr self.assertNotIn('stderr', err) @@ -765,7 +783,7 @@ self.assertRegex(out, 'tree0t-nz\s+FAIL non-zero exit status 7') # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nI am sick\n') + self.assertIn('\nI am sick\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -819,8 +837,8 @@ self.assertRegex(out, 'tree0t-pass\s+PASS') # should show test stdout/err - self.assertRegex(err, 'stdout [ -]+\nbabble\n') - self.assertRegex(err, 'stderr [ -]+\nI am fine\n') + self.assertRegex(out, '^babble\n') + self.assertIn('\nI am fine\n', err) def test_fancy_deps(self): '''wrapped and versioned test dependencies''' @@ -891,8 +909,8 @@ self.assertIn('processing dependency coreutils', log) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nstatic script OK\n') - self.assertRegex(log, 'stdout [ -]+\nstatic script OK\n') + self.assertIn('\nstatic script OK\n', out) + self.assertIn('\nstatic script OK\n', log) # should show test stderr self.assertRegex(err, 'stderr [ -]+\nI am sick\n') self.assertRegex(log, 'stderr [ -]+\nI am sick\n') @@ -968,8 +986,8 @@ self.assertRegex(out, 'ubtree0t-se\s+FAIL status: 0, stderr: fancy ‴ʎɔuɐɟ″!\n') # should show test stdout/stderr - self.assertRegex(err, 'stdout [ -]+\n‘a♩’\n') - self.assertRegex(err, 'stderr [ -]+\nfancy ‴ʎɔuɐɟ″!\n') + self.assertRegex(out, '‘a♩’\n') + self.assertRegex(err, '\nfancy ‴ʎɔuɐɟ″!\n') with open(summary, encoding='UTF-8') as f: self.assertEqual(f.read(), 'ubtree0t-se FAIL status: 0, stderr: fancy ‴ʎɔuɐɟ″!\n') @@ -988,7 +1006,7 @@ self.assertRegex(out, 'apt0t-pass\s+PASS', out) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nI am fine\n') + self.assertIn('\nI am fine\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1014,7 +1032,7 @@ self.assertRegex(out, 'tree0t-pass\s+PASS', out) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nsetup_success\n') + self.assertRegex(out, '^setup_success\n') # should not have any test stderr self.assertNotIn('stderr', err) @@ -1044,7 +1062,7 @@ self.assertRegex(out, 'tree0t-pass\s+PASS', out) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nsetup_success\n') + self.assertRegex(out, '^setup_success\n') # should not have any test stderr self.assertNotIn('stderr', err) @@ -1080,6 +1098,20 @@ deb-src http://my.distro/ fluffy-proposed main non-free ''') + def test_isolation(self): + '''isolation restrictions''' + + p = self.build_src('Tests: ic\nDepends:\nRestrictions: isolation-container\n\n' + 'Tests: im\nDepends:\nRestrictions: isolation-machine\n', + {'ic': '#!/bin/sh\necho container ok', + 'im': '#!/bin/sh\necho machine ok'}) + + (code, out, err) = self.adt_run(['-B', '--built-tree=' + p], [self.chroot]) + self.assertEqual(code, 2, out + err) + self.assertRegex(out, 'ic\s+SKIP .*container', out) + self.assertRegex(out, 'im\s+SKIP .*machine', out) + self.assertNotIn('ok', out) + @unittest.skipUnless('ADT_TEST_SCHROOT' in os.environ, 'Set $ADT_TEST_SCHROOT to an existing schroot') @@ -1104,8 +1136,8 @@ self.assertRegex(out, 'tree0t-p2\s+PASS', out) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nI am fine\n') - self.assertRegex(err, 'stdout [ -]+\nI am also fine\n') + self.assertIn('\nI am fine\n', out) + self.assertIn('\nI am also fine\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1124,7 +1156,7 @@ self.assertRegex(out, 'tree0t-nz\s+FAIL non-zero exit status 7') # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nI am sick\n') + self.assertRegex(out, '^I am sick\n') # should not have any test stderr self.assertNotIn('stderr', err) @@ -1159,7 +1191,7 @@ self.assertEqual(code, 4) self.assertRegex(out, 'tree0t-senz\s+FAIL non-zero exit status 7') - # should show test stderr separately (no real-time output for chroot) + # should show test stderr separately self.assertRegex(err, 'stderr [ -]+\nI am sick\n') self.assertNotIn('stdout', err) @@ -1175,8 +1207,10 @@ self.assertRegex(out, 'tree0t-pass\s+PASS', out) # should show test stdout/err - self.assertRegex(err, 'stdout [ -]+\nbabble\n') - self.assertRegex(err, 'stderr [ -]+\nI am fine\n') + self.assertRegex(out, '^babble\n') + self.assertIn('\nI am fine\n', err) + # stderr test output should be inline + self.assertNotIn('stderr', err) def test_tree_build_needed_success(self): '''source tree, build-needed restriction, test success''' @@ -1194,7 +1228,7 @@ self.assertIn('dh build', err) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nGOOD\n') + self.assertIn('\nGOOD\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1247,7 +1281,7 @@ self.assertEqual(code, 0, err) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nGOOD\n') + self.assertIn('\nGOOD\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1281,7 +1315,7 @@ self.assertEqual(code, 0, err) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nbuilt script OK\n') + self.assertIn('\nbuilt script OK\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1320,7 +1354,7 @@ self.assertEqual(code, 0, out + err) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nbuilt script OK\nbuilt script OK') + self.assertIn('\nbuilt script OK\nbuilt script OK', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1354,8 +1388,8 @@ self.assertIn('dh build', log) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nbuilt script OK\n') - self.assertRegex(log, 'stdout [ -]+\nbuilt script OK\n') + self.assertIn('\nbuilt script OK\n', out) + self.assertIn('\nbuilt script OK\n', log) # should show test stderr self.assertRegex(err, 'stderr [ -]+\nI am sick\n') self.assertRegex(log, 'stderr [ -]+\nI am sick\n') @@ -1379,7 +1413,7 @@ self.assertRegex(out, 'ubtree0t-broken\s+FAIL status: 0, stderr: kaputt', out) # should show test stdout and stderr - self.assertRegex(err, 'stdout [ -]+\nbuilt script OK\n') + self.assertIn('\nbuilt script OK\n', out) self.assertRegex(err, 'stderr [ -]+\nkaputt', err) # should build package @@ -1388,10 +1422,10 @@ # check outdir test stdout/err with open(os.path.join(outdir, 'ubtree0t-ok-stdout')) as f: self.assertEqual(f.read(), 'built script OK\n') - with open(os.path.join(outdir, 'ubtree0t-ok-stderr')) as f: - self.assertEqual(f.read(), '') - with open(os.path.join(outdir, 'ubtree0t-broken-stdout')) as f: - self.assertEqual(f.read(), '') + self.assertFalse(os.path.exists( + os.path.join(outdir, 'ubtree0t-ok-stderr'))) + self.assertFalse(os.path.exists( + os.path.join(outdir, 'ubtree0t-broken-stdout'))) with open(os.path.join(outdir, 'ubtree0t-broken-stderr')) as f: self.assertEqual(f.read(), 'kaputt\n') @@ -1417,7 +1451,7 @@ p = self.build_src('Tests: t\nDepends: aspell-doc\n', {'t': '''#!/bin/sh -e - echo world > $TMPDIR/hello.txt + echo world > ${TMPDIR:=/tmp}/hello.txt cat $TMPDIR/hello.txt whoami'''}) @@ -1435,14 +1469,14 @@ self.assertRegex(out, 'tree0t-t\s+PASS', out) # has output from cat and whoami - self.assertRegex(err, 'stdout [ -]+\nworld\nnobody\n') + self.assertIn('\nworld\nnobody\n', out) def test_user_needs_root(self): '''--user option with needs-root restriction''' p = self.build_src('Tests: t\nDepends: aspell-doc\nRestrictions: needs-root', {'t': '''#!/bin/sh -e - echo world > $TMPDIR/hello.txt + echo world > ${TMPDIR:=/tmp}/hello.txt cat $TMPDIR/hello.txt whoami'''}) @@ -1460,7 +1494,7 @@ self.assertRegex(out, 'tree0t-t\s+PASS', out) # has output from cat and whoami - self.assertRegex(err, 'stdout [ -]+\nworld\nroot\n') + self.assertIn('\nworld\nroot\n', out) def test_breaks_testbed(self): '''breaks-testbed restriction''' @@ -1474,6 +1508,21 @@ self.assertEqual(code, 2) self.assertRegex(out, 'rambo\s+SKIP Test breaks testbed') + def test_isolation(self): + '''isolation restrictions''' + + p = self.build_src('Tests: ic\nDepends:\nRestrictions: isolation-container\n\n' + 'Tests: im\nDepends:\nRestrictions: isolation-machine\n', + {'ic': '#!/bin/sh\necho container ok', + 'im': '#!/bin/sh\necho machine ok'}) + + (code, out, err) = self.adt_run(['-B', '--built-tree=' + p], + [self.schroot_name]) + self.assertEqual(code, 2, out + err) + self.assertRegex(out, 'ic\s+SKIP .*container', out) + self.assertRegex(out, 'im\s+SKIP .*machine', out) + self.assertNotIn('ok', out) + @unittest.skipUnless('ADT_TEST_LXC' in os.environ, 'Set $ADT_TEST_LXC to an existing container') @@ -1499,7 +1548,7 @@ self.assertRegex(out, 'tree0t-pass\s+PASS', out) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nI am fine\n') + self.assertIn('\nI am fine\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1518,7 +1567,7 @@ self.assertRegex(out, 'tree0t-nz\s+FAIL non-zero exit status 7') # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nI am sick\n') + self.assertRegex(out, '^I am sick\n') # should not have any test stderr self.assertNotIn('stderr', err) @@ -1550,7 +1599,7 @@ (code, out, err) = self.adt_run(['--no-built-binaries', '--built-tree=' + p], self.virt_args) # test should fail - self.assertEqual(code, 4) + self.assertEqual(code, 4, err) self.assertRegex(out, 'tree0t-senz\s+FAIL non-zero exit status 7') # should show test stderr separately (no real-time output for chroot) @@ -1569,8 +1618,10 @@ self.assertRegex(out, 'tree0t-pass\s+PASS', out) # should show test stdout/err - self.assertRegex(err, 'stdout [ -]+\nbabble\n') - self.assertRegex(err, 'stderr [ -]+\nI am fine\n') + self.assertRegex(out, '^babble\n') + # stderr test output should be inline + self.assertIn('\nI am fine\n', err) + self.assertNotIn('stderr', err) def test_tree_build_needed_success(self): '''source tree, build-needed restriction, test success''' @@ -1588,7 +1639,7 @@ self.assertIn('dh build', err) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nGOOD\n') + self.assertIn('\nGOOD\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1638,7 +1689,7 @@ self.assertEqual(code, 0, err) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nGOOD\n') + self.assertIn('\nGOOD\n', out) # should not have any test stderr self.assertNotIn('stderr', err) @@ -1655,7 +1706,7 @@ self.assertEqual(code, 4, err) self.assertRegex(out, 'tree0t-senz\s+FAIL non-zero exit status 7') - # should show test stderr separately (no real-time output for chroot) + # should show test stderr separately self.assertRegex(err, 'stderr [ -]+\nI am sick\n') self.assertNotIn('stdout', err) @@ -1691,13 +1742,28 @@ self.assertRegex(out, 'tree0t-t2\s+PASS', out) # should show test stdout - self.assertRegex(err, 'stdout [ -]+\nsetupok\nt1ok\n') - self.assertRegex(err, 'stdout [ -]+\nsetupok\nt2ok\n') + self.assertRegex(out, '^setupok\nt1ok\n') + self.assertIn('\nsetupok\nt2ok\n', out) # should not have any test stderr self.assertNotIn('stderr', err) self.assertIn('@@@@@@ test bed setup', err) + def test_isolation(self): + '''isolation restrictions''' + + p = self.build_src('Tests: ic\nDepends:\nRestrictions: isolation-container\n\n' + 'Tests: im\nDepends:\nRestrictions: isolation-machine\n', + {'ic': '#!/bin/sh\necho container ok', + 'im': '#!/bin/sh\necho machine ok'}) + + (code, out, err) = self.adt_run(['-B', '--built-tree=' + p], self.virt_args) + self.assertEqual(code, 2, out + err) + self.assertRegex(out, 'tree0t-ic\s+PASS', out) + self.assertRegex(out, 'im\s+SKIP .*machine', out) + self.assertIn('container ok\n', out) + self.assertNotIn('machine ok', out) + @unittest.skipUnless('ADT_TEST_QEMU' in os.environ, 'Set $ADT_TEST_QEMU to an existing autopkgtest QEMU image') @@ -1788,6 +1854,21 @@ self.assertIn('@@@@@@ test bed setup', err) + def test_isolation(self): + '''isolation restrictions''' + + p = self.build_src('Tests: ic\nDepends:\nRestrictions: isolation-container\n\n' + 'Tests: im\nDepends:\nRestrictions: isolation-machine\n', + {'ic': '#!/bin/sh\necho container ok', + 'im': '#!/bin/sh\necho machine ok'}) + + (code, out, err) = self.adt_run(['-B', '--built-tree=' + p], [self.image]) + self.assertEqual(code, 0, out + err) + self.assertRegex(out, 'tree0t-ic\s+PASS', out) + self.assertRegex(out, 'tree0t-im\s+PASS', out) + self.assertRegex(err, 'stdout [ -]+\ncontainer ok\n') + self.assertRegex(err, 'stdout [ -]+\nmachine ok\n') + if __name__ == '__main__': unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) diff -Nru autopkgtest-2.8.1/tests/testpkg/debian/links autopkgtest-2.9/tests/testpkg/debian/links --- autopkgtest-2.8.1/tests/testpkg/debian/links 1970-01-01 00:00:00.000000000 +0000 +++ autopkgtest-2.9/tests/testpkg/debian/links 2014-02-27 10:23:46.000000000 +0000 @@ -0,0 +1 @@ +/non/existing usr/share/testpkg/broken_link diff -Nru autopkgtest-2.8.1/tools/adt-build-lxc autopkgtest-2.9/tools/adt-build-lxc --- autopkgtest-2.8.1/tools/adt-build-lxc 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/tools/adt-build-lxc 2014-02-27 10:23:46.000000000 +0000 @@ -36,22 +36,38 @@ # fall back for older LXC option name LXCDIR=`lxc-config lxc.lxcpath` || LXCDIR=`lxc-config lxcpath` +LXC_ARGS="-t $DISTRO -- -r $RELEASE" + setup() { # add deb-src sed -i '/^deb/ { p; s/^deb/deb-src/}' $LXCDIR/$1/rootfs/etc/apt/sources.list lxc-start -d -n $1 - sleep 10 + + # wait until it is booted: lxc-attach works and we get a numeric runlevel + timeout=60 + while [ $timeout -ge 0 ]; do + timeout=$((timeout - 1)) + sleep 1 + O=`lxc-attach -n $1 runlevel 2>/dev/null` || continue + [ "$O" = "${O%[0-9]}" ] || break + done + [ $timeout -ge 0 ] || { + echo "Timed out waiting for container to boot" >&2 + lxc-stop -k -n $1 || true + lxc-destroy -n $1 || true + exit 1 + } lxc-attach -n $1 apt-get update lxc-stop -n $1 } if [ ! -e $LXCDIR/$NAME ]; then # first-time run: just create the container - APT_PROXY=apt lxc-create -n $NAME -t $DISTRO -- -r $RELEASE -F + APT_PROXY=apt lxc-create -n $NAME $LXC_ARGS setup $NAME else # create a new rootfs in a temp container - APT_PROXY=apt lxc-create -n ${NAME}.new -t $DISTRO -- -r $RELEASE -F + APT_PROXY=apt lxc-create -n ${NAME}.new $LXC_ARGS -F setup ${NAME}.new # replace the original rootfs rsync -aHAXS --numeric-ids --delete $LXCDIR/${NAME}.new/rootfs $LXCDIR/${NAME}/rootfs diff -Nru autopkgtest-2.8.1/tools/adt-build-lxc.1 autopkgtest-2.9/tools/adt-build-lxc.1 --- autopkgtest-2.8.1/tools/adt-build-lxc.1 1970-01-01 00:00:00.000000000 +0000 +++ autopkgtest-2.9/tools/adt-build-lxc.1 2014-02-27 10:23:46.000000000 +0000 @@ -0,0 +1,64 @@ +.TH adt-build-lxc 1 2014 "Linux Programmer's Manual" +.SH NAME +adt-build-lxc \- Create or update autopkgtest container for adt\-virt-lxc + +.SH SYNOPSYS +.B adt-build-lxc +.I distribution release + +.SH DESCRIPTION +.B adt-build-lxc +creates or updates an LXC container \fBadt-\fIrelease\fR which is suitable for +autopkgtest's LXC runner \fBadt-virt-lxc\fR(1). + +It calls +.B lxc-create +with the +.I distribution +template (which can currently be +.B debian +or +.B ubuntu) and +.BI -r release +to build a debootstrap-like container, then enables +.B deb-src +apt sources, and runs +.B apt-get update\fR. + +If the container already exists, it updates it as unintrusively as possible by +first creating a new temporary container \fBadt-\fIrelease\fB.new\fR, and then +rsyncing its root file system back to the existing \fBadt-\fIrelease\fR. Note +that this could cause some side-effects and breakage if you have running +containers with ephemeral overlays (i. e. using the +.B --ephemeral +option), but it does work in general. This update process does not interfere at +all if you use +.B adt-virt-lxc +with cloning. + +Note that you need to call this as root, unless you set up LXC to allow +per-user containers. But user containers will not work with many or even most +autopkgtests. + +.SH EXAMPLES + +# adt-build-lxc debian sid + +.PP +$ sudo adt-build-lxc ubuntu trusty + +.SH SEE ALSO +\fBadt\-virt-lxc\fR(1), +\fBadt\-run\fR(1), +\fBlxc-create\fR(1), +\fB/usr/share/doc/autopkgtest/\fR. + +.SH AUTHORS AND COPYRIGHT +.B adt-build-lxc +was written by Martin Pitt + +This manpage is part of autopkgtest, a tool for testing Debian binary +packages. autopkgtest is Copyright (C) 2006-2014 Canonical Ltd and others. + +See \fB/usr/share/doc/autopkgtest/CREDITS\fR for the list of +contributors and full copying conditions. diff -Nru autopkgtest-2.8.1/tools/adt-buildvm-ubuntu-cloud autopkgtest-2.9/tools/adt-buildvm-ubuntu-cloud --- autopkgtest-2.8.1/tools/adt-buildvm-ubuntu-cloud 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/tools/adt-buildvm-ubuntu-cloud 2014-02-27 10:23:46.000000000 +0000 @@ -163,6 +163,9 @@ apt_upgrade: %(upgr)s +packages: + - linux-generic + write_files: - path: /etc/init.d/autopkgtest permissions: '0755' diff -Nru autopkgtest-2.8.1/virt-subproc/adt-virt-chroot autopkgtest-2.9/virt-subproc/adt-virt-chroot --- autopkgtest-2.8.1/virt-subproc/adt-virt-chroot 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/virt-subproc/adt-virt-chroot 2014-02-27 10:23:46.000000000 +0000 @@ -33,10 +33,13 @@ sys.path.insert(1, our_base) import VirtSubproc -capabilities = [] +capabilities = ['downtmp-shared-fifo'] +chroot_dir = None def parse_args(): + global chroot_dir + usage = "%prog [] =|/path/to/chroot" parser = OptionParser(usage=usage) @@ -65,7 +68,9 @@ if down[0] == 'chroot': VirtSubproc.downkind = 'auxverb' + chroot_dir = chroot_arg elif down[0] == 'dchroot': + # FIXME: figure out chroot_dir VirtSubproc.downkind = 'shstring' else: print >> sys.stderr, 'down[0]=%s' % str(down[0]) @@ -85,11 +90,17 @@ def hook_downtmp(): - return VirtSubproc.downtmp_mktemp() + global capabilities + d = VirtSubproc.downtmp_mktemp() + if chroot_dir: + capabilities.append('downtmp-host=%s/%s' % (chroot_dir, d)) + return d def hook_cleanup(): + global capabilities VirtSubproc.downtmp_remove() + capabilities = [c for c in capabilities if not c.startswith('downtmp-host')] def hook_forked_inchild(): diff -Nru autopkgtest-2.8.1/virt-subproc/adt-virt-lxc autopkgtest-2.9/virt-subproc/adt-virt-lxc --- autopkgtest-2.8.1/virt-subproc/adt-virt-lxc 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/virt-subproc/adt-virt-lxc 2014-02-27 10:23:46.000000000 +0000 @@ -32,6 +32,8 @@ import random import subprocess import time +import tempfile +import shutil try: our_base = os.environ['AUTOPKGTEST_BASE'] + '/lib' @@ -41,12 +43,16 @@ import VirtSubproc +capabilities = ['revert', 'revert-full-system', 'root-on-testbed', + 'downtmp-shared-fifo', 'isolation-container'] + ephemeral = False eatmydata = False use_sudo = False lxc_template = None lxc_container_name = None normal_user = None +shared_dir = None def parse_args(): @@ -149,7 +155,7 @@ def determine_normal_user(lxc_name): '''Check for a normal user to run tests as.''' - global normal_user + global capabilities, normal_user # get the first UID >= 500 out = subprocess.check_output( @@ -159,19 +165,22 @@ universal_newlines=True).strip() if out: normal_user = out + capabilities.append('suggested-normal-user=' + normal_user) VirtSubproc.debug('determine_normal_user: got user "%s"' % normal_user) else: VirtSubproc.debug('determine_normal_user: no uid >= 500 available') def hook_open(): - global lxc_container_name + global lxc_container_name, shared_dir lxc_container_name = get_available_lxc_container_name() VirtSubproc.debug('using container name %s' % lxc_container_name) if ephemeral: - VirtSubproc.execute(sudoify('lxc-start-ephemeral -n %s -k -d -o') - % lxc_container_name, [lxc_template], + shared_dir = tempfile.mkdtemp(prefix='adt-virt-lxc.shared.') + os.chmod(shared_dir, 0o755) + VirtSubproc.execute(sudoify('lxc-start-ephemeral -n %s -k -d --bdir %s -o') + % (lxc_container_name, shared_dir), [lxc_template], downp=False, outp=True) else: VirtSubproc.execute(sudoify('lxc-clone -n %s -o' % lxc_container_name), @@ -200,19 +209,33 @@ def hook_downtmp(): - downtmp = '/tmp/adt-downtmp' - VirtSubproc.execute('mkdir %s' % downtmp, downp=True) - return downtmp + global capabilities, shared_dir + + if shared_dir: + d = os.path.join(shared_dir, 'downtmp') + # these permissions are ugly, but otherwise we can't clean up files + # written by the testbed when running as user + VirtSubproc.execute('mkdir -m 777 %s' % d, downp=True) + capabilities.append('downtmp-host=' + d) + else: + d = VirtSubproc.downtmp_mktemp() + return d def hook_revert(): - VirtSubproc.downtmp_remove() hook_cleanup() hook_open() def hook_cleanup(): + global capabilities, shared_dir + VirtSubproc.downtmp_remove() + capabilities = [c for c in capabilities if not c.startswith('downtmp-host')] + if shared_dir: + shutil.rmtree(shared_dir) + shared_dir = None + VirtSubproc.execute(sudoify('lxc-stop -n'), [lxc_container_name], downp=False, outp=True) VirtSubproc.execute(sudoify('lxc-destroy -n'), [lxc_container_name], @@ -224,10 +247,7 @@ def hook_capabilities(): - caps = ['revert', 'revert-full-system', 'root-on-testbed'] - if normal_user: - caps.append('suggested-normal-user=' + normal_user) - return caps + return capabilities parse_args() diff -Nru autopkgtest-2.8.1/virt-subproc/adt-virt-lxc.1 autopkgtest-2.9/virt-subproc/adt-virt-lxc.1 --- autopkgtest-2.8.1/virt-subproc/adt-virt-lxc.1 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/virt-subproc/adt-virt-lxc.1 2014-02-27 10:23:46.000000000 +0000 @@ -19,6 +19,10 @@ will be invoked by .BR adt-run . +You can conveniently create a suitable container using the +.BR adt-build-lxc (1) +script. + .SH REQUIREMENTS .B adt-virt-lxc assumes that you have already prepared a suitable Debian based LXC container. @@ -73,43 +77,45 @@ .SH EXAMPLE -Create a suitable container from a Debian or Ubuntu template and call it -\fIadt\fR. E. g. a Debian sid one: +Create a suitable debootstrap-based container for Debian or Ubuntu template, e. +g. a Debian sid one (will be named +.B adt-sid\fR): .RS .EX -lxc-create -t debian -n \fIadt\fR -- -r sid +adt-build-lxc debian sid .EE .RE -Or an Ubuntu one based on the cloud images (faster than the "ubuntu" template -with debootstrap, but much bigger): +Or an Ubuntu one based on the cloud images (faster than adt-build-lxc's +"ubuntu" template with debootstrap, but much bigger): .RS .EX -lxc-create -t ubuntu-cloud -n \fIadt\fR -- -r trusty -d daily +lxc-create -t ubuntu-cloud -n \fIadt-trusty\fR -- -r trusty -d daily .EE .RE -Run tests against \fIhello_2.8\-4.dsc\fR, using the LXC container \fIadt\fR, +Run tests against \fIhello_2.8\-4.dsc\fR, using the LXC container \fIadt-sid\fR, and with an ephemeral overlay for speed: .RS .EX -adt-run \fIhello_2.8\-4.dsc\fR --- adt-virt-lxc --ephemeral \fIadt\fR +adt-run \fIhello_2.8\-4.dsc\fR --- adt-virt-lxc -e \fIadt-sid\fR .EE .RE .SH SEE ALSO \fBadt\-run\fR(1), -\fBadt\-virt-schroot\fR(1), -\fBeatmydata\fR(1), +\fBadt\-build-lxc\fR(1), \fBlxc\-create\fR(1), +\fBeatmydata\fR(1), \fB/usr/share/doc/autopkgtest/\fR. .SH AUTHORS AND COPYRIGHT .B adt-virt-lxc -was written by Robie Basak . +was written by Robie Basak and Martin Pitt +. This manpage is part of autopkgtest, a tool for testing Debian binary packages. autopkgtest is Copyright (C) 2006-2013 Canonical Ltd and others. diff -Nru autopkgtest-2.8.1/virt-subproc/adt-virt-null autopkgtest-2.9/virt-subproc/adt-virt-null --- autopkgtest-2.8.1/virt-subproc/adt-virt-null 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/virt-subproc/adt-virt-null 2014-02-27 10:23:46.000000000 +0000 @@ -33,7 +33,7 @@ sys.path.insert(1, our_base) import VirtSubproc -capabilities = [] +capabilities = ['downtmp-shared-fifo', 'isolation-machine'] def parse_args(): @@ -60,11 +60,18 @@ def hook_downtmp(): - return VirtSubproc.downtmp_mktemp() + global capabilities + + d = VirtSubproc.downtmp_mktemp() + capabilities.append('downtmp-host=' + d) + return d def hook_cleanup(): + global capabilities + VirtSubproc.downtmp_remove() + capabilities = [c for c in capabilities if not c.startswith('downtmp-host')] def hook_forked_inchild(): diff -Nru autopkgtest-2.8.1/virt-subproc/adt-virt-qemu autopkgtest-2.9/virt-subproc/adt-virt-qemu --- autopkgtest-2.8.1/virt-subproc/adt-virt-qemu 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/virt-subproc/adt-virt-qemu 2014-02-27 10:23:46.000000000 +0000 @@ -315,12 +315,13 @@ def hook_downtmp(): - downtmp = '/tmp/adt-downtmp' - VirtSubproc.execute('mkdir -p %s' % downtmp, downp=True) + downtmp = '/autopkgtest/tmp' + VirtSubproc.execute('mkdir %s' % downtmp, downp=True) return downtmp def hook_revert(): + VirtSubproc.downtmp_remove() hook_cleanup() hook_open() @@ -342,7 +343,9 @@ def hook_capabilities(): - caps = ['revert', 'revert-full-system', 'root-on-testbed'] + caps = ['revert', 'revert-full-system', 'root-on-testbed', + 'isolation-machine', + 'downtmp-host=%s' % os.path.join(workdir, 'shared', 'tmp')] if args.user and args.user != 'root': caps.append('suggested-normal-user=' + args.user) return caps diff -Nru autopkgtest-2.8.1/virt-subproc/adt-virt-schroot autopkgtest-2.9/virt-subproc/adt-virt-schroot --- autopkgtest-2.8.1/virt-subproc/adt-virt-schroot 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/virt-subproc/adt-virt-schroot 2014-02-27 10:23:46.000000000 +0000 @@ -36,10 +36,11 @@ sys.path.insert(1, our_base) import VirtSubproc -capabilities = [] +capabilities = ['downtmp-shared-fifo'] schroot = None sessid = None +rootdir = None def pw_uid(exp_name): @@ -117,7 +118,23 @@ def hook_downtmp(): - return VirtSubproc.downtmp_mktemp() + global capabilities + + d = VirtSubproc.downtmp_mktemp() + + # determine mount location + # FIXME: "schroot --location -c sessid" really ought to work, but fails; so + # grep from --all-sessions until this gets fixed + location = VirtSubproc.execute('schroot --location --all-sessions | grep', + [sessid], downp=False, outp=True).strip() + VirtSubproc.debug('location of schroot session: %s' % location) + downtmp_host = '%s/%s' % (location, d) + if os.access(downtmp_host, os.W_OK): + VirtSubproc.debug('%s is writable, registering as downtmp_host' % downtmp_host) + capabilities.append('downtmp-host=' + downtmp_host) + else: + VirtSubproc.debug('%s is not writable, downtmp_host not supported' % downtmp_host) + return d def hook_revert(): @@ -126,9 +143,10 @@ def hook_cleanup(): - global schroot, sessid + global schroot, sessid, capabilities VirtSubproc.downtmp_remove() VirtSubproc.execute('schroot -e -c', [sessid], downp=False) + capabilities = [c for c in capabilities if not c.startswith('downtmp-host')] def hook_forked_inchild(): diff -Nru autopkgtest-2.8.1/xen/initscript autopkgtest-2.9/xen/initscript --- autopkgtest-2.8.1/xen/initscript 2014-02-22 01:15:44.000000000 +0000 +++ autopkgtest-2.9/xen/initscript 2014-02-27 10:23:46.000000000 +0000 @@ -11,14 +11,7 @@ # Short-Description: Prepare firewall tables for autopkgtest Xen guests ### END INIT INFO -lsbif=/lib/lsb/init-functions -if test -e $lsbif; then - . $lsbif -else - log_daemon_msg () { printf "%s: " "$1"; } - log_progress_msg () { printf "%s " "$1"; } - log_end_msg () { echo "done."; } -fi +. /lib/lsb/init-functions if test -f /etc/default/rcS; then . /etc/default/rcS; fi chains='AdtXenIn AdtXenFwd AdtXenIcmp'