← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/pocket-chroot-image-type into lp:launchpad with lp:~cjwatson/launchpad/pocket-chroot-gz as a prerequisite.

Commit message:
Allow setting PocketChroots with different image types.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1811677 in Launchpad itself: "Allow dispatching livefs and snap builds based on LXD images"
  https://bugs.launchpad.net/launchpad/+bug/1811677

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/pocket-chroot-image-type/+merge/361734

This needs https://code.launchpad.net/~cjwatson/launchpad/db-pocket-chroot-image-type/+merge/361733.  https://code.launchpad.net/~cjwatson/launchpad-buildd/image-type/+merge/361633 would also be a good idea, but isn't critical until we start dispatching builds with different image types (not done in this branch, but it should now just be a matter of setting image_types on the relevant build behaviours).
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/pocket-chroot-image-type into lp:launchpad.
=== modified file 'lib/lp/buildmaster/enums.py'
--- lib/lp/buildmaster/enums.py	2015-07-23 16:02:58 +0000
+++ lib/lp/buildmaster/enums.py	2019-01-14 14:21:36 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2014 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2019 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Common build interfaces."""
@@ -6,6 +6,7 @@
 __metaclass__ = type
 
 __all__ = [
+    'BuildBaseImageType',
     'BuilderCleanStatus',
     'BuilderResetProtocol',
     'BuildStatus',
@@ -238,3 +239,13 @@
         the webservice to set Builder.clean_status back to 'Clean' when
         the slave is reset and accepting requests.
         """)
+
+
+class BuildBaseImageType(DBEnumeratedType):
+    """Build base image type
+
+    The type of a base image that can be used for builds.
+    """
+
+    CHROOT = DBItem(0, "Chroot tarball")
+    LXD = DBItem(1, "LXD image")

=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py'
--- lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py	2019-01-14 14:21:36 +0000
+++ lib/lp/buildmaster/interfaces/buildfarmjobbehaviour.py	2019-01-14 14:21:36 +0000
@@ -21,6 +21,10 @@
         "The name of the builder type to use for this build, corresponding "
         "to a launchpad-buildd build manager tag.")
 
+    image_types = Attribute(
+        "A list of `BuildBaseImageType`s indicating which types of base "
+        "images can be used for this build.")
+
     archive = Attribute("The `Archive` to build against.")
 
     distro_arch_series = Attribute("The `DistroArchSeries` to build against.")

=== modified file 'lib/lp/buildmaster/model/buildfarmjobbehaviour.py'
--- lib/lp/buildmaster/model/buildfarmjobbehaviour.py	2019-01-14 14:21:36 +0000
+++ lib/lp/buildmaster/model/buildfarmjobbehaviour.py	2019-01-14 14:21:36 +0000
@@ -20,6 +20,7 @@
 from zope.component import getUtility
 
 from lp.buildmaster.enums import (
+    BuildBaseImageType,
     BuildFarmJobType,
     BuildStatus,
     )
