Merge lp:~stub/launchpad/garbo into lp:launchpad/db-devel

Proposed by Stuart Bishop
Status: Merged
Approved by: Stuart Bishop
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~stub/launchpad/garbo
Merge into: lp:launchpad/db-devel
Diff against target: 1206 lines
To merge this branch: bzr merge lp:~stub/launchpad/garbo
Reviewer Review Type Date Requested Status
Paul Hummer (community) code Approve
Review via email: mp+4834@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

The first cut of the database garbage collector. This is Oscar the Grounch, originally conceived when Launchpad was just an ER diagram.

I expect the framework will grow over time. This first version just prunes our Nonce tables and CodeImportResult entries.

There are some other cronscripts that could be folded in to this framework.

Some DB tweaks snuck in during the work.

Revision history for this message
Paul Hummer (rockstar) wrote :

Obviously, I can't comment on the database stuff, but the scripts themselves appear sound and well tested.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'cronscripts/garbo-daily.py'
--- cronscripts/garbo-daily.py 1970-01-01 00:00:00 +0000
+++ cronscripts/garbo-daily.py 2009-03-25 11:41:03 +0000
@@ -0,0 +1,19 @@
1#!/usr/bin/python2.4
2# Copyright 2009 Canonical Ltd. All rights reserved.
3
4"""Database garbage collector.
5
6Remove or archive unwanted data. Detect, warn and possibly repair data
7corruption.
8"""
9
10__metaclass__ = type
11__all__ = []
12
13import _pythonpath
14from canonical.launchpad.scripts.garbo import DailyDatabaseGarbageCollector
15
16if __name__ == '__main__':
17 script = DailyDatabaseGarbageCollector()
18 script.lock_and_run()
19
020
=== added file 'cronscripts/garbo-hourly.py'
--- cronscripts/garbo-hourly.py 1970-01-01 00:00:00 +0000
+++ cronscripts/garbo-hourly.py 2009-03-25 11:41:03 +0000
@@ -0,0 +1,19 @@
1#!/usr/bin/python2.4
2# Copyright 2009 Canonical Ltd. All rights reserved.
3
4"""Database garbage collector.
5
6Remove or archive unwanted data. Detect, warn and possibly repair data
7corruption.
8"""
9
10__metaclass__ = type
11__all__ = []
12
13import _pythonpath
14from canonical.launchpad.scripts.garbo import HourlyDatabaseGarbageCollector
15
16if __name__ == '__main__':
17 script = HourlyDatabaseGarbageCollector()
18 script.lock_and_run()
19
020
=== added file 'database/schema/patch-2109-42-0.sql'
--- database/schema/patch-2109-42-0.sql 1970-01-01 00:00:00 +0000
+++ database/schema/patch-2109-42-0.sql 2009-03-25 11:41:03 +0000
@@ -0,0 +1,8 @@
1SET client_min_messages=ERROR;
2
3ALTER TABLE OAuthNonce
4 ADD CONSTRAINT oauthnonce__access_token__fk
5 FOREIGN KEY (access_token) REFERENCES OAuthAccessToken;
6
7INSERT INTO LaunchpadDatabaseRevision VALUES (2109, 42, 0);
8
09
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2009-03-24 22:08:20 +0000
+++ database/schema/security.cfg 2009-03-25 11:41:03 +0000
@@ -95,7 +95,7 @@
95[launchpad_main]95[launchpad_main]
96# lpmain replication set access from the main Z3 application.96# lpmain replication set access from the main Z3 application.
97type=user97type=user
98groups=write98groups=write,script
99public.account = SELECT99public.account = SELECT
100public.announcement = SELECT, INSERT, UPDATE, DELETE100public.announcement = SELECT, INSERT, UPDATE, DELETE
101public.answercontact = SELECT, INSERT, UPDATE, DELETE101public.answercontact = SELECT, INSERT, UPDATE, DELETE
@@ -232,7 +232,7 @@
232public.revisionauthor = SELECT, INSERT, UPDATE232public.revisionauthor = SELECT, INSERT, UPDATE
233public.revisionnumber = SELECT, INSERT233public.revisionnumber = SELECT, INSERT
234public.revisionparent = SELECT, INSERT234public.revisionparent = SELECT, INSERT
235public.scriptactivity = SELECT, INSERT235public.scriptactivity = SELECT
236public.shipitreport = SELECT, INSERT236public.shipitreport = SELECT, INSERT
237public.shipitsurvey = SELECT, INSERT, UPDATE237public.shipitsurvey = SELECT, INSERT, UPDATE
238public.shipitsurveyquestion = SELECT, INSERT238public.shipitsurveyquestion = SELECT, INSERT
@@ -278,8 +278,14 @@
278type=user278type=user
279groups=launchpad_main279groups=launchpad_main
280280
281[script]
282# Permissions required by all scripts.
283type=group
284public.scriptactivity = SELECT, INSERT
285
281[statistician]286[statistician]
282type=user287type=user
288groups=script
283public.archive = SELECT, UPDATE289public.archive = SELECT, UPDATE
284public.archivearch = SELECT, UPDATE290public.archivearch = SELECT, UPDATE
285public.binarypackagename = SELECT291public.binarypackagename = SELECT
@@ -308,7 +314,6 @@
308public.product = SELECT314public.product = SELECT
309public.productseries = SELECT315public.productseries = SELECT
310public.question = SELECT316public.question = SELECT
311public.scriptactivity = SELECT, INSERT
312public.sourcepackagename = SELECT317public.sourcepackagename = SELECT
313public.sourcepackagepublishinghistory = SELECT318public.sourcepackagepublishinghistory = SELECT
314public.sourcepackagerelease = SELECT319public.sourcepackagerelease = SELECT
@@ -329,6 +334,7 @@
329334
330[librariangc]335[librariangc]
331type=user336type=user
337groups=script
332public.libraryfilealias = SELECT, UPDATE, DELETE338public.libraryfilealias = SELECT, UPDATE, DELETE
333public.libraryfilecontent = SELECT, UPDATE, DELETE339public.libraryfilecontent = SELECT, UPDATE, DELETE
334# This user needs select on every table that references LibraryFileAlias340# This user needs select on every table that references LibraryFileAlias
@@ -358,7 +364,6 @@
358public.product = SELECT364public.product = SELECT
359public.productreleasefile = SELECT365public.productreleasefile = SELECT
360public.project = SELECT366public.project = SELECT
361public.scriptactivity = SELECT, INSERT
362public.shipitreport = SELECT367public.shipitreport = SELECT
363public.shippingrun = SELECT368public.shippingrun = SELECT
364public.sprint = SELECT369public.sprint = SELECT
@@ -369,6 +374,7 @@
369[productreleasefinder]374[productreleasefinder]
370# Dyson release import script375# Dyson release import script
371type=user376type=user
377groups=script
372public.product = SELECT378public.product = SELECT
373public.productseries = SELECT379public.productseries = SELECT
374public.productrelease = SELECT, INSERT, UPDATE380public.productrelease = SELECT, INSERT, UPDATE
@@ -378,26 +384,24 @@
378# Needed to write to the librarian384# Needed to write to the librarian
379public.libraryfilealias = SELECT, INSERT385public.libraryfilealias = SELECT, INSERT
380public.libraryfilecontent = SELECT, INSERT386public.libraryfilecontent = SELECT, INSERT
381public.scriptactivity = SELECT, INSERT
382387
383[pofilestats]388[pofilestats]
384# Translations POFile statistics verification/update script389# Translations POFile statistics verification/update script
385type=user390type=user
391groups=script
386public.language = SELECT392public.language = SELECT
387public.pofile = SELECT, UPDATE393public.pofile = SELECT, UPDATE
388public.potemplate = SELECT394public.potemplate = SELECT
389public.potmsgset = SELECT395public.potmsgset = SELECT
390public.scriptactivity = SELECT, INSERT
391public.translationmessage = SELECT396public.translationmessage = SELECT
392public.translationtemplateitem = SELECT397public.translationtemplateitem = SELECT
393398
394[poimport]399[poimport]
395# Rosetta import script400# Rosetta import script
396type=user401type=user
397groups=write402groups=write,script
398public.account = SELECT, INSERT403public.account = SELECT, INSERT
399public.customlanguagecode = SELECT404public.customlanguagecode = SELECT
400public.scriptactivity = SELECT, INSERT
401public.translationgroup = SELECT405public.translationgroup = SELECT
402public.translationimportqueueentry = SELECT, DELETE406public.translationimportqueueentry = SELECT, DELETE
403public.translationmessage = SELECT, INSERT, UPDATE407public.translationmessage = SELECT, INSERT, UPDATE
@@ -409,6 +413,7 @@
409[poexport]413[poexport]
410# Rosetta export script414# Rosetta export script
411type=user415type=user
416groups=script
412public.distribution = SELECT417public.distribution = SELECT
413public.distroseries = SELECT418public.distroseries = SELECT
414public.emailaddress = SELECT419public.emailaddress = SELECT
@@ -427,7 +432,6 @@
427public.potranslation = SELECT432public.potranslation = SELECT
428public.product = SELECT433public.product = SELECT
429public.productseries = SELECT434public.productseries = SELECT
430public.scriptactivity = SELECT, INSERT
431public.sourcepackagename = SELECT435public.sourcepackagename = SELECT
432public.translationgroup = SELECT436public.translationgroup = SELECT
433public.translationmessage = SELECT437public.translationmessage = SELECT
@@ -439,6 +443,7 @@
439[langpack]443[langpack]
440# Language pack exporter script444# Language pack exporter script
441type=user445type=user
446groups=script
442public.distribution = SELECT447public.distribution = SELECT
443public.distroseries = SELECT, UPDATE448public.distroseries = SELECT, UPDATE
444public.emailaddress = SELECT449public.emailaddress = SELECT
@@ -457,7 +462,6 @@
457public.potranslation = SELECT462public.potranslation = SELECT
458public.product = SELECT463public.product = SELECT
459public.productseries = SELECT464public.productseries = SELECT
460public.scriptactivity = SELECT, INSERT
461public.sourcepackagename = SELECT465public.sourcepackagename = SELECT
462public.translationgroup = SELECT466public.translationgroup = SELECT
463public.translationmessage = SELECT467public.translationmessage = SELECT
@@ -469,6 +473,7 @@
469[checkwatches]473[checkwatches]
470# Malone bug watch script474# Malone bug watch script
471type=user475type=user
476groups=script
472public.account = SELECT, INSERT477public.account = SELECT, INSERT
473public.accountpassword = SELECT, INSERT478public.accountpassword = SELECT, INSERT
474public.answercontact = SELECT479public.answercontact = SELECT
@@ -506,7 +511,6 @@
506public.questionbug = SELECT511public.questionbug = SELECT
507public.question = SELECT512public.question = SELECT
508public.questionsubscription = SELECT513public.questionsubscription = SELECT
509public.scriptactivity = SELECT, INSERT
510public.sourcepackagename = SELECT514public.sourcepackagename = SELECT
511public.structuralsubscription = SELECT515public.structuralsubscription = SELECT
512public.teammembership = SELECT516public.teammembership = SELECT
@@ -532,7 +536,7 @@
532536
533[branchscanner]537[branchscanner]
534type=user538type=user
535groups=write539groups=write, script
536public.account = SELECT, INSERT540public.account = SELECT, INSERT
537public.accountpassword = SELECT, INSERT541public.accountpassword = SELECT, INSERT
538public.branch = SELECT, UPDATE542public.branch = SELECT, UPDATE
@@ -556,7 +560,6 @@
556public.revisionauthor = SELECT, INSERT, UPDATE560public.revisionauthor = SELECT, INSERT, UPDATE
557public.revisionparent = SELECT, INSERT561public.revisionparent = SELECT, INSERT
558public.revisionproperty = SELECT, INSERT562public.revisionproperty = SELECT, INSERT
559public.scriptactivity = SELECT, INSERT
560public.sourcepackagename = SELECT563public.sourcepackagename = SELECT
561public.staticdiff = SELECT, INSERT, DELETE564public.staticdiff = SELECT, INSERT, DELETE
562public.validpersoncache = SELECT565public.validpersoncache = SELECT
@@ -564,6 +567,7 @@
564567
565[targetnamecacheupdater]568[targetnamecacheupdater]
566type=user569type=user
570groups=script
567public.bugtask = SELECT, UPDATE571public.bugtask = SELECT, UPDATE
568public.product = SELECT572public.product = SELECT
569public.productseries = SELECT573public.productseries = SELECT
@@ -572,10 +576,10 @@
572public.sourcepackagename = SELECT576public.sourcepackagename = SELECT
573public.binarypackagename = SELECT577public.binarypackagename = SELECT
574public.potemplate = SELECT, UPDATE578public.potemplate = SELECT, UPDATE
575public.scriptactivity = SELECT, INSERT
576579
577[distributionmirror]580[distributionmirror]
578type=user581type=user
582groups=script
579public.archive = SELECT583public.archive = SELECT
580public.archivearch = SELECT584public.archivearch = SELECT
581public.binarypackagefile = SELECT585public.binarypackagefile = SELECT
@@ -597,7 +601,6 @@
597public.mirrorproberecord = SELECT, INSERT601public.mirrorproberecord = SELECT, INSERT
598public.person = SELECT602public.person = SELECT
599public.processorfamily = SELECT603public.processorfamily = SELECT
600public.scriptactivity = SELECT, INSERT
601public.securesourcepackagepublishinghistory = SELECT604public.securesourcepackagepublishinghistory = SELECT
602public.securebinarypackagepublishinghistory = SELECT605public.securebinarypackagepublishinghistory = SELECT
603public.sourcepackagerelease = SELECT606public.sourcepackagerelease = SELECT
@@ -608,15 +611,16 @@
608[teammembership]611[teammembership]
609# Update the TeamMembership table setting expired members612# Update the TeamMembership table setting expired members
610type=user613type=user
614groups=script
611public.teammembership = SELECT, UPDATE615public.teammembership = SELECT, UPDATE
612public.teamparticipation = SELECT, DELETE616public.teamparticipation = SELECT, DELETE
613public.person = SELECT617public.person = SELECT
614public.emailaddress = SELECT618public.emailaddress = SELECT
615public.scriptactivity = SELECT, INSERT
616619
617[karma]620[karma]
618# Update the KarmaCache table621# Update the KarmaCache table
619type=user622type=user
623groups=script
620public.karmacache = SELECT, INSERT, UPDATE, DELETE624public.karmacache = SELECT, INSERT, UPDATE, DELETE
621public.karma = SELECT625public.karma = SELECT
622public.karmacategory = SELECT626public.karmacategory = SELECT
@@ -627,11 +631,11 @@
627public.product = SELECT631public.product = SELECT
628public.validpersoncache = SELECT632public.validpersoncache = SELECT
629public.validpersonorteamcache = SELECT633public.validpersonorteamcache = SELECT
630public.scriptactivity = SELECT, INSERT
631634
632[revisionkarma]635[revisionkarma]
633# Allocate karma for revisions.636# Allocate karma for revisions.
634type=user637type=user
638groups=script
635public.branch = SELECT639public.branch = SELECT
636public.branchrevision = SELECT640public.branchrevision = SELECT
637public.karma = SELECT, INSERT641public.karma = SELECT, INSERT
@@ -642,20 +646,19 @@
642public.productseries = SELECT646public.productseries = SELECT
643public.revision = SELECT, UPDATE647public.revision = SELECT, UPDATE
644public.revisionauthor = SELECT648public.revisionauthor = SELECT
645public.scriptactivity = SELECT, INSERT
646public.validpersoncache = SELECT649public.validpersoncache = SELECT
647650
648[cve]651[cve]
649type=user652type=user
653groups=script
650public.cve = SELECT, INSERT, UPDATE654public.cve = SELECT, INSERT, UPDATE
651public.cvereference = SELECT, INSERT, UPDATE, DELETE655public.cvereference = SELECT, INSERT, UPDATE, DELETE
652public.scriptactivity = SELECT, INSERT
653656
654657
655[gina]658[gina]
656# Unpack sourcepackages and extract metadata659# Unpack sourcepackages and extract metadata
657type=user660type=user
658groups=write661groups=write,script
659public.account = SELECT, INSERT662public.account = SELECT, INSERT
660public.accountpassword = SELECT, INSERT663public.accountpassword = SELECT, INSERT
661public.archive = SELECT, UPDATE664public.archive = SELECT, UPDATE
@@ -663,14 +666,13 @@
663public.distribution = SELECT666public.distribution = SELECT
664public.openidrpsummary = SELECT667public.openidrpsummary = SELECT
665public.packagediff = SELECT, INSERT, UPDATE668public.packagediff = SELECT, INSERT, UPDATE
666public.scriptactivity = SELECT, INSERT
667public.securebinarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE669public.securebinarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE
668public.securesourcepackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE670public.securesourcepackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE
669671
670[lucille]672[lucille]
671# Soyuz archive publisher.673# Soyuz archive publisher.
672type=user674type=user
673groups=write675groups=write,script
674public.archive = SELECT, UPDATE676public.archive = SELECT, UPDATE
675public.archivearch = SELECT677public.archivearch = SELECT
676public.archiveauthtoken = SELECT, UPDATE678public.archiveauthtoken = SELECT, UPDATE
@@ -679,7 +681,6 @@
679public.gpgkey = SELECT, INSERT, UPDATE681public.gpgkey = SELECT, INSERT, UPDATE
680public.packagecopyrequest = SELECT, INSERT, UPDATE682public.packagecopyrequest = SELECT, INSERT, UPDATE
681public.packagediff = SELECT, INSERT, UPDATE683public.packagediff = SELECT, INSERT, UPDATE
682public.scriptactivity = SELECT, INSERT
683public.securebinarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE684public.securebinarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE
684public.securesourcepackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE685public.securesourcepackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE
685public.sourcepackagepublishinghistory = SELECT686public.sourcepackagepublishinghistory = SELECT
@@ -719,6 +720,7 @@
719720
720[fiera]721[fiera]
721type=user722type=user
723groups=script
722public.account = SELECT724public.account = SELECT
723public.archive = SELECT, UPDATE725public.archive = SELECT, UPDATE
724public.archivearch = SELECT, UPDATE726public.archivearch = SELECT, UPDATE
@@ -750,11 +752,11 @@
750public.person = SELECT752public.person = SELECT
751public.emailaddress = SELECT753public.emailaddress = SELECT
752public.teammembership = SELECT754public.teammembership = SELECT
753public.scriptactivity = SELECT, INSERT
754public.gpgkey = SELECT755public.gpgkey = SELECT
755756
756[sourcerer]757[sourcerer]
757type=user758type=user
759groups=script
758public.archive = SELECT760public.archive = SELECT
759public.archivearch = SELECT761public.archivearch = SELECT
760public.branch = SELECT, INSERT, UPDATE762public.branch = SELECT, INSERT, UPDATE
@@ -779,7 +781,6 @@
779# To get stuff from the librarian781# To get stuff from the librarian
780public.libraryfilealias = SELECT782public.libraryfilealias = SELECT
781public.libraryfilecontent = SELECT783public.libraryfilecontent = SELECT
782public.scriptactivity = SELECT, INSERT
783784
784[write]785[write]
785type=group786type=group
@@ -884,6 +885,7 @@
884885
885[shipit]886[shipit]
886type=user887type=user
888groups=script
887public.account = SELECT889public.account = SELECT
888public.continent = SELECT890public.continent = SELECT
889public.country = SELECT891public.country = SELECT
@@ -893,7 +895,6 @@
893public.libraryfilecontent = SELECT, INSERT895public.libraryfilecontent = SELECT, INSERT
894public.person = SELECT896public.person = SELECT
895public.requestedcds = SELECT, INSERT, UPDATE897public.requestedcds = SELECT, INSERT, UPDATE
896public.scriptactivity = SELECT, INSERT
897public.shipitreport = SELECT, INSERT898public.shipitreport = SELECT, INSERT
898public.shipment = SELECT, INSERT899public.shipment = SELECT, INSERT
899public.shippingrequest = SELECT, UPDATE900public.shippingrequest = SELECT, UPDATE
@@ -905,17 +906,18 @@
905[standingupdater]906[standingupdater]
906# For the personal standing updater cron script.907# For the personal standing updater cron script.
907type=user908type=user
909groups=script
908public.emailaddress = SELECT910public.emailaddress = SELECT
909public.mailinglist = SELECT911public.mailinglist = SELECT
910public.message = SELECT912public.message = SELECT
911public.messageapproval = SELECT913public.messageapproval = SELECT
912public.person = SELECT, UPDATE914public.person = SELECT, UPDATE
913public.scriptactivity = SELECT, INSERT
914public.teamparticipation = SELECT915public.teamparticipation = SELECT
915916
916[answertracker]917[answertracker]
917# User running expire-questions.py918# User running expire-questions.py
918type=user919type=user
920groups=script
919public.account = SELECT, INSERT921public.account = SELECT, INSERT
920public.accountpassword = SELECT, INSERT922public.accountpassword = SELECT, INSERT
921public.answercontact = SELECT923public.answercontact = SELECT
@@ -935,7 +937,6 @@
935public.questionbug = SELECT937public.questionbug = SELECT
936public.questionmessage = SELECT, INSERT938public.questionmessage = SELECT, INSERT
937public.questionsubscription = SELECT939public.questionsubscription = SELECT
938public.scriptactivity = SELECT, INSERT
939public.sourcepackagename = SELECT940public.sourcepackagename = SELECT
940public.teammembership = SELECT941public.teammembership = SELECT
941public.validpersoncache = SELECT942public.validpersoncache = SELECT
@@ -943,6 +944,7 @@
943944
944[uploader]945[uploader]
945type=user946type=user
947groups=script
946# Everything is keyed off an archive948# Everything is keyed off an archive
947public.archive = SELECT, INSERT, UPDATE949public.archive = SELECT, INSERT, UPDATE
948public.archivearch = SELECT, INSERT, UPDATE950public.archivearch = SELECT, INSERT, UPDATE
@@ -1002,8 +1004,6 @@
1002public.packageuploadbuild = SELECT, INSERT1004public.packageuploadbuild = SELECT, INSERT
1003public.packageuploadcustom = SELECT, INSERT1005public.packageuploadcustom = SELECT, INSERT
10041006
1005public.scriptactivity = SELECT, INSERT
1006
1007# For premature source-only publication1007# For premature source-only publication
1008public.securesourcepackagepublishinghistory = SELECT, INSERT1008public.securesourcepackagepublishinghistory = SELECT, INSERT
10091009
@@ -1045,6 +1045,7 @@
10451045
1046[queued]1046[queued]
1047type=user1047type=user
1048groups=script
1048# Announce handling1049# Announce handling
1049public.account = SELECT, INSERT1050public.account = SELECT, INSERT
1050public.person = SELECT, INSERT1051public.person = SELECT, INSERT
@@ -1097,8 +1098,6 @@
1097# rosetta auto imports1098# rosetta auto imports
1098public.translationimportqueueentry = SELECT, INSERT, UPDATE1099public.translationimportqueueentry = SELECT, INSERT, UPDATE
10991100
1100public.scriptactivity = SELECT, INSERT
1101
1102# Closing bugs.1101# Closing bugs.
1103public.bug = SELECT, UPDATE1102public.bug = SELECT, UPDATE
1104public.bugactivity = SELECT, INSERT1103public.bugactivity = SELECT, INSERT
@@ -1135,10 +1134,10 @@
11351134
1136[ppad]1135[ppad]
1137type=user1136type=user
1137groups=script
1138public.archive = SELECT1138public.archive = SELECT
1139public.archivearch = SELECT1139public.archivearch = SELECT
1140public.person = SELECT1140public.person = SELECT
1141public.scriptactivity = SELECT, INSERT
11421141
1143[session]1142[session]
1144# This user doesn't have access to any tables in the main launchpad1143# This user doesn't have access to any tables in the main launchpad
@@ -1155,6 +1154,7 @@
1155# send-bug-notifications.py needs them. They should be removed1154# send-bug-notifications.py needs them. They should be removed
1156# when bug 37456 is fixed.1155# when bug 37456 is fixed.
1157type=user1156type=user
1157groups=script
1158public.account = SELECT1158public.account = SELECT
1159public.archive = SELECT1159public.archive = SELECT
1160public.archivearch = SELECT1160public.archivearch = SELECT
@@ -1187,7 +1187,6 @@
1187public.message = SELECT, INSERT1187public.message = SELECT, INSERT
1188public.messagechunk = SELECT, INSERT1188public.messagechunk = SELECT, INSERT
1189public.milestone = SELECT1189public.milestone = SELECT
1190public.scriptactivity = SELECT, INSERT
1191public.structuralsubscription = SELECT1190public.structuralsubscription = SELECT
1192public.teammembership = SELECT1191public.teammembership = SELECT
1193public.teamparticipation = SELECT1192public.teamparticipation = SELECT
@@ -1196,6 +1195,7 @@
11961195
1197[personnotification]1196[personnotification]
1198type=user1197type=user
1198groups=script
1199public.personnotification = SELECT, UPDATE, DELETE1199public.personnotification = SELECT, UPDATE, DELETE
1200public.person = SELECT1200public.person = SELECT
1201public.emailaddress = SELECT1201public.emailaddress = SELECT
@@ -1203,7 +1203,6 @@
1203public.libraryfilecontent = SELECT1203public.libraryfilecontent = SELECT
1204public.message = SELECT1204public.message = SELECT
1205public.messagechunk = SELECT1205public.messagechunk = SELECT
1206public.scriptactivity = SELECT, INSERT
1207public.teammembership = SELECT1206public.teammembership = SELECT
1208public.teamparticipation = SELECT1207public.teamparticipation = SELECT
1209public.validpersoncache = SELECT1208public.validpersoncache = SELECT
@@ -1211,6 +1210,7 @@
12111210
1212[rosettaadmin]1211[rosettaadmin]
1213type=user1212type=user
1213groups=script
1214public.customlanguagecode = SELECT, INSERT, UPDATE, DELETE1214public.customlanguagecode = SELECT, INSERT, UPDATE, DELETE
1215public.distribution = SELECT1215public.distribution = SELECT
1216public.distroseries = SELECT1216public.distroseries = SELECT
@@ -1225,7 +1225,6 @@
1225public.potranslation = SELECT1225public.potranslation = SELECT
1226public.product = SELECT1226public.product = SELECT
1227public.productseries = SELECT1227public.productseries = SELECT
1228public.scriptactivity = SELECT, INSERT
1229public.sourcepackagename = SELECT1228public.sourcepackagename = SELECT
1230public.translationimportqueueentry = SELECT, INSERT, UPDATE, DELETE1229public.translationimportqueueentry = SELECT, INSERT, UPDATE, DELETE
1231public.translationmessage = SELECT, INSERT, UPDATE, DELETE1230public.translationmessage = SELECT, INSERT, UPDATE, DELETE
@@ -1234,12 +1233,12 @@
12341233
1235[oopsprune]1234[oopsprune]
1236type=user1235type=user
1236groups=script
1237public.bug = SELECT1237public.bug = SELECT
1238public.bugtask = SELECT1238public.bugtask = SELECT
1239public.message = SELECT1239public.message = SELECT
1240public.messagechunk = SELECT1240public.messagechunk = SELECT
1241public.question = SELECT1241public.question = SELECT
1242public.scriptactivity = SELECT, INSERT
12431242
1244[listteammembers]1243[listteammembers]
1245type=user1244type=user
@@ -1260,7 +1259,7 @@
12601259
1261[processmail]1260[processmail]
1262type=user1261type=user
1263public.scriptactivity = SELECT, INSERT1262groups=script
12641263
1265# Incoming emails are stored in the librarian1264# Incoming emails are stored in the librarian
1266public.libraryfilealias = SELECT, INSERT1265public.libraryfilealias = SELECT, INSERT
@@ -1361,10 +1360,10 @@
1361[mlist-sync]1360[mlist-sync]
1362# The mailing list sync user1361# The mailing list sync user
1363type=user1362type=user
1363groups=script
1364public.mailinglist = SELECT1364public.mailinglist = SELECT
1365public.person = SELECT1365public.person = SELECT
1366public.emailaddress = SELECT, UPDATE1366public.emailaddress = SELECT, UPDATE
1367public.scriptactivity = SELECT, INSERT
13681367
1369[mlist-import]1368[mlist-import]
1370# The mailing list import user1369# The mailing list import user
@@ -1379,6 +1378,7 @@
1379[hwdb-submission-processor]1378[hwdb-submission-processor]
1380# The user that updates the HWDB with data from new submissions1379# The user that updates the HWDB with data from new submissions
1381type=user1380type=user
1381groups=script
1382public.person = SELECT1382public.person = SELECT
1383public.hwdevicedriverlink = SELECT, INSERT1383public.hwdevicedriverlink = SELECT, INSERT
1384public.hwdevicenamevariant = SELECT, INSERT1384public.hwdevicenamevariant = SELECT, INSERT
@@ -1397,7 +1397,6 @@
1397public.hwvendorname = SELECT, INSERT1397public.hwvendorname = SELECT, INSERT
1398public.libraryfilealias = SELECT1398public.libraryfilealias = SELECT
1399public.libraryfilecontent = SELECT1399public.libraryfilecontent = SELECT
1400public.scriptactivity = SELECT, INSERT
1401public.teamparticipation = SELECT1400public.teamparticipation = SELECT
14021401
1403[builddcontroller]1402[builddcontroller]
@@ -1409,6 +1408,7 @@
1409[binaryfile-expire]1408[binaryfile-expire]
1410# The user that expires binary files from the librarian.1409# The user that expires binary files from the librarian.
1411type=user1410type=user
1411groups=script
1412public.archive = SELECT1412public.archive = SELECT
1413public.binarypackagefile = SELECT1413public.binarypackagefile = SELECT
1414public.binarypackagepublishinghistory = SELECT1414public.binarypackagepublishinghistory = SELECT
@@ -1417,10 +1417,10 @@
1417public.person = SELECT1417public.person = SELECT
1418public.libraryfilealias = SELECT, UPDATE1418public.libraryfilealias = SELECT, UPDATE
1419public.securebinarypackagepublishinghistory = SELECT1419public.securebinarypackagepublishinghistory = SELECT
1420public.scriptactivity = SELECT, UPDATE, INSERT, DELETE
14211420
1422[create-merge-proposals]1421[create-merge-proposals]
1423type=user1422type=user
1423groups=script
1424public.account = SELECT1424public.account = SELECT
1425public.accountpassword = SELECT1425public.accountpassword = SELECT
1426public.branch = SELECT, INSERT, UPDATE1426public.branch = SELECT, INSERT, UPDATE
@@ -1446,13 +1446,13 @@
1446public.product = SELECT1446public.product = SELECT
1447public.productseries = SELECT1447public.productseries = SELECT
1448public.project = SELECT1448public.project = SELECT
1449public.scriptactivity = SELECT, INSERT
1450public.staticdiff = SELECT, INSERT1449public.staticdiff = SELECT, INSERT
1451public.teamparticipation = SELECT1450public.teamparticipation = SELECT
1452public.validpersoncache = SELECT1451public.validpersoncache = SELECT
14531452
1454[mp-creation-job]1453[mp-creation-job]
1455type=user1454type=user
1455groups=script
1456public.account = SELECT1456public.account = SELECT
1457public.accountpassword = SELECT1457public.accountpassword = SELECT
1458public.branch = SELECT1458public.branch = SELECT
@@ -1475,7 +1475,6 @@
1475public.person = SELECT1475public.person = SELECT
1476public.product = SELECT1476public.product = SELECT
1477public.productseries = SELECT1477public.productseries = SELECT
1478public.scriptactivity = SELECT, INSERT
1479public.staticdiff = SELECT, INSERT1478public.staticdiff = SELECT, INSERT
1480public.teammembership = SELECT1479public.teammembership = SELECT
1481public.teamparticipation = SELECT1480public.teamparticipation = SELECT
@@ -1483,6 +1482,7 @@
14831482
1484[send-branch-mail]1483[send-branch-mail]
1485type=user1484type=user
1485groups=script
1486public.account = SELECT1486public.account = SELECT
1487public.accountpassword = SELECT1487public.accountpassword = SELECT
1488public.branch = SELECT1488public.branch = SELECT
@@ -1507,7 +1507,6 @@
1507public.product = SELECT1507public.product = SELECT
1508public.productseries = SELECT1508public.productseries = SELECT
1509public.revision = SELECT1509public.revision = SELECT
1510public.scriptactivity = SELECT, INSERT
1511public.staticdiff = SELECT, INSERT1510public.staticdiff = SELECT, INSERT
1512public.teammembership = SELECT1511public.teammembership = SELECT
1513public.teamparticipation = SELECT1512public.teamparticipation = SELECT
@@ -1516,6 +1515,7 @@
1516[updateremoteproduct]1515[updateremoteproduct]
1517# Updates Product.remote_product using bug watch information.1516# Updates Product.remote_product using bug watch information.
1518type=user1517type=user
1518groups=script
1519public.account = SELECT, INSERT, UPDATE1519public.account = SELECT, INSERT, UPDATE
1520public.person = SELECT, INSERT1520public.person = SELECT, INSERT
1521public.product = SELECT, INSERT, UPDATE1521public.product = SELECT, INSERT, UPDATE
@@ -1537,14 +1537,13 @@
1537public.bugsubscription = SELECT, INSERT1537public.bugsubscription = SELECT, INSERT
1538public.bugmessage = SELECT, INSERT1538public.bugmessage = SELECT, INSERT
1539public.sourcepackagename = SELECT1539public.sourcepackagename = SELECT
1540public.scriptactivity = SELECT, INSERT
15411540
1542[updatesourceforgeremoteproduct]1541[updatesourceforgeremoteproduct]
1543# Updates Product.remote_product using SourceForge project data.1542# Updates Product.remote_product using SourceForge project data.
1544type=user1543type=user
1544groups=script
1545public.product = SELECT, UPDATE1545public.product = SELECT, UPDATE
1546public.bugtracker = SELECT1546public.bugtracker = SELECT
1547public.scriptactivity = SELECT, INSERT
15481547
1549[weblogstats]1548[weblogstats]
1550# For the script that parses our Apache/Squid logfiles and updates statistics1549# For the script that parses our Apache/Squid logfiles and updates statistics
@@ -1552,6 +1551,25 @@
1552public.libraryfilealias = SELECT1551public.libraryfilealias = SELECT
1553public.libraryfiledownloadcount = SELECT, INSERT, UPDATE, DELETE1552public.libraryfiledownloadcount = SELECT, INSERT, UPDATE, DELETE
15541553
1554[garbo]
1555# garbo-hourly and garbo-daily script permissions. We define the
1556# permissions here in this group instead of in the users, so tasks can
1557# be shuffled around between the daily and hourly sections without
1558# changing DB permissions.
1559type=user
1560groups=script
1561public.codeimportresult = SELECT, DELETE
1562public.oauthnonce = SELECT, DELETE
1563public.openidnonce = SELECT, DELETE
1564
1565[garbo-daily]
1566type=user
1567groups=garbo
1568
1569[garbo-hourly]
1570type=user
1571groups=garbo
1572
1555[generateppahtaccess]1573[generateppahtaccess]
1556# For the generate_ppa_htaccess.py cronscript.1574# For the generate_ppa_htaccess.py cronscript.
1557type=user1575type=user
15581576
=== modified file 'database/schema/security.py'
--- database/schema/security.py 2009-03-23 19:32:14 +0000
+++ database/schema/security.py 2009-03-25 11:41:03 +0000
@@ -255,7 +255,8 @@
255 obj.fullname, quote_identifier(options.owner)255 obj.fullname, quote_identifier(options.owner)
256 ))256 ))
257257
258 # Revoke all privs258 # Revoke all privs from known groups. Don't revoke anything for
259 # users or groups not defined in our security.cfg.
259 for section_name in config.sections():260 for section_name in config.sections():
260 for obj in schema.values():261 for obj in schema.values():
261 if obj.type == 'function':262 if obj.type == 'function':
@@ -263,13 +264,16 @@
263 else:264 else:
264 t = 'TABLE'265 t = 'TABLE'
265266
266 cur.execute('REVOKE ALL ON %s %s FROM %s%s' % (267 roles = [quote_identifier(section_name)]
267 t, obj.fullname, g, quote_identifier(section_name)268 if section_name != 'public':
268 ))269 roles.append(quote_identifier(section_name + '_ro'))
269 if schema.has_key(obj.seqname):270 for role in roles:
270 cur.execute('REVOKE ALL ON SEQUENCE %s FROM %s%s' % (271 cur.execute(
271 obj.seqname, g, quote_identifier(section_name),272 'REVOKE ALL ON %s %s FROM %s' % (t, obj.fullname, role))
272 ))273 if schema.has_key(obj.seqname):
274 cur.execute(
275 'REVOKE ALL ON SEQUENCE %s FROM %s'
276 % (obj.seqname, role))
273277
274 # Set of all tables we have granted permissions on. After we have assigned278 # Set of all tables we have granted permissions on. After we have assigned
275 # permissions, we can use this to determine what tables have been279 # permissions, we can use this to determine what tables have been
276280
=== modified file 'importfascist.py'
--- importfascist.py 2009-03-23 19:32:14 +0000
+++ importfascist.py 2009-03-25 11:41:03 +0000
@@ -21,20 +21,20 @@
2121
22# zope.testing.doctest: called as part of creating a DocTestSuite.22# zope.testing.doctest: called as part of creating a DocTestSuite.
23permitted_database_imports = text_lines_to_set("""23permitted_database_imports = text_lines_to_set("""
24 zope.testing.doctest24 canonical.archivepublisher.deathrow
25 canonical.librarian.db25 canonical.archivepublisher.domination
26 canonical.doap.fileimporter
27 canonical.archivepublisher.ftparchive26 canonical.archivepublisher.ftparchive
28 canonical.archivepublisher.publishing27 canonical.archivepublisher.publishing
29 canonical.archivepublisher.domination
30 canonical.archivepublisher.deathrow
31 canonical.codehosting.inmemory28 canonical.codehosting.inmemory
32 canonical.launchpad.browser.branchlisting29 canonical.launchpad.browser.branchlisting
33 canonical.launchpad.feed.branch30 canonical.launchpad.feed.branch
31 canonical.launchpad.scripts.garbo
32 canonical.launchpad.scripts.librarian_apache_log_parser
33 canonical.launchpad.validators.person
34 canonical.launchpad.vocabularies.dbobjects34 canonical.launchpad.vocabularies.dbobjects
35 canonical.launchpad.validators.person
36 canonical.librarian.client35 canonical.librarian.client
37 canonical.launchpad.scripts.librarian_apache_log_parser36 canonical.librarian.db
37 zope.testing.doctest
38 """)38 """)
39# It's not worth creating a *Set utility for ParsedApacheLog, to be used only39# It's not worth creating a *Set utility for ParsedApacheLog, to be used only
40# in librarian_apache_log_parser, so instead we allow that module to import40# in librarian_apache_log_parser, so instead we allow that module to import
4141
=== added file 'lib/canonical/launchpad/database/openidconsumer.py'
--- lib/canonical/launchpad/database/openidconsumer.py 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/database/openidconsumer.py 2009-03-25 11:41:03 +0000
@@ -0,0 +1,23 @@
1# Copyright 2009 Canonical Ltd. All rights reserved.
2
3"""OpenID Consumer related database classes."""
4
5__metaclass__ = type
6__all__ = ['OpenIDNonce']
7
8from storm.locals import DateTime, Int, Storm, Unicode
9
10class OpenIDNonce(Storm):
11 """An OpenIDNonce.
12
13 The table definition matches that required by the openid library,
14 so doesn't follow our standards. In particular, it doesn't have an
15 id column and the timestamp is an epoch time integer rather than a
16 datetime.
17 """
18 __storm_table__ = "OpenIDNonce"
19 __storm_primary__ = "server_url", "timestamp", "salt"
20
21 server_url = Unicode()
22 timestamp = Int()
23 salt = Unicode()
024
=== modified file 'lib/canonical/launchpad/doc/script-monitoring.txt'
--- lib/canonical/launchpad/doc/script-monitoring.txt 2008-12-01 18:05:42 +0000
+++ lib/canonical/launchpad/doc/script-monitoring.txt 2009-03-25 11:41:03 +0000
@@ -22,12 +22,13 @@
22 >>> import sys22 >>> import sys
23 >>> import tempfile23 >>> import tempfile
24 >>> import pytz24 >>> import pytz
25 >>> import transaction
25 >>> from zope.component import getUtility26 >>> from zope.component import getUtility
26 >>> from canonical.database.sqlbase import ZopelessTransactionManager
27 >>> from canonical.launchpad.interfaces import IScriptActivitySet27 >>> from canonical.launchpad.interfaces import IScriptActivitySet
28 >>> from canonical.testing.layers import LaunchpadZopelessLayer
2829
29 >>> UTC = pytz.timezone('UTC')30 >>> UTC = pytz.timezone('UTC')
30 >>> ztm = ZopelessTransactionManager._installed31 >>> LaunchpadZopelessLayer.switchDbUser('garbo-daily') # A script db user
3132
32 >>> activity = getUtility(IScriptActivitySet).recordSuccess(33 >>> activity = getUtility(IScriptActivitySet).recordSuccess(
33 ... name='script-name',34 ... name='script-name',
@@ -88,21 +89,21 @@
88 ... raise RuntimeError('Some failure')89 ... raise RuntimeError('Some failure')
89 ...90 ...
90 ... if __name__ == '__main__':91 ... if __name__ == '__main__':
91 ... script = TestScript('test-script')92 ... script = TestScript('test-script', 'garbo-daily')
92 ... script.run()93 ... script.run()
93 ... """)94 ... """)
94 >>> script_file.flush()95 >>> script_file.flush()
9596
96We'll now run this script, telling it to fail:97We'll now run this script, telling it to fail:
9798
98 >>> ztm.commit()99 >>> transaction.commit()
99 >>> env = dict(os.environ)100 >>> env = dict(os.environ)
100 >>> env['LPCONFIG'] = 'testrunner'101 >>> env['LPCONFIG'] = 'testrunner'
101 >>> proc = subprocess.Popen([sys.executable, script_file.name, 'fail'],102 >>> proc = subprocess.Popen([sys.executable, script_file.name, 'fail'],
102 ... env=env, stdin=subprocess.PIPE,103 ... env=env, stdin=subprocess.PIPE,
103 ... stdout=subprocess.PIPE, stderr=subprocess.PIPE)104 ... stdout=subprocess.PIPE, stderr=subprocess.PIPE)
104 >>> (out, err) = proc.communicate()105 >>> (out, err) = proc.communicate()
105 >>> ztm.abort()106 >>> transaction.abort()
106107
107The process failed:108The process failed:
108109
@@ -120,7 +121,7 @@
120 ... env=env, stdin=subprocess.PIPE,121 ... env=env, stdin=subprocess.PIPE,
121 ... stdout=subprocess.PIPE, stderr=subprocess.PIPE)122 ... stdout=subprocess.PIPE, stderr=subprocess.PIPE)
122 >>> (out, err) = proc.communicate()123 >>> (out, err) = proc.communicate()
123 >>> ztm.abort()124 >>> transaction.abort()
124125
125 >>> print proc.returncode126 >>> print proc.returncode
126 0127 0
127128
=== modified file 'lib/canonical/launchpad/scripts/base.py'
--- lib/canonical/launchpad/scripts/base.py 2008-10-21 11:30:50 +0000
+++ lib/canonical/launchpad/scripts/base.py 2009-03-25 11:41:03 +0000
@@ -70,7 +70,8 @@
70 What you get:70 What you get:
71 - self.logger71 - self.logger
72 - self.txn72 - self.txn
73 - self.options73 - self.parser (the OptionParser)
74 - self.options (the parsed options)
7475
75 "Give me convenience or give me death."76 "Give me convenience or give me death."
76 """77 """
7778
=== added file 'lib/canonical/launchpad/scripts/garbo.py'
--- lib/canonical/launchpad/scripts/garbo.py 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/scripts/garbo.py 2009-03-25 11:41:03 +0000
@@ -0,0 +1,182 @@
1# Copyright 2009 Canonical Ltd. All rights reserved.
2
3"""Database garbage collection."""
4
5__metaclass__ = type
6__all__ = ['DailyDatabaseGarbageCollector', 'HourlyDatabaseGarbageCollector']
7
8import time
9
10import transaction
11from zope.component import getUtility
12from zope.interface import implements
13from storm.locals import SQL, Max, Min
14
15from canonical.database.sqlbase import sqlvalues
16from canonical.launchpad.database.codeimportresult import CodeImportResult
17from canonical.launchpad.database.oauth import OAuthNonce
18from canonical.launchpad.database.openidconsumer import OpenIDNonce
19from canonical.launchpad.interfaces import IMasterStore
20from canonical.launchpad.interfaces.looptuner import ITunableLoop
21from canonical.launchpad.scripts.base import LaunchpadCronScript
22from canonical.launchpad.utilities.looptuner import LoopTuner
23from canonical.launchpad.webapp.interfaces import (
24 IStoreSelector, MAIN_STORE, MASTER_FLAVOR)
25
26
27ONE_DAY_IN_SECONDS = 24*60*60
28
29
30class TunableLoop:
31 implements(ITunableLoop)
32
33 goal_seconds = 4
34 minimum_chunk_size = 1
35 maximum_chunk_size = None # Override
36 cooldown_time = 0
37
38 def run(self):
39 assert self.maximum_chunk_size is not None, "Did not override."
40 LoopTuner(
41 self, self.goal_seconds,
42 minimum_chunk_size = self.minimum_chunk_size,
43 maximum_chunk_size = self.maximum_chunk_size,
44 cooldown_time = self.cooldown_time).run()
45
46
47class OAuthNoncePruner(TunableLoop):
48 """An ITunableLoop to prune old OAuthNonce records.
49
50 We remove all OAuthNonce records older than 1 day.
51 """
52 maximum_chunk_size = 6*60*60 # 6 hours in seconds.
53
54 def __init__(self):
55 self.store = IMasterStore(OAuthNonce)
56 self.oldest_age = self.store.execute("""
57 SELECT COALESCE(EXTRACT(EPOCH FROM
58 CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
59 - MIN(request_timestamp)), 0)
60 FROM OAuthNonce
61 """).get_one()[0]
62
63 def isDone(self):
64 return self.oldest_age <= ONE_DAY_IN_SECONDS
65
66 def __call__(self, chunk_size):
67 self.oldest_age = max(ONE_DAY_IN_SECONDS, self.oldest_age - chunk_size)
68
69 self.store.find(
70 OAuthNonce,
71 OAuthNonce.request_timestamp < SQL(
72 "CURRENT_TIMESTAMP AT TIME ZONE 'UTC' - interval '%d seconds'"
73 % self.oldest_age)).remove()
74 transaction.commit()
75
76
77class OpenIDNoncePruner(TunableLoop):
78 """An ITunableLoop to prune old OpenIDNonce records.
79
80 We remove all OpenIDNonce records older than 1 day.
81 """
82 maximum_chunk_size = 6*60*60 # 6 hours in seconds.
83
84 def __init__(self):
85 self.store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
86 self.earliest_timestamp = self.store.find(
87 Min(OpenIDNonce.timestamp)).one()
88 utc_now = int(time.mktime(time.gmtime()))
89 self.earliest_wanted_timestamp = utc_now - ONE_DAY_IN_SECONDS
90
91 def isDone(self):
92 return (
93 self.earliest_timestamp is None
94 or self.earliest_timestamp >= self.earliest_wanted_timestamp)
95
96 def __call__(self, chunk_size):
97 self.earliest_timestamp = min(
98 self.earliest_wanted_timestamp,
99 self.earliest_timestamp + chunk_size)
100
101 self.store.find(
102 OpenIDNonce,
103 OpenIDNonce.timestamp < self.earliest_timestamp).remove()
104 transaction.commit()
105
106
107class CodeImportResultPruner(TunableLoop):
108 """A TunableLoop to prune unwanted CodeImportResult rows.
109
110 Removes CodeImportResult rows if they are older than 30 days
111 and they are not one of the 4 most recent results for that
112 CodeImport.
113 """
114 maximum_chunk_size = 100
115 def __init__(self):
116 self.store = IMasterStore(CodeImportResult)
117
118 self.min_code_import = self.store.find(
119 Min(CodeImportResult.code_importID)).one()
120 self.max_code_import = self.store.find(
121 Max(CodeImportResult.code_importID)).one()
122
123 self.next_code_import_id = self.min_code_import
124
125 def isDone(self):
126 return (
127 self.min_code_import is None
128 or self.next_code_import_id > self.max_code_import)
129
130 def __call__(self, chunk_size):
131 self.store.execute("""
132 DELETE FROM CodeImportResult
133 WHERE
134 CodeImportResult.date_created
135 < CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
136 - interval '30 days'
137 AND CodeImportResult.code_import >= %s
138 AND CodeImportResult.code_import < %s + %s
139 AND CodeImportResult.id NOT IN (
140 SELECT LatestResult.id
141 FROM CodeImportResult AS LatestResult
142 WHERE
143 LatestResult.code_import
144 = CodeImportResult.code_import
145 ORDER BY LatestResult.date_created DESC
146 LIMIT 4)
147 """ % sqlvalues(
148 self.next_code_import_id,
149 self.next_code_import_id,
150 chunk_size))
151 self.next_code_import_id += chunk_size
152 transaction.commit()
153
154
155class BaseDatabaseGarbageCollector(LaunchpadCronScript):
156 """Abstract base class to run a collection of TunableLoops."""
157 script_name = None # Script name for locking and database user. Override.
158 tunable_loops = None # Collection of TunableLoops. Override.
159
160 def __init__(self, test_args=None):
161 super(BaseDatabaseGarbageCollector, self).__init__(
162 self.script_name, dbuser=self.script_name, test_args=test_args)
163
164 def main(self):
165 for tunable_loop in self.tunable_loops:
166 self.logger.info("Running %s" % tunable_loop.__name__)
167 tunable_loop().run()
168
169
170class HourlyDatabaseGarbageCollector(BaseDatabaseGarbageCollector):
171 script_name = 'garbo-hourly'
172 tunable_loops = [
173 OAuthNoncePruner,
174 OpenIDNoncePruner,
175 ]
176
177class DailyDatabaseGarbageCollector(BaseDatabaseGarbageCollector):
178 script_name = 'garbo-daily'
179 tunable_loops = [
180 CodeImportResultPruner,
181 ]
182
0183
=== added file 'lib/canonical/launchpad/scripts/tests/test_garbo.py'
--- lib/canonical/launchpad/scripts/tests/test_garbo.py 1970-01-01 00:00:00 +0000
+++ lib/canonical/launchpad/scripts/tests/test_garbo.py 2009-03-25 11:41:03 +0000
@@ -0,0 +1,192 @@
1# Copyright 2009 Canonical Ltd. All rights reserved.
2
3"""Test the database garbage collector."""
4
5__metaclass__ = type
6__all__ = []
7
8from datetime import datetime, timedelta
9import time
10import unittest
11
12from pytz import UTC
13from storm.locals import Min
14import transaction
15
16from canonical.launchpad.database.codeimportresult import CodeImportResult
17from canonical.launchpad.database.oauth import OAuthNonce
18from canonical.launchpad.database.openidconsumer import OpenIDNonce
19from canonical.launchpad.interfaces import IMasterStore
20from canonical.launchpad.interfaces.codeimportresult import (
21 CodeImportResultStatus)
22from canonical.launchpad.testing import TestCase
23from canonical.launchpad.scripts.garbo import (
24 DailyDatabaseGarbageCollector, HourlyDatabaseGarbageCollector)
25from canonical.launchpad.scripts.tests import run_script
26from canonical.launchpad.scripts.logger import QuietFakeLogger
27from canonical.testing.layers import (
28 DatabaseLayer, LaunchpadScriptLayer, LaunchpadZopelessLayer)
29
30
31class TestGarboScript(TestCase):
32 layer = LaunchpadScriptLayer
33
34 def test_daily_script(self):
35 """Ensure garbo-daily.py actually runs."""
36 rv, out, err = run_script(
37 "cronscripts/garbo-daily.py", ["-q"], expect_returncode=0)
38 self.failIf(out.strip(), "Output to stdout: %s" % out)
39 self.failIf(err.strip(), "Output to stderr: %s" % err)
40 DatabaseLayer.force_dirty_database()
41
42 def test_hourly_script(self):
43 """Ensure garbo-hourly.py actually runs."""
44 rv, out, err = run_script(
45 "cronscripts/garbo-hourly.py", ["-q"], expect_returncode=0)
46 self.failIf(out.strip(), "Output to stdout: %s" % out)
47 self.failIf(err.strip(), "Output to stderr: %s" % err)
48
49
50class TestGarbo(TestCase):
51 layer = LaunchpadZopelessLayer
52
53 def setUp(self):
54 super(TestGarbo, self).setUp()
55 # Run the garbage collectors to remove any existing garbage,
56 # starting us in a known state.
57 self.runDaily()
58 self.runHourly()
59
60 def runDaily(self):
61 LaunchpadZopelessLayer.switchDbUser('garbo-daily')
62 collector = DailyDatabaseGarbageCollector(test_args=[])
63 collector.logger = QuietFakeLogger()
64 collector.main()
65
66 def runHourly(self):
67 LaunchpadZopelessLayer.switchDbUser('garbo-hourly')
68 collector = HourlyDatabaseGarbageCollector(test_args=[])
69 collector.logger = QuietFakeLogger()
70 collector.main()
71
72 def test_OAuthNoncePruner(self):
73 store = IMasterStore(OAuthNonce)
74 now = datetime.utcnow().replace(tzinfo=UTC)
75 timestamps = [
76 now - timedelta(days=2), # Garbage
77 now - timedelta(days=1) - timedelta(seconds=60), # Garbage
78 now - timedelta(days=1) + timedelta(seconds=60), # Not garbage
79 now, # Not garbage
80 ]
81 LaunchpadZopelessLayer.switchDbUser('testadmin')
82
83 # Make sure we start with 0 nonces.
84 self.failUnlessEqual(store.find(OAuthNonce).count(), 0)
85
86 for timestamp in timestamps:
87 OAuthNonce(
88 access_tokenID=1,
89 request_timestamp = timestamp,
90 nonce = str(timestamp))
91 transaction.commit()
92
93 # Make sure we have 4 nonces now.
94 self.failUnlessEqual(store.find(OAuthNonce).count(), 4)
95
96 self.runHourly()
97
98 # Now back to two, having removed the two garbage entries.
99 self.failUnlessEqual(store.find(OAuthNonce).count(), 2)
100
101 # And none of them are older than a day.
102 # Hmm... why is it I'm putting tz aware datetimes in and getting
103 # naive datetimes back? Bug in the SQLObject compatibility layer?
104 # Test is still fine as we know the timezone.
105 self.failUnless(
106 store.find(
107 Min(OAuthNonce.request_timestamp)).one().replace(tzinfo=UTC)
108 >= now - timedelta(days=1))
109
110 def test_OpenIDNoncePruner(self):
111 now = int(time.mktime(time.gmtime()))
112 MINUTES = 60
113 HOURS = 60 * 60
114 DAYS = 24 * HOURS
115 timestamps = [
116 now - 2 * DAYS, # Garbage
117 now - 1 * DAYS - 1 * MINUTES, # Garbage
118 now - 1 * DAYS + 1 * MINUTES, # Not garbage
119 now, # Not garbage
120 ]
121 LaunchpadZopelessLayer.switchDbUser('testadmin')
122
123 store = IMasterStore(OpenIDNonce)
124
125 # Make sure we start with 0 nonces.
126 self.failUnlessEqual(store.find(OpenIDNonce).count(), 0)
127
128 for timestamp in timestamps:
129 nonce = store.add(OpenIDNonce())
130 nonce.server_url = unicode(timestamp)
131 nonce.timestamp = timestamp
132 nonce.salt = u'aa'
133 store.add(nonce)
134 transaction.commit()
135
136 # Make sure we have 4 nonces now.
137 self.failUnlessEqual(store.find(OpenIDNonce).count(), 4)
138
139 # Run the garbage collector.
140 self.runHourly()
141
142 # We should now have 2 nonces.
143 self.failUnlessEqual(store.find(OpenIDNonce).count(), 2)
144
145 # And none of them are older than 1 day
146 earliest = store.find(Min(OpenIDNonce.timestamp)).one()
147 self.failUnless(earliest >= now - 24*60*60, 'Still have old nonces')
148
149 def test_CodeImportResultPruner(self):
150 now = datetime.utcnow().replace(tzinfo=UTC)
151 store = IMasterStore(CodeImportResult)
152
153 def new_code_import_result(timestamp):
154 LaunchpadZopelessLayer.switchDbUser('testadmin')
155 CodeImportResult(
156 date_created=timestamp,
157 code_importID=1, machineID=1, requesting_userID=1,
158 status=CodeImportResultStatus.FAILURE,
159 date_job_started=timestamp)
160 transaction.commit()
161
162 new_code_import_result(now - timedelta(days=60))
163 new_code_import_result(now - timedelta(days=19))
164 new_code_import_result(now - timedelta(days=20))
165 new_code_import_result(now - timedelta(days=21))
166
167 # Run the garbage collector
168 self.runDaily()
169
170 # Nothing is removed, because we always keep the 4 latest.
171 self.failUnlessEqual(
172 store.find(CodeImportResult).count(), 4)
173
174 new_code_import_result(now - timedelta(days=31))
175 self.runDaily()
176 self.failUnlessEqual(
177 store.find(CodeImportResult).count(), 4)
178
179 new_code_import_result(now - timedelta(days=29))
180 self.runDaily()
181 self.failUnlessEqual(
182 store.find(CodeImportResult).count(), 4)
183
184 # We now have no CodeImportResults older than 30 days
185 self.failUnless(
186 store.find(
187 Min(CodeImportResult.date_created)).one().replace(tzinfo=UTC)
188 >= now - timedelta(days=30))
189
190
191def test_suite():
192 return unittest.TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches

to status/vote changes: