← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad-buildd/image-type into lp:launchpad-buildd

 

Colin Watson has proposed merging lp:~cjwatson/launchpad-buildd/image-type into lp:launchpad-buildd.

Commit message:
Allow the LXD backend to accept a LXD image instead of a chroot tarball, skipping the conversion step.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad-buildd/image-type/+merge/361633
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad-buildd/image-type into lp:launchpad-buildd.
=== modified file 'debian/changelog'
--- debian/changelog	2018-11-07 11:48:18 +0000
+++ debian/changelog	2019-01-10 18:17:59 +0000
@@ -2,6 +2,8 @@
 
   [ Colin Watson ]
   * Run all tests at package build time, not just those in lpbuildd.tests.
+  * Allow the LXD backend to accept a LXD image instead of a chroot tarball,
+    skipping the conversion step.
 
   [ Tobias Koch ]
   * Update LXD backend to work with LXD 3.

=== modified file 'lpbuildd/slave.py'
--- lpbuildd/slave.py	2018-05-02 09:00:10 +0000
+++ lpbuildd/slave.py	2019-01-10 18:17:59 +0000
@@ -182,7 +182,9 @@
 
     def doUnpack(self):
         """Unpack the build chroot."""
-        self.runTargetSubProcess("unpack-chroot", self._chroottarfile)
+        self.runTargetSubProcess(
+            "unpack-chroot", "--image-type", self.image_type,
+            self._chroottarfile)
 
     def doReapProcesses(self, state, notify=True):
         """Reap any processes left lying around in the chroot."""
@@ -242,6 +244,7 @@
                                             self._buildid, f))
         self._chroottarfile = self._slave.cachePath(chroot)
 
+        self.image_type = extra_args.get('image_type', 'chroot')
         self.series = extra_args['series']
         self.arch_tag = extra_args.get('arch_tag', self._slave.getArch())
         self.fast_cleanup = extra_args.get('fast_cleanup', False)

=== modified file 'lpbuildd/target/backend.py'
--- lpbuildd/target/backend.py	2017-11-10 20:55:33 +0000
+++ lpbuildd/target/backend.py	2019-01-10 18:17:59 +0000
@@ -22,8 +22,8 @@
         self.arch = arch
         self.build_path = os.path.join(os.environ["HOME"], "build-" + build_id)
 
