Merge ~cjwatson/launchpad:fix-cibuild-deletion into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 5721a104512416403b09eaf7bc25d252ba04d86a
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:fix-cibuild-deletion
Merge into: launchpad:master
Diff against target: 116 lines (+49/-3)
2 files modified
lib/lp/code/model/cibuild.py (+32/-3)
lib/lp/code/model/tests/test_cibuild.py (+17/-0)
Reviewer Review Type Date Requested Status
Jürgen Gmach Approve
Review via email: mp+427772@code.launchpad.net

Commit message

Fix CI build deletion when deleting a Git repository

Description of the change

We need to delete associated `BuildQueue` and `BuildFarmJob` rows as well, otherwise various things break if there happens to be a running CI build for a repository that's being deleted.

To post a comment you must log in.
Revision history for this message
Jürgen Gmach (jugmac00) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/code/model/cibuild.py b/lib/lp/code/model/cibuild.py
2index e65493d..7adcc0e 100644
3--- a/lib/lp/code/model/cibuild.py
4+++ b/lib/lp/code/model/cibuild.py
5@@ -29,7 +29,11 @@ from lp.buildmaster.enums import (
6 )
7 from lp.buildmaster.interfaces.builder import CannotBuild
8 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSource
9-from lp.buildmaster.model.buildfarmjob import SpecificBuildFarmJobSourceMixin
10+from lp.buildmaster.model.buildfarmjob import (
11+ BuildFarmJob,
12+ SpecificBuildFarmJobSourceMixin,
13+)
14+from lp.buildmaster.model.buildqueue import BuildQueue
15 from lp.buildmaster.model.packagebuild import PackageBuildMixin
16 from lp.code.errors import GitRepositoryBlobNotFound, GitRepositoryScanFault
17 from lp.code.interfaces.cibuild import (
18@@ -594,11 +598,18 @@ class CIBuildSet(SpecificBuildFarmJobSourceMixin):
19 store.flush()
20 return cibuild
21
22- def findByGitRepository(self, git_repository, commit_sha1s=None):
23- """See `ICIBuildSet`."""
24+ def _findByGitRepositoryClauses(self, git_repository, commit_sha1s=None):
25+ """Return a list of Storm clauses to find builds for a repository."""
26 clauses = [CIBuild.git_repository == git_repository]
27 if commit_sha1s is not None:
28 clauses.append(CIBuild.commit_sha1.is_in(commit_sha1s))
29+ return clauses
30+
31+ def findByGitRepository(self, git_repository, commit_sha1s=None):
32+ """See `ICIBuildSet`."""
33+ clauses = self._findByGitRepositoryClauses(
34+ git_repository, commit_sha1s=commit_sha1s
35+ )
36 return IStore(CIBuild).find(CIBuild, *clauses)
37
38 def _isBuildableArchitectureAllowed(self, das):
39@@ -759,7 +770,25 @@ class CIBuildSet(SpecificBuildFarmJobSourceMixin):
40
41 def deleteByGitRepository(self, git_repository):
42 """See `ICIBuildSet`."""
43+ # Remove build jobs. There won't be many queued builds, so we can
44+ # afford to do this the safe but slow way via BuildQueue.destroySelf
45+ # rather than in bulk.
46+ build_clauses = self._findByGitRepositoryClauses(git_repository)
47+ store = IStore(CIBuild)
48+ buildqueue_records = store.find(
49+ BuildQueue,
50+ BuildQueue._build_farm_job_id == CIBuild.build_farm_job_id,
51+ *build_clauses,
52+ )
53+ for buildqueue_record in buildqueue_records:
54+ buildqueue_record.destroySelf()
55+ build_farm_job_ids = list(
56+ store.find(CIBuild.build_farm_job_id, *build_clauses)
57+ )
58 self.findByGitRepository(git_repository).remove()
59+ store.find(
60+ BuildFarmJob, BuildFarmJob.id.is_in(build_farm_job_ids)
61+ ).remove()
62
63
64 @implementer(IMacaroonIssuer)
65diff --git a/lib/lp/code/model/tests/test_cibuild.py b/lib/lp/code/model/tests/test_cibuild.py
66index a72e744..8f555a1 100644
67--- a/lib/lp/code/model/tests/test_cibuild.py
68+++ b/lib/lp/code/model/tests/test_cibuild.py
69@@ -32,6 +32,7 @@ from lp.buildmaster.enums import BuildQueueStatus, BuildStatus
70 from lp.buildmaster.interfaces.buildqueue import IBuildQueue
71 from lp.buildmaster.interfaces.packagebuild import IPackageBuild
72 from lp.buildmaster.interfaces.processor import IProcessorSet
73+from lp.buildmaster.model.buildfarmjob import BuildFarmJob
74 from lp.buildmaster.model.buildqueue import BuildQueue
75 from lp.code.errors import GitRepositoryBlobNotFound, GitRepositoryScanFault
76 from lp.code.interfaces.cibuild import (
77@@ -54,6 +55,7 @@ from lp.registry.interfaces.series import SeriesStatus
78 from lp.registry.interfaces.sourcepackage import SourcePackageType
79 from lp.services.authserver.xmlrpc import AuthServerAPIView
80 from lp.services.config import config
81+from lp.services.database.sqlbase import flush_database_caches
82 from lp.services.librarian.browser import ProxiedLibraryFileAlias
83 from lp.services.log.logger import BufferLogger
84 from lp.services.macaroons.interfaces import IMacaroonIssuer
85@@ -1158,16 +1160,31 @@ class TestCIBuildSet(TestCaseWithFactory):
86 builds.extend(
87 [self.factory.makeCIBuild(repository) for _ in range(2)]
88 )
89+ build_queue = builds[0].queueBuild()
90+ other_build = self.factory.makeCIBuild()
91+ other_build.queueBuild()
92+ store = Store.of(builds[0])
93+ store.flush()
94+ build_queue_id = build_queue.id
95+ build_farm_job_id = removeSecurityProxy(builds[0]).build_farm_job_id
96 ci_build_set = getUtility(ICIBuildSet)
97
98 ci_build_set.deleteByGitRepository(repositories[0])
99
100+ flush_database_caches()
101+ # The deleted CI builds are gone.
102 self.assertContentEqual(
103 [], ci_build_set.findByGitRepository(repositories[0])
104 )
105 self.assertContentEqual(
106 builds[2:], ci_build_set.findByGitRepository(repositories[1])
107 )
108+ self.assertIsNone(store.get(BuildQueue, build_queue_id))
109+ self.assertIsNone(store.get(BuildFarmJob, build_farm_job_id))
110+ # Unrelated CI builds are still present.
111+ clear_property_cache(other_build)
112+ self.assertEqual(other_build, ci_build_set.getByID(other_build.id))
113+ self.assertIsNotNone(other_build.buildqueue_record)
114
115
116 class TestDetermineDASesToBuild(TestCaseWithFactory):

Subscribers

People subscribed via source and target branches

to status/vote changes: