← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~pappacena/launchpad:oci-build-info-to-buildd into launchpad:master

 

Thiago F. Pappacena has proposed merging ~pappacena/launchpad:oci-build-info-to-buildd into launchpad:master.

Commit message:
Adding extra meta info when requesting an OCI build to buildd

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/392500

This information will be useful on buildd side to create the OCI security manifest file.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:oci-build-info-to-buildd into launchpad:master.
diff --git a/database/schema/security.cfg b/database/schema/security.cfg
index d5a23f1..11aba6f 100644
--- a/database/schema/security.cfg
+++ b/database/schema/security.cfg
@@ -1004,6 +1004,7 @@ public.ocipushrule                            = SELECT
 public.ocirecipe                              = SELECT
 public.ocirecipebuild                         = SELECT, UPDATE
 public.ocirecipebuildjob                      = SELECT, INSERT
+public.ocirecipejob                           = SELECT
 public.openididentifier                       = SELECT
 public.packageset                             = SELECT
 public.packagesetgroup                        = SELECT
diff --git a/lib/lp/oci/model/ocirecipebuildbehaviour.py b/lib/lp/oci/model/ocirecipebuildbehaviour.py
index 1008884..e83d8fe 100644
--- a/lib/lp/oci/model/ocirecipebuildbehaviour.py
+++ b/lib/lp/oci/model/ocirecipebuildbehaviour.py
@@ -38,6 +38,7 @@ from lp.buildmaster.model.buildfarmjobbehaviour import (
 from lp.oci.interfaces.ocirecipebuild import IOCIFileSet
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.librarian.utils import copy_and_close
+from lp.services.webapp import canonical_url
 from lp.snappy.model.snapbuildbehaviour import SnapProxyMixin
 from lp.soyuz.adapters.archivedependencies import (
     get_sources_list_for_building,
@@ -77,6 +78,38 @@ class OCIRecipeBuildBehaviour(SnapProxyMixin, BuildFarmJobBehaviourBase):
             raise CannotBuild(
                 "Missing chroot for %s" % build.distro_arch_series.displayname)
 
+    def getBuildInfoArgs(self):
+        def format_user(user):
+            if user is None:
+                return None
+            hide_email = not user.preferredemail or user.hide_email_addresses
+            return {
+                "name": user.name,
+                "email": (None if hide_email else user.preferredemail.email)}
+        build = self.build
+        build_request = build.build_request
+        builds = list(build_request.builds) if build_request else [build]
+        info = {
+            "architectures": [],
+            "recipe_owner": format_user(self.build.recipe.owner),
+            "build_request_id": None,
+            "build_request_timestamp": None,
+            # With build_request set, all builds in this list will have the
+            # same requester. Without build_request, we only care about the
+            # only existing build in this list.
+            "build_requester": format_user(builds[0].requester),
+            # Build URL per architecture.
+            "build_urls": {},
+        }
+        if build_request:
+            info["build_request_id"] = build_request.id
+            info["build_request_timestamp"] = (
+                build_request.date_requested.isoformat())
+        info["architectures"] = [i.processor.name for i in builds]
+        info["build_urls"] = {
+            i.processor.name: canonical_url(i) for i in builds}
+        return info
+
     @defer.inlineCallbacks
     def extraBuildArgs(self, logger=None):
         """
@@ -101,6 +134,7 @@ class OCIRecipeBuildBehaviour(SnapProxyMixin, BuildFarmJobBehaviourBase):
         # XML-RPC.
         args['build_args'] = removeSecurityProxy(build.recipe.build_args)
         args['build_path'] = build.recipe.build_path
+        args['metadata'] = self.getBuildInfoArgs()
 
         if build.recipe.git_ref is not None:
             args["git_repository"] = (
diff --git a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
index ded60cf..476899d 100644
--- a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
+++ b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
@@ -104,7 +104,7 @@ class MakeOCIBuildMixin:
         build.queueBuild()
         return build
 
-    def makeJob(self, git_ref, recipe=None, build=None, **kwargs):
+    def makeJob(self, git_ref=None, recipe=None, build=None, **kwargs):
         """Create a sample `IOCIRecipeBuildBehaviour`."""
         if build is None:
             if recipe is None:
@@ -112,6 +112,8 @@ class MakeOCIBuildMixin:
             else:
                 build = self.factory.makeOCIRecipeBuild(
                     recipe=recipe, **kwargs)
+        if git_ref is None:
+            [git_ref] = self.factory.makeGitRefs()
         build.recipe.git_ref = git_ref
         build.recipe.build_args = {"BUILD_VAR": "123"}
 
@@ -131,7 +133,7 @@ class MakeOCIBuildMixin:
         return job
 
 
-class TestOCIBuildBehaviour(TestCaseWithFactory):
+class TestOCIBuildBehaviour(TestCaseWithFactory, MakeOCIBuildMixin):
 
     layer = LaunchpadZopelessLayer
 
@@ -150,6 +152,89 @@ class TestOCIBuildBehaviour(TestCaseWithFactory):
         job = IBuildFarmJobBehaviour(build)
         self.assertProvides(job, IBuildFarmJobBehaviour)
 
+    def makeRecipe(self, **kwargs):
+        amd64 = getUtility(IProcessorSet).getByName("amd64")
+        recipe = self.factory.makeOCIRecipe(**kwargs)
+        distroseries = self.factory.makeDistroSeries(
+            distribution=recipe.oci_project.distribution)
+        distro = self.factory.makeDistroArchSeries(
+            distroseries=distroseries, architecturetag="amd64",
+            processor=amd64)
+        distro.addOrUpdateChroot(self.factory.makeLibraryFileAlias())
+        recipe.setProcessors([amd64])
+        return recipe
+
+    def makeBuildRequest(self, recipe, requester):
+        build_request = recipe.requestBuilds(requester)
+        # Create the builds for the build request, and set them at the build
+        # request job.
+        builds = recipe.requestBuildsFromJob(requester, build_request)
+        job = removeSecurityProxy(build_request).job
+        removeSecurityProxy(job).builds = builds
+        return build_request
+
+    def test_getBuildInfoArgs_with_build_request(self):
+        owner = self.factory.makePerson()
+        owner.setPreferredEmail(self.factory.makeEmail('owner@xxxxxxx', owner))
+        oci_project = self.factory.makeOCIProject(registrant=owner)
+        recipe = self.makeRecipe(
+            oci_project=oci_project, registrant=owner, owner=owner)
+        build_request = self.makeBuildRequest(recipe, recipe.owner)
+        build = build_request.builds[0]
+        job = self.makeJob(build=build)
+
+        self.assertThat(job.getBuildInfoArgs(), MatchesDict({
+            "architectures": Equals(["amd64"]),
+            "recipe_owner": Equals({
+                "name": recipe.owner.name,
+                "email": "owner@xxxxxxx"}),
+            "build_request_id": Equals(build_request.id),
+            "build_requester": Equals({
+                "name": build.requester.name,
+                "email": "owner@xxxxxxx"}),
+            "build_request_timestamp": Equals(
+                build_request.date_requested.isoformat()),
+            "build_urls": MatchesDict({
+                "amd64": Equals(canonical_url(build_request.builds[0]))
+            }),
+        }))
+
+    def test_getBuildInfoArgs_hide_email(self):
+        owner = self.factory.makePerson()
+        owner.setPreferredEmail(self.factory.makeEmail('owner@xxxxxxx', owner))
+        owner.hide_email_addresses = True
+        oci_project = self.factory.makeOCIProject(registrant=owner)
+        recipe = self.makeRecipe(
+            oci_project=oci_project, registrant=owner, owner=owner)
+        build_request = self.makeBuildRequest(recipe, recipe.owner)
+        build = build_request.builds[0]
+        job = self.makeJob(build=build)
+
+        self.assertThat(job.getBuildInfoArgs(), MatchesDict({
+            "architectures": Equals(["amd64"]),
+            "recipe_owner": Equals({"name": recipe.owner.name, "email": None}),
+            "build_request_id": Equals(build_request.id),
+            "build_requester": Equals({
+                "name": build.requester.name, "email": None}),
+            "build_request_timestamp": Equals(
+                build_request.date_requested.isoformat()),
+            "build_urls": MatchesDict({
+                "amd64": Equals(canonical_url(build_request.builds[0]))
+            }),
+        }))
+
+    def test_getBuildInfoArgs_without_build_request(self):
+        build = self.factory.makeOCIRecipeBuild()
+        job = self.makeJob(build=build)
+        self.assertThat(job.getBuildInfoArgs(), ContainsDict({
+            "architectures": Equals(["386"]),
+            "build_request_id": Equals(None),
+            "build_request_timestamp": Equals(None),
+            "build_urls": MatchesDict({
+                "386": Equals(canonical_url(build))
+            }),
+        }))
+
 
 class TestAsyncOCIRecipeBuildBehaviour(
         StatsMixin, MakeOCIBuildMixin, TestCaseWithFactory):
@@ -279,7 +364,14 @@ class TestAsyncOCIRecipeBuildBehaviour(
             "revocation_endpoint":  RevocationEndpointMatcher(job, self.now),
             "series": Equals(job.build.distro_arch_series.distroseries.name),
             "trusted_keys": Equals(expected_trusted_keys),
-            }))
+            # 'metadata' has detailed tests at TestOCIBuildBehaviour class.
+            "metadata": ContainsDict({
+                "architectures": Equals(["386"]),
+                "build_request_id": Equals(None),
+                "build_request_timestamp": Equals(None),
+                "build_urls": Equals({"386": canonical_url(job.build)})
+            })
+        }))
 
     @defer.inlineCallbacks
     def test_extraBuildArgs_git_HEAD(self):
@@ -311,7 +403,14 @@ class TestAsyncOCIRecipeBuildBehaviour(
             "revocation_endpoint":  RevocationEndpointMatcher(job, self.now),
             "series": Equals(job.build.distro_arch_series.distroseries.name),
             "trusted_keys": Equals(expected_trusted_keys),
-            }))
+            # 'metadata' has detailed tests at TestOCIBuildBehaviour class.
+            "metadata": ContainsDict({
+                "architectures": Equals(["386"]),
+                "build_request_id": Equals(None),
+                "build_request_timestamp": Equals(None),
+                "build_urls": Equals({"386": canonical_url(job.build)})
+            })
+        }))
 
     @defer.inlineCallbacks
     def test_composeBuildRequest_proxy_url_set(self):