Merge lp:~cjwatson/launchpad/snap-pending-import into lp:launchpad

Proposed by Colin Watson on 2016-11-21
Status: Needs review
Proposed branch: lp:~cjwatson/launchpad/snap-pending-import
Merge into: lp:launchpad
Diff against target: 140 lines (+97/-0)
2 files modified
lib/lp/snappy/model/snapbuild.py (+25/-0)
lib/lp/snappy/tests/test_snapbuild.py (+72/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-pending-import
Reviewer Review Type Date Requested Status
Launchpad code reviewers 2016-11-21 Pending
Review via email: mp+311391@code.launchpad.net

Commit message

Don't dispatch SnapBuilds whose code object is empty, perhaps because an associated code import hasn't finished.

Description of the change

This should make it easier to create a code import, a snap, and some builds in quick succession (via automation) without needing to worry about whether the builds will be dispatched before the import has completed.

To post a comment you must log in.
William Grant (wgrant) wrote :

This seems risky. The set of snap builds that have to be considered in each dispatch will grow without bound as imports fail or refs to non-commits attempt to be built. I suppose we could just have graphs of that and check them occasionally.

Unmerged revisions

18280. By Colin Watson on 2016-11-21

Don't dispatch SnapBuilds whose code object is empty, perhaps because an associated code import hasn't finished.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/snappy/model/snapbuild.py'
2--- lib/lp/snappy/model/snapbuild.py 2016-08-16 16:24:51 +0000
3+++ lib/lp/snappy/model/snapbuild.py 2016-11-21 14:12:14 +0000
4@@ -48,6 +48,7 @@
5 IMasterStore,
6 IStore,
7 )
8+from lp.services.database.sqlbase import sqlvalues
9 from lp.services.job.interfaces.job import JobStatus
10 from lp.services.job.model.job import Job
11 from lp.services.librarian.browser import ProxiedLibraryFileAlias
12@@ -469,3 +470,27 @@
13 SnapBuild, SnapBuild.build_farm_job_id.is_in(
14 bfj.id for bfj in build_farm_jobs))
15 return DecoratedResultSet(rows, pre_iter_hook=self.preloadBuildsData)
16+
17+ def addCandidateSelectionCriteria(self, processor, virtualized):
18+ """See `ISpecificBuildFarmJobSource`."""
19+ return """
20+ SELECT 1 FROM Snap, SnapBuild
21+ WHERE
22+ SnapBuild.build_farm_job = BuildQueue.build_farm_job AND
23+ SnapBuild.snap = Snap.id AND
24+ ((Snap.branch IS NOT NULL AND
25+ EXISTS (
26+ SELECT 1 FROM Branch
27+ WHERE
28+ Branch.id = Snap.branch AND
29+ Branch.revision_count != 0))
30+ OR
31+ (Snap.git_repository IS NOT NULL AND
32+ EXISTS (
33+ SELECT 1 FROM GitRef
34+ WHERE
35+ GitRef.repository = Snap.git_repository AND
36+ GitRef.path = Snap.git_path AND
37+ GitRef.commit_message IS NOT NULL))) AND
38+ SnapBuild.status = %s
39+ """ % sqlvalues(BuildStatus.NEEDSBUILD)
40
41=== modified file 'lib/lp/snappy/tests/test_snapbuild.py'
42--- lib/lp/snappy/tests/test_snapbuild.py 2016-08-16 16:24:51 +0000
43+++ lib/lp/snappy/tests/test_snapbuild.py 2016-11-21 14:12:14 +0000
44@@ -15,6 +15,10 @@
45 )
46
47 import pytz
48+from testscenarios import (
49+ load_tests_apply_scenarios,
50+ WithScenarios,
51+ )
52 from testtools.matchers import (
53 Equals,
54 MatchesDict,
55@@ -29,6 +33,8 @@
56 from lp.buildmaster.interfaces.buildqueue import IBuildQueue
57 from lp.buildmaster.interfaces.packagebuild import IPackageBuild
58 from lp.buildmaster.interfaces.processor import IProcessorSet
59+from lp.buildmaster.tests.mock_slaves import make_publisher
60+from lp.code.interfaces.revision import IRevisionSet
61 from lp.registry.enums import PersonVisibility
62 from lp.services.config import config
63 from lp.services.features.testing import FeatureFixture
64@@ -466,6 +472,69 @@
65 [], getUtility(ISnapBuildSet).getByBuildFarmJobs([]))
66
67
68+class TestSnapBuildFindBuildCandidate(WithScenarios, TestCaseWithFactory):
69+
70+ layer = LaunchpadZopelessLayer
71+
72+ scenarios = [
73+ ("Branch", {"context_type": "branch"}),
74+ ("GitRef", {"context_type": "gitref"}),
75+ ]
76+
77+ def setUp(self):
78+ super(TestSnapBuildFindBuildCandidate, self).setUp()
79+ self.publisher = make_publisher()
80+ self.publisher.prepareBreezyAutotest()
81+ self.builder = self.factory.makeBuilder(
82+ processors=[self.publisher.breezy_autotest_i386.processor],
83+ virtualized=True)
84+
85+ def makeSnap(self, with_revisions=True):
86+ if self.context_type == "branch":
87+ branch = self.factory.makeBranch()
88+ if with_revisions:
89+ self.factory.makeRevisionsForBranch(branch)
90+ return self.factory.makeSnap(branch=branch)
91+ elif self.context_type == "gitref":
92+ [ref] = self.factory.makeGitRefs()
93+ if with_revisions:
94+ person = self.factory.makePerson()
95+ email = removeSecurityProxy(person).preferredemail.email
96+ author = getUtility(IRevisionSet).acquireRevisionAuthors(
97+ [email])[email]
98+ now = datetime.now(pytz.UTC)
99+ naked_ref = removeSecurityProxy(ref)
100+ naked_ref.author = naked_ref.committer = author
101+ naked_ref.author_date = naked_ref.committer_date = now
102+ naked_ref.commit_message = u"something"
103+ return self.factory.makeSnap(git_ref=ref)
104+ else:
105+ raise AssertionError("unknown context_type %s" % self.context_type)
106+
107+ def test_findBuildCandidate_ready(self):
108+ # Builder._findBuildCandidate finds a SnapBuild with a usable code
109+ # object.
110+ snap = self.makeSnap()
111+ build = self.factory.makeSnapBuild(
112+ snap=snap, distroarchseries=self.publisher.breezy_autotest_i386)
113+ build.queueBuild()
114+ next_job = removeSecurityProxy(self.builder)._findBuildCandidate()
115+ self.assertEqual(
116+ build,
117+ getUtility(ISnapBuildSet).getByBuildFarmJob(
118+ removeSecurityProxy(next_job)._build_farm_job))
119+
120+ def test_findBuildCandidate_no_revisions(self):
121+ # Builder._findBuildCandidate skips SnapBuilds without a usable code
122+ # object, such as an imported branch whose import hasn't run yet.
123+ snap = self.makeSnap(with_revisions=False)
124+ build = self.factory.makeSnapBuild(
125+ snap=snap, distroarchseries=self.publisher.breezy_autotest_i386)
126+ build.queueBuild()
127+ self.assertIsNone(
128+ removeSecurityProxy(self.builder)._findBuildCandidate())
129+
130+
131 class TestSnapBuildWebservice(TestCaseWithFactory):
132
133 layer = LaunchpadFunctionalLayer
134@@ -621,3 +690,6 @@
135 browser = self.getNonRedirectingBrowser(user=self.person)
136 for file_url in file_urls:
137 self.assertCanOpenRedirectedUrl(browser, file_url)
138+
139+
140+load_tests = load_tests_apply_scenarios