Package: piuparts
Version: 0.84
Severity: normal
Tags: patch

I have written a patch for adding docker support on piuparts. A new option is
introduced `--docker-image` e.g.

$ piuparts --docker-image debian:unstable package.deb

Honestly I didn't do too much testing on this, I just have tried using the new
feature of course and I have tried using schroot to check it's not breaking
something. I am willing to help you if you need/want to do extra testing on 
this.
The patch attached is made against origin/master.

Cheers,

-- 
TiN
diff --git a/piuparts.py b/piuparts.py
index 3daba797..a13bd9fc 100644
--- a/piuparts.py
+++ b/piuparts.py
@@ -47,6 +47,7 @@ import os
 import tarfile
 import stat
 import re
+import json
 import pickle
 import subprocess
 import traceback
@@ -191,6 +192,7 @@ class Settings:
         self.skip_minimize = True
         self.minimize = False
         self.debfoster_options = None
+        self.docker_image = None
         # tests and checks
         self.no_install_purge_test = False
         self.no_upgrade_test = False
@@ -769,7 +771,7 @@ class Chroot:
     def create(self, temp_tgz=None):
         """Create a chroot according to user's wishes."""
         self.panic_handler_id = do_on_panic(self.remove)
-        if not settings.schroot:
+        if not settings.schroot and not settings.docker_image:
             self.create_temp_dir()
 
         if temp_tgz:
@@ -782,10 +784,12 @@ class Chroot:
             self.setup_from_dir(settings.existing_chroot)
         elif settings.schroot:
             self.setup_from_schroot(settings.schroot)
+        elif settings.docker_image:
+            self.setup_from_docker(settings.docker_image)
         else:
             self.setup_minimal_chroot()
 
-        if not settings.schroot:
+        if not settings.schroot and not settings.docker_image:
             self.mount_proc()
         self.configure_chroot()
 
@@ -807,7 +811,7 @@ class Chroot:
         self.run_scripts("post_chroot_unpack")
 
         self.run(["apt-get", "update"])
-        if settings.basetgz or settings.schroot or settings.existing_chroot:
+        if settings.basetgz or settings.docker_image or settings.schroot or settings.existing_chroot:
             self.run(["apt-get", "-yf", "dist-upgrade"])
         self.minimize()
         self.remember_available_md5()
@@ -832,7 +836,10 @@ class Chroot:
             if settings.schroot:
                 logging.debug("Terminate schroot session '%s'" % self.name)
                 run(['schroot', '--end-session', '--chroot', "session:" + self.schroot_session])
-            if not settings.schroot:
+            if settings.docker_image:
+                logging.debug("Destroy docker container '%s'" % self.docker_container)
+                run(['docker', 'rm', '-f', self.docker_container])
+            if not settings.schroot and not settings.docker_image:
                 run(['rm', '-rf', '--one-file-system', self.name])
                 if os.path.exists(self.name):
                     create_file(os.path.join(self.name, ".piuparts.tmpdir"), "removal failed")
@@ -840,6 +847,8 @@ class Chroot:
         elif settings.keep_tmpdir:
             if settings.schroot:
                 logging.debug("Keeping schroot session %s at %s" % (self.schroot_session, self.name))
+            elif settings.docker_image:
+                logging.debug("Keeping container %s" % self.docker_container)
             else:
                 logging.debug("Keeping directory tree at %s" % self.name)
         dont_do_on_panic(self.panic_handler_id)
@@ -892,6 +901,25 @@ class Chroot:
         self.name = output.strip()
         logging.info("New schroot session in '%s'" % self.name)
 
+    @staticmethod
+    def check_if_docker_storage_driver_is_supported():
+        ret_code, output = run(['docker', 'info'])
+        if 'overlay2' not in output:
+            logging.error('Only overlay2 storage driver is supported')
+            panic()
+
+    def setup_from_docker(self, docker_image):
+        self.check_if_docker_storage_driver_is_supported()
+        ret_code, output = run(['docker', 'run', '-d', '-it', docker_image, 'bash'])
+        if ret_code != 0:
+            logging.error("Couldn't start the container from '%s'" % docker_image)
+            panic()
+        self.docker_container = output.strip()
+        ret_code, output = run(['docker', 'inspect', self.docker_container])
+        container_data = json.loads(output)[0]
+        self.name = container_data['GraphDriver']['Data']['MergedDir']
+        logging.info("New container created '%s'" % self.docker_container)
+
     def setup_from_lvm(self, lvm_volume):
         """Create a chroot by creating an LVM snapshot."""
         self.lvm_base = os.path.dirname(lvm_volume)
@@ -938,6 +966,12 @@ class Chroot:
                 ["schroot", "--preserve-environment", "--run-session", "--chroot", "session:" +
                     self.schroot_session, "--directory", "/", "-u", "root", "--"] + prefix + command,
                    ignore_errors=ignore_errors, timeout=settings.max_command_runtime)
+        elif settings.docker_image:
+            return run(
+                ['docker', 'exec', self.docker_container,] + prefix + command,
+                ignore_errors=ignore_errors,
+                timeout=settings.max_command_runtime
+            )
         else:
             return run(["chroot", self.name] + prefix + command,
                        ignore_errors=ignore_errors, timeout=settings.max_command_runtime)
@@ -1042,6 +1076,9 @@ class Chroot:
 
     def create_resolv_conf(self):
         """Update resolv.conf based on the current configuration in the host system. Strip comments and whitespace."""
+        if settings.docker_image:
+            # Do nothing, docker already takes care of this
+            return
         full_name = self.relative("etc/resolv.conf")
         resolvconf = ""
         with open("/etc/resolv.conf", "r") as f:
@@ -1624,8 +1661,12 @@ class Chroot:
 
     def check_for_no_processes(self, fail=None):
         """Check there are no processes running inside the chroot."""
-        (status, output) = run(["lsof", "-w", "+D", self.name], ignore_errors=True)
-        count = len(output.split("\n")) - 1
+        if settings.docker_image:
+            (status, output) = run(["docker", "top", self.docker_container])
+            count = len(output.strip().split("\n")) - 2 # header + bash launched on container creation
+        else:
+            (status, output) = run(["lsof", "-w", "+D", self.name], ignore_errors=True)
+            count = len(output.split("\n")) - 1
         if count > 0:
             if fail is None:
                 fail = not settings.allow_database
@@ -1637,6 +1678,9 @@ class Chroot:
 
     def terminate_running_processes(self):
         """Terminate all processes running in the chroot."""
+        if settings.docker_image:
+            # docker takes care of this
+            return
         seen = []
         while True:
             p = subprocess.Popen(["lsof", "-t", "+D", self.name],
@@ -2726,6 +2770,10 @@ def parse_command_line():
                       help="Use schroot session named SCHROOT-NAME for the chroot, instead of building " +
                            "a new one with debootstrap.")
 
+    parser.add_option("--docker-image", metavar="DOCKER-IMAGE", action="store",
+                      help="Use docker image DOCKER-IMAGE for the chroot, instead of building " +
+                           "a new one with debootstrap.")
+
     parser.add_option("-m", "--mirror", action="append", metavar="URL",
                       default=[],
                       help="Which Debian mirror to use.")
@@ -2948,6 +2996,7 @@ def parse_command_line():
     if settings.minimize:
         settings.skip_minimize = False
     settings.debfoster_options = opts.debfoster_options.split()
+    settings.docker_image = opts.docker_image
     # tests and checks
     settings.no_install_purge_test = opts.no_install_purge_test
     settings.no_upgrade_test = opts.no_upgrade_test

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to