Merge lp:~cjwatson/launchpad/snap-backfill-store-series into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18156
Proposed branch: lp:~cjwatson/launchpad/snap-backfill-store-series
Merge into: lp:launchpad
Diff against target: 135 lines (+81/-0)
3 files modified
database/schema/security.cfg (+3/-0)
lib/lp/scripts/garbo.py (+43/-0)
lib/lp/scripts/tests/test_garbo.py (+35/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-backfill-store-series
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+300548@code.launchpad.net

Commit message

Backfill Snap.store_series based on Snap.distro_series.

Description of the change

Backfill Snap.store_series based on Snap.distro_series.

At the moment, each DistroSeries is only usable with at most one SnappySeries, so we can fill this in unambiguously (although the backfilling code here takes care to check for the case where this is not true). Doing this is useful because it means we can deal with store/distro series combinations more consistently, and because it reduces the number of steps involved for people to configure existing snap packages to be uploaded to the store.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'database/schema/security.cfg'
2--- database/schema/security.cfg 2016-07-14 16:07:35 +0000
3+++ database/schema/security.cfg 2016-07-20 01:55:19 +0000
4@@ -2369,7 +2369,10 @@
5 public.previewdiff = SELECT, DELETE
6 public.revisionauthor = SELECT, UPDATE
7 public.revisioncache = SELECT, DELETE
8+public.snap = SELECT, UPDATE
9 public.snapfile = SELECT, DELETE
10+public.snappydistroseries = SELECT
11+public.snappyseries = SELECT
12 public.sourcepackagename = SELECT
13 public.sourcepackagerelease = SELECT
14 public.sourcepackagepublishinghistory = SELECT, UPDATE
15
16=== modified file 'lib/lp/scripts/garbo.py'
17--- lib/lp/scripts/garbo.py 2016-06-14 14:25:24 +0000
18+++ lib/lp/scripts/garbo.py 2016-07-20 01:55:19 +0000
19@@ -12,6 +12,7 @@
20 'save_garbo_job_state',
21 ]
22
23+from collections import defaultdict
24 from datetime import (
25 datetime,
26 timedelta,
27@@ -119,6 +120,8 @@
28 from lp.services.verification.model.logintoken import LoginToken
29 from lp.services.webhooks.interfaces import IWebhookJobSource
30 from lp.services.webhooks.model import WebhookJob
31+from lp.snappy.interfaces.snappyseries import ISnappyDistroSeriesSet
32+from lp.snappy.model.snap import Snap
33 from lp.soyuz.enums import PackagePublishingStatus
34 from lp.soyuz.model.archive import Archive
35 from lp.soyuz.model.distributionsourcepackagecache import (
36@@ -1553,6 +1556,45 @@
37 """
38
39
40+class SnapStoreSeriesPopulator(TunableLoop):
41+ """Populates Snap.store_series based on Snap.distro_series.
42+
43+ This only touches rows where there is exactly one SnappySeries that
44+ could be built from the relevant DistroSeries.
45+ """
46+
47+ maximum_chunk_size = 5000
48+
49+ def __init__(self, log, abort_time=None):
50+ super(SnapStoreSeriesPopulator, self).__init__(log, abort_time)
51+ self.start_at = 1
52+ self.store = IMasterStore(Snap)
53+ all_series_map = defaultdict(list)
54+ for sds in getUtility(ISnappyDistroSeriesSet).getAll():
55+ all_series_map[sds.distro_series.id].append(sds.snappy_series)
56+ self.series_map = {
57+ distro_series_id: snappy_serieses[0]
58+ for distro_series_id, snappy_serieses in all_series_map.items()
59+ if len(snappy_serieses) == 1}
60+
61+ def findSnaps(self):
62+ return self.store.find(
63+ Snap,
64+ Snap.id >= self.start_at,
65+ Snap.distro_series_id.is_in(self.series_map),
66+ Snap.store_series == None).order_by(Snap.id)
67+
68+ def isDone(self):
69+ return self.findSnaps().is_empty()
70+
71+ def __call__(self, chunk_size):
72+ snaps = list(self.findSnaps()[:chunk_size])
73+ for snap in snaps:
74+ snap.store_series = self.series_map[snap.distro_series_id]
75+ self.start_at = snaps[-1].id + 1
76+ transaction.commit()
77+
78+
79 class BaseDatabaseGarbageCollector(LaunchpadCronScript):
80 """Abstract base class to run a collection of TunableLoops."""
81 script_name = None # Script name for locking and database user. Override.
82@@ -1844,6 +1886,7 @@
83 RevisionAuthorEmailLinker,
84 ScrubPOFileTranslator,
85 SnapBuildJobPruner,
86+ SnapStoreSeriesPopulator,
87 SuggestiveTemplatesCacheUpdater,
88 TeamMembershipPruner,
89 UnlinkedAccountPruner,
90
91=== modified file 'lib/lp/scripts/tests/test_garbo.py'
92--- lib/lp/scripts/tests/test_garbo.py 2016-06-14 12:40:30 +0000
93+++ lib/lp/scripts/tests/test_garbo.py 2016-07-20 01:55:19 +0000
94@@ -1497,6 +1497,41 @@
95 self._test_LiveFSFilePruner(
96 'application/octet-stream', 0, expected_count=1)
97
98+ def test_SnapStoreSeriesPopulator(self):
99+ switch_dbuser('testadmin')
100+ # Make some series.
101+ dses = [self.factory.makeDistroSeries() for _ in range(4)]
102+ sses = [
103+ self.factory.makeSnappySeries(usable_distro_series=[dses[1]]),
104+ self.factory.makeSnappySeries(usable_distro_series=[dses[2]]),
105+ self.factory.makeSnappySeries(usable_distro_series=[dses[3]]),
106+ self.factory.makeSnappySeries(usable_distro_series=[dses[3]]),
107+ ]
108+ # Make some snap packages.
109+ snaps = [
110+ self.factory.makeSnap(distroseries=dses[0]),
111+ self.factory.makeSnap(distroseries=dses[1], store_series=sses[1]),
112+ self.factory.makeSnap(distroseries=dses[1]),
113+ self.factory.makeSnap(distroseries=dses[2], store_series=sses[0]),
114+ self.factory.makeSnap(distroseries=dses[2]),
115+ self.factory.makeSnap(distroseries=dses[3]),
116+ ]
117+ transaction.commit()
118+
119+ self.runDaily()
120+
121+ # Snaps with no possible store series are untouched.
122+ self.assertIsNone(snaps[0].store_series)
123+ # Snaps that already have a store series are untouched.
124+ self.assertEqual(sses[1], snaps[1].store_series)
125+ self.assertEqual(sses[0], snaps[3].store_series)
126+ # Snaps with no current store series and exactly one possible store
127+ # series have it filled in.
128+ self.assertEqual(sses[0], snaps[2].store_series)
129+ self.assertEqual(sses[1], snaps[4].store_series)
130+ # Snaps with more than one possible store series are untouched.
131+ self.assertIsNone(snaps[5].store_series)
132+
133
134 class TestGarboTasks(TestCaseWithFactory):
135 layer = LaunchpadZopelessLayer