← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/slimmer-bfjo into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/slimmer-bfjo into lp:launchpad.

Commit message:
Eliminate most of IBuildFarmJobOld.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/slimmer-bfjo/+merge/194767

Inline IBuildFarmJobOld.{processor,virtualized,jobStarted,jobCancelled,jobReset} and move IBuildFarmJobOld.{addCandidateSelectionCriteria,postprocessCandidate} to ISpecificBuildFarmJobSource.

IBuildFarmJobOld will shortly perish.
-- 
https://code.launchpad.net/~wgrant/launchpad/slimmer-bfjo/+merge/194767
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/slimmer-bfjo into lp:launchpad.
=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
--- lib/lp/buildmaster/interfaces/buildfarmjob.py	2013-09-02 12:45:50 +0000
+++ lib/lp/buildmaster/interfaces/buildfarmjob.py	2013-11-11 23:47:32 +0000
@@ -59,67 +59,9 @@
     BuildQueue have been transitioned to the new database schema.
     """
 
-    processor = Reference(
-        IProcessor, title=_("Processor"), required=False, readonly=True,
-        description=_(
-            "The Processor required by this build farm job. "
-            "This should be None for processor-independent job types."))
-
-    virtualized = Bool(
-        title=_('Virtualized'), required=False, readonly=True,
-        description=_(
-            "The virtualization setting required by this build farm job. "
-            "This should be None for job types that do not care whether "
-            "they run virtualized."))
-
     def score():
         """Calculate a job score appropriate for the job type in question."""
 
-    def jobStarted():
-        """'Job started' life cycle event, handle as appropriate."""
-
-    def jobReset():
-        """'Job reset' life cycle event, handle as appropriate."""
-
-    def jobCancel():
-        """'Job cancel' life cycle event."""
-
-    def addCandidateSelectionCriteria(processor, virtualized):
-        """Provide a sub-query to refine the candidate job selection.
-
-        Return a sub-query to narrow down the list of candidate jobs.
-        The sub-query will become part of an "outer query" and is free to
-        refer to the `BuildQueue` and `Job` tables already utilized in the
-        latter.
-
-        Example (please see the `BuildPackageJob` implementation for a
-        complete example):
-
-            SELECT TRUE
-            FROM Archive, Build, BuildPackageJob, DistroArchSeries
-            WHERE
-            BuildPackageJob.job = Job.id AND
-            ..
-
-        :param processor: the type of processor that the candidate jobs are
-            expected to run on.
-        :param virtualized: whether the candidate jobs are expected to run on
-            the `processor` natively or inside a virtual machine.
-        :return: a string containing a sub-query that narrows down the list of
-            candidate jobs.
-        """
-
-    def postprocessCandidate(job, logger):
-        """True if the candidate job is fine and should be dispatched
-        to a builder, False otherwise.
-
-        :param job: The `BuildQueue` instance to be scrutinized.
-        :param logger: The logger to use.
-
-        :return: True if the candidate job should be dispatched
-            to a builder, False otherwise.
-        """
-
     def getByJob(job):
         """Get the specific `IBuildFarmJob` for the given `Job`.
 
