Merge ~pappacena/launchpad:git-repo-async-provacy-garbo into launchpad:master

Proposed by Thiago F. Pappacena
Status: Needs review
Proposed branch: ~pappacena/launchpad:git-repo-async-provacy-garbo
Merge into: launchpad:master
Prerequisite: ~pappacena/launchpad:git-repo-async-privacy
Diff against target: 157 lines (+114/-0)
2 files modified
lib/lp/scripts/garbo.py (+54/-0)
lib/lp/scripts/tests/test_garbo.py (+60/-0)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+392809@code.launchpad.net

Commit message

Adding garbo to update GitRepository's status if corresponding information type change job fails

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) wrote :

LGTM

review: Approve

Unmerged commits

6118345... by Thiago F. Pappacena

Improving tests for more scenarios

20ea95f... by Thiago F. Pappacena

Adding garbo to keep GitRepo status in line with info type change job

4bd7dcc... by Thiago F. Pappacena

Fixing configuration of info type transition job

76e0272... by Thiago F. Pappacena

Blocking at the UI information type change while it is already changing

caf2673... by Thiago F. Pappacena

Adding tests for async GitRepo.transitionToInformationType

3a5e053... by Thiago F. Pappacena

Changing GitRepo.transitionInformationType method to be async

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/scripts/garbo.py b/lib/lp/scripts/garbo.py
index 6e0feac..ea248a4 100644
--- a/lib/lp/scripts/garbo.py
+++ b/lib/lp/scripts/garbo.py
@@ -38,6 +38,7 @@ from storm.expr import (
38 Cast,38 Cast,
39 In,39 In,
40 Join,40 Join,
41 LeftJoin,
41 Max,42 Max,
42 Min,43 Min,
43 Or,44 Or,
@@ -68,6 +69,10 @@ from lp.code.model.diff import (
68 Diff,69 Diff,
69 PreviewDiff,70 PreviewDiff,
70 )71 )
72from lp.code.model.gitjob import (
73 GitJob,
74 GitJobType,
75 )
71from lp.code.model.gitrepository import GitRepository76from lp.code.model.gitrepository import GitRepository
72from lp.code.model.revision import (77from lp.code.model.revision import (
73 RevisionAuthor,78 RevisionAuthor,
@@ -1554,6 +1559,54 @@ class GitRepositoryPruner(TunableLoop):
1554 transaction.commit()1559 transaction.commit()
15551560
15561561
1562class GitRepositoryBrokenInfoTypeTransition(TunableLoop):
1563 """Put back to "AVAILABLE" repositories that are pending information
1564 type changes, but we don't have any git job that will actually do that
1565 in the upcoming future.
1566 """
1567
1568 maximum_chunk_size = 500
1569
1570 def __init__(self, log, abort_time=None):
1571 super(GitRepositoryBrokenInfoTypeTransition, self).__init__(
1572 log, abort_time)
1573 self.store = IMasterStore(GitRepository)
1574
1575 def findRepositories(self):
1576 pending_change = (
1577 GitRepositoryStatus.PENDING_INFORMATION_TYPE_TRANSITION)
1578 job_type = GitJobType.REPOSITORY_TRANSITION_TO_INFO_TYPE
1579 job_pending_statuses = (JobStatus.WAITING, JobStatus.RUNNING)
1580 # Get git repositories left-joining with
1581 # REPOSITORY_TRANSITION_TO_INFO_TYPE GitJobs waiting to be run (or
1582 # already running).
1583 join = [
1584 GitRepository,
1585 LeftJoin(
1586 GitJob,
1587 And(GitJob.repository_id == GitRepository.id,
1588 GitJob.job_type == job_type)),
1589 LeftJoin(
1590 Job,
1591 And(GitJob.job_id == Job.id,
1592 Job._status.is_in(job_pending_statuses)))]
1593 # We get only the repositories pending change without associated job.
1594 result_set = self.store.using(*join).find(
1595 GitRepository,
1596 GitRepository.status == pending_change,
1597 Job._status == None)
1598 return result_set.order_by(GitRepository.date_created)
1599
1600 def isDone(self):
1601 return self.findRepositories().is_empty()
1602
1603 def __call__(self, chunk_size):
1604 repositories = self.findRepositories()[:chunk_size]
1605 for repository in repositories:
1606 repository.status = GitRepositoryStatus.AVAILABLE
1607 transaction.commit()
1608
1609
1557class BaseDatabaseGarbageCollector(LaunchpadCronScript):1610class BaseDatabaseGarbageCollector(LaunchpadCronScript):
1558 """Abstract base class to run a collection of TunableLoops."""1611 """Abstract base class to run a collection of TunableLoops."""
1559 script_name = None # Script name for locking and database user. Override.1612 script_name = None # Script name for locking and database user. Override.
@@ -1806,6 +1859,7 @@ class HourlyDatabaseGarbageCollector(BaseDatabaseGarbageCollector):
1806 BugHeatUpdater,1859 BugHeatUpdater,
1807 DuplicateSessionPruner,1860 DuplicateSessionPruner,
1808 GitRepositoryPruner,1861 GitRepositoryPruner,
1862 GitRepositoryBrokenInfoTypeTransition,
1809 RevisionCachePruner,1863 RevisionCachePruner,
1810 UnusedSessionPruner,1864 UnusedSessionPruner,
1811 ]1865 ]
diff --git a/lib/lp/scripts/tests/test_garbo.py b/lib/lp/scripts/tests/test_garbo.py
index 2fbc80d..ae3b0f1 100644
--- a/lib/lp/scripts/tests/test_garbo.py
+++ b/lib/lp/scripts/tests/test_garbo.py
@@ -1126,6 +1126,66 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
1126 {old_available, recent_available, recent_creating},1126 {old_available, recent_available, recent_creating},
1127 set(remaining_repos))1127 set(remaining_repos))
11281128
1129 def test_GitRepositoryBrokenInfoTypeTransition_changes_status(self):
1130 self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: 'on'}))
1131 # Shortcuts.
1132 available = GitRepositoryStatus.AVAILABLE
1133 pending_transition = (
1134 GitRepositoryStatus.PENDING_INFORMATION_TYPE_TRANSITION)
1135
1136 switch_dbuser('testadmin')
1137 store = IMasterStore(GitRepository)
1138 created_repos = [self.factory.makeGitRepository() for i in range(5)]
1139
1140 pending_without_job_repos = []
1141 for i in range(2):
1142 repo = self.factory.makeGitRepository()
1143 removeSecurityProxy(repo)._status = pending_transition
1144
1145 pending_with_failed_jobs = []
1146 for i in range(3):
1147 repo = self.factory.makeGitRepository()
1148 # For some repos, create the job but force them to fail
1149 job = repo.transitionToInformationType(
1150 InformationType.PRIVATESECURITY, repo.owner)
1151 job.start()
1152 job.fail()
1153 pending_with_failed_jobs.append(repo)
1154
1155 pending_with_started_job_repos = []
1156 for i in range(2):
1157 repo = self.factory.makeGitRepository()
1158 job = repo.transitionToInformationType(
1159 InformationType.PRIVATESECURITY, repo.owner)
1160 job.start()
1161 pending_with_started_job_repos.append(repo)
1162
1163 pending_with_waiting_jobs = []
1164 for i in range(3):
1165 repo = self.factory.makeGitRepository()
1166 repo.transitionToInformationType(
1167 InformationType.PRIVATESECURITY, repo.owner)
1168 pending_with_started_job_repos.append(repo)
1169
1170 self.assertEqual(15, store.find(GitRepository).count())
1171
1172 self.runHourly(maximum_chunk_size=2)
1173
1174 switch_dbuser('testadmin')
1175 self.assertEqual(15, store.find(GitRepository).count())
1176 self.assertTrue(
1177 all(i.status == available for i in created_repos))
1178 self.assertTrue(
1179 all(i.status == available for i in pending_without_job_repos))
1180 self.assertTrue(
1181 all(i.status == available for i in pending_with_failed_jobs))
1182 self.assertTrue(
1183 all(i.status == pending_transition
1184 for i in pending_with_started_job_repos))
1185 self.assertTrue(
1186 all(i.status == pending_transition
1187 for i in pending_with_waiting_jobs))
1188
1129 def test_WebhookJobPruner(self):1189 def test_WebhookJobPruner(self):
1130 # Garbo should remove jobs completed over 30 days ago.1190 # Garbo should remove jobs completed over 30 days ago.
1131 switch_dbuser('testadmin')1191 switch_dbuser('testadmin')