[Piuparts-commits] [SCM] piuparts git repository branch, master, updated. eda668423fa87898c59d1075118693714aa5a053

Holger Levsen holger at layer-acht.org
Fri Dec 23 10:26:52 UTC 2011


The following commit has been merged in the master branch:
commit 1ee6adf8dc50b09c6e8537d26751d4386a22e4c5
Merge: 1a94ef1f4be33b6d8112f885f2ab1fb14ea03086 3e4c3ea7c531244a5a06d1b86c81866ab7a2ea2c
Author: Holger Levsen <holger at layer-acht.org>
Date:   Sun Nov 27 11:05:34 2011 +0100

    Merge branch 'feature/522918' into develop
    
      [ Scott Schaefer ]
      * piuparty.py: kill (via SIGTERM, then if that fails, via SIGKILL),
        leftover processes. (Closes: #522918)

diff --combined debian/changelog
index c73b7cb,b9ce8b0..5e548f5
--- a/debian/changelog
+++ b/debian/changelog
@@@ -3,80 -3,20 +3,85 @@@ piuparts (0.42) UNRELEASED; urgency=lo
    [ Holger Levsen ]
    * piuparts.py:
      - add to self.ignored_files: /etc/blkid.tab (Closes: #638831)
 +    - add to self.ignored_patterns: /var/lib/apt/lists/.*
      - apply patch by Stefano Rivera to properly install and remove
        logrotate. Thanks Stefano! (Closes: #638832)
 +    - apply patch by Gregor Herrmann to fix --minimize. (Closes: #648423)
    * Remove Debian.NEWS entry about source in git. (Closes: #640121)
 +  * piuparts.py, piuparts-report.py, ChangeLog: Expand tabs to spaces.
 +  * Remove whitespaces from whitespace-only lines.
+   * piuparts-slave.py: Replace deprecated os.popen2 with subprocess.Popen,
+     thanks to Scott Schaefer and Evgeni Golov for their patches. 
+     (Closes: #640646)
 -  * piuparty.py: kill (via SIGTERM, then if that fails, via SIGKILL), leftover
 -    processes. (Closes: #522918) Thanks to Scott Schaefer for the patch.
  
    [ Mika Pflüger ]
 -  * piuparts-analyze.py: Rewrite to use python-debianbts to analyze if bugs
 -    are filed already.
 +  * piuparts-analyze.py:
 +    - Rewrite to use python-debianbts to analyze if bugs are filed already.
 +    - The BTS only tracks source versions, so remove binNMU-part from
 +      package versions when comparing with versions from the BTS.
 +    - Reduce noise in the output by only printing one action/advise per
 +      package.
 +    - Fix extraction of package versions from bug reports. Thanks to
 +      Andreas Beckmann for catching and solving the error.
    * debian/control: Add python-apt and python-debianbts to piuparts depends.
  
 +  [ Scott Schaefer ]
 +  * debian/copyright: Make it compliant with DEP-5.
 +  * piuparts-slave.py: 
 +    - Replace deprecated os.popen2 with subprocess.Popen. (Closes: #640646)
 +    - Add some more logging.
++  * piuparty.py: kill (via SIGTERM, then if that fails, via SIGKILL), leftover
++    processes. (Closes: #522918)
 +
 +  [ Andreas Beckmann ]
 +  * *.py: Add vim modeline.
 +  * piuparts.py:
 +    - Add unittests for misbehaving symlinks.
 +    - Fix resolving absolute symlinks of intermediate directory components,
 +      i.e. /var/run -> /run while checking /etc/motd -> /var/run/motd.
 +      Solves about 30000 false positives of
 +      'Broken symlinks: /etc/motd -> /var/run/motd'.  (Closes: #648784)
 +    - When running commands in the chroot, redirect stdin from /dev/null.
 +    - Stop using Popen.communicate() as it may run out of memory.
 +    - Terminate commands producing more than 2 MB of output.
 +    - Add missing post_install_* hook to install_packages_by_name().
 +      (Closes: #628077)
 +    - Create /etc/dpkg/dpkg.cfg.d/ if missing inside the chroot (e.g. on
 +      lenny).  (Closes: #647752)
 +    - Remove logrotate and its dependencies only once.
 +    - Only run 'apt-get update' after updating the sources.list.
 +    - Only run 'apt-get clean' before creating tarballs or saving meta data.
 +    - Do the same checks for running processes and broken symlinks in all
 +      tests.
 +    - Use consistent variable names for package lists.
 +    - Compute the changes in restore_selections().
 +    - Check for settings.scriptsdir inside run_scripts().
 +    - Consistently use chroot.relative() to build filenames inside the chroot.
 +    - Create piupart's apt config in the chroot as /etc/apt.conf.d/piuparts
 +      instead of /etc/apt.conf in order to allow overriding the settings from
 +      custom scripts by just dropping new config bits in e.g.
 +      /etc/apt/apt.conf.d/piuparts-foobar.
 +      apt.conf.d is supported in lenny, possibly earlier releases, too.
 +  * piuparts-slave.py:
 +    - Fix triggering tarball recreation.
 +    - Check tarball age regularily.
 +    - Log tarball creation in *.tgz.log.
 +  * piuparts-report.py: 
 +    - state-*.html: Sort package lists by name.
 +    - source/?/*.html: Sort binary packages by name.
 +    - Dependency lists: resolve virtual packages and link to a real package.
 +    - Update list of error states to be highlighted.
 +
 +  [ Dave Steele ]
 +  * piuparts-slave.py: make Section.run() report the number of packages
 +    processed and use this to decide whether a slave should sleep.
 +    (Closes: #649967)
 +
 +  [ Stefano Rivera ]
 +  * piuparts.py: use eatmydata by default, add option --no-eatmydata. (This
 +    was discussed in #633033.)
 +  * debian/control: Add eatmydata to recommends.
 +
   -- Holger Levsen <holger at debian.org>  Sun, 28 Aug 2011 09:50:12 +0200
  
  piuparts (0.41) unstable; urgency=low
@@@ -109,7 -49,7 +114,7 @@@
        environment variable, the latter overwriting the former (if present) 
        - Thanks to Scott Schaefer for the patch. (Closes: #632046)
      - new option "--no-install-purge-test" to only do upgrade tests
 -      - Thanks to Andreas Bergmann for the patch (Closes: #588482)
 +      - Thanks to Andreas Beckmann for the patch (Closes: #588482)
      - run dpkg with --force-unsafe-io by default and introduce new option
        "--dpkg-noforce-unsafe-io" to disable this feature. (Closes: #633033)
        Thanks to Scott once more!
diff --combined piuparts.py
index 7e30dee,efb0aec..96e8bfe
--- a/piuparts.py
+++ b/piuparts.py
@@@ -49,6 -49,7 +49,7 @@@ import subproces
  import unittest
  import urllib
  import uuid
+ from signal import signal, SIGTERM, SIGKILL
  
  try:
      from debian import deb822
@@@ -58,13 -59,13 +59,13 @@@ except ImportError
  class Defaults:
  
      """Default settings which depend on flavor of Debian.
 -    
 +
      Some settings, such as the default mirror and distribution, depend on
      which flavor of Debian we run under: Debian itself, or a derived
      distribution such as Ubuntu. This class abstracts away the defaults
      so that the rest of the code can just refer to the values defined
      herein.
 -    
 +
      """
  
      def get_components(self):
@@@ -72,10 -73,10 +73,10 @@@
  
      def get_mirror(self):
          """Return default mirror."""
 -    
 +
      def get_distribution(self):
          """Return default distribution."""
 -        
 +
  
  class DebianDefaults(Defaults):
  
@@@ -104,13 -105,13 +105,13 @@@ class UbuntuDefaults(Defaults)
  class DefaultsFactory:
  
      """Instantiate the right defaults class."""
 -    
 +
      def guess_flavor(self):
          p = subprocess.Popen(["lsb_release", "-i", "-s"], 
                               stdout=subprocess.PIPE)
          stdout, stderr = p.communicate()
          return stdout.strip().lower()
 -    
 +
      def new_defaults(self):
          if not settings.defaults:
              settings.defaults = self.guess_flavor()
@@@ -126,7 -127,7 +127,7 @@@
  class Settings:
  
      """Global settings for this program."""
 -    
 +
      def __init__(self):
          self.defaults = None
          self.tmpdir = None
@@@ -253,7 -254,6 +254,7 @@@
              "/usr/lib/python2\../site-packages/debconf.py[co]",
              "/var/backups/.*",
              "/var/cache/man/(/.*)?",
 +            "/var/lib/apt/lists/.*",
              "/var/lib/cvs(/.*)?",
              "/var/lib/dpkg/alternatives",
              "/var/lib/dpkg/triggers/.*",
@@@ -325,7 -325,7 +326,7 @@@ def setup_logging(log_level, log_file_n
      handler.setFormatter(formatter)
      logger.addHandler(handler)
      HANDLERS.append(handler)
 -    
 +
      if log_file_name:
          handler = logging.FileHandler(log_file_name)
          handler.setFormatter(formatter)
@@@ -381,30 -381,9 +382,30 @@@ def run(command, ignore_errors=False)
      env["LC_ALL"] = "C"
      env["LANGUAGES"] = ""
      env["PIUPARTS_OBJECTS"] = ' '.join(str(vobject) for vobject in settings.testobjects )
 -    p = subprocess.Popen(command, env=env, stdin=subprocess.PIPE, 
 +    devnull = open('/dev/null', 'r')
 +    p = subprocess.Popen(command, env=env, stdin=devnull, 
                           stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 -    (output, _) = p.communicate()
 +    output = ""
 +    excessive_output = False
 +    while p.poll() is None:
 +        """Read 64 KB chunks, but depending on the output buffering behavior
 +        of the command we may get less even if more output is coming later.
 +        Abort after reading 2 MB."""
 +        output += p.stdout.read(1 << 16)
 +        if (len(output) > (1 << 21)):
 +            excessive_output = True
 +            logging.error("Terminating command due to excessive output")
 +            p.terminate()
 +            for i in range(10):
 +                time.sleep(0.5)
 +                if p.poll() is not None:
 +                    break
 +            else:
 +                logging.error("Killing command due to excessive output")
 +                p.kill()
 +            p.wait()
 +            break
 +    devnull.close()
  
      if output:
          dump("\n" + indent_string(output.rstrip("\n")))
@@@ -417,8 -396,6 +418,8 @@@
      else:
          logging.error("Command failed (status=%d): %s\n%s" % 
                (p.returncode, repr(command), indent_string(output)))
 +        if excessive_output:
 +            logging.error("Command was terminated while producing excessive output")
          panic()
      return p.returncode, output
  
@@@ -465,7 -442,7 +466,7 @@@ def remove_files(filenames)
  
  def make_metapackage(name, depends, conflicts):
      """Return the path to a .deb created just for satisfying dependencies
 -    
 +
      Caller is responsible for removing the temporary directory containing the
      .deb when finished.
      """
@@@ -497,78 -474,32 +498,78 @@@
      return os.path.join(tmpdir, name) + '.deb'
  
  
 -def is_broken_symlink(root, dirpath, filename):
 -    """Is symlink dirpath+filename broken?
 -    
 -    When resolving the symlink, pretend (similar to chroot) that root is
 -    the root of the filesystem. Note that this does NOT work completely
 -    correctly if the symlink target contains .. path components. This is
 -    good enough for my immediate purposes, but nowhere near good enough
 -    for anything that needs to be secure. For that, use chroot and have
 -    the kernel resolve symlinks instead.
 +def split_path(pathname):
 +    parts = []
 +    while pathname:
 +        (head, tail) = os.path.split(pathname)
 +        #print "split '%s' => '%s' + '%s'" % (pathname, head, tail)
 +        if tail:
 +            parts.append(tail)
 +        elif not head:
 +            break
 +        elif head == pathname:
 +            parts.append(head)
 +            break
 +        pathname = head
 +    return parts
 +
 +def canonicalize_path(root, pathname):
 +    """Canonicalize a path name, simulating chroot at 'root'.
 +
 +    When resolving the symlink, pretend (similar to chroot) that
 +    'root' is the root of the filesystem.  Also resolve '..' and
 +    '.' components.  This should not escape the chroot below
 +    'root', but for security concerns, use chroot and have the
 +    kernel resolve symlinks instead.
  
      """
 -
 -    pathname = os.path.join(dirpath, filename)
 -    i = 0
 -    while os.path.islink(pathname):
 -        if i >= 10: # let's avoid infinite loops...
 -            return True
 -        i += 1
 -        target = os.readlink(pathname)
 -        if os.path.isabs(target):
 -            pathname = os.path.join(root, target[1:]) # Assume Unix filenames
 +    #print "\nCANONICALIZE %s %s" % (root, pathname)
 +    seen = []
 +    parts = split_path(pathname)
 +    #print "PARTS ", list(reversed(parts))
 +    path = "/"
 +    while parts:
 +        tag = "\n".join(parts + [path])
 +        #print "TEST '%s' + " % path, list(reversed(parts))
 +        if tag in seen or len(seen) > 1024:
 +            fullpath = os.path.join(path, *reversed(parts))
 +            #print "LOOP %s" % fullpath
 +            path = fullpath
 +            logging.error("ELOOP: Too many symbolic links in '%s'" % path)
 +            break
 +        seen.append(tag)
 +        part = parts.pop()
 +        # Using normpath() to cleanup '.', '..' and multiple slashes.
 +        # Removing a suffix 'foo/..' is safe here since it can't change the
 +        # meaning of 'path' because it contains no symlinks - they have been
 +        # resolved already.
 +        newpath = os.path.normpath(os.path.join(path, part))
 +        rootedpath = os.path.join(root, newpath[1:])
 +        if newpath == "/":
 +            path = "/"
 +        elif os.path.islink(rootedpath):
 +            target = os.readlink(rootedpath)
 +            #print "LINK to '%s'" % target
 +            if os.path.isabs(target):
 +                path = "/"
 +            parts.extend(split_path(target))
          else:
 -            pathname = os.path.join(os.path.dirname(pathname), target)
 +            path = newpath
 +    #print "FINAL '%s'" % path
 +    return path
 +
 +
 +def is_broken_symlink(root, dirpath, filename):
 +    """Is symlink dirpath+filename broken?"""
 +
 +    if dirpath[:len(root)] == root:
 +        dirpath = dirpath[len(root):]
 +    pathname = canonicalize_path(root, os.path.join(dirpath, filename))
 +    pathname = os.path.join(root, pathname[1:])
  
      # The symlink chain, if any, has now been resolved. Does the target
      # exist?
 +    #print "EXISTS ", pathname, os.path.exists(pathname)
      return not os.path.exists(pathname)
  
  
@@@ -592,25 -523,10 +593,25 @@@ class IsBrokenSymlinkTests(unittest.Tes
          self.symlink("absolute-broken", "absolute-broken-to-symlink")
          self.symlink("/", "absolute-works")
          self.symlink("/absolute-works", "absolute-works-to-symlink")
 -        
 +        os.mkdir(os.path.join(self.testdir, "dir"))
 +        self.symlink("dir", "dir-link")
 +        os.mkdir(os.path.join(self.testdir, "dir/subdir"))
 +        self.symlink("subdir", "dir/subdir-link")
 +        self.symlink("notexist/", "trailing-slash-broken")
 +        self.symlink("dir/", "trailing-slash-works")
 +        self.symlink("selfloop", "selfloop")
 +        self.symlink("/absolute-selfloop", "absolute-selfloop")
 +        self.symlink("../dir/selfloop", "dir/selfloop")
 +        self.symlink("../dir-link/selfloop", "dir/selfloop1")
 +        self.symlink("../../dir/subdir/selfloop", "dir/subdir/selfloop")
 +        self.symlink("../../dir-link/subdir/selfloop", "dir/subdir/selfloop1")
 +        self.symlink("../../link/subdir-link/selfloop", "dir/subdir/selfloop2")
 +        self.symlink("../../dir-link/subdir-link/selfloop", "dir/subdir/selfloop3")
 +        self.symlink("explode/bomb", "explode")
 +
      def tearDown(self):
          shutil.rmtree(self.testdir)
 -        
 +
      def testRelativeBroken(self):
          self.failUnless(is_broken_symlink(self.testdir, self.testdir, 
                                            "relative-broken"))
@@@ -626,37 -542,7 +627,37 @@@
      def testAbsoluteBrokenToSymlink(self):
          self.failUnless(is_broken_symlink(self.testdir, self.testdir, 
                                            "absolute-broken-to-symlink"))
 -        
 +
 +    def testTrailingSlashBroken(self):
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "trailing-slash-broken"))
 +
 +    def testSelfLoopBroken(self):
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "selfloop"))
 +
 +    def testExpandingSelfLoopBroken(self):
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "explode"))
 +
 +    def testAbsoluteSelfLoopBroken(self):
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "absolute-selfloop"))
 +
 +    def testSubdirSelfLoopBroken(self):
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "dir/selfloop"))
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "dir/selfloop1"))
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "dir/subdir/selfloop"))
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "dir/subdir/selfloop1"))
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "dir/subdir/selfloop2"))
 +        self.failUnless(is_broken_symlink(self.testdir, self.testdir,
 +                                          "dir/subdir/selfloop3"))
 +
      def testRelativeWorks(self):
          self.failIf(is_broken_symlink(self.testdir, self.testdir, 
                                        "relative-works"))
@@@ -673,10 -559,6 +674,10 @@@
          self.failIf(is_broken_symlink(self.testdir, self.testdir, 
                                        "absolute-works-to-symlink"))
  
 +    def testTrailingSlashWorks(self):
 +        self.failIf(is_broken_symlink(self.testdir, self.testdir,
 +                                      "trailing-slash-works"))
 +
      def testMultiLevelNestedSymlinks(self):
          # target/first-link -> ../target/second-link -> ../target
  
@@@ -686,25 -568,14 +687,25 @@@
          self.failIf(is_broken_symlink(self.testdir, self.testdir,
                                        "target/first-link"))
  
 +    def testMultiLevelNestedAbsoluteSymlinks(self):
 +        # first-link -> /second-link/final-target
 +        # second-link -> /target-dir
 +
 +        os.mkdir(os.path.join(self.testdir, "final-dir"))
 +        os.mkdir(os.path.join(self.testdir, "final-dir/final-target"))
 +        self.symlink("/second-link/final-target", "first-link")
 +        self.symlink("/final-dir", "second-link")
 +        self.failIf(is_broken_symlink(self.testdir, self.testdir,
 +                                      "first-link"))
 +
  
  class Chroot:
  
      """A chroot for testing things in."""
 -    
 +
      def __init__(self):
          self.name = None
 -        
 +
      def create_temp_dir(self):
          """Create a temporary directory for the chroot."""
          self.name = tempfile.mkdtemp(dir=settings.tmpdir)
@@@ -729,6 -600,7 +730,6 @@@
          if settings.basetgz:
              self.run(["apt-get", "-yf", "upgrade"])
          self.minimize()
 -        self.run(["apt-get", "clean"])
  
          #copy scripts dir into the chroot
          if settings.scriptsdir is not None:
@@@ -741,7 -613,8 +742,7 @@@
                      shutil.copy(os.path.join((settings.scriptsdir), sfile), dest) 
  
          # Run custom scripts after creating the chroot.
 -        if settings.scriptsdir is not None: 
 -            self.run_scripts("post_setup")
 +        self.run_scripts("post_setup")
  
          if settings.savetgz:
              self.pack_into_tgz(settings.savetgz)
@@@ -760,7 -633,7 +761,7 @@@
              shutil.rmtree(self.name)
              logging.debug("Removed directory tree at %s" % self.name)
          elif settings.keep_tmpdir:
 -            logging.debug("Keeping directory tree at %s" % self.name)	
 +            logging.debug("Keeping directory tree at %s" % self.name)   
  
      def create_temp_tgz_file(self):
          """Return the path to a file to be used as a temporary tgz file"""
@@@ -771,7 -644,6 +772,7 @@@
  
      def pack_into_tgz(self, result):
          """Tar and compress all files in the chroot."""
 +        self.run(["apt-get", "clean"])
          logging.debug("Saving %s to %s." % (self.name, result))
  
          run(['tar', '--exclude', './proc/*', '-czf', result, '-C', self.name, './'])
@@@ -779,10 -651,7 +780,10 @@@
      def unpack_from_tgz(self, tarball):
          """Unpack a tarball to a chroot."""
          logging.debug("Unpacking %s into %s" % (tarball, self.name))
 -        run(["tar", "-C", self.name, "-zxf", tarball])
 +        prefix = []
 +        if settings.eatmydata and os.path.isfile('/usr/bin/eatmydata'):
 +            prefix.append('eatmydata')
 +        run(prefix + ["tar", "-C", self.name, "-zxf", tarball])
  
      def setup_from_lvm(self, lvm_volume):
          """Create a chroot by creating an LVM snapshot."""
@@@ -797,11 -666,7 +798,11 @@@
          run(['mount', self.lvm_snapshot, self.name])
  
      def run(self, command, ignore_errors=False):
 -        return run(["chroot", self.name] + command,
 +        prefix = []
 +        if settings.eatmydata and os.path.isfile(os.path.join(self.name,
 +                                                 'usr/bin/eatmydata')):
 +            prefix.append('eatmydata')
 +        return run(["chroot", self.name] + prefix + command,
                     ignore_errors=ignore_errors)
  
      def create_apt_sources(self, distro):
@@@ -810,11 -675,11 +811,11 @@@
          for mirror, components in settings.debian_mirrors:
              lines.append("deb %s %s %s\n" % 
                           (mirror, distro, " ".join(components)))
 -        create_file(os.path.join(self.name, "etc/apt/sources.list"), 
 +        create_file(self.relative("etc/apt/sources.list"), 
                      "".join(lines))
  
      def create_apt_conf(self):
 -        """Create /etc/apt/apt.conf inside the chroot."""
 +        """Create /etc/apt/apt.conf.d/piuparts inside the chroot."""
          lines = [
              'APT::Get::Assume-Yes "yes";\n',
              'APT::Install-Recommends "0";\n',
@@@ -841,7 -706,7 +842,7 @@@
          if settings.dpkg_force_confdef:
              lines.append('Dpkg::Options {"--force-confdef";};\n')
  
 -        create_file(self.relative("etc/apt/apt.conf"),
 +        create_file(self.relative("etc/apt/apt.conf.d/piuparts"),
              "".join(lines))
  
      def create_dpkg_conf(self):
@@@ -853,33 -718,24 +854,33 @@@
              lines.append('force-confdef\n')
              logging.info("Warning: dpkg has been configured to use the force-confdef option. This will hide problems, see #466118.")
          if lines:
 +          if not os.path.exists(self.relative("etc/dpkg/dpkg.cfg.d")):
 +              os.mkdir(self.relative("etc/dpkg/dpkg.cfg.d"))
            create_file(self.relative("etc/dpkg/dpkg.cfg.d/piuparts"),
              "".join(lines))
  
      def create_policy_rc_d(self):
          """Create a policy-rc.d that prevents daemons from running."""
 -	full_name = os.path.join(self.name, "usr/sbin/policy-rc.d")
 +        full_name = self.relative("usr/sbin/policy-rc.d")
          create_file(full_name, "#!/bin/sh\nexit 101\n")
 -	os.chmod(full_name, 0777)
 -	logging.debug("Created policy-rc.d and chmodded it.")
 +        os.chmod(full_name, 0777)
 +        logging.debug("Created policy-rc.d and chmodded it.")
  
      def setup_minimal_chroot(self):
          """Set up a minimal Debian system in a chroot."""
          logging.debug("Setting up minimal chroot for %s at %s." % 
                (settings.debian_distros[0], self.name))
 +        prefix = []
 +        if settings.eatmydata and os.path.isfile('/usr/bin/eatmydata'):
 +            prefix.append('eatmydata')
          if settings.do_not_verify_signatures:
            logging.info("Warning: not using --keyring option when running debootstrap!")
 -        run(["debootstrap", "--variant=minbase", settings.keyringoption, settings.debian_distros[0], 
 -             self.name, settings.debian_mirrors[0][0]])
 +        options = [settings.keyringoption]
 +        if settings.eatmydata:
 +            options.append('--include=eatmydata')
 +            options.append('--components=%s' % ','.join(settings.debian_mirrors[0][1]))
 +        run(prefix + ["debootstrap", "--variant=minbase"] + options +
 +            [settings.debian_distros[0], self.name, settings.debian_mirrors[0][0]])
  
      def minimize(self):
          """Minimize a chroot by removing (almost all) unnecessary packages"""
@@@ -907,8 -763,9 +908,8 @@@
          for distro in distros:
              logging.debug("Upgrading %s to %s" % (self.name, distro))
              self.create_apt_sources(distro)
 -	    # Run custom scripts before upgrade
 -            if settings.scriptsdir is not None:
 -                self.run_scripts("pre_distupgrade")
 +            # Run custom scripts before upgrade
 +            self.run_scripts("pre_distupgrade")
              self.run(["apt-get", "update"])
              self.run(["apt-get", "-yf", "dist-upgrade"])
              # Sometimes dist-upgrade won't upgrade the packages we want
@@@ -918,13 -775,14 +919,13 @@@
              # packages. So, we force the installation like this.
              self.install_packages_by_name(packages)
              # Run custom scripts after upgrade
 -            if settings.scriptsdir is not None:
 -                self.run_scripts("post_distupgrade")
 +            self.run_scripts("post_distupgrade")
              self.check_for_no_processes()
 -    
 -    def apt_get_knows(self, package_names):
 +
 +    def apt_get_knows(self, packages):
          """Does apt-get (or apt-cache) know about a set of packages?"""
  
 -        for name in package_names:
 +        for name in packages:
              (status, output) = self.run(["apt-cache", "show", name],
                                          ignore_errors=True)
              if status != 0:
@@@ -934,7 -792,7 +935,7 @@@
      def copy_files(self, source_names, target_name):
          """Copy files in 'source_name' to file/dir 'target_name', relative
          to the root of the chroot."""
 -        target_name = os.path.join(self.name, target_name)
 +        target_name = self.relative(target_name)
          logging.debug("Copying %s to %s" % 
                        (", ".join(source_names), target_name))
          for source_name in source_names:
@@@ -944,7 -802,7 +945,7 @@@
                  logging.error("Error copying %s to %s: %s" % 
                        (source_name, target_name, detail))
                  panic()
 -		
 +
      def list_installed_files (self, pre_info, post_info):
          """List the new files installed, removed and modified between two dir trees.
          Actually, it is a nice output of the funcion diff_meta_dat."""
@@@ -954,7 -812,7 +955,7 @@@
          if new:
              logging.debug("New installed files on system:\n" + file_list(new, file_owners))
          else:
 -            logging.debug("The package did not install any new file.\n")		    
 +            logging.debug("The package did not install any new file.\n")                    
  
          if removed:
              logging.debug("The following files have disappeared:\n" +
@@@ -964,16 -822,17 +965,16 @@@
              logging.debug("The following files have been modified:\n" +
                            file_list(modified, file_owners))
          else:
 -            logging.debug("The package did not modify any file.\n")	
 +            logging.debug("The package did not modify any file.\n")     
  
  
 -    def install_package_files(self, filenames):
 -        if filenames:
 -            self.copy_files(filenames, "tmp")
 -            tmp_files = [os.path.basename(a) for a in filenames]
 +    def install_package_files(self, package_files):
 +        if package_files:
 +            self.copy_files(package_files, "tmp")
 +            tmp_files = [os.path.basename(a) for a in package_files]
              tmp_files = [os.path.join("tmp", name) for name in tmp_files]
  
 -            if settings.scriptsdir is not None:
 -                self.run_scripts("pre_install")
 +            self.run_scripts("pre_install")
  
              if settings.list_installed_files:
                  pre_info = self.save_meta_data()
@@@ -990,9 -849,12 +991,9 @@@
  
              logging.info ("Installation of %s ok", tmp_files)
  
 -            if settings.scriptsdir is not None:
 -                self.run_scripts("post_install")
 +            self.run_scripts("post_install")
  
 -            self.run(["apt-get", "clean"])
 -            remove_files([os.path.join(self.name, name) 
 -                            for name in tmp_files])
 +            remove_files([self.relative(name) for name in tmp_files])
  
      def get_selections(self):
          """Get current package selections in a chroot."""
@@@ -1009,12 -871,11 +1010,12 @@@
              self.run(["dpkg", "--" + operation, name], ignore_errors=True)
          self.run(["dpkg", "--remove", "--pending"], ignore_errors=True)
  
 - 
 -    def restore_selections(self, changes, packages):
 -        """Restore package selections in a chroot by applying 'changes'.
 -           'changes' is a return value from diff_selections."""
 - 
 +
 +    def restore_selections(self, selections, packages):
 +        """Restore package selections in a chroot to the state in
 +        'selections'."""
 +
 +        changes = diff_selections(self, selections)
          deps = {}
          nondeps = {}
          for name, state in changes.iteritems():
@@@ -1022,7 -883,7 +1023,7 @@@
                  nondeps[name] = state
              else:
                  deps[name] = state
 -    
 +
          deps_to_remove = [name for name, state in deps.iteritems()
                            if state == "remove"]
          deps_to_purge = [name for name, state in deps.iteritems()
@@@ -1031,29 -892,32 +1032,29 @@@
                               if state == "remove"]
          nondeps_to_purge = [name for name, state in nondeps.iteritems()
                              if state == "purge"]
 -    
 +
          # Run custom scripts before removing all packages. 
 -	if settings.scriptsdir is not None:
 -            self.run_scripts("pre_remove")	
 +        self.run_scripts("pre_remove")
  
          # First remove all packages.
          self.remove_or_purge("remove", deps_to_remove + deps_to_purge +
                                          nondeps_to_remove + nondeps_to_purge)
          # Run custom scripts after removing all packages. 
 -	if settings.scriptsdir is not None:
 -            self.run_scripts("post_remove")	
 +        self.run_scripts("post_remove")
  
          if not settings.skip_cronfiles_test:
              cronfiles, cronfiles_list = self.check_if_cronfiles(packages)
 -	
 +
          if not settings.skip_cronfiles_test and cronfiles:
              self.check_output_cronfiles(cronfiles_list)
  
          if not settings.skip_logrotatefiles_test:
              logrotatefiles, logrotatefiles_list = self.check_if_logrotatefiles(packages)
 -	
 +
          if not settings.skip_logrotatefiles_test and logrotatefiles:
              installed = self.install_logrotate()
              self.check_output_logrotatefiles(logrotatefiles_list)
 -            for pkg in installed:
 -                self.remove_or_purge("purge", installed)
 +            self.remove_or_purge("purge", installed)
  
          # Then purge all packages being depended on.
          self.remove_or_purge("purge", deps_to_purge)
@@@ -1062,7 -926,8 +1063,7 @@@
          self.remove_or_purge("purge", nondeps_to_purge)
  
          # Run custom scripts after purge all packages.
 -        if settings.scriptsdir is not None: 
 -            self.run_scripts("post_purge")
 +        self.run_scripts("post_purge")
  
          # Now do a final run to see that everything worked.
          self.run(["dpkg", "--purge", "--pending"])
@@@ -1070,8 -935,7 +1071,8 @@@
  
      def save_meta_data(self):
          """Return the filesystem meta data for all objects in the chroot."""
 -        root = os.path.join(self.name, ".")
 +        self.run(["apt-get", "clean"])
 +        root = self.relative(".")
          vdict = {}
          proc = os.path.join(root, "proc")
          for dirpath, dirnames, filenames in os.walk(root):
@@@ -1112,18 -976,16 +1113,18 @@@
  
      def install_packages_by_name(self, packages):
          if packages:
 -            if settings.scriptsdir is not None:
 -                self.run_scripts("pre_install")
 +            self.run_scripts("pre_install")
  
 -	    if settings.list_installed_files:
 +            if settings.list_installed_files:
                  pre_info = self.save_meta_data()
                  self.run(["apt-get", "-y", "install"] + packages)
                  self.list_installed_files (pre_info, self.save_meta_data())
              else:
                  self.run(["apt-get", "-y", "install"] + packages)
  
 +            self.run_scripts("post_install")
 +
 +
      def check_for_no_processes(self):
          """Check there are no processes running inside the chroot."""
          (status, output) = run(["lsof", "-w", "+D", self.name], ignore_errors=True)
@@@ -1131,6 -993,21 +1132,21 @@@
          if count > 0:
              logging.error("FAIL: Processes are running inside chroot:\n%s" % 
                            indent_string(output))
+             for signo in [ 15, 9 ]:
+                 p = subprocess.Popen(["lsof", "-t", "+D", self.name],
+                                stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+                 stdout, _ = p.communicate()
+                 if stdout:
+                     pidlist = [int(pidstr) for pidstr in stdout.split("\n") if len(pidstr)]
+                     for pid in pidlist:
+                         if pid > 0:
+                             try:
+                                 if signo == 15:
+                                     os.kill(pid, SIGTERM)
+                                 else:
+                                     os.kill(pid, SIGKILL)
+                             except OSError:
+                                 pass
              panic()
  
  
@@@ -1194,7 -1071,7 +1210,7 @@@
                  panic()
          else:
              logging.debug("No broken symlinks as far as we can find.")
 -	    
 +
      def check_if_cronfiles(self, packages):
          """Check if the packages have cron files under /etc/cron.d and in case positive, 
          it returns the list of files. """
@@@ -1205,7 -1082,7 +1221,7 @@@
          for p in packages:
              basename = p + ".list"
  
 -	    if not os.path.exists(os.path.join(vdir,basename)):
 +            if not os.path.exists(os.path.join(vdir,basename)):
                  continue
  
              f = file(os.path.join(vdir,basename), "r")
@@@ -1227,7 -1104,7 +1243,7 @@@
  
      def check_output_cronfiles (self, list):
          """Check if a given list of cronfiles has any output. Executes 
 -	cron file as cron would do (except for SHELL)"""
 +        cron file as cron would do (except for SHELL)"""
          failed = False
          for vfile in list:
  
@@@ -1252,7 -1129,7 +1268,7 @@@
          for p in packages:
              basename = p + ".list"
  
 -	    if not os.path.exists(os.path.join(vdir,basename)):
 +            if not os.path.exists(os.path.join(vdir,basename)):
                  continue
  
              f = file(os.path.join(vdir,basename), "r")
@@@ -1273,6 -1150,7 +1289,6 @@@
          list of packages that were installed"""
          old_selections = self.get_selections()
          self.run(['apt-get', 'install', '-y', 'logrotate'])
 -        self.run(['apt-get', 'clean'])
          diff = diff_selections(self, old_selections)
          return diff.keys()
  
@@@ -1296,8 -1174,6 +1312,8 @@@
      def run_scripts (self, step):
          """ Run custom scripts to given step post-install|remove|purge"""
  
 +        if settings.scriptsdir is None:
 +            return
          logging.info("Running scripts "+ step)
          basepath = self.relative("tmp/scripts/")
          if not os.path.exists(basepath):
@@@ -1633,11 -1509,13 +1649,11 @@@ def diff_meta_data(tree1, tree2)
      for name1, data1 in removed[:]:
          m = pat1.search(name1)
          if m:
 -            pat2 = re.compile(r"^" + m.group(1) + r"[SK][0-9]{2}" + m.group(2) + 
 -r"$")
 +            pat2 = re.compile(r"^" + m.group(1) + r"[SK][0-9]{2}" + m.group(2) + r"$")
              for name2, data2 in new[:]:
                  m = pat2.search(name2)
                  if m:
 -                    logging.debug("File was renamed: %s\t=> %s" % (name1, 
 -name2))
 +                    logging.debug("File was renamed: %s\t=> %s" % (name1, name2))
                      removed.remove((name1, data1))
                      new.remove((name2, data2))
      # this is again special casing due to the behaviour of a single package :(
@@@ -1655,8 -1533,8 +1671,8 @@@ def file_list(meta_infos, file_owners)
          vlist.append("  %s\t" % name)
          if name in file_owners:
              vlist.append(" owned by: %s\n" % ", ".join(file_owners[name]))
 -	else:
 -            vlist.append(" not owned\n")	
 +        else:
 +            vlist.append(" not owned\n")        
  
      return "".join(vlist)
  
@@@ -1699,10 -1577,10 +1715,10 @@@ def diff_selections(chroot, selections)
      return changes
  
  
 -def get_package_names_from_package_files(filenames):
 +def get_package_names_from_package_files(package_files):
      """Return list of package names given list of package file names."""
      vlist = []
 -    for filename in filenames:
 +    for filename in package_files:
          (status, output) = run(["dpkg", "--info", filename])
          for line in [line.lstrip() for line in output.split("\n")]:
              if line[:len("Package:")] == "Package:":
@@@ -1752,7 -1630,7 +1768,7 @@@ def process_changes(changes)
  
  def check_results(chroot, root_info, file_owners, deps_info=None):
      """Check that current chroot state matches 'root_info'.
 -    
 +
      If settings.warn_on_others is True and deps_info is not None, then only
      print a warning rather than failing if the current chroot contains files
      that are in deps_info but not in root_info.  (In this case, deps_info
@@@ -1815,7 -1693,7 +1831,7 @@@
      return ok
  
  
 -def install_purge_test(chroot, root_info, selections, package_list, packages):
 +def install_purge_test(chroot, root_info, selections, package_files, packages):
      """Do an install-purge test. Return True if successful, False if not.
         Assume 'root' is a directory already populated with a working
         chroot, with packages in states given by 'selections'."""
@@@ -1824,11 -1702,11 +1840,11 @@@
  
      if settings.warn_on_others:
          # Create a metapackage with dependencies from the given packages
 -        if package_list:
 +        if package_files:
              control_infos = []
              # We were given package files, so let's get the Depends and
              # Conflicts directly from the .debs
 -            for deb in package_list:
 +            for deb in package_files:
                  returncode, output = run(["dpkg", "-f", deb])
                  control = deb822.Deb822(output)
                  control_infos.append(control)
@@@ -1839,7 -1717,7 +1855,7 @@@
              apt_cache_args.extend(packages)
              returncode, output = chroot.run(apt_cache_args)
              control_infos = deb822.Deb822.iter_paragraphs(output.splitlines())
 -            
 +
          depends = []
          conflicts = []
          for control in control_infos:
@@@ -1851,7 -1729,7 +1867,7 @@@
          all_conflicts = ", ".join(conflicts)
          metapackage = make_metapackage("piuparts-depends-dummy",
                                         all_depends, all_conflicts)
 -        
 +
          # Install the metapackage
          chroot.install_package_files([metapackage])
          # Now remove it
@@@ -1866,10 -1744,11 +1882,10 @@@
      else:
          deps_info = None
  
 -    if package_list:
 -        chroot.install_package_files(package_list)
 +    if package_files:
 +        chroot.install_package_files(package_files)
      else:
          chroot.install_packages_by_name(packages)
 -        chroot.run(["apt-get", "clean"])
  
  
      chroot.check_for_no_processes()
@@@ -1878,37 -1757,36 +1894,37 @@@
      file_owners = chroot.get_files_owned_by_packages()
  
      # Remove all packages from the chroot that weren't there initially.    
 -    changes = diff_selections(chroot, selections)
 -    chroot.restore_selections(changes, packages)
 -    
 +    chroot.restore_selections(selections, packages)
 +
 +    chroot.check_for_no_processes()
      chroot.check_for_broken_symlinks()
  
      return check_results(chroot, root_info, file_owners, deps_info=deps_info)
  
  
 -def install_upgrade_test(chroot, root_info, selections, package_list, package_names):
 +def install_upgrade_test(chroot, root_info, selections, package_files, packages):
      """Install package via apt-get, then upgrade from package files.
      Return True if successful, False if not."""
  
      # First install via apt-get.
 -    chroot.install_packages_by_name(package_names)
 -    
 -    if settings.scriptsdir is not None:
 -        chroot.run_scripts("pre_upgrade")
 +    chroot.install_packages_by_name(packages)
  
 +    chroot.run_scripts("pre_upgrade")
 +
 +    chroot.check_for_no_processes()
      chroot.check_for_broken_symlinks()
  
      # Then from the package files.
 -    chroot.install_package_files(package_list)
 -    
 +    chroot.install_package_files(package_files)
 +
 +    chroot.check_for_no_processes()
 +    chroot.check_for_broken_symlinks()
 +
      file_owners = chroot.get_files_owned_by_packages()
  
 -    # Remove all packages from the chroot that weren't there
 -    # initially.
 -    changes = diff_selections(chroot, selections)
 -    chroot.restore_selections(changes, package_names)
 -    
 +    # Remove all packages from the chroot that weren't there initially.
 +    chroot.restore_selections(selections, packages)
 +
      chroot.check_for_no_processes()
      chroot.check_for_broken_symlinks()
  
@@@ -1932,7 -1810,7 +1948,7 @@@ def load_meta_data(filename)
      return root_info, selections
  
  
 -def install_and_upgrade_between_distros(filenames, packages):
 +def install_and_upgrade_between_distros(package_files, packages):
      """Install package and upgrade it between distributions, then remove.
         Return True if successful, False if not."""
  
@@@ -1967,23 -1845,22 +1983,23 @@@
      else:
          root_tgz = chroot.create_temp_tgz_file()
          chroot.pack_into_tgz(root_tgz)
 -        
 +
      if settings.end_meta:
          # load root_info and selections
          root_info, selections = load_meta_data(settings.end_meta)
      else:
          chroot.upgrade_to_distros(settings.debian_distros[1:], [])
 -        chroot.run(["apt-get", "clean"])
 +
 +        chroot.check_for_no_processes()
  
          # set root_info and selections
          root_info = chroot.save_meta_data()
          selections = chroot.get_selections()
 -        
 +
          if settings.save_end_meta:
              # save root_info and selections
              save_meta_data(settings.save_end_meta, root_info, selections)
 -    
 +
          chroot.remove()
          dont_do_on_panic(cid)
          chroot = get_chroot()
@@@ -1995,9 -1872,11 +2011,9 @@@
  
      chroot.check_for_no_processes()
  
 -    chroot.run(["apt-get", "update"])
      chroot.install_packages_by_name(packages)
  
 -    if settings.scriptsdir is not None:
 -        chroot.run_scripts("pre_upgrade")
 +    chroot.run_scripts("pre_upgrade")
  
      chroot.check_for_no_processes()
  
@@@ -2005,18 -1884,20 +2021,18 @@@
  
      chroot.check_for_no_processes()
  
 -    chroot.install_package_files(filenames)
 -    chroot.run(["apt-get", "clean"])
 -    
 +    chroot.install_package_files(package_files)
 +
      chroot.check_for_no_processes()
  
      file_owners = chroot.get_files_owned_by_packages()
  
      # use root_info and selections
 -    changes = diff_selections(chroot, selections)
 -    chroot.restore_selections(changes, packages)
 +    chroot.restore_selections(selections, packages)
      result = check_results(chroot, root_info, file_owners)
  
      chroot.check_for_no_processes()
 -    
 +
      if root_tgz != settings.basetgz:
          remove_files([root_tgz])
      chroot.remove()
@@@ -2061,11 -1942,11 +2077,11 @@@ def set_basetgz_to_pbuilder(option, opt
  
  def parse_command_line():
      """Parse the command line, change global settings, return non-options."""
 -    
 +
      parser = optparse.OptionParser(usage="%prog [options] package ...",
                                     version="piuparts %s" % VERSION)
 -    
 -   
 +
 +
      parser.add_option("-a", "--apt", action="store_true", default=False,
                        help="Command line arguments are package names " +
                             "to be installed via apt.")
@@@ -2074,7 -1955,7 +2090,7 @@@
                        metavar='CMDLINE', default=None,
                        help="Use CMDLINE via autopkgtest (adt-virt-*)"
                             " protocol instead of managing a chroot.")
 -    
 +
      parser.add_option("-b", "--basetgz", metavar="TARBALL",
                        help="Use TARBALL as the contents of the initial " +
                             "chroot, instead of building a new one with " +
@@@ -2083,7 -1964,7 +2099,7 @@@
      parser.add_option("--bindmount", action="append", metavar="DIR",
                        default=[],
                        help="Directory to be bind-mounted inside the chroot.")
 -    
 +
      parser.add_option("-d", "--distribution", action="append", metavar="NAME",
                        help="Which Debian distribution to use: a code name " +
                             "(for example lenny, squeeze, sid) or experimental. The " +
@@@ -2092,26 -1973,21 +2108,26 @@@
      parser.add_option("-D", "--defaults", action="store",
                        help="Choose which set of defaults to use "
                             "(debian/ubuntu).")
 - 
 +
      parser.add_option("--debfoster-options",
                        default="-o MaxPriority=required -o UseRecommends=no -f -n apt debfoster",
 -		      help="Run debfoster with different parameters (default: -o MaxPriority=required -o UseRecommends=no -f -n apt debfoster).")
 +                      help="Run debfoster with different parameters (default: -o MaxPriority=required -o UseRecommends=no -f -n apt debfoster).")
 +
 +    parser.add_option("--no-eatmydata",
 +                      default=False,
 +                      action='store_true',
 +                      help="Default is to use libeatmydata in the chroot")
  
      parser.add_option("--dpkg-noforce-unsafe-io",
                        default=False,
                        action='store_true',
 -		      help="Default is to run dpkg with --force-unsafe-io option, which causes dpkg to skip certain file system syncs known to cause substantial performance degradation on some filesystems.  This option turns that off and dpkg will use safe I/O operations.")
 +                      help="Default is to run dpkg with --force-unsafe-io option, which causes dpkg to skip certain file system syncs known to cause substantial performance degradation on some filesystems.  This option turns that off and dpkg will use safe I/O operations.")
  
      parser.add_option("--dpkg-force-confdef",
                        default=False,
                        action='store_true',
 -		      help="Make dpkg use --force-confdef, which lets dpkg always choose the default action when a modified conffile is found. This option will make piuparts ignore errors it was designed to report and therefore should only be used to hide problems in depending packages.  (See #466118.)")
 -     
 +                      help="Make dpkg use --force-confdef, which lets dpkg always choose the default action when a modified conffile is found. This option will make piuparts ignore errors it was designed to report and therefore should only be used to hide problems in depending packages.  (See #466118.)")
 +
      parser.add_option("--do-not-verify-signatures", default=False,
                        action='store_true',
                        help="Do not verify signatures from the Release files when running debootstrap.")
@@@ -2120,13 -1996,13 +2136,13 @@@
                        default=[],
                        help="Add FILENAME to list of filenames to be " +
                             "ignored when comparing changes to chroot.")
 -    
 +
      parser.add_option("-I", "--ignore-regex", action="append", 
                        metavar="REGEX", default=[],
                        help="Add REGEX to list of Perl compatible regular " +
                             "expressions for filenames to be " +
                             "ignored when comparing changes to chroot.")
 -    
 +
      parser.add_option("-k", "--keep-tmpdir", 
                        action="store_true", default=False,
                        help="Don't remove the temporary directory for the " +
@@@ -2135,7 -2011,7 +2151,7 @@@
      parser.add_option("-K", "--keyring", metavar="FILE",  
                        default = "/usr/share/keyrings/debian-archive-keyring.gpg", 
                        help="Use FILE as the keyring to use with debootstrap when creating chroots.")
 -    
 +
      parser.add_option("--keep-sources-list", 
                        action="store_true", default=False,
                        help="Don't modify the chroot's " +
@@@ -2149,7 -2025,7 +2165,7 @@@
      parser.add_option("--list-installed-files", 
                        action="store_true", default=False,
                        help="List files added to the chroot after the " +
 -		      "installation of the package.")
 +                      "installation of the package.")
  
      parser.add_option("--lvm-volume", metavar="LVM-VOL", action="store",
                        help="Use LVM-VOL as source for the chroot, instead of building " +
@@@ -2159,16 -2035,16 +2175,16 @@@
      parser.add_option("--lvm-snapshot-size", metavar="SNAPSHOT-SIZE", action="store",
                        default="1G", help="Use SNAPSHOT-SIZE as snapshot size when creating " +
                        "a new LVM snapshot (default: 1G)")
 -    
 +
      parser.add_option("-m", "--mirror", action="append", metavar="URL",
                        default=[],
                        help="Which Debian mirror to use.")
 -    
 +
      parser.add_option("-n", "--no-ignores", action="callback",
                        callback=forget_ignores,
                        help="Forget all ignores set so far, including " +
                             "built-in ones.")
 -    
 +
      parser.add_option("-N", "--no-symlinks", action="store_true",
                        default=False,
                        help="Don't check for broken symlinks.")
@@@ -2176,8 -2052,8 +2192,8 @@@
      parser.add_option("--no-upgrade-test", 
                        action="store_true", default=False,
                        help="Skip testing the upgrade from an existing version " +
 -		      "in the archive.")
 -    
 +                      "in the archive.")
 +
      parser.add_option("--no-install-purge-test", 
                        action="store_true", default=False,
                        help="Skip install and purge test.")
@@@ -2190,13 -2066,13 +2206,13 @@@
      parser.add_option("--pedantic-purge-test", 
                        action="store_true", default=False,
                        help="Be pedantic when checking if a purged package leaves files behind. If this option is not set, files left in /tmp are ignored.")
 - 
 +
      parser.add_option("-s", "--save", metavar="FILENAME",
                        help="Save the chroot into FILENAME.")
  
      parser.add_option("-B", "--end-meta", metavar="FILE",
                        help="Save chroot package selection and file meta data in FILE for later use. See the function install_and_upgrade_between_distros() in piuparts.py for defaults. Mostly useful for large scale distro upgrade tests.")
 - 
 +
      parser.add_option("-S", "--save-end-meta", metavar="FILE",
                        help="Load chroot package selection and file meta data from FILE. See the function install_and_upgrade_between_distros() in piuparts.py for defaults. Mostly useful for large scale distro upgrade tests.")
  
@@@ -2207,7 -2083,7 +2223,7 @@@
      parser.add_option("--skip-cronfiles-test", 
                        action="store_true", default=False,
                        help="Skip testing the output from the cron files.")
 -		   
 +
      parser.add_option("--skip-logrotatefiles-test", 
                        action="store_true", default=False,
                        help="Skip testing the output from the logrotate files.")
@@@ -2222,7 -2098,7 +2238,7 @@@
  
      parser.add_option("--scriptsdir", metavar="DIR",
                        help="Directory where are placed the custom scripts.")
 -    
 +
      parser.add_option("-t", "--tmpdir", metavar="DIR",
                        help="Use DIR for temporary storage. Default is " +
                             "$TMPDIR or /tmp.")
@@@ -2251,7 -2127,7 +2267,7 @@@
      parser.add_option("--fail-on-broken-symlinks", action="store_true",
                        default=False,
                        help="Fail if broken symlinks are detected.")
 -    
 +
      parser.add_option("--log-level", action="store",metavar='LEVEL',
                        default="dump",
                        help="Displays messages from LEVEL level, possible values are: error, info, dump, debug. The default is dump.")
@@@ -2272,8 -2148,6 +2288,8 @@@
      settings.keep_sources_list = opts.keep_sources_list
      settings.skip_minimize = opts.skip_minimize
      settings.minimize = opts.minimize
 +    if settings.minimize:
 +      settings.skip_minimize = False
      settings.list_installed_files = opts.list_installed_files
      settings.no_install_purge_test = opts.no_install_purge_test
      settings.no_upgrade_test = opts.no_upgrade_test
@@@ -2290,11 -2164,11 +2306,11 @@@
      settings.pedantic_purge_test = opts.pedantic_purge_test
      if not settings.pedantic_purge_test:
        settings.ignored_patterns += settings.non_pedantic_ignore_patterns
 -   
 +
      log_file_name = opts.log_file
  
      defaults = DefaultsFactory().new_defaults()
 -    
 +
      settings.debian_mirrors = [parse_mirror_spec(x, defaults.get_components())
                                 for x in opts.mirror]
      settings.check_broken_symlinks = not opts.no_symlinks
@@@ -2303,7 -2177,6 +2319,7 @@@
      settings.warn_on_others = opts.warn_on_others
      settings.warn_on_leftovers_after_purge = opts.warn_on_leftovers_after_purge
      settings.debfoster_options = opts.debfoster_options.split()
 +    settings.eatmydata = not opts.no_eatmydata
      settings.dpkg_force_unsafe_io = not opts.dpkg_noforce_unsafe_io
      settings.dpkg_force_confdef = opts.dpkg_force_confdef
  
@@@ -2338,7 -2211,7 +2354,7 @@@
  
      if opts.scriptsdir is not None:
          settings.scriptsdir = opts.scriptsdir
 -	if not os.path.isdir(settings.scriptsdir):
 +        if not os.path.isdir(settings.scriptsdir):
              logging.error("Scripts directory is not a directory: %s" % 
                            settings.scriptsdir)
              panic()
@@@ -2367,7 -2240,7 +2383,7 @@@
          sys.exit(exitcode)
  
      return args
 -    
 +
  
  def get_chroot():
      if settings.adt_virt is None: return Chroot()
@@@ -2378,10 -2251,9 +2394,10 @@@ def process_packages(package_list)
      # Find the names of packages.
      if settings.args_are_package_files:
          packages = get_package_names_from_package_files(package_list)
 +        package_files = package_list
      else:
          packages = package_list
 -        package_list = []
 +        package_files = []
  
      if len(settings.debian_distros) == 1:
          chroot = get_chroot()
@@@ -2393,7 -2265,7 +2409,7 @@@
  
          if not settings.no_install_purge_test:
              if not install_purge_test(chroot, root_info, selections,
 -                      package_list, packages):
 +                      package_files, packages):
                  logging.error("FAIL: Installation and purging test.")
                  panic()
              logging.info("PASS: Installation and purging test.")
@@@ -2403,17 -2275,17 +2419,17 @@@
                  logging.info("Can't test upgrades: -a or --apt option used.")
              elif not chroot.apt_get_knows(packages):
                  logging.info("Can't test upgrade: packages not known by apt-get.")
 -            elif install_upgrade_test(chroot, root_info, selections, package_list, 
 +            elif install_upgrade_test(chroot, root_info, selections, package_files,
                                    packages):
                  logging.info("PASS: Installation, upgrade and purging tests.")
              else:
                  logging.error("FAIL: Installation, upgrade and purging tests.")
                  panic()
 -    
 +
          chroot.remove()
          dont_do_on_panic(cid)
      else:
 -        if install_and_upgrade_between_distros(package_list, packages):
 +        if install_and_upgrade_between_distros(package_files, packages):
              logging.info("PASS: Upgrading between Debian distributions.")
          else:
              logging.error("FAIL: Upgrading between Debian distributions.")
@@@ -2480,5 -2352,3 +2496,5 @@@ if __name__ == "__main__"
          print ''
          print 'Piuparts interrupted by the user, exiting...'
          sys.exit(1)
 +
 +# vi:set et ts=4 sw=4 :

-- 
piuparts git repository



More information about the Piuparts-commits mailing list