@@ -45,6 +46,8 @@
     All build-farm job behaviours should inherit from this.
     """
 
+    image_types = [BuildBaseImageType.CHROOT]
+
     def __init__(self, build):
         """Store a reference to the job_type with which we were created."""
         self.build = build
@@ -113,10 +116,17 @@
             self.composeBuildRequest(logger))
 
         # First cache the chroot and any other files that the job needs.
-        chroot = das.getChroot(pocket=pocket)
-        if chroot is None:
+        pocket_chroot = None
+        for image_type in self.image_types:
+            pocket_chroot = das.getPocketChroot(
+                pocket=pocket, image_type=image_type)
+            if pocket_chroot is not None:
+                break
+        if pocket_chroot is None:
             raise CannotBuild(
                 "Unable to find a chroot for %s" % das.displayname)
+        chroot = pocket_chroot.chroot
+        args["image_type"] = pocket_chroot.image_type.name.lower()
 
         filename_to_sha1 = {}
         dl = []

=== modified file 'lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py'
--- lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py	2019-01-14 14:21:36 +0000
+++ lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py	2019-01-14 14:21:36 +0000
@@ -20,7 +20,10 @@
 from zope.security.proxy import removeSecurityProxy
 
 from lp.archiveuploader.uploadprocessor import parse_build_upload_leaf_name
-from lp.buildmaster.enums import BuildStatus
+from lp.buildmaster.enums import (
+    BuildBaseImageType,
+    BuildStatus,
+    )
 from lp.buildmaster.interactor import BuilderInteractor
 from lp.buildmaster.interfaces.builder import BuildDaemonError
 from lp.buildmaster.interfaces.buildfarmjobbehaviour import (
@@ -74,10 +77,26 @@
         self.http_url = 'http://librarian.dev/%s' % filename
 
 
+class FakePocketChroot:
+
+    def __init__(self, chroot, image_type):
+        self.chroot = chroot
+        self.image_type = image_type
+
+
 class FakeDistroArchSeries:
 
-    def getChroot(self, pocket=None):
-        return FakeLibraryFileAlias('chroot-fooix-bar-y86.tar.gz')
+    def __init__(self):
+        self.images = {
+            BuildBaseImageType.CHROOT: 'chroot-fooix-bar-y86.tar.gz',
+            }
+
+    def getPocketChroot(self, pocket, exact_pocket=False, image_type=None):
+        if image_type in self.images:
+            return FakePocketChroot(
+                FakeLibraryFileAlias(self.images[image_type]), image_type)
+        else:
+            return None
 
 
 class TestBuildFarmJobBehaviourBase(TestCaseWithFactory):
@@ -137,8 +156,7 @@
 
     run_tests_with = AsynchronousDeferredRunTest
 
-    @defer.inlineCallbacks
-    def test_dispatchBuildToSlave(self):
+    def makeBehaviour(self, das):
         files = {
             'foo.dsc': {'url': 'http://host/foo.dsc', 'sha1': '0'},
             'bar.tar': {
@@ -146,27 +164,25 @@
                 'username': 'admin', 'password': 'sekrit'}}
 
         behaviour = BuildFarmJobBehaviourBase(FakeBuildFarmJob())
-        builder = MockBuilder()
-        slave = OkSlave()
-        logger = BufferLogger()
         behaviour.composeBuildRequest = FakeMethod(
-            ('foobuild', FakeDistroArchSeries(),
-             PackagePublishingPocket.RELEASE, files,
+            ('foobuild', das, PackagePublishingPocket.RELEASE, files,
              {'some': 'arg', 'archives': ['http://admin:sekrit@blah/']}))
-        behaviour.setBuilder(builder, slave)
-        yield behaviour.dispatchBuildToSlave(logger)
+        return behaviour
 
+    def assertDispatched(self, slave, logger, chroot_filename, image_type):
         # The slave's been asked to cache the chroot and both source
         # files, and then to start the build.
         expected_calls = [
             ('ensurepresent',
-             'http://librarian.dev/chroot-fooix-bar-y86.tar.gz', '', ''),
+             'http://librarian.dev/%s' % chroot_filename, '', ''),
             ('ensurepresent', 'http://host/bar.tar', 'admin', 'sekrit'),
             ('ensurepresent', 'http://host/foo.dsc', '', ''),
             ('build', 'PACKAGEBUILD-1', 'foobuild',
-             hashlib.sha1('chroot-fooix-bar-y86.tar.gz').hexdigest(),
+             hashlib.sha1(chroot_filename).hexdigest(),
              ['foo.dsc', 'bar.tar'],
-             {'archives': ['http://admin:sekrit@blah/'], 'some': 'arg'})]
+             {'archives': ['http://admin:sekrit@blah/'],
+              'image_type': image_type,
+              'some': 'arg'})]
         self.assertEqual(expected_calls, slave.call_log)
 
         # And details have been logged, including the build arguments
@@ -184,6 +200,63 @@
             "INFO Job PACKAGEBUILD-1 (some job for something) started on "
             "http://fake:0000: BuildStatus.BUILDING PACKAGEBUILD-1\n")
 
+    @defer.inlineCallbacks
+    def test_dispatchBuildToSlave(self):
+        behaviour = self.makeBehaviour(FakeDistroArchSeries())
+        builder = MockBuilder()
+        slave = OkSlave()
+        logger = BufferLogger()
+        behaviour.setBuilder(builder, slave)
+        yield behaviour.dispatchBuildToSlave(logger)
+
+        self.assertDispatched(
+            slave, logger, 'chroot-fooix-bar-y86.tar.gz', 'chroot')
+
+    @defer.inlineCallbacks
+    def test_dispatchBuildToSlave_with_other_image_available(self):
+        # If a base image is available but isn't in the behaviour's image
+        # types, it isn't used.
+        das = FakeDistroArchSeries()
+        das.images[BuildBaseImageType.LXD] = 'lxd-fooix-bar-y86.tar.gz'
+        behaviour = self.makeBehaviour(das)
+        builder = MockBuilder()
+        slave = OkSlave()
+        logger = BufferLogger()
+        behaviour.setBuilder(builder, slave)
+        yield behaviour.dispatchBuildToSlave(logger)
+
+        self.assertDispatched(
+            slave, logger, 'chroot-fooix-bar-y86.tar.gz', 'chroot')
+
+    @defer.inlineCallbacks
+    def test_dispatchBuildToSlave_lxd(self):
+        das = FakeDistroArchSeries()
+        das.images[BuildBaseImageType.LXD] = 'lxd-fooix-bar-y86.tar.gz'
+        behaviour = self.makeBehaviour(das)
+        behaviour.image_types = [
+            BuildBaseImageType.LXD, BuildBaseImageType.CHROOT]
+        builder = MockBuilder()
+        slave = OkSlave()
+        logger = BufferLogger()
+        behaviour.setBuilder(builder, slave)
+        yield behaviour.dispatchBuildToSlave(logger)
+
+        self.assertDispatched(slave, logger, 'lxd-fooix-bar-y86.tar.gz', 'lxd')
+
+    @defer.inlineCallbacks
+    def test_dispatchBuildToSlave_fallback(self):
+        behaviour = self.makeBehaviour(FakeDistroArchSeries())
+        behaviour.image_types = [
+            BuildBaseImageType.LXD, BuildBaseImageType.CHROOT]
+        builder = MockBuilder()
+        slave = OkSlave()
+        logger = BufferLogger()
+        behaviour.setBuilder(builder, slave)
+        yield behaviour.dispatchBuildToSlave(logger)
+
+        self.assertDispatched(
+            slave, logger, 'chroot-fooix-bar-y86.tar.gz', 'chroot')
+
 
 class TestGetUploadMethodsMixin:
     """Tests for `IPackageBuild` that need objects from the rest of LP."""

=== modified file 'lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py'
--- lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py	2019-01-14 14:21:36 +0000
+++ lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py	2019-01-14 14:21:36 +0000
@@ -13,6 +13,7 @@
     )
 from zope.security.management import endInteraction
 
+from lp.buildmaster.enums import BuildBaseImageType
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.services.features.testing import FeatureFixture
 from lp.soyuz.interfaces.livefs import LIVEFS_FEATURE_FLAG
@@ -155,6 +156,31 @@
         self.assertEqual(
             release_chroot_url, ws_das.getChrootURL(pocket='Proposed'))
 
+    def test_setChroot_removeChroot_image_type(self):
+        das = self.factory.makeDistroArchSeries()
+        user = das.distroseries.distribution.main_archive.owner
+        webservice = launchpadlib_for("testing", user)
+        ws_das = ws_object(webservice, das)
+        sha1_1 = hashlib.sha1('abcxyz').hexdigest()
+        ws_das.setChroot(data='abcxyz', sha1sum=sha1_1)
+        sha1_2 = hashlib.sha1('123456').hexdigest()
+        ws_das.setChroot(data='123456', sha1sum=sha1_2, image_type='LXD image')
+        chroot_image = das.getChroot(image_type=BuildBaseImageType.CHROOT)
+        self.assertEqual(sha1_1, chroot_image.content.sha1)
+        lxd_image = das.getChroot(image_type=BuildBaseImageType.LXD)
+        self.assertEqual(sha1_2, lxd_image.content.sha1)
+        with person_logged_in(user):
+            chroot_image_url = chroot_image.http_url
+            lxd_image_url = lxd_image.http_url
+        self.assertEqual(
+            chroot_image_url, ws_das.getChrootURL(image_type='Chroot tarball'))
+        self.assertEqual(
+            lxd_image_url, ws_das.getChrootURL(image_type='LXD image'))
+        ws_das.removeChroot(image_type='LXD image')
+        self.assertEqual(
+            chroot_image_url, ws_das.getChrootURL(image_type='Chroot tarball'))
+        self.assertIsNone(ws_das.getChrootURL(image_type='LXD image'))
+
     def test_setChrootFromBuild(self):
         self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
         das = self.factory.makeDistroArchSeries()
@@ -210,3 +236,21 @@
             das.getChroot(pocket=PackagePublishingPocket.RELEASE))
         self.assertEqual(
             lfa, das.getChroot(pocket=PackagePublishingPocket.UPDATES))
+
+    def test_setChrootFromBuild_image_type(self):
+        self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: "on"}))
+        das = self.factory.makeDistroArchSeries()
+        build = self.factory.makeLiveFSBuild()
+        build_url = api_url(build)
+        login_as(build.livefs.owner)
+        lfa = self.factory.makeLibraryFileAlias(
+            filename="livecd.ubuntu-base.lxd.tar.gz")
+        build.addFile(lfa)
+        user = das.distroseries.distribution.main_archive.owner
+        webservice = launchpadlib_for("testing", user)
+        ws_das = ws_object(webservice, das)
+        ws_das.setChrootFromBuild(
+            livefsbuild=build_url, filename="livecd.ubuntu-base.lxd.tar.gz",
+            image_type="LXD image")
+        self.assertIsNone(das.getChroot(image_type=BuildBaseImageType.CHROOT))
+        self.assertEqual(lfa, das.getChroot(image_type=BuildBaseImageType.LXD))

=== modified file 'lib/lp/soyuz/interfaces/distroarchseries.py'
--- lib/lp/soyuz/interfaces/distroarchseries.py	2019-01-14 14:21:36 +0000
+++ lib/lp/soyuz/interfaces/distroarchseries.py	2019-01-14 14:21:36 +0000
@@ -41,6 +41,7 @@
 
 from lp import _
 from lp.app.validators.name import name_validator
+from lp.buildmaster.enums import BuildBaseImageType
 from lp.buildmaster.interfaces.processor import IProcessor
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.person import IPerson
@@ -153,8 +154,8 @@
         series.
         """
 
