Merge lp:~cjwatson/launchpad/init-branch-livefses into lp:launchpad

Proposed by Colin Watson
Status: Rejected
Rejected by: Colin Watson
Proposed branch: lp:~cjwatson/launchpad/init-branch-livefses
Merge into: lp:launchpad
Diff against target: 197 lines (+127/-1)
2 files modified
lib/lp/soyuz/scripts/initialize_distroseries.py (+31/-1)
lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py (+96/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/init-branch-livefses
Reviewer Review Type Date Requested Status
Colin Watson (community) Disapprove
Review via email: mp+274277@code.launchpad.net

Commit message

Copy live filesystems from the previous series on initialisation.

Description of the change

Copy live filesystems from the previous series on initialisation.

To avoid zombie livefses hanging around forever, this only copies those that have had builds created for them in the last 30 days, so at worst they should exist for one series longer than their true lifetime.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

We talked about this in the LP team meeting a couple of weeks ago. We might want to copy "official" livefses (mostly those owned by ~ubuntu-cdimage) but not other ones, because that isn't the kind of change LP would normally apply to user-owned artifacts. The 30-day limit is too much of a hack.

If somebody wants to tackle this in future, the place to start would be by introducing a database-backed notion of whether livefses are official. For now it's not too bad to keep this client-side.

review: Disapprove

Unmerged revisions

17814. By Colin Watson

Copy live filesystems from the previous series on initialisation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/scripts/initialize_distroseries.py'
2--- lib/lp/soyuz/scripts/initialize_distroseries.py 2015-05-13 12:40:44 +0000
3+++ lib/lp/soyuz/scripts/initialize_distroseries.py 2015-10-13 15:40:46 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Initialize a distroseries from its parent distroseries."""
10@@ -21,6 +21,7 @@
11 from lp.registry.interfaces.pocket import PackagePublishingPocket
12 from lp.registry.model.distroseries import DistroSeries
13 from lp.services.database import bulk
14+from lp.services.database.constants import THIRTY_DAYS_AGO
15 from lp.services.database.interfaces import IMasterStore
16 from lp.services.database.sqlbase import sqlvalues
17 from lp.services.helpers import ensure_unicode
18@@ -38,6 +39,7 @@
19 from lp.soyuz.interfaces.distributionjob import (
20 IDistroSeriesDifferenceJobSource,
21 )
22+from lp.soyuz.interfaces.livefs import ILiveFSSet
23 from lp.soyuz.interfaces.packagecloner import IPackageCloner
24 from lp.soyuz.interfaces.packageset import (
25 IPackagesetSet,
26@@ -45,6 +47,8 @@
27 )
28 from lp.soyuz.interfaces.queue import IPackageUploadSet
29 from lp.soyuz.model.binarypackagebuild import COPY_ARCHIVE_SCORE_PENALTY
30+from lp.soyuz.model.livefs import LiveFS
31+from lp.soyuz.model.livefsbuild import LiveFSBuild
32 from lp.soyuz.model.packageset import Packageset
33 from lp.soyuz.scripts.packagecopier import do_copy
34
35@@ -289,6 +293,7 @@
36 self._copy_pocket_permissions()
37 self._create_dsds()
38 self._set_initialized()
39+ self._copy_livefses()
40 transaction.commit()
41
42 def _set_parents(self):
43@@ -737,3 +742,28 @@
44 """ % sqlvalues(
45 self.distroseries.main_archive, self.distroseries.id,
46 parent.id))
47+
48+ def _copy_livefses(self):
49+ """Copy live filesystem configurations from the parent series."""
50+ # We could probably be more efficient here, but the number of
51+ # livefses is likely to be small.
52+ livefs_set = getUtility(ILiveFSSet)
53+ for parent in self.derivation_parents:
54+ livefses = self._store.find(
55+ LiveFS,
56+ LiveFS.distro_series == parent,
57+ LiveFSBuild.livefs == LiveFS.id,
58+ LiveFSBuild.date_created >= THIRTY_DAYS_AGO)
59+ for livefs in livefses:
60+ if not livefs_set.exists(
61+ livefs.owner, self.distroseries, livefs.name):
62+ self._store.execute("""
63+ INSERT INTO LiveFS (
64+ registrant, owner, distro_series, name, json_data,
65+ require_virtualized)
66+ SELECT
67+ registrant, owner, %s, name, json_data,
68+ require_virtualized
69+ FROM LiveFS AS parent_livefs
70+ WHERE parent_livefs.id = %s
71+ """ % sqlvalues(self.distroseries.id, livefs.id))
72
73=== modified file 'lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py'
74--- lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py 2015-04-20 15:59:52 +0000
75+++ lib/lp/soyuz/scripts/tests/test_initialize_distroseries.py 2015-10-13 15:40:46 +0000
76@@ -5,6 +5,13 @@
77
78 __metaclass__ = type
79
80+from datetime import (
81+ datetime,
82+ timedelta,
83+ )
84+
85+import pytz
86+from testtools.matchers import MatchesStructure
87 import transaction
88 from zope.component import getUtility
89
90@@ -19,7 +26,9 @@
91 )
92 from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
93 from lp.registry.interfaces.pocket import PackagePublishingPocket
94+from lp.services.database.constants import ONE_DAY_AGO
95 from lp.services.database.interfaces import IStore
96+from lp.services.features.testing import FeatureFixture
97 from lp.soyuz.enums import (
98 ArchivePurpose,
99 PackageUploadStatus,
100@@ -28,6 +37,10 @@
101 from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
102 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
103 from lp.soyuz.interfaces.component import IComponentSet
104+from lp.soyuz.interfaces.livefs import (
105+ ILiveFSSet,
106+ LIVEFS_FEATURE_FLAG,
107+ )
108 from lp.soyuz.interfaces.packageset import (
109 IPackagesetSet,
110 NoSuchPackageSet,
111@@ -1632,3 +1645,86 @@
112 ("The selected architecture independent architecture tag is not "
113 "among the selected architectures."),
114 ids.check)
115+
116+ def test_copying_livefses(self):
117+ # If a parent series has live filesystems, we should copy them.
118+ self.parent, self.parent_das = self.setupParent()
119+ with FeatureFixture({LIVEFS_FEATURE_FLAG: u'on'}):
120+ livefs1 = self.factory.makeLiveFS(distroseries=self.parent)
121+ self.factory.makeLiveFSBuild(
122+ livefs=livefs1, date_created=ONE_DAY_AGO)
123+ livefs2 = self.factory.makeLiveFS(
124+ distroseries=self.parent,
125+ metadata={'project': 'ubuntu-core', 'image_format': 'plain'},
126+ require_virtualized=False)
127+ self.factory.makeLiveFSBuild(
128+ livefs=livefs2, date_created=ONE_DAY_AGO)
129+ child = self._fullInitialize([self.parent])
130+ child_livefs1 = getUtility(ILiveFSSet).getByName(
131+ livefs1.owner, child, livefs1.name)
132+ self.assertThat(child_livefs1, MatchesStructure.byEquality(
133+ registrant=livefs1.registrant, owner=livefs1.owner,
134+ distro_series=child, name=livefs1.name, metadata=livefs1.metadata,
135+ require_virtualized=livefs1.require_virtualized))
136+ child_livefs2 = getUtility(ILiveFSSet).getByName(
137+ livefs2.owner, child, livefs2.name)
138+ self.assertThat(child_livefs2, MatchesStructure.byEquality(
139+ registrant=livefs2.registrant, owner=livefs2.owner,
140+ distro_series=child, name=livefs2.name, metadata=livefs2.metadata,
141+ require_virtualized=livefs2.require_virtualized))
142+
143+ def test_copying_livefses_no_duplication(self):
144+ # Copying live filesystems only copies them from the most recent
145+ # series, rather than merging them from all series.
146+ previous_parent, _ = self.setupParent()
147+ parent = self._fullInitialize([previous_parent])
148+ with FeatureFixture({LIVEFS_FEATURE_FLAG: u'on'}):
149+ parent_livefs1 = self.factory.makeLiveFS(
150+ distroseries=parent, metadata={'marker': 'parent'})
151+ self.factory.makeLiveFSBuild(
152+ livefs=parent_livefs1, date_created=ONE_DAY_AGO)
153+ parent_livefs2 = self.factory.makeLiveFS(distroseries=parent)
154+ self.factory.makeLiveFSBuild(
155+ livefs=parent_livefs2, date_created=ONE_DAY_AGO)
156+ child = self._fullInitialize(
157+ [previous_parent], previous_series=parent,
158+ distribution=parent.distribution)
159+ child_livefs1 = getUtility(ILiveFSSet).getByName(
160+ parent_livefs1.owner, child, parent_livefs1.name)
161+ child_livefs1.metadata = {'marker': 'child'}
162+ with FeatureFixture({LIVEFS_FEATURE_FLAG: u'on'}):
163+ self.factory.makeLiveFSBuild(
164+ livefs=child_livefs1, date_created=ONE_DAY_AGO)
165+ child_livefs2 = getUtility(ILiveFSSet).getByName(
166+ parent_livefs2.owner, child, parent_livefs2.name)
167+ child_livefs2.destroySelf()
168+ grandchild = self._fullInitialize(
169+ [previous_parent], previous_series=child,
170+ distribution=parent.distribution)
171+ grandchild_livefs1 = getUtility(ILiveFSSet).getByName(
172+ child_livefs1.owner, grandchild, child_livefs1.name)
173+ self.assertEqual({'marker': 'child'}, grandchild_livefs1.metadata)
174+ self.assertFalse(
175+ getUtility(ILiveFSSet).exists(
176+ parent_livefs2.owner, grandchild, parent_livefs2.name))
177+
178+ def test_copying_livefses_skips_dormant(self):
179+ # Live filesystems that have not been built in the last 30 days are
180+ # not copied.
181+ self.parent, self.parent_das = self.setupParent()
182+ with FeatureFixture({LIVEFS_FEATURE_FLAG: u'on'}):
183+ livefs1 = self.factory.makeLiveFS(distroseries=self.parent)
184+ for days in 31, 30, 29:
185+ self.factory.makeLiveFSBuild(
186+ livefs=livefs1,
187+ date_created=datetime.now(pytz.UTC) - timedelta(days=days))
188+ livefs2 = self.factory.makeLiveFS(distroseries=self.parent)
189+ for days in 33, 32, 31:
190+ self.factory.makeLiveFSBuild(
191+ livefs=livefs2,
192+ date_created=datetime.now(pytz.UTC) - timedelta(days=days))
193+ child = self._fullInitialize([self.parent])
194+ self.assertTrue(
195+ getUtility(ILiveFSSet).exists(livefs1.owner, child, livefs1.name))
196+ self.assertFalse(
197+ getUtility(ILiveFSSet).exists(livefs2.owner, child, livefs2.name))