-    def create(self, tarball_path):
-        """Create the backend based on a chroot tarball.
+    def create(self, image_path, image_type):
+        """Create the backend based on a base image.
 
         This puts the backend into a state where it is ready to be started.
         """

=== modified file 'lpbuildd/target/chroot.py'
--- lpbuildd/target/chroot.py	2018-06-12 23:23:13 +0000
+++ lpbuildd/target/chroot.py	2019-01-10 18:17:59 +0000
@@ -28,10 +28,13 @@
         super(Chroot, self).__init__(build_id, series=series, arch=arch)
         self.chroot_path = os.path.join(self.build_path, "chroot-autobuild")
 
-    def create(self, tarball_path):
+    def create(self, image_path, image_type):
         """See `Backend`."""
-        subprocess.check_call(
-            ["sudo", "tar", "-C", self.build_path, "-xf", tarball_path])
+        if image_type == "chroot":
+            subprocess.check_call(
+                ["sudo", "tar", "-C", self.build_path, "-xf", image_path])
+        else:
+            raise ValueError("Unhandled image type: %s" % image_type)
 
     def start(self):
         """See `Backend`."""

=== modified file 'lpbuildd/target/lifecycle.py'
--- lpbuildd/target/lifecycle.py	2017-08-22 14:48:06 +0000
+++ lpbuildd/target/lifecycle.py	2019-01-10 18:17:59 +0000
@@ -23,11 +23,13 @@
     @classmethod
     def add_arguments(cls, parser):
         super(Create, cls).add_arguments(parser)
-        parser.add_argument("tarball_path", help="path to chroot tarball")
+        parser.add_argument(
+            "--image-type", default="chroot", help="base image type")
+        parser.add_argument("image_path", help="path to base image")
 
     def run(self):
         logger.info("Creating target for build %s", self.args.build_id)
-        self.backend.create(self.args.tarball_path)
+        self.backend.create(self.args.image_path, self.args.image_type)
         return 0
 
 

=== modified file 'lpbuildd/target/lxd.py'
--- lpbuildd/target/lxd.py	2018-10-25 09:54:49 +0000
+++ lpbuildd/target/lxd.py	2019-01-10 18:17:59 +0000
@@ -170,22 +170,29 @@
                 if fileptr is not None:
                     fileptr.close()
 
-    def create(self, tarball_path):
+    def create(self, image_path, image_type):
         """See `Backend`."""
         self.remove_image()
 
         # This is a lot of data to shuffle around in Python, but there
         # doesn't currently seem to be any way to ask pylxd to ask lxd to
         # import an image from a file on disk.
-        with io.BytesIO() as target_file:
-            with tarfile.open(name=tarball_path, mode="r") as source_tarball:
-                with tarfile.open(
-                        fileobj=target_file, mode="w") as target_tarball:
-                    self._convert(source_tarball, target_tarball)
-
-            image = self.client.images.create(
-                target_file.getvalue(), wait=True)
-            image.add_alias(self.alias, self.alias)
+        if image_type == "chroot":
+            with io.BytesIO() as target_file:
+                with tarfile.open(name=image_path, mode="r") as source_tarball:
+                    with tarfile.open(
+                            fileobj=target_file, mode="w") as target_tarball:
+                        self._convert(source_tarball, target_tarball)
+
+                image = self.client.images.create(
+                    target_file.getvalue(), wait=True)
+        elif image_type == "lxd":
+            with open(image_path, "rb") as image_file:
+                image = self.client.images.create(image_file.read(), wait=True)
+        else:
+            raise ValueError("Unhandled image type: %s" % image_type)
+
+        image.add_alias(self.alias, self.alias)
 
     @property
     def sys_dir(self):

=== modified file 'lpbuildd/target/tests/test_chroot.py'
--- lpbuildd/target/tests/test_chroot.py	2018-06-12 23:23:13 +0000
+++ lpbuildd/target/tests/test_chroot.py	2019-01-10 18:17:59 +0000
@@ -35,7 +35,7 @@
         self.useFixture(EnvironmentVariable("HOME", "/expected/home"))
         processes_fixture = self.useFixture(FakeProcesses())
         processes_fixture.add(lambda _: {}, name="sudo")
-        Chroot("1", "xenial", "amd64").create("/path/to/tarball")
+        Chroot("1", "xenial", "amd64").create("/path/to/tarball", "chroot")
 
         expected_args = [
             ["sudo", "tar", "-C", "/expected/home/build-1",

=== modified file 'lpbuildd/target/tests/test_lifecycle.py'
--- lpbuildd/target/tests/test_lifecycle.py	2017-08-22 15:09:09 +0000
+++ lpbuildd/target/tests/test_lifecycle.py	2019-01-10 18:17:59 +0000
@@ -24,7 +24,20 @@
         create = parse_args(args=args).operation
         self.assertEqual(0, create.run())
         self.assertEqual(
-            [(("/path/to/tarball",), {})], create.backend.create.calls)
+            [(("/path/to/tarball", "chroot"), {})],
+            create.backend.create.calls)
+
+    def test_image_type(self):
+        args = [
+            "unpack-chroot",
+            "--backend=fake", "--series=xenial", "--arch=amd64", "1",
+            "--image-type", "lxd", "/path/to/tarball",
+            ]
+        create = parse_args(args=args).operation
+        self.assertEqual(0, create.run())
+        self.assertEqual(
+            [(("/path/to/tarball", "lxd"), {})],
+            create.backend.create.calls)
 
 
 class TestStart(TestCase):

=== modified file 'lpbuildd/target/tests/test_lxd.py'
--- lpbuildd/target/tests/test_lxd.py	2018-10-25 09:54:49 +0000
+++ lpbuildd/target/tests/test_lxd.py	2019-01-10 18:17:59 +0000
@@ -12,6 +12,7 @@
 import os.path
 import tarfile
 from textwrap import dedent
+import time
 try:
     from unittest import mock
 except ImportError:
@@ -101,6 +102,32 @@
         with tarfile.open(output_path, "w:bz2") as tar:
             tar.add(source, arcname="chroot-autobuild")
 
+    def make_lxd_image(self, output_path):
+        source = self.useFixture(TempDir()).path
+        hello = os.path.join(source, "bin", "hello")
+        os.mkdir(os.path.dirname(hello))
+        with open(hello, "w") as f:
+            f.write("hello\n")
+            os.fchmod(f.fileno(), 0o755)
+        metadata = {
+            "architecture": "x86_64",
+            "creation_date": time.time(),
+            "properties": {
+                "os": "Ubuntu",
+                "series": "xenial",
+                "architecture": "amd64",
+                "description": "Launchpad chroot for Ubuntu xenial (amd64)",
+                },
+            }
+        metadata_yaml = json.dumps(
+            metadata, sort_keys=True, indent=4, separators=(",", ": "),
+            ensure_ascii=False).encode("UTF-8") + b"\n"
+        with tarfile.open(output_path, "w:gz") as tar:
+            metadata_file = tarfile.TarInfo(name="metadata.yaml")
+            metadata_file.size = len(metadata_yaml)
+            tar.addfile(metadata_file, io.BytesIO(metadata_yaml))
+            tar.add(source, arcname="rootfs")
+
     def test_convert(self):
         tmp = self.useFixture(TempDir()).path
         source_tarball_path = os.path.join(tmp, "source.tar.bz2")
@@ -136,7 +163,7 @@
         self.assertThat(hello, FileContains("hello\n"))
         self.assertThat(hello, HasPermissions("0755"))
 
-    def test_create(self):
+    def test_create_from_chroot(self):
         tmp = self.useFixture(TempDir()).path
         source_tarball_path = os.path.join(tmp, "source.tar.bz2")
         self.make_chroot_tarball(source_tarball_path)
@@ -145,7 +172,26 @@
         client.images.all.return_value = []
         image = mock.MagicMock()
         client.images.create.return_value = image
-        LXD("1", "xenial", "amd64").create(source_tarball_path)
+        LXD("1", "xenial", "amd64").create(source_tarball_path, "chroot")
+
+        client.images.create.assert_called_once_with(mock.ANY, wait=True)
+        with io.BytesIO(client.images.create.call_args[0][0]) as f:
+            with tarfile.open(fileobj=f) as tar:
+                with closing(tar.extractfile("rootfs/bin/hello")) as hello:
+                    self.assertEqual("hello\n", hello.read())
+        image.add_alias.assert_called_once_with(
+            "lp-xenial-amd64", "lp-xenial-amd64")
+
+    def test_create_from_lxd(self):
+        tmp = self.useFixture(TempDir()).path
+        source_image_path = os.path.join(tmp, "source.tar.gz")
+        self.make_lxd_image(source_image_path)
+        self.useFixture(MockPatch("pylxd.Client"))
+        client = pylxd.Client()
+        client.images.all.return_value = []
+        image = mock.MagicMock()
+        client.images.create.return_value = image
+        LXD("1", "xenial", "amd64").create(source_image_path, "lxd")
 
         client.images.create.assert_called_once_with(mock.ANY, wait=True)
         with io.BytesIO(client.images.create.call_args[0][0]) as f:

=== modified file 'lpbuildd/tests/test_debian.py'
--- lpbuildd/tests/test_debian.py	2018-05-02 09:00:10 +0000
+++ lpbuildd/tests/test_debian.py	2019-01-10 18:17:59 +0000
@@ -109,6 +109,7 @@
               'unpack-chroot',
               '--backend=chroot', '--series=xenial', '--arch=amd64',
               self.buildid,
+              '--image-type', 'chroot',
               os.path.join(self.buildmanager._cachepath, 'chroot.tar.gz')],
              None),
             self.buildmanager.commands[-1])
@@ -223,6 +224,7 @@
               'unpack-chroot',
               '--backend=chroot', '--series=xenial', '--arch=amd64',
               self.buildid,
+              '--image-type', 'chroot',
               os.path.join(self.buildmanager._cachepath, 'chroot.tar.gz')],
              None),
             self.buildmanager.commands[-1])
@@ -351,6 +353,7 @@
               'unpack-chroot',
               '--backend=chroot', '--series=xenial', '--arch=amd64',
               self.buildid,
+              '--image-type', 'chroot',
               os.path.join(self.buildmanager._cachepath, 'chroot.tar.gz')],
              None),
             self.buildmanager.commands[-1])
@@ -421,3 +424,27 @@
         self.assertTrue(self.slave.wasCalled('buildOK'))
         self.assertTrue(self.slave.wasCalled('buildComplete'))
 
+    def test_iterate_lxd(self):
+        # The build manager passes the image_type argument through to
+        # unpack-chroot.
+        self.buildmanager.backend_name = 'lxd'
+        extra_args = {
+            'image_type': 'lxd',
+            'arch_tag': 'amd64',
+            'series': 'xenial',
+            }
+        self.startBuild(extra_args)
+
+        self.buildmanager.iterate(0)
+        self.assertEqual(DebianBuildState.UNPACK, self.getState())
+        self.assertEqual(
+            (['sharepath/slavebin/in-target', 'in-target',
+              'unpack-chroot',
+              '--backend=lxd', '--series=xenial', '--arch=amd64',
+              self.buildid,
+              '--image-type', 'lxd',
+              os.path.join(self.buildmanager._cachepath, 'chroot.tar.gz')],
+             None),
+            self.buildmanager.commands[-1])
+        self.assertEqual(
+            self.buildmanager.iterate, self.buildmanager.iterators[-1])