@@ -328,6 +270,42 @@
             job.
         """
 
+    def addCandidateSelectionCriteria(processor, virtualized):
+        """Provide a sub-query to refine the candidate job selection.
+
+        Return a sub-query to narrow down the list of candidate jobs.
+        The sub-query will become part of an "outer query" and is free to
+        refer to the `BuildQueue` and `Job` tables already utilized in the
+        latter.
+
+        Example (please see the `BuildPackageJob` implementation for a
+        complete example):
+
+            SELECT TRUE
+            FROM Archive, Build, BuildPackageJob, DistroArchSeries
+            WHERE
+            BuildPackageJob.job = Job.id AND
+            ..
+
+        :param processor: the type of processor that the candidate jobs are
+            expected to run on.
+        :param virtualized: whether the candidate jobs are expected to run on
+            the `processor` natively or inside a virtual machine.
+        :return: a string containing a sub-query that narrows down the list of
+            candidate jobs.
+        """
+
+    def postprocessCandidate(job, logger):
+        """True if the candidate job is fine and should be dispatched
+        to a builder, False otherwise.
+
+        :param job: The `BuildQueue` instance to be scrutinized.
+        :param logger: The logger to use.
+
+        :return: True if the candidate job should be dispatched
+            to a builder, False otherwise.
+        """
+
 
 class IBuildFarmJobSource(Interface):
     """A utility of BuildFarmJob used to create _things_."""

=== modified file 'lib/lp/buildmaster/model/builder.py'
--- lib/lp/buildmaster/model/builder.py	2013-10-26 11:00:53 +0000
+++ lib/lp/buildmaster/model/builder.py	2013-11-11 23:47:32 +0000
@@ -35,7 +35,7 @@
 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
 from lp.buildmaster.model.buildqueue import (
     BuildQueue,
-    specific_job_classes,
+    specific_build_farm_job_sources,
     )
 from lp.registry.interfaces.person import validate_public_person
 from lp.services.database.interfaces import (
@@ -206,9 +206,9 @@
         order_clause = " ORDER BY buildqueue.lastscore DESC, buildqueue.id"
 
         extra_queries = []
-        job_classes = specific_job_classes()
-        for job_type, job_class in job_classes.iteritems():
-            query = job_class.addCandidateSelectionCriteria(
+        job_sources = specific_build_farm_job_sources()
+        for job_type, job_source in job_sources.iteritems():
+            query = job_source.addCandidateSelectionCriteria(
                 self.processor, self.virtualized)
             if query == '':
                 # This job class does not need to refine candidate jobs
@@ -224,8 +224,8 @@
 
         for (candidate_id,) in candidate_jobs:
             candidate = getUtility(IBuildQueueSet).get(candidate_id)
-            job_class = job_classes[candidate.job_type]
-            candidate_approved = job_class.postprocessCandidate(
+            job_source = job_sources[candidate.job_type]
+            candidate_approved = job_source.postprocessCandidate(
                 candidate, logger)
             if candidate_approved:
                 return candidate

=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
--- lib/lp/buildmaster/model/buildfarmjob.py	2013-09-02 12:45:50 +0000
+++ lib/lp/buildmaster/model/buildfarmjob.py	2013-11-11 23:47:32 +0000
@@ -6,6 +6,7 @@
     'BuildFarmJob',
     'BuildFarmJobMixin',
     'BuildFarmJobOld',
+    'SpecificBuildFarmJobSourceMixin',
     ]
 
 import datetime
@@ -50,9 +51,6 @@
 
     implements(IBuildFarmJobOld)
 
-    processor = None
-    virtualized = None
-
     @staticmethod
     def preloadBuildFarmJobs(jobs):
         """Preload the build farm jobs to which the given jobs will delegate.
