Merge lp:~sinzui/launchpad/cleanup-merged-people-1 into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 16338
Proposed branch: lp:~sinzui/launchpad/cleanup-merged-people-1
Merge into: lp:launchpad
Diff against target: 97 lines (+42/-0)
3 files modified
database/schema/security.cfg (+1/-0)
lib/lp/scripts/garbo.py (+20/-0)
lib/lp/scripts/tests/test_garbo.py (+21/-0)
To merge this branch: bzr merge lp:~sinzui/launchpad/cleanup-merged-people-1
Reviewer Review Type Date Requested Status
Richard Harding (community) Approve
Review via email: mp+137700@code.launchpad.net

Commit message

clean up merged people data.

Description of the change

Merged teams and users continue to have relationships because some data
could not be transferred or deleted during the merge process, because
there are race conditions. TeamMembership records continue to exist for
example and they cause 404's because they are listed in pending,
invited, and former members. In the case or merged private-teams, the
+members page will raise a 403 error for team admins.

RULES

    Pre-implementation: lifeless and stub
    * Create a daily garbo job that deletes TeamMembership rows for
      merged people.
    * This could also do the same for deactivated users or user who
      are suspended for more than 30 days.
    * A successful implementation could also be extended to remove
      archive, bug, blueprint, branch, and question subscriptions.

QA

    * Ask a webops to run ./cronscripts/garbo-hourly.py for qastaging
      (wampee).
    * Visit https://qastaging.launchpad.net/~launchpad-users/+members
      and step to the second batch of Former members
    * Verify that easter_egg (a.j-merged) is not listed among the former
      members.

LINT

    database/schema/security.cfg
    lib/lp/scripts/garbo.py
    lib/lp/scripts/tests/test_garbo.py

LoC

    I have more than 10,000 lines of credit this week.

TEST

    ./bin/test -vvc -t TeamMembershipPruner lp.scripts.tests.test_garbo

IMPLEMENTATION

I added a garbo job that deletes all the membership records for merged
users and teams. There are about 176 TeamMemberships that were left
behind by merge. The TeamParticipation rules already discounted the
merged users, so it is just the TM rows that are causing 403 and 404
issues for team admins.
    database/schema/security.cfg
    lib/lp/scripts/garbo.py
    lib/lp/scripts/tests/test_garbo.py

To post a comment you must log in.
Revision history for this message
Richard Harding (rharding) :
review: Approve

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 2012-11-30 10:15:26 +0000
3+++ database/schema/security.cfg 2012-12-03 22:09:24 +0000
4@@ -2264,6 +2264,7 @@
5 public.sourcepackagerelease = SELECT
6 public.sourcepackagepublishinghistory = SELECT, UPDATE
7 public.suggestivepotemplate = INSERT, DELETE
8+public.teammembership = SELECT, DELETE
9 public.teamparticipation = SELECT, DELETE
10 public.translationmessage = SELECT, DELETE
11 public.translationtemplateitem = SELECT, DELETE
12
13=== modified file 'lib/lp/scripts/garbo.py'
14--- lib/lp/scripts/garbo.py 2012-11-26 08:33:03 +0000
15+++ lib/lp/scripts/garbo.py 2012-12-03 22:09:24 +0000
16@@ -70,6 +70,7 @@
17 from lp.registry.model.commercialsubscription import CommercialSubscription
18 from lp.registry.model.person import Person
19 from lp.registry.model.product import Product
20+from lp.registry.model.teammembership import TeamMembership
21 from lp.services.config import config
22 from lp.services.database import postgresql
23 from lp.services.database.bulk import (
24@@ -1005,6 +1006,24 @@
25 % chunk_size)
26
27
28+class TeamMembershipPruner(BulkPruner):
29+ """Remove team memberships for merged people.
30+
31+ People merge can leave team membership records behind because:
32+ * The membership duplicates another membership.
33+ * The membership would have created a cyclic relationshop.
34+ * The operation avoid a race condition.
35+ """
36+ target_table_class = TeamMembership
37+ ids_to_prune_query = """
38+ SELECT TeamMembership.id
39+ FROM TeamMembership, Person
40+ WHERE
41+ TeamMembership.person = person.id
42+ AND person.merged IS NOT NULL
43+ """
44+
45+
46 class BugNotificationPruner(BulkPruner):
47 """Prune `BugNotificationRecipient` records no longer of interest.
48
49@@ -1632,6 +1651,7 @@
50 ScrubPOFileTranslator,
51 ProductInformationTypeDefault,
52 SuggestiveTemplatesCacheUpdater,
53+ TeamMembershipPruner,
54 POTranslationPruner,
55 UnlinkedAccountPruner,
56 UnusedAccessPolicyPruner,
57
58=== modified file 'lib/lp/scripts/tests/test_garbo.py'
59--- lib/lp/scripts/tests/test_garbo.py 2012-11-26 08:33:03 +0000
60+++ lib/lp/scripts/tests/test_garbo.py 2012-12-03 22:09:24 +0000
61@@ -61,8 +61,10 @@
62 )
63 from lp.registry.interfaces.accesspolicy import IAccessPolicySource
64 from lp.registry.interfaces.person import IPersonSet
65+from lp.registry.interfaces.teammembership import TeamMembershipStatus
66 from lp.registry.model.commercialsubscription import CommercialSubscription
67 from lp.registry.model.product import Product
68+from lp.registry.model.teammembership import TeamMembership
69 from lp.scripts.garbo import (
70 AntiqueSessionPruner,
71 BulkPruner,
72@@ -739,6 +741,25 @@
73 personset.getByName('test-unlinked-person-new'), None)
74 self.assertIs(personset.getByName('test-unlinked-person-old'), None)
75
76+ def test_TeamMembershipPruner(self):
77+ # Garbo should remove team memberships for meregd users and teams.
78+ switch_dbuser('testadmin')
79+ merged_user = self.factory.makePerson()
80+ team = self.factory.makeTeam(members=[merged_user])
81+ merged_team = self.factory.makeTeam()
82+ team.addMember(
83+ merged_team, team.teamowner, status=TeamMembershipStatus.PROPOSED)
84+ # This is fast and dirty way to place the user and team in a
85+ # merged state to verify what the TeamMembershipPruner sees.
86+ removeSecurityProxy(merged_user).merged = self.factory.makePerson()
87+ removeSecurityProxy(merged_team).merged = self.factory.makeTeam()
88+ store = Store.of(team)
89+ store.flush()
90+ result = store.find(TeamMembership, TeamMembership.team == team.id)
91+ self.assertEqual(3, result.count())
92+ self.runDaily()
93+ self.assertContentEqual([team.teamowner], [tm.person for tm in result])
94+
95 def test_BugNotificationPruner(self):
96 # Create some sample data
97 switch_dbuser('testadmin')