Merge lp:~cjwatson/launchpad/livefs-personmerge into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 17058
Proposed branch: lp:~cjwatson/launchpad/livefs-personmerge
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/livefs
Diff against target: 182 lines (+82/-5)
5 files modified
lib/lp/registry/personmerge.py (+23/-4)
lib/lp/registry/tests/test_personmerge.py (+39/-1)
lib/lp/soyuz/interfaces/livefs.py (+3/-0)
lib/lp/soyuz/model/livefs.py (+4/-0)
lib/lp/soyuz/tests/test_livefs.py (+13/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/livefs-personmerge
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+223654@code.launchpad.net

Commit message

Handle LiveFS.owner during person merging.

Description of the change

Handle LiveFS.owner during person merging. This reverts the temporary hack from https://code.launchpad.net/~cjwatson/launchpad/livefs-personmerge-whitelist/+merge/223537.

The main thing I was unsure about here was the manual flush I had to do at the end of _mergeLiveFS. It makes sense that I should have to flush database updates, given that merge_people calls store.invalidate() not long afterwards, and my tests failed when I didn't do that; but how come _mergeSourcePackageRecipes apparently doesn't need to do the same?

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 'lib/lp/registry/personmerge.py'
2--- lib/lp/registry/personmerge.py 2014-06-18 10:52:12 +0000
3+++ lib/lp/registry/personmerge.py 2014-06-18 22:20:53 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Person/team merger implementation."""
10@@ -34,6 +34,7 @@
11 from lp.services.mail.helpers import get_email_template
12 from lp.soyuz.enums import ArchiveStatus
13 from lp.soyuz.interfaces.archive import IArchiveSet
14+from lp.soyuz.interfaces.livefs import ILiveFSSet
15
16
17 def _merge_person_decoration(to_person, from_person, skip,
18@@ -577,6 +578,24 @@
19 ''' % params)
20
21
22+def _mergeLiveFS(cur, from_person, to_person):
23+ # This shouldn't use removeSecurityProxy.
24+ livefses = getUtility(ILiveFSSet).getByPerson(from_person)
25+ existing_names = [
26+ l.name for l in getUtility(ILiveFSSet).getByPerson(to_person)]
27+ for livefs in livefses:
28+ new_name = livefs.name
29+ count = 1
30+ while new_name in existing_names:
31+ new_name = '%s-%s' % (livefs.name, count)
32+ count += 1
33+ naked_livefs = removeSecurityProxy(livefs)
34+ naked_livefs.owner = to_person
35+ naked_livefs.name = new_name
36+ if not livefses.is_empty():
37+ IStore(livefses[0]).flush()
38+
39+
40 def _purgeUnmergableTeamArtifacts(from_team, to_team, reviewer):
41 """Purge team artifacts that cannot be merged, but can be removed."""
42 # A team cannot have more than one mailing list.
43@@ -672,9 +691,6 @@
44 ('bugsummaryjournal', 'viewed_by'),
45 ('latestpersonsourcepackagereleasecache', 'creator'),
46 ('latestpersonsourcepackagereleasecache', 'maintainer'),
47- # This needs handling before we deploy the livefs code, but can be
48- # ignored for the purpose of deploying the database tables.
49- ('livefs', 'owner'),
50 ]
51
52 references = list(postgresql.listReferences(cur, 'person', 'id'))
53@@ -799,6 +815,9 @@
54 _mergeCodeReviewInlineCommentDraft(cur, from_id, to_id)
55 skip.append(('codereviewinlinecommentdraft', 'person'))
56
57+ _mergeLiveFS(cur, from_person, to_person)
58+ skip.append(('livefs', 'owner'))
59+
60 # Sanity check. If we have a reference that participates in a
61 # UNIQUE index, it must have already been handled by this point.
62 # We can tell this by looking at the skip list.
63
64=== modified file 'lib/lp/registry/tests/test_personmerge.py'
65--- lib/lp/registry/tests/test_personmerge.py 2013-06-21 02:39:33 +0000
66+++ lib/lp/registry/tests/test_personmerge.py 2014-06-18 22:20:53 +0000
67@@ -1,4 +1,4 @@
68-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
69+# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
70 # GNU Affero General Public License version 3 (see the file LICENSE).
71
72 """Tests for merge_people."""
73@@ -34,11 +34,16 @@
74 from lp.registry.tests.test_person import KarmaTestMixin
75 from lp.services.config import config
76 from lp.services.database.sqlbase import cursor
77+from lp.services.features.testing import FeatureFixture
78 from lp.services.identity.interfaces.emailaddress import (
79 EmailAddressStatus,
80 IEmailAddressSet,
81 )
82 from lp.soyuz.enums import ArchiveStatus
83+from lp.soyuz.interfaces.livefs import (
84+ ILiveFSSet,
85+ LIVEFS_FEATURE_FLAG,
86+ )
87 from lp.testing import (
88 celebrity_logged_in,
89 login_person,
90@@ -488,6 +493,39 @@
91 self.assertEqual(0, inviting_team.invited_member_count)
92 self.assertEqual(0, proposed_team.proposed_member_count)
93
94+ def test_merge_moves_livefses(self):
95+ # When person/teams are merged, live filesystems owned by the from
96+ # person are moved.
97+ self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
98+ duplicate = self.factory.makePerson()
99+ mergee = self.factory.makePerson()
100+ self.factory.makeLiveFS(registrant=duplicate, owner=duplicate)
101+ self._do_premerge(duplicate, mergee)
102+ login_person(mergee)
103+ duplicate, mergee = self._do_merge(duplicate, mergee)
104+ self.assertEqual(1, getUtility(ILiveFSSet).getByPerson(mergee).count())
105+
106+ def test_merge_with_duplicated_livefses(self):
107+ # If both the from and to people have live filesystems with the same
108+ # name, merging renames the duplicate from the from person's side.
109+ self.useFixture(FeatureFixture({LIVEFS_FEATURE_FLAG: u"on"}))
110+ duplicate = self.factory.makePerson()
111+ mergee = self.factory.makePerson()
112+ self.factory.makeLiveFS(
113+ registrant=duplicate, owner=duplicate, name=u'foo',
114+ metadata={'project': 'FROM'})
115+ self.factory.makeLiveFS(
116+ registrant=mergee, owner=mergee, name=u'foo',
117+ metadata={'project': 'TO'})
118+ self._do_premerge(duplicate, mergee)
119+ login_person(mergee)
120+ duplicate, mergee = self._do_merge(duplicate, mergee)
121+ livefses = getUtility(ILiveFSSet).getByPerson(mergee)
122+ self.assertEqual(2, livefses.count())
123+ project_names = [l.metadata['project'] for l in livefses]
124+ self.assertEqual(['TO', 'FROM'], project_names)
125+ self.assertEqual(u'foo-1', livefses[1].name)
126+
127
128 class TestMergeMailingListSubscriptions(TestCaseWithFactory):
129
130
131=== modified file 'lib/lp/soyuz/interfaces/livefs.py'
132--- lib/lp/soyuz/interfaces/livefs.py 2014-06-18 22:20:52 +0000
133+++ lib/lp/soyuz/interfaces/livefs.py 2014-06-18 22:20:53 +0000
134@@ -280,6 +280,9 @@
135 def interpret(owner_name, distribution_name, distro_series_name, name):
136 """Like `getByName`, but takes names of objects."""
137
138+ def getByPerson(owner):
139+ """Return all live filesystems with the given `owner`."""
140+
141 @collection_default_content()
142 def getAll():
143 """Return all of the live filesystems in Launchpad.
144
145=== modified file 'lib/lp/soyuz/model/livefs.py'
146--- lib/lp/soyuz/model/livefs.py 2014-06-18 22:20:52 +0000
147+++ lib/lp/soyuz/model/livefs.py 2014-06-18 22:20:53 +0000
148@@ -283,6 +283,10 @@
149 getUtility(IDistroSeriesSet).queryByName, distribution)
150 return self.getByName(owner, distro_series, name)
151
152+ def getByPerson(self, owner):
153+ """See `ILiveFSSet`."""
154+ return IStore(LiveFS).find(LiveFS, LiveFS.owner == owner)
155+
156 def getAll(self):
157 """See `ILiveFSSet`."""
158 user = getUtility(ILaunchBag).user
159
160=== modified file 'lib/lp/soyuz/tests/test_livefs.py'
161--- lib/lp/soyuz/tests/test_livefs.py 2014-06-18 22:20:52 +0000
162+++ lib/lp/soyuz/tests/test_livefs.py 2014-06-18 22:20:53 +0000
163@@ -301,6 +301,19 @@
164 getUtility(ILiveFSSet).exists(
165 livefs.owner, livefs.distro_series, u"different"))
166
167+ def test_getByPerson(self):
168+ # ILiveFSSet.getByPerson returns all LiveFSes with the given owner.
169+ owners = [self.factory.makePerson() for i in range(2)]
170+ livefses = []
171+ for owner in owners:
172+ for i in range(2):
173+ livefses.append(self.factory.makeLiveFS(
174+ registrant=owner, owner=owner))
175+ self.assertContentEqual(
176+ livefses[:2], getUtility(ILiveFSSet).getByPerson(owners[0]))
177+ self.assertContentEqual(
178+ livefses[2:], getUtility(ILiveFSSet).getByPerson(owners[1]))
179+
180 def test_getAll(self):
181 # ILiveFSSet.getAll returns all LiveFSes.
182 livefses = [self.factory.makeLiveFS() for i in range(3)]