-    def getPocketChroot(pocket, exact_pocket=False):
-        """Return the PocketChroot for this distroarchseries and given pocket.
+    def getPocketChroot(pocket, exact_pocket=False, image_type=None):
+        """Return the PocketChroot for this series, pocket, and image type.
 
         If exact_pocket is False, this follows pocket dependencies and finds
         the chroot for the closest pocket that exists: for example, if no
@@ -163,20 +164,21 @@
         given pocket.
         """
 
-    def getChroot(default=None, pocket=None):
-        """Return the Chroot for this distroarchseries and pocket.
+    def getChroot(default=None, pocket=None, image_type=None):
+        """Return the Chroot for this series, pocket, and image type.
 
         It uses getPocketChroot and if not found returns 'default'.
         """
 
     @operation_parameters(
-        pocket=Choice(vocabulary=PackagePublishingPocket, required=False))
+        pocket=Choice(vocabulary=PackagePublishingPocket, required=False),
+        image_type=Choice(vocabulary=BuildBaseImageType, required=False))
     @export_read_operation()
     @operation_for_version("devel")
-    def getChrootURL(pocket=None):
-        """Return the chroot URL for this distroarchseries and pocket."""
+    def getChrootURL(pocket=None, image_type=None):
+        """Return the chroot URL for this series, pocket, and image type."""
 