@@ -83,29 +81,6 @@
         """
         Store.of(self).remove(self)
 
-    @staticmethod
-    def addCandidateSelectionCriteria(processor, virtualized):
-        """See `IBuildFarmJobOld`."""
-        return ('')
-
-    @staticmethod
-    def postprocessCandidate(job, logger):
-        """See `IBuildFarmJobOld`."""
-        return True
-
-    def jobStarted(self):
-        """See `IBuildFarmJobOld`."""
-        # XXX wgrant: builder should be set here.
-        self.build.updateStatus(BuildStatus.BUILDING)
-
-    def jobReset(self):
-        """See `IBuildFarmJob`."""
-        self.build.updateStatus(BuildStatus.NEEDSBUILD)
-
-    def jobCancel(self):
-        """See `IBuildFarmJob`."""
-        self.build.updateStatus(BuildStatus.CANCELLED)
-
 
 class BuildFarmJob(Storm):
     """A base implementation for `IBuildFarmJob` classes."""
@@ -249,6 +224,19 @@
         self.failure_count += 1
 
 
+class SpecificBuildFarmJobSourceMixin:
+
+    @staticmethod
+    def addCandidateSelectionCriteria(processor, virtualized):
+        """See `ISpecificBuildFarmJobSource`."""
+        return ('')
+
+    @staticmethod
+    def postprocessCandidate(job, logger):
+        """See `ISpecificBuildFarmJobSource`."""
+        return True
+
+
 class BuildFarmJobSet:
     implements(IBuildFarmJobSet)
 

=== modified file 'lib/lp/buildmaster/model/buildqueue.py'
--- lib/lp/buildmaster/model/buildqueue.py	2013-11-10 23:56:44 +0000
+++ lib/lp/buildmaster/model/buildqueue.py	2013-11-11 23:47:32 +0000
@@ -7,6 +7,7 @@
     'BuildQueue',
     'BuildQueueSet',
     'specific_job_classes',
+    'specific_build_farm_job_sources',
     ]
 
 from datetime import datetime
@@ -21,11 +22,18 @@
     IntervalCol,
     StringCol,
     )
+from storm.store import Store
 from zope.component import getSiteManager
 from zope.interface import implements
 
-from lp.buildmaster.enums import BuildFarmJobType
-from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
+from lp.buildmaster.enums import (
+    BuildFarmJobType,
+    BuildStatus,
+    )
+from lp.buildmaster.interfaces.buildfarmjob import (
+    IBuildFarmJob,
+    ISpecificBuildFarmJobSource,
+    )
 from lp.buildmaster.interfaces.buildqueue import (
     IBuildQueue,
     IBuildQueueSet,
@@ -58,6 +66,26 @@
     return job_classes
 
 
+def specific_build_farm_job_sources():
+    """Sources for specific jobs that may run on the build farm."""
+    job_sources = dict()
+    # Get all components that implement the `ISpecificBuildFarmJobSource`
+    # interface.
+    components = getSiteManager()
+    implementations = sorted(
+        components.getUtilitiesFor(ISpecificBuildFarmJobSource))
+    # The above yields a collection of 2-tuples where the first element
+    # is the name of the `BuildFarmJobType` enum and the second element
+    # is the implementing class respectively.
+    for job_enum_name, job_source in implementations:
+        if not job_enum_name:
+            continue
+        job_enum = getattr(BuildFarmJobType, job_enum_name)
+        job_sources[job_enum] = job_source
+
+    return job_sources
+
+
 class BuildQueue(SQLBase):
     implements(IBuildQueue)
     _table = "BuildQueue"
@@ -133,8 +161,9 @@
         job = self.job
         specific_job = self.specific_job
         builder = self.builder
-        SQLBase.destroySelf(self)
+        Store.of(self).remove(self)
         specific_job.cleanUp()
+        Store.of(self).flush()
         job.destroySelf()
         if builder is not None:
             del get_property_cache(builder).currentjob
@@ -158,7 +187,7 @@
         self.builder = builder
         if self.job.status != JobStatus.RUNNING:
             self.job.start()
-        self.specific_job.jobStarted()
+        self.specific_job.build.updateStatus(BuildStatus.BUILDING)
         if builder is not None:
             del get_property_cache(builder).currentjob
 
@@ -171,13 +200,13 @@
         self.job.date_started = None
         self.job.date_finished = None
         self.logtail = None
-        self.specific_job.jobReset()
+        self.specific_job.build.updateStatus(BuildStatus.NEEDSBUILD)
         if builder is not None:
             del get_property_cache(builder).currentjob
 
     def cancel(self):
         """See `IBuildQueue`."""
-        self.specific_job.jobCancel()
+        self.specific_job.build.updateStatus(BuildStatus.CANCELLED)
         self.destroySelf()
 
     def getEstimatedJobStartTime(self, now=None):

=== modified file 'lib/lp/buildmaster/model/packagebuild.py'
--- lib/lp/buildmaster/model/packagebuild.py	2013-08-28 04:40:32 +0000
+++ lib/lp/buildmaster/model/packagebuild.py	2013-11-11 23:47:32 +0000
@@ -120,11 +120,10 @@
 
         duration_estimate = self.estimateDuration()
         job = specific_job.job
-        processor = specific_job.processor
         queue_entry = BuildQueue(
             estimated_duration=duration_estimate,
             job_type=self.job_type,
-            job=job, processor=processor,
-            virtualized=specific_job.virtualized)
+            job=job, processor=self.processor,
+            virtualized=self.virtualized)
         Store.of(self).add(queue_entry)
         return queue_entry

=== modified file 'lib/lp/buildmaster/tests/test_buildqueue.py'
--- lib/lp/buildmaster/tests/test_buildqueue.py	2013-10-31 06:29:13 +0000
+++ lib/lp/buildmaster/tests/test_buildqueue.py	2013-11-11 23:47:32 +0000
@@ -14,9 +14,11 @@
     BuildStatus,
     )
 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
-from lp.buildmaster.model.builder import specific_job_classes
 from lp.buildmaster.model.buildfarmjob import BuildFarmJobMixin
-from lp.buildmaster.model.buildqueue import BuildQueue
+from lp.buildmaster.model.buildqueue import (
+    BuildQueue,
+    specific_job_classes,
+    )
 from lp.services.database.interfaces import IStore
 from lp.soyuz.enums import (
     ArchivePurpose,

=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2013-09-11 06:05:44 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py	2013-11-11 23:47:32 +0000
@@ -1614,6 +1614,8 @@
             owner=self.user, name=u'my-recipe')
         distro_series = self.factory.makeDistroSeries(
             name='squirrel', distribution=archive.distribution)
+        removeSecurityProxy(distro_series).nominatedarchindep = (
+            self.factory.makeDistroArchSeries(distroseries=distro_series))
         build = self.factory.makeSourcePackageRecipeBuild(
             requester=self.user, archive=archive, recipe=recipe,
             distroseries=distro_series)

=== modified file 'lib/lp/code/mail/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/mail/tests/test_sourcepackagerecipebuild.py	2013-01-23 10:16:18 +0000
+++ lib/lp/code/mail/tests/test_sourcepackagerecipebuild.py	2013-11-11 23:47:32 +0000
@@ -60,6 +60,8 @@
         pantry_owner = self.factory.makePerson(name='archiveowner')
         pantry = self.factory.makeArchive(name='ppa', owner=pantry_owner)
         secret = self.factory.makeDistroSeries(name=u'distroseries')
+        removeSecurityProxy(secret).nominatedarchindep = (
+            self.factory.makeDistroArchSeries(distroseries=secret))
         build = self.factory.makeSourcePackageRecipeBuild(
             recipe=cake, distroseries=secret, archive=pantry,
             status=BuildStatus.FULLYBUILT, duration=timedelta(minutes=5))
@@ -95,6 +97,8 @@
         pantry_owner = self.factory.makePerson(name='archiveowner')
         pantry = self.factory.makeArchive(name='ppa', owner=pantry_owner)
         secret = self.factory.makeDistroSeries(name=u'distroseries')
+        removeSecurityProxy(secret).nominatedarchindep = (
+            self.factory.makeDistroArchSeries(distroseries=secret))
         build = self.factory.makeSourcePackageRecipeBuild(
             recipe=cake, distroseries=secret, archive=pantry,
             status=BuildStatus.SUPERSEDED)

=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py	2013-09-24 05:45:06 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py	2013-11-11 23:47:32 +0000
@@ -43,6 +43,7 @@
 from lp.buildmaster.model.buildfarmjob import (
     BuildFarmJob,
     BuildFarmJobOld,
+    SpecificBuildFarmJobSourceMixin,
     )
 from lp.buildmaster.model.buildqueue import BuildQueue
 from lp.buildmaster.model.packagebuild import PackageBuildMixin
@@ -78,7 +79,8 @@
 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
 
 
-class SourcePackageRecipeBuild(PackageBuildMixin, Storm):
+class SourcePackageRecipeBuild(SpecificBuildFarmJobSourceMixin,
+                               PackageBuildMixin, Storm):
 
     __storm_table__ = 'SourcePackageRecipeBuild'
 
@@ -212,6 +214,7 @@
         self.archive = archive
         self.pocket = pocket
         self.status = BuildStatus.NEEDSBUILD
+        self.processor = self.distroseries.nominatedarchindep.processor
         self.virtualized = True
         if date_created is not None:
             self.date_created = date_created
@@ -431,15 +434,6 @@
     build = Reference(
         build_id, 'SourcePackageRecipeBuild.id')
 
-    @property
-    def processor(self):
-        return self.build.distroseries.nominatedarchindep.processor
-
-    @property
-    def virtualized(self):
-        """See `IBuildFarmJob`."""
-        return self.build.is_virtualized
-
     def __init__(self, build, job):
         self.build = build
         self.job = job

=== modified file 'lib/lp/code/model/tests/test_recipebuilder.py'
--- lib/lp/code/model/tests/test_recipebuilder.py	2013-10-01 00:32:26 +0000
+++ lib/lp/code/model/tests/test_recipebuilder.py	2013-11-11 23:47:32 +0000
@@ -9,8 +9,6 @@
 import tempfile
 from textwrap import dedent
 
-from zope.component import getUtility
-
 from testtools import run_test_with
 from testtools.deferredruntest import (
     assert_fails_with,
@@ -20,6 +18,7 @@
 import transaction
 from twisted.internet import defer
 from twisted.trial.unittest import TestCase as TrialTestCase
+from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
 from lp.buildmaster.enums import (
@@ -72,7 +71,7 @@
         distroseries = self.factory.makeDistroSeries(name="mydistro",
             distribution=distro)
         processor = getUtility(IProcessorSet).getByName('386')
-        distroseries.newArch(
+        distroseries.nominatedarchindep = distroseries.newArch(
             'i386', processor, True, self.factory.makePerson())
         sourcepackage = self.factory.makeSourcePackage(spn, distroseries)
         if recipe_registrant is None:

=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipebuild.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2013-09-24 05:45:06 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipebuild.py	2013-11-11 23:47:32 +0000
@@ -423,6 +423,8 @@
         requester = self.factory.makePerson()
         recipe = self.factory.makeSourcePackageRecipe()
         series = self.factory.makeDistroSeries()
+        removeSecurityProxy(series).nominatedarchindep = (
+            self.factory.makeDistroArchSeries(distroseries=series))
         now = self.factory.getUniqueDate()
         build = self.factory.makeSourcePackageRecipeBuild(recipe=recipe,
             requester=requester)
@@ -526,6 +528,8 @@
             name=u'recipe', owner=person)
         pantry = self.factory.makeArchive(name='ppa')
         secret = self.factory.makeDistroSeries(name=u'distroseries')
+        secret.nominatedarchindep = self.factory.makeDistroArchSeries(
+            distroseries=secret)
         build = self.factory.makeSourcePackageRecipeBuild(
             recipe=cake, distroseries=secret, archive=pantry)
         build.updateStatus(BuildStatus.FULLYBUILT)
@@ -556,6 +560,8 @@
             name=u'recipe', owner=person)
         pantry = self.factory.makeArchive(name='ppa')
         secret = self.factory.makeDistroSeries(name=u'distroseries')
+        secret.nominatedarchindep = self.factory.makeDistroArchSeries(
+            distroseries=secret)
         build = self.factory.makeSourcePackageRecipeBuild(
             recipe=cake, distroseries=secret, archive=pantry)
         build.updateStatus(BuildStatus.FULLYBUILT)

=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py	2013-08-28 12:03:57 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py	2013-11-11 23:47:32 +0000
@@ -44,12 +44,16 @@
     )
 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSource
 from lp.buildmaster.model.builder import Builder
-from lp.buildmaster.model.buildfarmjob import BuildFarmJob
+from lp.buildmaster.model.buildfarmjob import (
+    BuildFarmJob,
+    SpecificBuildFarmJobSourceMixin,
+    )
 from lp.buildmaster.model.buildqueue import BuildQueue
 from lp.buildmaster.model.packagebuild import PackageBuildMixin
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.model.sourcepackagename import SourcePackageName
 from lp.services.config import config
 from lp.services.database.bulk import load_related
@@ -75,7 +79,10 @@
     simple_sendmail,
     )
 from lp.services.webapp import canonical_url
-from lp.soyuz.enums import ArchivePurpose
+from lp.soyuz.enums import (
+    ArchivePurpose,
+    PackagePublishingStatus,
+    )
 from lp.soyuz.interfaces.binarypackagebuild import (
     BuildSetStatus,
     CannotBeRescored,
@@ -825,7 +832,7 @@
         return changes.signer
 
 
-class BinaryPackageBuildSet:
+class BinaryPackageBuildSet(SpecificBuildFarmJobSourceMixin):
     implements(IBinaryPackageBuildSet)
 
     def new(self, distro_arch_series, source_package_release, processor,
@@ -1204,3 +1211,75 @@
         return IStore(BinaryPackageBuild).using(*origin).find(
             (BuildQueue, Builder, BuildPackageJob),
             BinaryPackageBuild.id.is_in(build_ids))
+
+    @staticmethod
+    def addCandidateSelectionCriteria(processor, virtualized):
+        """See `ISpecificBuildFarmJobSource`."""
+        private_statuses = (
+            PackagePublishingStatus.PUBLISHED,
+            PackagePublishingStatus.SUPERSEDED,
+            PackagePublishingStatus.DELETED,
+            )
+        return """
+            SELECT TRUE FROM Archive, BinaryPackageBuild, BuildPackageJob,
+                             DistroArchSeries
+            WHERE
+            BuildPackageJob.job = Job.id AND
+            BuildPackageJob.build = BinaryPackageBuild.id AND
+            BinaryPackageBuild.distro_arch_series =
+                DistroArchSeries.id AND
+            BinaryPackageBuild.archive = Archive.id AND
+            ((Archive.private IS TRUE AND
+              EXISTS (
+                  SELECT SourcePackagePublishingHistory.id
+                  FROM SourcePackagePublishingHistory
+                  WHERE
+                      SourcePackagePublishingHistory.distroseries =
+                         DistroArchSeries.distroseries AND
+                      SourcePackagePublishingHistory.sourcepackagerelease =
+                         BinaryPackageBuild.source_package_release AND
+                      SourcePackagePublishingHistory.archive = Archive.id AND
+                      SourcePackagePublishingHistory.status IN %s))
+              OR
+              archive.private IS FALSE) AND
+            BinaryPackageBuild.status = %s
+        """ % sqlvalues(private_statuses, BuildStatus.NEEDSBUILD)
+
+    @staticmethod
+    def postprocessCandidate(job, logger):
+        """See `ISpecificBuildFarmJobSource`."""
+        # Mark build records targeted to old source versions as SUPERSEDED
+        # and build records target to SECURITY pocket or against an OBSOLETE
+        # distroseries without a flag as FAILEDTOBUILD.
+        # Builds in those situation should not be built because they will
+        # be wasting build-time.  In the former case, there is already a
+        # newer source; the latter case needs an overhaul of the way
+        # security builds are handled (by copying from a PPA) to avoid
+        # creating duplicate builds.
+        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
+        distroseries = build.distro_arch_series.distroseries
+        if (
+            build.pocket == PackagePublishingPocket.SECURITY or
+            (distroseries.status == SeriesStatus.OBSOLETE and
+                not build.archive.permit_obsolete_series_uploads)):
+            # We never build anything in the security pocket, or for obsolete
+            # series without the flag set.
+            logger.debug(
+                "Build %s FAILEDTOBUILD, queue item %s REMOVED"
+                % (build.id, job.id))
+            build.updateStatus(BuildStatus.FAILEDTOBUILD)
+            job.destroySelf()
+            return False
+
+        publication = build.current_source_publication
+        if publication is None:
+            # The build should be superseded if it no longer has a
+            # current publishing record.
+            logger.debug(
+                "Build %s SUPERSEDED, queue item %s REMOVED"
+                % (build.id, job.id))
+            build.updateStatus(BuildStatus.SUPERSEDED)
+            job.destroySelf()
+            return False
+
+        return True

=== modified file 'lib/lp/soyuz/model/buildpackagejob.py'
--- lib/lp/soyuz/model/buildpackagejob.py	2013-09-04 08:04:25 +0000
+++ lib/lp/soyuz/model/buildpackagejob.py	2013-11-11 23:47:32 +0000
@@ -15,14 +15,9 @@
 from zope.component import getUtility
 from zope.interface import implements
 
-from lp.buildmaster.enums import BuildStatus
 from lp.buildmaster.model.buildfarmjob import BuildFarmJobOld
-from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.registry.interfaces.series import SeriesStatus
 from lp.services.database.bulk import load_related
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import sqlvalues
-from lp.soyuz.enums import PackagePublishingStatus
 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
 from lp.soyuz.interfaces.buildpackagejob import (
     COPY_ARCHIVE_SCORE_PENALTY,
@@ -99,16 +94,6 @@
 
         return score
 
-    @property
-    def processor(self):
-        """See `IBuildFarmJob`."""
-        return self.build.processor
-
-    @property
-    def virtualized(self):
-        """See `IBuildFarmJob`."""
-        return self.build.is_virtualized
-
     @classmethod
     def preloadJobsData(cls, jobs):
         from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
@@ -116,75 +101,3 @@
         load_related(Job, jobs, ['job_id'])
         builds = load_related(BinaryPackageBuild, jobs, ['build_id'])
         getUtility(IBinaryPackageBuildSet).preloadBuildsData(list(builds))
-
-    @staticmethod
-    def addCandidateSelectionCriteria(processor, virtualized):
-        """See `IBuildFarmJob`."""
-        private_statuses = (
-            PackagePublishingStatus.PUBLISHED,
-            PackagePublishingStatus.SUPERSEDED,
-            PackagePublishingStatus.DELETED,
-            )
-        return """
-            SELECT TRUE FROM Archive, BinaryPackageBuild, BuildPackageJob,
-                             DistroArchSeries
-            WHERE
-            BuildPackageJob.job = Job.id AND
-            BuildPackageJob.build = BinaryPackageBuild.id AND
-            BinaryPackageBuild.distro_arch_series =
-                DistroArchSeries.id AND
-            BinaryPackageBuild.archive = Archive.id AND
-            ((Archive.private IS TRUE AND
-              EXISTS (
-                  SELECT SourcePackagePublishingHistory.id
-                  FROM SourcePackagePublishingHistory
-                  WHERE
-                      SourcePackagePublishingHistory.distroseries =
-                         DistroArchSeries.distroseries AND
-                      SourcePackagePublishingHistory.sourcepackagerelease =
-                         BinaryPackageBuild.source_package_release AND
-                      SourcePackagePublishingHistory.archive = Archive.id AND
-                      SourcePackagePublishingHistory.status IN %s))
-              OR
-              archive.private IS FALSE) AND
-            BinaryPackageBuild.status = %s
-        """ % sqlvalues(private_statuses, BuildStatus.NEEDSBUILD)
-
-    @staticmethod
-    def postprocessCandidate(job, logger):
-        """See `IBuildFarmJob`."""
-        # Mark build records targeted to old source versions as SUPERSEDED
-        # and build records target to SECURITY pocket or against an OBSOLETE
-        # distroseries without a flag as FAILEDTOBUILD.
-        # Builds in those situation should not be built because they will
-        # be wasting build-time.  In the former case, there is already a
-        # newer source; the latter case needs an overhaul of the way
-        # security builds are handled (by copying from a PPA) to avoid
-        # creating duplicate builds.
-        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
-        distroseries = build.distro_arch_series.distroseries
-        if (
-            build.pocket == PackagePublishingPocket.SECURITY or
-            (distroseries.status == SeriesStatus.OBSOLETE and
-                not build.archive.permit_obsolete_series_uploads)):
-            # We never build anything in the security pocket, or for obsolete
-            # series without the flag set.
-            logger.debug(
-                "Build %s FAILEDTOBUILD, queue item %s REMOVED"
-                % (build.id, job.id))
-            build.updateStatus(BuildStatus.FAILEDTOBUILD)
-            job.destroySelf()
-            return False
-
-        publication = build.current_source_publication
-        if publication is None:
-            # The build should be superseded if it no longer has a
-            # current publishing record.
-            logger.debug(
-                "Build %s SUPERSEDED, queue item %s REMOVED"
-                % (build.id, job.id))
-            build.updateStatus(BuildStatus.SUPERSEDED)
-            job.destroySelf()
-            return False
-
-        return True

=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
--- lib/lp/soyuz/tests/test_binarypackagebuild.py	2013-09-10 06:28:26 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuild.py	2013-11-11 23:47:32 +0000
@@ -17,7 +17,9 @@
 from lp.buildmaster.interfaces.buildqueue import IBuildQueue
 from lp.buildmaster.interfaces.packagebuild import IPackageBuild
 from lp.buildmaster.model.buildqueue import BuildQueue
+from lp.registry.interfaces.series import SeriesStatus
 from lp.services.job.model.job import Job
+from lp.services.log.logger import DevNullLogger
 from lp.services.webapp.interaction import ANONYMOUS
 from lp.services.webapp.interfaces import OAuthPermission
 from lp.soyuz.enums import (
@@ -31,7 +33,10 @@
     )
 from lp.soyuz.interfaces.buildpackagejob import IBuildPackageJob
 from lp.soyuz.interfaces.component import IComponentSet
-from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
+from lp.soyuz.model.binarypackagebuild import (
+    BinaryPackageBuild,
+    BinaryPackageBuildSet,
+    )
 from lp.soyuz.model.buildpackagejob import BuildPackageJob
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import (
@@ -497,3 +502,42 @@
         logout()
         entry = self.webservice.get(build_url, api_version='devel').jsonBody()
         self.assertEndsWith(entry['builder_link'], builder_url)
+
+
+class TestPostprocessCandidate(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def makeBuildJob(self, pocket="RELEASE"):
+        build = self.factory.makeBinaryPackageBuild(pocket=pocket)
+        return build.queueBuild()
+
+    def test_release_job(self):
+        job = self.makeBuildJob()
+        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
+        self.assertTrue(BinaryPackageBuildSet.postprocessCandidate(job, None))
+        self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
+
+    def test_security_job_is_failed(self):
+        job = self.makeBuildJob(pocket="SECURITY")
+        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
+        BinaryPackageBuildSet.postprocessCandidate(job, DevNullLogger())
+        self.assertEqual(BuildStatus.FAILEDTOBUILD, build.status)
+
+    def test_obsolete_job_without_flag_is_failed(self):
+        job = self.makeBuildJob()
+        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
+        distroseries = build.distro_arch_series.distroseries
+        removeSecurityProxy(distroseries).status = SeriesStatus.OBSOLETE
+        BinaryPackageBuildSet.postprocessCandidate(job, DevNullLogger())
+        self.assertEqual(BuildStatus.FAILEDTOBUILD, build.status)
+
+    def test_obsolete_job_with_flag_is_not_failed(self):
+        job = self.makeBuildJob()
+        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
+        distroseries = build.distro_arch_series.distroseries
+        archive = build.archive
+        removeSecurityProxy(distroseries).status = SeriesStatus.OBSOLETE
+        removeSecurityProxy(archive).permit_obsolete_series_uploads = True
+        BinaryPackageBuildSet.postprocessCandidate(job, DevNullLogger())
+        self.assertEqual(BuildStatus.NEEDSBUILD, build.status)

=== modified file 'lib/lp/soyuz/tests/test_buildpackagejob.py'
--- lib/lp/soyuz/tests/test_buildpackagejob.py	2013-09-10 06:28:26 +0000
+++ lib/lp/soyuz/tests/test_buildpackagejob.py	2013-11-11 23:47:32 +0000
@@ -13,16 +13,13 @@
 from lp.buildmaster.enums import BuildStatus
 from lp.buildmaster.interfaces.builder import IBuilderSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.interfaces.sourcepackage import SourcePackageUrgency
 from lp.services.database.interfaces import IStore
-from lp.services.log.logger import DevNullLogger
 from lp.services.webapp.interfaces import OAuthPermission
 from lp.soyuz.enums import (
     ArchivePurpose,
     PackagePublishingStatus,
     )
-from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
 from lp.soyuz.interfaces.buildfarmbuildjob import IBuildFarmBuildJob
 from lp.soyuz.interfaces.buildpackagejob import (
     COPY_ARCHIVE_SCORE_PENALTY,
@@ -34,7 +31,6 @@
     )
 from lp.soyuz.interfaces.processor import IProcessorSet
 from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
-from lp.soyuz.model.buildpackagejob import BuildPackageJob
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import (
     anonymous_logged_in,
@@ -197,24 +193,6 @@
             removeSecurityProxy(bq).estimated_duration = timedelta(
                 seconds=duration)
 
-    def test_processor(self):
-        # Test that BuildPackageJob returns the correct processor.
-        build, bq = find_job(self, 'gcc', '386')
-        bpj = bq.specific_job
-        self.assertEqual(bpj.processor.id, 1)
-        build, bq = find_job(self, 'bison', 'hppa')
-        bpj = bq.specific_job
-        self.assertEqual(bpj.processor.id, 3)
-
-    def test_virtualized(self):
-        # Test that BuildPackageJob returns the correct virtualized flag.
-        build, bq = find_job(self, 'apg', '386')
-        bpj = bq.specific_job
-        self.assertEqual(bpj.virtualized, False)
-        build, bq = find_job(self, 'flex', 'hppa')
-        bpj = bq.specific_job
-        self.assertEqual(bpj.virtualized, False)
-
     def test_providesInterfaces(self):
         # Ensure that a BuildPackageJob generates an appropriate cookie.
         build, bq = find_job(self, 'gcc', '386')
@@ -222,17 +200,6 @@
         self.assertProvides(build_farm_job, IBuildPackageJob)
         self.assertProvides(build_farm_job, IBuildFarmBuildJob)
 
-    def test_jobStarted(self):
-        # Starting a build updates the status.
-        build, bq = find_job(self, 'gcc', '386')
-        build_package_job = bq.specific_job
-        build_package_job.jobStarted()
-        self.assertEqual(
-            BuildStatus.BUILDING, build_package_job.build.status)
-        self.assertIsNot(None, build_package_job.build.date_started)
-        self.assertIsNot(None, build_package_job.build.date_first_dispatched)
-        self.assertIs(None, build_package_job.build.date_finished)
-
 
 class TestBuildPackageJobScore(TestCaseWithFactory):
 
@@ -469,42 +436,3 @@
         with anonymous_logged_in():
             self.assertScoreWriteableByTeam(
                 archive, getUtility(ILaunchpadCelebrities).commercial_admin)
-
-
-class TestBuildPackageJobPostProcess(TestCaseWithFactory):
-
-    layer = DatabaseFunctionalLayer
-
-    def makeBuildJob(self, pocket="RELEASE"):
-        build = self.factory.makeBinaryPackageBuild(pocket=pocket)
-        return build.queueBuild()
-
-    def test_release_job(self):
-        job = self.makeBuildJob()
-        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
-        self.assertTrue(BuildPackageJob.postprocessCandidate(job, None))
-        self.assertEqual(BuildStatus.NEEDSBUILD, build.status)
-
-    def test_security_job_is_failed(self):
-        job = self.makeBuildJob(pocket="SECURITY")
-        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
-        BuildPackageJob.postprocessCandidate(job, DevNullLogger())
-        self.assertEqual(BuildStatus.FAILEDTOBUILD, build.status)
-
-    def test_obsolete_job_without_flag_is_failed(self):
-        job = self.makeBuildJob()
-        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
-        distroseries = build.distro_arch_series.distroseries
-        removeSecurityProxy(distroseries).status = SeriesStatus.OBSOLETE
-        BuildPackageJob.postprocessCandidate(job, DevNullLogger())
-        self.assertEqual(BuildStatus.FAILEDTOBUILD, build.status)
-
-    def test_obsolete_job_with_flag_is_not_failed(self):
-        job = self.makeBuildJob()
-        build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(job)
-        distroseries = build.distro_arch_series.distroseries
-        archive = build.archive
-        removeSecurityProxy(distroseries).status = SeriesStatus.OBSOLETE
-        removeSecurityProxy(archive).permit_obsolete_series_uploads = True
-        BuildPackageJob.postprocessCandidate(job, DevNullLogger())
-        self.assertEqual(BuildStatus.NEEDSBUILD, build.status)

=== modified file 'lib/lp/translations/model/translationtemplatesbuild.py'
--- lib/lp/translations/model/translationtemplatesbuild.py	2013-09-24 05:45:06 +0000
+++ lib/lp/translations/model/translationtemplatesbuild.py	2013-11-11 23:47:32 +0000
@@ -28,7 +28,10 @@
     BuildStatus,
     )
 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSource
-from lp.buildmaster.model.buildfarmjob import BuildFarmJobMixin
+from lp.buildmaster.model.buildfarmjob import (
+    BuildFarmJobMixin,
+    SpecificBuildFarmJobSourceMixin,
+    )
 from lp.code.model.branch import Branch
 from lp.code.model.branchcollection import GenericBranchCollection
 from lp.code.model.branchjob import (
@@ -49,7 +52,8 @@
     )
 
 
-class TranslationTemplatesBuild(BuildFarmJobMixin, Storm):
+class TranslationTemplatesBuild(SpecificBuildFarmJobSourceMixin,
+                                BuildFarmJobMixin, Storm):
     """A `BuildFarmJob` extension for translation templates builds."""
 
     implements(ITranslationTemplatesBuild)


Follow ups