-    def addOrUpdateChroot(chroot, pocket=None):
+    def addOrUpdateChroot(chroot, pocket=None, image_type=None):
         """Return the just added or modified PocketChroot."""
 
     def searchBinaryPackages(text):
@@ -196,10 +198,11 @@
 
     @operation_parameters(
         data=Bytes(), sha1sum=Text(),
-        pocket=Choice(vocabulary=PackagePublishingPocket, required=False))
+        pocket=Choice(vocabulary=PackagePublishingPocket, required=False),
+        image_type=Choice(vocabulary=BuildBaseImageType, required=False))
     @export_write_operation()
     @operation_for_version("devel")
-    def setChroot(data, sha1sum, pocket=None):
+    def setChroot(data, sha1sum, pocket=None, image_type=None):
         """Set the chroot tarball used for builds in this architecture.
 
         The SHA-1 checksum must match the chroot file.
@@ -210,17 +213,20 @@
         livefsbuild=Reference(
             Interface, title=_("Live filesystem build"), required=True),
         filename=TextLine(title=_("Filename"), required=True),
-        pocket=Choice(vocabulary=PackagePublishingPocket, required=False))
+        pocket=Choice(vocabulary=PackagePublishingPocket, required=False),
+        image_type=Choice(vocabulary=BuildBaseImageType, required=False))
     @export_write_operation()
     @operation_for_version("devel")
-    def setChrootFromBuild(livefsbuild, filename, pocket=None):
+    def setChrootFromBuild(livefsbuild, filename, pocket=None,
+                           image_type=None):
         """Set the chroot tarball from a live filesystem build."""
 
     @operation_parameters(
-        pocket=Choice(vocabulary=PackagePublishingPocket, required=False))
+        pocket=Choice(vocabulary=PackagePublishingPocket, required=False),
+        image_type=Choice(vocabulary=BuildBaseImageType, required=False))
     @export_write_operation()
     @operation_for_version("devel")
-    def removeChroot(pocket=None):
+    def removeChroot(pocket=None, image_type=None):
         """Remove the chroot tarball used for builds in this architecture."""
 
 
@@ -236,6 +242,7 @@
         "The DistroArchSeries this chroot belongs to.")
     pocket = Attribute("The Pocket this chroot is for.")
     chroot = Attribute("The file alias of the chroot.")
+    image_type = Attribute("The type of this image.")
 
     def syncUpdate():
         """Commit changes to DB."""

=== modified file 'lib/lp/soyuz/model/distroarchseries.py'
--- lib/lp/soyuz/model/distroarchseries.py	2019-01-14 14:21:36 +0000
+++ lib/lp/soyuz/model/distroarchseries.py	2019-01-14 14:21:36 +0000
@@ -28,6 +28,7 @@
 from zope.component import getUtility
 from zope.interface import implementer
 
+from lp.buildmaster.enums import BuildBaseImageType
 from lp.buildmaster.model.processor import Processor
 from lp.registry.interfaces.person import validate_public_person
 from lp.registry.interfaces.pocket import PackagePublishingPocket
@@ -128,8 +129,10 @@
         return (self.distroseries.nominatedarchindep is not None and
                 self.id == self.distroseries.nominatedarchindep.id)
 
-    def getPocketChroot(self, pocket, exact_pocket=False):
+    def getPocketChroot(self, pocket, exact_pocket=False, image_type=None):
         """See `IDistroArchSeries`."""
+        if image_type is None:
+            image_type = BuildBaseImageType.CHROOT
         pockets = [pocket] if exact_pocket else pocket_dependencies[pocket]
         pocket_chroots = {
             pocket_chroot.pocket: pocket_chroot
@@ -137,26 +140,27 @@
                 PocketChroot,
                 PocketChroot.distroarchseries == self,
                 PocketChroot.pocket.is_in(pockets),
+                PocketChroot.image_type == image_type,
                 PocketChroot.chroot != None)}
         for pocket_dep in reversed(pockets):
             if pocket_dep in pocket_chroots:
                 return pocket_chroots[pocket_dep]
         return None
 
-    def getChroot(self, default=None, pocket=None):
+    def getChroot(self, default=None, pocket=None, image_type=None):
         """See `IDistroArchSeries`."""
         if pocket is None:
             pocket = PackagePublishingPocket.RELEASE
-        pocket_chroot = self.getPocketChroot(pocket)
+        pocket_chroot = self.getPocketChroot(pocket, image_type=image_type)
 
         if pocket_chroot is None:
             return default
 
         return pocket_chroot.chroot
 
-    def getChrootURL(self, pocket=None):
+    def getChrootURL(self, pocket=None, image_type=None):
         """See `IDistroArchSeries`."""
-        chroot = self.getChroot(pocket=pocket)
+        chroot = self.getChroot(pocket=pocket, image_type=image_type)
         if chroot is None:
             return None
         return chroot.http_url
@@ -166,21 +170,25 @@
         """See `IDistroArchSeries`."""
         return self.getChrootURL()
 
-    def addOrUpdateChroot(self, chroot, pocket=None):
+    def addOrUpdateChroot(self, chroot, pocket=None, image_type=None):
         """See `IDistroArchSeries`."""
         if pocket is None:
             pocket = PackagePublishingPocket.RELEASE
-        pocket_chroot = self.getPocketChroot(pocket, exact_pocket=True)
+        if image_type is None:
+            image_type = BuildBaseImageType.CHROOT
+        pocket_chroot = self.getPocketChroot(
+            pocket, exact_pocket=True, image_type=image_type)
 
         if pocket_chroot is None:
             return PocketChroot(
-                distroarchseries=self, pocket=pocket, chroot=chroot)
+                distroarchseries=self, pocket=pocket, chroot=chroot,
+                image_type=image_type)
         else:
             pocket_chroot.chroot = chroot
 
         return pocket_chroot
 
-    def setChroot(self, data, sha1sum, pocket=None):
+    def setChroot(self, data, sha1sum, pocket=None, image_type=None):
         """See `IDistroArchSeries`."""
         # XXX: StevenK 2013-06-06 bug=1116954: We should not need to refetch
         # the file content from the request, since the passed in one has been
@@ -204,7 +212,13 @@
         if content_sha1sum != sha1sum:
             raise InvalidChrootUploaded("Chroot upload checksums do not match")
 
-        filename = 'chroot-%s-%s-%s.tar.gz' % (
+        # This duplicates addOrUpdateChroot, but we need it to build a
+        # reasonable filename.
+        if image_type is None:
+            image_type = BuildBaseImageType.CHROOT
+
+        filename = '%s-%s-%s-%s.tar.gz' % (
+            image_type.name.lower().split()[0],
             self.distroseries.distribution.name, self.distroseries.name,
             self.architecturetag)
         lfa = getUtility(ILibraryFileAliasSet).create(
@@ -212,16 +226,18 @@
             contentType='application/octet-stream')
         if lfa.content.sha1 != sha1sum:
             raise InvalidChrootUploaded("Chroot upload checksums do not match")
-        self.addOrUpdateChroot(lfa, pocket=pocket)
+        self.addOrUpdateChroot(lfa, pocket=pocket, image_type=image_type)
 
-    def setChrootFromBuild(self, livefsbuild, filename, pocket=None):
+    def setChrootFromBuild(self, livefsbuild, filename, pocket=None,
+                           image_type=None):
         """See `IDistroArchSeries`."""
         self.addOrUpdateChroot(
-            livefsbuild.getFileByName(filename), pocket=pocket)
+            livefsbuild.getFileByName(filename), pocket=pocket,
+            image_type=image_type)
 
-    def removeChroot(self, pocket=None):
+    def removeChroot(self, pocket=None, image_type=None):
         """See `IDistroArchSeries`."""
-        self.addOrUpdateChroot(None, pocket=pocket)
+        self.addOrUpdateChroot(None, pocket=pocket, image_type=image_type)
 
     def searchBinaryPackages(self, text):
         """See `IDistroArchSeries`."""
@@ -322,3 +338,7 @@
         default=PackagePublishingPocket.RELEASE, notNull=True)
 
     chroot = ForeignKey(dbName='chroot', foreignKey='LibraryFileAlias')
+
+    image_type = EnumCol(
+        schema=BuildBaseImageType, default=BuildBaseImageType.CHROOT,
+        notNull=True)

=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py'
--- lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py	2018-05-02 13:22:17 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuildbehaviour.py	2019-01-14 14:21:36 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010-2018 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2019 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for BinaryPackageBuildBehaviour."""
@@ -144,6 +144,7 @@
             'build_url': canonical_url(build),
             'distribution': das.distroseries.distribution.name,
             'fast_cleanup': builder.virtualized,
+            'image_type': 'chroot',
             'ogrecomponent': component,
             'series': ds_name,
             'suite': suite,


Follow ups