Merge lp:~brian-murray/launchpad/api-export-messages-count into lp:launchpad/db-devel
- api-export-messages-count
- Merge into db-devel
Proposed by
Brian Murray
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~brian-murray/launchpad/api-export-messages-count | ||||
Merge into: | lp:launchpad/db-devel | ||||
Diff against target: |
1855 lines (+581/-358) 42 files modified
configs/development/launchpad-lazr.conf (+0/-2) configs/replicated-development/launchpad-lazr.conf (+0/-3) configs/test-playground/launchpad-lazr.conf (+0/-2) configs/testrunner/launchpad-lazr.conf (+0/-2) database/schema/security.cfg (+6/-2) lib/canonical/config/__init__.py (+2/-2) lib/canonical/config/schema-lazr.conf (+0/-8) lib/canonical/configure.zcml (+0/-2) lib/canonical/database/harness.py (+1/-2) lib/canonical/database/sqlbase.py (+2/-4) lib/canonical/database/tests/test_zopeless_transaction_manager.py (+0/-31) lib/canonical/launchpad/database/tests/test_oauth.py (+1/-1) lib/canonical/launchpad/doc/account.txt (+1/-26) lib/canonical/launchpad/doc/storm.txt (+51/-71) lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt (+0/-2) lib/canonical/launchpad/scripts/garbo.py (+5/-13) lib/canonical/launchpad/scripts/tests/test_garbo.py (+7/-9) lib/canonical/launchpad/webapp/adapter.py (+5/-16) lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt (+1/-24) lib/canonical/launchpad/webapp/interfaces.py (+1/-3) lib/canonical/launchpad/webapp/tests/test_dbpolicy.py (+1/-4) lib/lp/bugs/doc/checkwatches.txt (+4/-0) lib/lp/bugs/doc/externalbugtracker-comment-imports.txt (+3/-3) lib/lp/bugs/interfaces/bug.py (+3/-3) lib/lp/bugs/stories/webservice/xx-bug.txt (+1/-0) lib/lp/code/doc/branch-karma.txt (+1/-0) lib/lp/registry/configure.zcml (+47/-5) lib/lp/registry/doc/distribution-mirror.txt (+119/-1) lib/lp/registry/interfaces/distribution.py (+9/-1) lib/lp/registry/interfaces/distributionmirror.py (+96/-47) lib/lp/registry/model/distribution.py (+11/-0) lib/lp/registry/model/distributionmirror.py (+63/-1) lib/lp/registry/model/person.py (+2/-17) lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt (+11/-11) lib/lp/registry/stories/person/xx-admin-person-review.txt (+1/-1) lib/lp/registry/stories/webservice/xx-distribution-mirror.txt (+20/-11) lib/lp/registry/stories/webservice/xx-distribution.txt (+74/-0) lib/lp/registry/tests/test_distributionmirror.py (+4/-14) lib/lp/registry/tests/test_personset.py (+6/-10) lib/lp/testing/factory.py (+16/-2) lib/lp/testopenid/browser/server.py (+6/-1) utilities/sourcedeps.conf (+0/-1) |
||||
To merge this branch: | bzr merge lp:~brian-murray/launchpad/api-export-messages-count | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eleanor Berger (community) | Approve | ||
Review via email: mp+17697@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Brian Murray (brian-murray) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'configs/development/launchpad-lazr.conf' |
2 | --- configs/development/launchpad-lazr.conf 2010-03-05 20:49:31 +0000 |
3 | +++ configs/development/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 |
4 | @@ -97,8 +97,6 @@ |
5 | # the rw_* configs. |
6 | ro_main_master: dbname=launchpad_dev_template |
7 | ro_main_slave: dbname=launchpad_dev_template |
8 | -auth_master: dbname=launchpad_dev |
9 | -auth_slave: dbname=launchpad_dev |
10 | |
11 | [distributionmirrorprober] |
12 | use_proxy: False |
13 | |
14 | === modified file 'configs/replicated-development/launchpad-lazr.conf' |
15 | --- configs/replicated-development/launchpad-lazr.conf 2010-01-05 19:09:58 +0000 |
16 | +++ configs/replicated-development/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 |
17 | @@ -10,6 +10,3 @@ |
18 | rw_main_slave: dbname=launchpad_dev_slave |
19 | ro_main_master: dbname=launchpad_dev |
20 | ro_main_slave: dbname=launchpad_dev_slave |
21 | -auth_master: dbname=launchpad_dev |
22 | -auth_slave: dbname=launchpad_dev_slave |
23 | - |
24 | |
25 | === modified file 'configs/test-playground/launchpad-lazr.conf' |
26 | --- configs/test-playground/launchpad-lazr.conf 2010-01-05 19:09:58 +0000 |
27 | +++ configs/test-playground/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 |
28 | @@ -10,5 +10,3 @@ |
29 | rw_main_slave: dbname=launchpad_ftest_playground |
30 | ro_main_master: dbname=launchpad_ftest_playground |
31 | ro_main_slave: dbname=launchpad_ftest_playground |
32 | -auth_master: dbname=launchpad_ftest_playground |
33 | -auth_slave: dbname=launchpad_ftest_playground |
34 | |
35 | === modified file 'configs/testrunner/launchpad-lazr.conf' |
36 | --- configs/testrunner/launchpad-lazr.conf 2010-03-11 01:39:25 +0000 |
37 | +++ configs/testrunner/launchpad-lazr.conf 2010-04-05 20:44:34 +0000 |
38 | @@ -51,8 +51,6 @@ |
39 | # the rw_* configs. |
40 | ro_main_master: dbname=launchpad_ftest_template |
41 | ro_main_slave: dbname=launchpad_ftest_template |
42 | -auth_master: dbname=launchpad_ftest |
43 | -auth_slave: dbname=launchpad_ftest |
44 | randomise_select_results: true |
45 | |
46 | [error_reports] |
47 | |
48 | === modified file 'database/schema/security.cfg' |
49 | --- database/schema/security.cfg 2010-04-02 17:29:13 +0000 |
50 | +++ database/schema/security.cfg 2010-04-05 20:44:34 +0000 |
51 | @@ -118,7 +118,8 @@ |
52 | # lpmain replication set access from the main Z3 application. |
53 | type=user |
54 | groups=write,script |
55 | -public.account = SELECT |
56 | +public.account = SELECT, INSERT, UPDATE, DELETE |
57 | +public.accountpassword = SELECT, INSERT, UPDATE, DELETE |
58 | public.announcement = SELECT, INSERT, UPDATE, DELETE |
59 | public.answercontact = SELECT, INSERT, UPDATE, DELETE |
60 | public.apportjob = SELECT, INSERT, UPDATE, DELETE |
61 | @@ -176,7 +177,7 @@ |
62 | public.distributionsourcepackagecache = SELECT |
63 | public.distroserieslanguage = SELECT, INSERT, UPDATE |
64 | public.distroseriespackagecache = SELECT |
65 | -public.emailaddress = SELECT |
66 | +public.emailaddress = SELECT, INSERT, UPDATE, DELETE |
67 | public.entitlement = SELECT, INSERT, UPDATE, DELETE |
68 | public.faq = SELECT, INSERT, UPDATE, DELETE |
69 | public.featuredproject = SELECT, INSERT, DELETE |
70 | @@ -916,6 +917,8 @@ |
71 | # Full access except for tables that are exclusively updated by |
72 | # certain processes, such as the librarian tables. This group is deprecated - |
73 | # access should be explicitly granted to users. |
74 | +public.account = SELECT, INSERT, UPDATE |
75 | +public.accountpassword = SELECT, INSERT |
76 | public.archive = SELECT, INSERT, UPDATE |
77 | public.archivearch = SELECT, INSERT, UPDATE, DELETE |
78 | public.binarypackagerelease = SELECT, INSERT, UPDATE |
79 | @@ -949,6 +952,7 @@ |
80 | public.distribution = SELECT, INSERT, UPDATE |
81 | public.distroarchseries = SELECT, INSERT, UPDATE |
82 | public.distroseries = SELECT, INSERT, UPDATE |
83 | +public.openidrpsummary = SELECT, INSERT, UPDATE |
84 | public.packageupload = SELECT, INSERT, UPDATE |
85 | public.packageuploadbuild = SELECT, INSERT, UPDATE |
86 | public.packageuploadsource = SELECT, INSERT, UPDATE |
87 | |
88 | === modified file 'lib/canonical/config/__init__.py' |
89 | --- lib/canonical/config/__init__.py 2010-01-14 16:39:18 +0000 |
90 | +++ lib/canonical/config/__init__.py 2010-04-05 20:44:34 +0000 |
91 | @@ -374,13 +374,13 @@ |
92 | _db_config_attrs = frozenset([ |
93 | 'dbuser', 'auth_dbuser', |
94 | 'rw_main_master', 'rw_main_slave', |
95 | - 'ro_main_master', 'ro_main_slave', 'auth_master', 'auth_slave', |
96 | + 'ro_main_master', 'ro_main_slave', |
97 | 'db_statement_timeout', 'db_statement_timeout_precision', |
98 | 'isolation_level', 'randomise_select_results', |
99 | 'soft_request_timeout', 'storm_cache', 'storm_cache_size']) |
100 | _db_config_required_attrs = frozenset([ |
101 | 'dbuser', 'rw_main_master', 'rw_main_slave', 'ro_main_master', |
102 | - 'ro_main_slave', 'auth_master', 'auth_slave']) |
103 | + 'ro_main_slave']) |
104 | |
105 | @property |
106 | def main_master(self): |
107 | |
108 | === modified file 'lib/canonical/config/schema-lazr.conf' |
109 | --- lib/canonical/config/schema-lazr.conf 2010-03-24 02:37:55 +0000 |
110 | +++ lib/canonical/config/schema-lazr.conf 2010-04-05 20:44:34 +0000 |
111 | @@ -559,8 +559,6 @@ |
112 | rw_main_slave: dbname=launchpad_prod_2 host=chokecherry.canonical.com |
113 | ro_main_master: dbname=launchpad_standalone_1 host=chokecherry.canonical.com |
114 | ro_main_slave: dbname=launchpad_standalone_1 host=chokecherry.canonical.com |
115 | -auth_master: dbname=launchpad_prod_3 host=wildcherry.canonical.com |
116 | -auth_slave: dbname=launchpad_prod_2 host=chokecherry.canonical.com |
117 | |
118 | # If the replication lag is more than this many seconds, slave databases |
119 | # will not be used. |
120 | @@ -867,16 +865,10 @@ |
121 | # datatype: integer |
122 | max_scaling: 500 |
123 | |
124 | -[sso] |
125 | -dbuser: sso_main |
126 | -auth_dbuser: sso_auth |
127 | - |
128 | - |
129 | [launchpad] |
130 | # The database user which will be used by this process. |
131 | # datatype: string |
132 | dbuser: launchpad_main |
133 | -auth_dbuser: launchpad_auth |
134 | storm_cache: generational |
135 | storm_cache_size: 10000 |
136 | |
137 | |
138 | === modified file 'lib/canonical/configure.zcml' |
139 | --- lib/canonical/configure.zcml 2010-01-08 21:23:15 +0000 |
140 | +++ lib/canonical/configure.zcml 2010-04-05 20:44:34 +0000 |
141 | @@ -154,7 +154,5 @@ |
142 | <include package="canonical.lazr" /> |
143 | <include zcml:condition="installed canonical.shipit" |
144 | package="canonical.shipit" /> |
145 | - <include zcml:condition="installed canonical.signon" |
146 | - package="canonical.signon" /> |
147 | |
148 | </configure> |
149 | |
150 | === modified file 'lib/canonical/database/harness.py' |
151 | --- lib/canonical/database/harness.py 2009-11-05 03:52:51 +0000 |
152 | +++ lib/canonical/database/harness.py 2010-04-05 20:44:34 +0000 |
153 | @@ -48,8 +48,7 @@ |
154 | from storm.locals import * |
155 | from storm.expr import * |
156 | from canonical.launchpad.webapp.interfaces import ( |
157 | - IStoreSelector, MAIN_STORE, AUTH_STORE, MASTER_FLAVOR, |
158 | - SLAVE_FLAVOR, DEFAULT_FLAVOR) |
159 | + IStoreSelector, MAIN_STORE, MASTER_FLAVOR, SLAVE_FLAVOR, DEFAULT_FLAVOR) |
160 | |
161 | |
162 | def switch_db_user(dbuser, commit_first=True): |
163 | |
164 | === modified file 'lib/canonical/database/sqlbase.py' |
165 | --- lib/canonical/database/sqlbase.py 2010-03-23 15:04:41 +0000 |
166 | +++ lib/canonical/database/sqlbase.py 2010-04-05 20:44:34 +0000 |
167 | @@ -271,7 +271,6 @@ |
168 | # This is only used by scripts, so we must connect to the read-write |
169 | # DB here -- that's why we use rw_main_master directly. |
170 | main_connection_string = dbconfig.rw_main_master |
171 | - auth_connection_string = dbconfig.auth_master |
172 | |
173 | # Override dbname and dbhost in the connection string if they |
174 | # have been passed in. |
175 | @@ -290,7 +289,7 @@ |
176 | match = re.search(r'host=(\S*)', main_connection_string) |
177 | if match is not None: |
178 | dbhost = match.group(1) |
179 | - return main_connection_string, auth_connection_string, dbname, dbhost |
180 | + return main_connection_string, dbname, dbhost |
181 | |
182 | @classmethod |
183 | def initZopeless(cls, dbname=None, dbhost=None, dbuser=None, |
184 | @@ -298,7 +297,7 @@ |
185 | # Connect to the auth master store as well, as some scripts might need |
186 | # to create EmailAddresses and Accounts. |
187 | |
188 | - main_connection_string, auth_connection_string, dbname, dbhost = ( |
189 | + main_connection_string, dbname, dbhost = ( |
190 | cls._get_zopeless_connection_config(dbname, dbhost)) |
191 | |
192 | assert dbuser is not None, ''' |
193 | @@ -315,7 +314,6 @@ |
194 | overlay = dedent("""\ |
195 | [database] |
196 | rw_main_master: %(main_connection_string)s |
197 | - auth_master: %(auth_connection_string)s |
198 | isolation_level: %(isolation_level)s |
199 | """ % vars()) |
200 | |
201 | |
202 | === modified file 'lib/canonical/database/tests/test_zopeless_transaction_manager.py' |
203 | --- lib/canonical/database/tests/test_zopeless_transaction_manager.py 2009-12-14 17:38:34 +0000 |
204 | +++ lib/canonical/database/tests/test_zopeless_transaction_manager.py 2010-04-05 20:44:34 +0000 |
205 | @@ -1,48 +1,17 @@ |
206 | # Copyright 2009 Canonical Ltd. This software is licensed under the |
207 | # GNU Affero General Public License version 3 (see the file LICENSE). |
208 | |
209 | -from textwrap import dedent |
210 | import unittest |
211 | |
212 | from zope.component import getUtility |
213 | |
214 | from storm.zope.interfaces import IZStorm |
215 | |
216 | -from canonical.config import config |
217 | from canonical.database.sqlbase import ZopelessTransactionManager |
218 | from canonical.testing.layers import LaunchpadZopelessLayer |
219 | from lp.testing import TestCase |
220 | |
221 | |
222 | -class TestZopelessTransactionManagerNoLayer(TestCase): |
223 | - |
224 | - def test_initZopeless_connects_to_auth_master_db(self): |
225 | - # Some scripts might create EmailAddress and Account entries, so |
226 | - # initZopeless has to connect to the auth master db. This is a |
227 | - # bugfix test. The error that this test detects is that the |
228 | - # script used to use the main_master database for the |
229 | - # auth_master. In this test, we make sure that the auth_master |
230 | - # and main_master have different values in the config, and then |
231 | - # show that they are honored. Prior to the fix, ``auth_master`` |
232 | - # would have been changed to the same value as ``main_master``. |
233 | - # Now we set up our test data and push it on the config. |
234 | - auth_master = "dbname=example_launchpad_auth_does_not_exist" |
235 | - overlay = dedent(""" |
236 | - [database] |
237 | - main_master: dbname=launchpad_dev |
238 | - auth_master: %s |
239 | - """ % (auth_master,)) |
240 | - config.push('new-db', overlay) |
241 | - try: |
242 | - main_connection_string, auth_connection_string, dbname, dbhost = ( |
243 | - ZopelessTransactionManager._get_zopeless_connection_config( |
244 | - None, None)) |
245 | - self.assertEqual(auth_connection_string, auth_master) |
246 | - finally: |
247 | - # Clean up the configuration |
248 | - config.pop('new-db') |
249 | - |
250 | - |
251 | class TestZopelessTransactionManager(TestCase): |
252 | layer = LaunchpadZopelessLayer |
253 | |
254 | |
255 | === modified file 'lib/canonical/launchpad/database/tests/test_oauth.py' |
256 | --- lib/canonical/launchpad/database/tests/test_oauth.py 2009-06-25 05:30:52 +0000 |
257 | +++ lib/canonical/launchpad/database/tests/test_oauth.py 2010-04-05 20:44:34 +0000 |
258 | @@ -22,7 +22,7 @@ |
259 | """Base tests for the OAuth database classes.""" |
260 | layer = DatabaseFunctionalLayer |
261 | |
262 | - def test__get_store_should_return_the_auth_master_store(self): |
263 | + def test__get_store_should_return_the_main_master_store(self): |
264 | """We want all OAuth classes to use the master store. |
265 | Otherwise, the OAuth exchanges will fail because the authorize |
266 | screen won't probably find the new request token on the slave store. |
267 | |
268 | === modified file 'lib/canonical/launchpad/doc/account.txt' |
269 | --- lib/canonical/launchpad/doc/account.txt 2010-02-12 15:57:27 +0000 |
270 | +++ lib/canonical/launchpad/doc/account.txt 2010-04-05 20:44:34 +0000 |
271 | @@ -196,32 +196,6 @@ |
272 | >>> account.status = AccountStatus.ACTIVE |
273 | >>> login('no-priv@canonical.com') |
274 | |
275 | -The Account's displayname is synced to the Person's displayname if there |
276 | -is one. If the Person.displayname is changed, the Account.displayname is |
277 | -changed too. |
278 | - |
279 | - >>> from canonical.launchpad.interfaces import IPersonSet |
280 | - |
281 | - >>> personset = getUtility(IPersonSet) |
282 | - >>> person = personset.getByEmail('no-priv@canonical.com') |
283 | - >>> person.displayname = 'Something New' |
284 | - >>> print account.displayname |
285 | - Something New |
286 | - |
287 | -However, the reverse is not true. If we change the Account.displayname, |
288 | -the linked Person.displayname (if there is one) is not updated |
289 | -immediately. Instead, a cron job will sync this information later. This |
290 | -allows displayname changes to happen even when the Person table is |
291 | -unavailable. |
292 | - |
293 | - >>> account.displayname = 'No Privileges Account' |
294 | - >>> print person.displayname |
295 | - Something New |
296 | - |
297 | - >>> person.displayname = 'No Privileges Person' |
298 | - >>> print account.displayname |
299 | - No Privileges Person |
300 | - |
301 | An Account has an OpenID identifier used to generate the OpenID identity |
302 | URL. |
303 | |
304 | @@ -242,6 +216,7 @@ |
305 | >>> login('admin@canonical.com') |
306 | >>> passwordless_account = account_set.new( |
307 | ... AccountCreationRationale.USER_CREATED, 'Passwordless') |
308 | + >>> transaction.commit() |
309 | >>> print passwordless_account.creation_rationale.name |
310 | USER_CREATED |
311 | >>> print passwordless_account.displayname |
312 | |
313 | === modified file 'lib/canonical/launchpad/doc/storm.txt' |
314 | --- lib/canonical/launchpad/doc/storm.txt 2010-02-22 10:33:10 +0000 |
315 | +++ lib/canonical/launchpad/doc/storm.txt 2010-04-05 20:44:34 +0000 |
316 | @@ -7,10 +7,11 @@ |
317 | specific Storm tools to cope with our master and slave store arrangement. |
318 | |
319 | >>> from canonical.launchpad.interfaces import ( |
320 | - ... EmailAddressStatus, IAccountSet, IEmailAddressSet, |
321 | + ... EmailAddressStatus, IEmailAddressSet, |
322 | ... IMasterObject, IMasterStore, ISlaveStore, IStore) |
323 | >>> from canonical.launchpad.database import ( |
324 | ... Account, AccountPassword, EmailAddress) |
325 | + >>> from lp.registry.interfaces.person import IPersonSet |
326 | >>> from lp.registry.model.person import Person |
327 | >>> from zope.security.proxy import ProxyFactory |
328 | |
329 | @@ -19,30 +20,14 @@ |
330 | a Launchpad database object. You can use adapters to |
331 | retrieve the correct Store. |
332 | |
333 | - >>> auth_master = IMasterStore(Account) |
334 | >>> main_master = IMasterStore(Person) |
335 | - >>> auth_master is main_master |
336 | - False |
337 | - |
338 | - |
339 | -You can read most tables from any Store, which is required for doing |
340 | -fast joins in the database. However, when it is not necessary to |
341 | -retrieve objects from the same store as another object, it is better to |
342 | -explicitly use the explicit Store for its replication set. Some tables |
343 | -are only available from this store, such as the AccountPassword table. |
344 | - |
345 | - >>> auth_slave = ISlaveStore(AccountPassword) |
346 | - >>> main_slave = ISlaveStore(Person) |
347 | - >>> auth_slave is main_slave |
348 | - False |
349 | - |
350 | |
351 | You can detect if a store is writable by checking what interfaces it |
352 | provides. |
353 | |
354 | - >>> IMasterStore.providedBy(auth_master) |
355 | + >>> IMasterStore.providedBy(main_master) |
356 | True |
357 | - >>> ISlaveStore.providedBy(auth_master) |
358 | + >>> ISlaveStore.providedBy(main_master) |
359 | False |
360 | |
361 | |
362 | @@ -53,7 +38,6 @@ |
363 | Otherwise, it gives you the master. See IStoreSelector for details. |
364 | |
365 | >>> main_default = IStore(Person) |
366 | - >>> main_master = IMasterStore(Person) |
367 | >>> main_slave = ISlaveStore(Person) |
368 | >>> main_default is main_master |
369 | True |
370 | @@ -78,11 +62,10 @@ |
371 | changes to an object, just in case you have been passed an instance |
372 | from a store other than the correct Master. |
373 | |
374 | - >>> auth_slave = ISlaveStore(Account) |
375 | + >>> main_slave = ISlaveStore(Person) |
376 | >>> t = transaction.begin() |
377 | - >>> account = auth_slave.find( |
378 | - ... Account, openid_identifier='mark_oid').one() |
379 | - >>> account.displayname = 'Cannot change' |
380 | + >>> person = main_slave.find(Person, name='mark').one() |
381 | + >>> person.displayname = 'Cannot change' |
382 | >>> transaction.commit() |
383 | Traceback (most recent call last): |
384 | ... |
385 | @@ -90,9 +73,8 @@ |
386 | |
387 | >>> transaction.abort() |
388 | >>> t = transaction.begin() |
389 | - >>> account = auth_slave.find( |
390 | - ... Account, openid_identifier='mark_oid').one() |
391 | - >>> IMasterObject(account).displayname = 'Can change' |
392 | + >>> person = main_slave.find(Person, name='mark').one() |
393 | + >>> IMasterObject(person).displayname = 'Can change' |
394 | >>> transaction.commit() |
395 | |
396 | |
397 | @@ -100,61 +82,60 @@ |
398 | similarly wrapped. |
399 | |
400 | >>> from zope.security.proxy import removeSecurityProxy |
401 | - >>> account = getUtility(IAccountSet).getByEmail('no-priv@canonical.com') |
402 | - >>> removeSecurityProxy(account) is account |
403 | - False |
404 | - >>> account.displayname |
405 | - u'No Privileges Person' |
406 | - >>> account.password |
407 | - Traceback (most recent call last): |
408 | - ... |
409 | - Unauthorized: ... |
410 | - |
411 | - >>> account = IMasterObject(account) |
412 | - >>> removeSecurityProxy(account) is account |
413 | - False |
414 | - >>> account.displayname |
415 | - u'No Privileges Person' |
416 | - >>> account.password |
417 | - Traceback (most recent call last): |
418 | - ... |
419 | - Unauthorized: ... |
420 | - |
421 | - >>> account = IMasterObject(removeSecurityProxy(account)) |
422 | - >>> removeSecurityProxy(account) is account |
423 | + >>> person = getUtility(IPersonSet).getByEmail('no-priv@canonical.com') |
424 | + >>> removeSecurityProxy(person) is person |
425 | + False |
426 | + >>> person.displayname |
427 | + u'No Privileges Person' |
428 | + >>> person.name = 'foo' |
429 | + Traceback (most recent call last): |
430 | + ... |
431 | + Unauthorized: ... |
432 | + |
433 | + >>> person = IMasterObject(person) |
434 | + >>> removeSecurityProxy(person) is person |
435 | + False |
436 | + >>> person.displayname |
437 | + u'No Privileges Person' |
438 | + >>> person.name = 'foo' |
439 | + Traceback (most recent call last): |
440 | + ... |
441 | + Unauthorized: ... |
442 | + |
443 | + >>> person = IMasterObject(removeSecurityProxy(person)) |
444 | + >>> removeSecurityProxy(person) is person |
445 | True |
446 | - >>> account.displayname |
447 | + >>> person.displayname |
448 | u'No Privileges Person' |
449 | - >>> account.password |
450 | - u'...' |
451 | + >>> person.name = 'foo' |
452 | |
453 | Our objects may compare equal even if they have come from different |
454 | stores. |
455 | |
456 | - >>> auth_master_email = IMasterStore(EmailAddress).find( |
457 | - ... EmailAddress, Person.name == 'janitor', |
458 | - ... EmailAddress.person==Person.id).one() |
459 | - >>> auth_slave_email = ISlaveStore(EmailAddress).find( |
460 | - ... EmailAddress, Person.name == 'janitor', |
461 | - ... EmailAddress.person==Person.id).one() |
462 | - >>> auth_master_email is auth_slave_email |
463 | + >>> master_email = IMasterStore(EmailAddress).find( |
464 | + ... EmailAddress, Person.name == 'janitor', |
465 | + ... EmailAddress.person==Person.id).one() |
466 | + >>> slave_email = ISlaveStore(EmailAddress).find( |
467 | + ... EmailAddress, Person.name == 'janitor', |
468 | + ... EmailAddress.person==Person.id).one() |
469 | + >>> master_email is slave_email |
470 | False |
471 | - >>> auth_master_email == auth_slave_email |
472 | + >>> master_email == slave_email |
473 | True |
474 | - >>> auth_master_email != auth_slave_email |
475 | + >>> master_email != slave_email |
476 | False |
477 | |
478 | Comparison works for security wrapped objects too. |
479 | |
480 | >>> wrapped_email = getUtility(IEmailAddressSet).getByEmail( |
481 | - ... auth_master_email.email) |
482 | - >>> removeSecurityProxy(wrapped_email) is auth_master_email |
483 | + ... master_email.email) |
484 | + >>> removeSecurityProxy(wrapped_email) is master_email |
485 | True |
486 | - >>> wrapped_email is auth_master_email |
487 | + >>> wrapped_email is master_email |
488 | False |
489 | - >>> wrapped_email == auth_master_email |
490 | + >>> wrapped_email == master_email |
491 | True |
492 | - >>> wrapped_email != auth_master_email |
493 | + >>> wrapped_email != master_email |
494 | False |
495 | |
496 | Objects not yet flushed to the database also compare equal. |
497 | @@ -176,10 +157,9 @@ |
498 | |
499 | Objects differing by class never compare equal. |
500 | |
501 | - >>> account_one = IMasterStore(Account).get(Account, 1) |
502 | - >>> person_one = IMasterStore(Account).get(Person, 1) |
503 | - >>> account_one == person_one |
504 | + >>> email_one = IMasterStore(EmailAddress).get(EmailAddress, 1) |
505 | + >>> person_one = IMasterStore(Person).get(Person, 1) |
506 | + >>> email_one == person_one |
507 | False |
508 | - >>> account_one != person_one |
509 | + >>> email_one != person_one |
510 | True |
511 | - |
512 | |
513 | === modified file 'lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt' |
514 | --- lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt 2010-01-13 13:50:39 +0000 |
515 | +++ lib/canonical/launchpad/pagetests/standalone/xx-opstats.txt 2010-04-05 20:44:34 +0000 |
516 | @@ -236,8 +236,6 @@ |
517 | ... [database] |
518 | ... rw_main_master: dbname=nonexistant |
519 | ... rw_main_slave: dbname=nonexistant |
520 | - ... auth_master: dbname=nonexistant |
521 | - ... auth_slave: dbname=nonexistant |
522 | ... |
523 | ... [launchpad_session] |
524 | ... dbname: nonexistant |
525 | |
526 | === modified file 'lib/canonical/launchpad/scripts/garbo.py' |
527 | --- lib/canonical/launchpad/scripts/garbo.py 2010-03-26 14:33:46 +0000 |
528 | +++ lib/canonical/launchpad/scripts/garbo.py 2010-04-05 20:44:34 +0000 |
529 | @@ -29,7 +29,7 @@ |
530 | from canonical.launchpad.utilities.looptuner import ( |
531 | DBLoopTuner, TunableLoop) |
532 | from canonical.launchpad.webapp.interfaces import ( |
533 | - IStoreSelector, AUTH_STORE, MAIN_STORE, MASTER_FLAVOR) |
534 | + IStoreSelector, MAIN_STORE, MASTER_FLAVOR) |
535 | from lp.bugs.interfaces.bug import IBugSet |
536 | from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource |
537 | from lp.bugs.model.bugnotification import BugNotification |
538 | @@ -121,19 +121,17 @@ |
539 | transaction.commit() |
540 | |
541 | |
542 | -class OpenIDAssociationPruner(TunableLoop): |
543 | +class OpenIDConsumerAssociationPruner(TunableLoop): |
544 | minimum_chunk_size = 3500 |
545 | maximum_chunk_size = 50000 |
546 | |
547 | - table_name = 'OpenIDAssociation' |
548 | - store_name = AUTH_STORE |
549 | + table_name = 'OpenIDConsumerAssociation' |
550 | |
551 | _num_removed = None |
552 | |
553 | def __init__(self, log, abort_time=None): |
554 | - super(OpenIDAssociationPruner, self).__init__(log, abort_time) |
555 | - self.store = getUtility(IStoreSelector).get( |
556 | - self.store_name, MASTER_FLAVOR) |
557 | + super(OpenIDConsumerAssociationPruner, self).__init__(log, abort_time) |
558 | + self.store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR) |
559 | |
560 | def __call__(self, chunksize): |
561 | result = self.store.execute(""" |
562 | @@ -152,11 +150,6 @@ |
563 | return self._num_removed == 0 |
564 | |
565 | |
566 | -class OpenIDConsumerAssociationPruner(OpenIDAssociationPruner): |
567 | - table_name = 'OpenIDConsumerAssociation' |
568 | - store_name = MAIN_STORE |
569 | - |
570 | - |
571 | class RevisionCachePruner(TunableLoop): |
572 | """A tunable loop to remove old revisions from the cache.""" |
573 | |
574 | @@ -869,7 +862,6 @@ |
575 | tunable_loops = [ |
576 | OAuthNoncePruner, |
577 | OpenIDConsumerNoncePruner, |
578 | - OpenIDAssociationPruner, |
579 | OpenIDConsumerAssociationPruner, |
580 | RevisionCachePruner, |
581 | BugHeatUpdater, |
582 | |
583 | === modified file 'lib/canonical/launchpad/scripts/tests/test_garbo.py' |
584 | --- lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-01-22 06:03:19 +0000 |
585 | +++ lib/canonical/launchpad/scripts/tests/test_garbo.py 2010-04-05 20:44:34 +0000 |
586 | @@ -29,11 +29,11 @@ |
587 | from lp.testing import TestCase, TestCaseWithFactory |
588 | from canonical.launchpad.scripts.garbo import ( |
589 | DailyDatabaseGarbageCollector, HourlyDatabaseGarbageCollector, |
590 | - OpenIDAssociationPruner, OpenIDConsumerAssociationPruner) |
591 | + OpenIDConsumerAssociationPruner) |
592 | from canonical.launchpad.scripts.tests import run_script |
593 | from canonical.launchpad.scripts.logger import QuietFakeLogger |
594 | from canonical.launchpad.webapp.interfaces import ( |
595 | - IStoreSelector, MASTER_FLAVOR) |
596 | + IStoreSelector, MAIN_STORE, MASTER_FLAVOR) |
597 | from canonical.testing.layers import ( |
598 | DatabaseLayer, LaunchpadScriptLayer, LaunchpadZopelessLayer) |
599 | from lp.bugs.model.bugnotification import ( |
600 | @@ -219,12 +219,12 @@ |
601 | Min(CodeImportResult.date_created)).one().replace(tzinfo=UTC) |
602 | >= now - timedelta(days=30)) |
603 | |
604 | - def test_OpenIDAssociationPruner(self, pruner=OpenIDAssociationPruner): |
605 | - store_name = pruner.store_name |
606 | + def test_OpenIDConsumerAssociationPruner(self): |
607 | + pruner = OpenIDConsumerAssociationPruner |
608 | table_name = pruner.table_name |
609 | LaunchpadZopelessLayer.switchDbUser('testadmin') |
610 | store_selector = getUtility(IStoreSelector) |
611 | - store = store_selector.get(store_name, MASTER_FLAVOR) |
612 | + store = store_selector.get(MAIN_STORE, MASTER_FLAVOR) |
613 | now = time.time() |
614 | # Create some associations in the past with lifetimes |
615 | for delta in range(0, 20): |
616 | @@ -247,7 +247,7 @@ |
617 | self.runHourly() |
618 | |
619 | LaunchpadZopelessLayer.switchDbUser('testadmin') |
620 | - store = store_selector.get(store_name, MASTER_FLAVOR) |
621 | + store = store_selector.get(MAIN_STORE, MASTER_FLAVOR) |
622 | # Confirm all the rows we know should have been expired have |
623 | # been expired. These are the ones that would be expired using |
624 | # the test start time as 'now'. |
625 | @@ -263,9 +263,6 @@ |
626 | "SELECT COUNT(*) FROM %s" % table_name).get_one()[0] |
627 | self.failUnless(num_unexpired > 0) |
628 | |
629 | - def test_OpenIDConsumerAssociationPruner(self): |
630 | - self.test_OpenIDAssociationPruner(OpenIDConsumerAssociationPruner) |
631 | - |
632 | def test_RevisionAuthorEmailLinker(self): |
633 | LaunchpadZopelessLayer.switchDbUser('testadmin') |
634 | rev1 = self.factory.makeRevision('Author 1 <author-1@Example.Org>') |
635 | @@ -373,6 +370,7 @@ |
636 | |
637 | # If we remove the email address that was subscribed, the |
638 | # garbage collector removes the subscription. |
639 | + LaunchpadZopelessLayer.switchDbUser('testadmin') |
640 | Store.of(email).remove(email) |
641 | transaction.commit() |
642 | self.runDaily() |
643 | |
644 | === modified file 'lib/canonical/launchpad/webapp/adapter.py' |
645 | --- lib/canonical/launchpad/webapp/adapter.py 2010-03-25 11:58:42 +0000 |
646 | +++ lib/canonical/launchpad/webapp/adapter.py 2010-04-05 20:44:34 +0000 |
647 | @@ -40,14 +40,13 @@ |
648 | from canonical.launchpad.readonly import is_read_only |
649 | from canonical.launchpad.webapp.dbpolicy import MasterDatabasePolicy |
650 | from canonical.launchpad.webapp.interfaces import ( |
651 | - AUTH_STORE, DEFAULT_FLAVOR, IStoreSelector, |
652 | - MAIN_STORE, MASTER_FLAVOR, ReadOnlyModeViolation, SLAVE_FLAVOR) |
653 | + DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE, MASTER_FLAVOR, |
654 | + ReadOnlyModeViolation, SLAVE_FLAVOR) |
655 | from canonical.launchpad.webapp.opstats import OpStats |
656 | from canonical.lazr.utils import safe_hasattr |
657 | |
658 | |
659 | __all__ = [ |
660 | - 'DisconnectionError', |
661 | 'RequestExpired', |
662 | 'set_request_started', |
663 | 'clear_request_started', |
664 | @@ -327,7 +326,7 @@ |
665 | |
666 | def __init__(self, uri): |
667 | # The uri is just a property name in the config, such as main_master |
668 | - # or auth_slave. |
669 | + # or main_slave. |
670 | # We don't invoke the superclass constructor as it has a very limited |
671 | # opinion on what uri is. |
672 | # pylint: disable-msg=W0231 |
673 | @@ -356,7 +355,7 @@ |
674 | 'Connection uri %s does not match section-realm-flavor format' |
675 | % repr(self._uri.database)) |
676 | |
677 | - assert realm in ('main', 'auth'), 'Unknown realm %s' % realm |
678 | + assert realm == 'main', 'Unknown realm %s' % realm |
679 | assert flavor in ('master', 'slave'), 'Unknown flavor %s' % flavor |
680 | |
681 | my_dbconfig = DatabaseConfig() |
682 | @@ -572,14 +571,6 @@ |
683 | return db_policy.getStore(name, flavor) |
684 | |
685 | |
686 | -# There are not many tables outside of the main replication set, so we |
687 | -# can just maintain a hardcoded list of what isn't in there for now. |
688 | -_auth_store_tables = frozenset([ |
689 | - 'Account', 'AccountPassword', 'AuthToken', 'EmailAddress', |
690 | - 'OpenIDAssociation', 'OpenIDAuthorization', 'OpenIDNonce', |
691 | - 'OpenIDRPSummary']) |
692 | - |
693 | - |
694 | # We want to be able to adapt a Storm class to an IStore, IMasterStore or |
695 | # ISlaveStore. Unfortunately, the component architecture provides no |
696 | # way for us to declare that a class, and all its subclasses, provides |
697 | @@ -588,9 +579,7 @@ |
698 | def get_store(storm_class, flavor=DEFAULT_FLAVOR): |
699 | """Return a flavored Store for the given database class.""" |
700 | table = getattr(removeSecurityProxy(storm_class), '__storm_table__', None) |
701 | - if table in _auth_store_tables: |
702 | - return getUtility(IStoreSelector).get(AUTH_STORE, flavor) |
703 | - elif table is not None: |
704 | + if table is not None: |
705 | return getUtility(IStoreSelector).get(MAIN_STORE, flavor) |
706 | else: |
707 | return None |
708 | |
709 | === modified file 'lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt' |
710 | --- lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2009-06-11 01:28:55 +0000 |
711 | +++ lib/canonical/launchpad/webapp/ftests/test_adapter_permissions.txt 2010-04-05 20:44:34 +0000 |
712 | @@ -10,8 +10,7 @@ |
713 | |
714 | >>> from lp.registry.model.person import Person |
715 | >>> from canonical.launchpad.webapp.interfaces import ( |
716 | - ... IStoreSelector, AUTH_STORE, MAIN_STORE, |
717 | - ... MASTER_FLAVOR, SLAVE_FLAVOR) |
718 | + ... IStoreSelector, MAIN_STORE, MASTER_FLAVOR, SLAVE_FLAVOR) |
719 | >>> import transaction |
720 | >>> from zope.component import getUtility |
721 | |
722 | @@ -47,25 +46,3 @@ |
723 | >>> main_master.find(Person, name='janitor').one().displayname |
724 | u'BenD' |
725 | >>> transaction.abort() |
726 | - |
727 | - |
728 | -A MASTER_FLAVOR Store does not allow writes to tables outside of that |
729 | -Store's replication set. |
730 | - |
731 | - >>> t = transaction.begin() |
732 | - >>> person = main_master.find(Person, name='no-priv').one() |
733 | - >>> account = person.account |
734 | - >>> account.displayname = "Ben Dover" |
735 | - >>> main_master.flush() |
736 | - Traceback (most recent call last): |
737 | - ... |
738 | - ProgrammingError: permission denied for relation account |
739 | - >>> transaction.abort() |
740 | - |
741 | - >>> t = transaction.begin() |
742 | - >>> auth_master = getUtility(IStoreSelector).get(AUTH_STORE, MASTER_FLAVOR) |
743 | - >>> person = auth_master.find(Person, name='no-priv').one() |
744 | - >>> account = person.account |
745 | - >>> account.displayname = "Ben Dover" |
746 | - >>> auth_master.flush() |
747 | - >>> transaction.abort() |
748 | |
749 | === modified file 'lib/canonical/launchpad/webapp/interfaces.py' |
750 | --- lib/canonical/launchpad/webapp/interfaces.py 2010-03-26 22:57:38 +0000 |
751 | +++ lib/canonical/launchpad/webapp/interfaces.py 2010-04-05 20:44:34 +0000 |
752 | @@ -745,9 +745,7 @@ |
753 | # |
754 | |
755 | MAIN_STORE = 'main' # The main database. |
756 | -AUTH_STORE = 'auth' # The authentication database. |
757 | - |
758 | -ALL_STORES = frozenset([MAIN_STORE, AUTH_STORE]) |
759 | +ALL_STORES = frozenset([MAIN_STORE]) |
760 | |
761 | DEFAULT_FLAVOR = 'default' # Default flavor for current state. |
762 | MASTER_FLAVOR = 'master' # The master database. |
763 | |
764 | === modified file 'lib/canonical/launchpad/webapp/tests/test_dbpolicy.py' |
765 | --- lib/canonical/launchpad/webapp/tests/test_dbpolicy.py 2010-02-24 23:18:40 +0000 |
766 | +++ lib/canonical/launchpad/webapp/tests/test_dbpolicy.py 2010-04-05 20:44:34 +0000 |
767 | @@ -25,7 +25,7 @@ |
768 | ReadOnlyLaunchpadDatabasePolicy, SlaveDatabasePolicy, |
769 | SlaveOnlyDatabasePolicy) |
770 | from canonical.launchpad.webapp.interfaces import ( |
771 | - ALL_STORES, AUTH_STORE, DEFAULT_FLAVOR, DisallowedStore, IDatabasePolicy, |
772 | + ALL_STORES, DEFAULT_FLAVOR, DisallowedStore, IDatabasePolicy, |
773 | IStoreSelector, MAIN_STORE, MASTER_FLAVOR, ReadOnlyModeDisallowedStore, |
774 | SLAVE_FLAVOR) |
775 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
776 | @@ -47,9 +47,6 @@ |
777 | main_store = store_selector.get(MAIN_STORE, DEFAULT_FLAVOR) |
778 | self.failUnlessEqual(self.getDBUser(main_store), 'launchpad_main') |
779 | |
780 | - auth_store = store_selector.get(AUTH_STORE, DEFAULT_FLAVOR) |
781 | - self.failUnlessEqual(self.getDBUser(auth_store), 'launchpad_auth') |
782 | - |
783 | def getDBUser(self, store): |
784 | return store.execute( |
785 | 'SHOW session_authorization').get_one()[0] |
786 | |
787 | === removed symlink 'lib/canonical/signon' |
788 | === target was u'../../sourcecode/canonical-identity-provider' |
789 | === modified file 'lib/lp/bugs/doc/checkwatches.txt' |
790 | --- lib/lp/bugs/doc/checkwatches.txt 2010-03-26 15:24:59 +0000 |
791 | +++ lib/lp/bugs/doc/checkwatches.txt 2010-04-05 20:44:34 +0000 |
792 | @@ -337,9 +337,13 @@ |
793 | If a bug tracker doesn't have any watches to update, forceUpdateAll() |
794 | will ignore it. |
795 | |
796 | + >>> transaction.commit() |
797 | + >>> LaunchpadZopelessLayer.switchDbUser('launchpad') |
798 | >>> login('test@canonical.com') |
799 | >>> empty_tracker = factory.makeBugTracker( |
800 | ... 'http://example.com', BugTrackerType.ROUNDUP) |
801 | + >>> transaction.commit() |
802 | + >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser) |
803 | >>> empty_tracker_name = empty_tracker.name |
804 | >>> update_all(empty_tracker_name) |
805 | INFO Bug tracker 'auto-example.com' doesn't have any watches. Ignoring. |
806 | |
807 | === modified file 'lib/lp/bugs/doc/externalbugtracker-comment-imports.txt' |
808 | --- lib/lp/bugs/doc/externalbugtracker-comment-imports.txt 2010-03-25 14:28:33 +0000 |
809 | +++ lib/lp/bugs/doc/externalbugtracker-comment-imports.txt 2010-04-05 20:44:34 +0000 |
810 | @@ -373,8 +373,10 @@ |
811 | since they aren't a valid Launchpad user, having been created during the |
812 | import process. |
813 | |
814 | -We'll add a listener to check for Karma events. |
815 | +We'll create a bug watch and add a listener to check for Karma events. |
816 | |
817 | + >>> LaunchpadZopelessLayer.switchDbUser('launchpad') |
818 | + >>> bug_watch = factory.makeBugWatch('123456') |
819 | >>> from lp.registry.tests.karma import KarmaAssignedEventListener |
820 | >>> karma_helper = KarmaAssignedEventListener() |
821 | >>> karma_helper.register_listener() |
822 | @@ -382,8 +384,6 @@ |
823 | Importing a comment with a CVE reference will produce a CVE link in |
824 | Launchpad but will result in no Karma records being created. |
825 | |
826 | - >>> LaunchpadZopelessLayer.switchDbUser('launchpad') |
827 | - >>> bug_watch = factory.makeBugWatch('123456') |
828 | >>> transaction.commit() |
829 | >>> LaunchpadZopelessLayer.switchDbUser(config.checkwatches.dbuser) |
830 | >>> external_bugtracker.remote_comments = { |
831 | |
832 | === modified file 'lib/lp/bugs/interfaces/bug.py' |
833 | --- lib/lp/bugs/interfaces/bug.py 2010-03-12 06:24:59 +0000 |
834 | +++ lib/lp/bugs/interfaces/bug.py 2010-04-05 20:44:34 +0000 |
835 | @@ -288,9 +288,9 @@ |
836 | number_of_duplicates = exported( |
837 | Int(title=_('The number of bugs marked as duplicates of this bug'), |
838 | required=True, readonly=True)) |
839 | - message_count = Int( |
840 | - title=_('The number of comments on this bug'), |
841 | - required=True, readonly=True) |
842 | + message_count = exported( |
843 | + Int(title=_('The number of comments on this bug'), |
844 | + required=True, readonly=True)) |
845 | users_affected_count = exported( |
846 | Int(title=_('The number of users affected by this bug ' |
847 | '(not including duplicates)'), |
848 | |
849 | === modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt' |
850 | --- lib/lp/bugs/stories/webservice/xx-bug.txt 2010-03-24 21:59:44 +0000 |
851 | +++ lib/lp/bugs/stories/webservice/xx-bug.txt 2010-04-05 20:44:34 +0000 |
852 | @@ -34,6 +34,7 @@ |
853 | id: 11 |
854 | latest_patch_uploaded: None |
855 | linked_branches_collection_link: u'http://.../bugs/11/linked_branches' |
856 | + message_count: 7 |
857 | messages_collection_link: u'http://.../bugs/11/messages' |
858 | name: None |
859 | number_of_duplicates: 0 |
860 | |
861 | === modified file 'lib/lp/code/doc/branch-karma.txt' |
862 | --- lib/lp/code/doc/branch-karma.txt 2010-02-16 20:36:48 +0000 |
863 | +++ lib/lp/code/doc/branch-karma.txt 2010-04-05 20:44:34 +0000 |
864 | @@ -49,6 +49,7 @@ |
865 | You get karma for linking a bug to a branch. |
866 | |
867 | >>> bug = factory.makeBug(product=fooix) |
868 | + Karma added: action=bugcreated, product=fooix, person=person-name11 |
869 | >>> branch_link = bug.linkBranch(branch, eric) |
870 | Karma added: action=bugbranchcreated, product=fooix, person=eric |
871 | |
872 | |
873 | === modified file 'lib/lp/registry/configure.zcml' |
874 | --- lib/lp/registry/configure.zcml 2010-03-19 11:33:44 +0000 |
875 | +++ lib/lp/registry/configure.zcml 2010-04-05 20:44:34 +0000 |
876 | @@ -1644,18 +1644,60 @@ |
877 | <!-- DistributionMirror --> |
878 | <class class="lp.registry.model.distributionmirror.DistributionMirror"> |
879 | <allow |
880 | - interface="lp.registry.interfaces.distributionmirror.IDistributionMirrorPublic" /> |
881 | + attributes=" |
882 | + id |
883 | + name |
884 | + displayname |
885 | + description |
886 | + distribution |
887 | + http_base_url |
888 | + ftp_base_url |
889 | + rsync_base_url |
890 | + enabled |
891 | + speed |
892 | + status |
893 | + country |
894 | + content |
895 | + owner |
896 | + title |
897 | + cdimage_series |
898 | + source_series |
899 | + arch_series |
900 | + last_probe_record |
901 | + all_probe_records |
902 | + has_ftp_or_rsync_base_url |
903 | + base_url |
904 | + date_created |
905 | + country_dns_mirror |
906 | + mirrorMustHaveHTTPOrFTPURL |
907 | + getSummarizedMirroredSourceSeries |
908 | + getSummarizedMirroredArchSeries |
909 | + getOverallFreshness |
910 | + isOfficial |
911 | + shouldDisable |
912 | + disable |
913 | + newProbeRecord |
914 | + deleteMirrorDistroArchSeries |
915 | + ensureMirrorDistroArchSeries |
916 | + ensureMirrorDistroSeriesSource |
917 | + deleteMirrorDistroSeriesSource |
918 | + ensureMirrorCDImageSeries |
919 | + deleteMirrorCDImageSeries |
920 | + deleteAllMirrorCDImageSeries |
921 | + getExpectedPackagesPaths |
922 | + getExpectedSourcesPaths |
923 | + canTransitionToCountryMirror" /> |
924 | <require |
925 | permission="launchpad.Edit" |
926 | - interface="lp.registry.interfaces.distributionmirror.IDistributionMirrorEditRestricted" |
927 | set_attributes="name displayname description whiteboard |
928 | http_base_url ftp_base_url rsync_base_url enabled |
929 | - speed country content official_candidate owner" /> |
930 | + speed country content official_candidate owner" |
931 | + attributes="official_candidate whiteboard" /> |
932 | <require |
933 | permission="launchpad.Admin" |
934 | - interface="lp.registry.interfaces.distributionmirror.IDistributionMirrorAdminRestricted" |
935 | set_attributes="status reviewer date_reviewed" |
936 | - attributes="destroySelf" /> |
937 | + attributes="reviewer date_reviewed destroySelf |
938 | + transitionToCountryMirror" /> |
939 | </class> |
940 | |
941 | <adapter |
942 | |
943 | === modified file 'lib/lp/registry/doc/distribution-mirror.txt' |
944 | --- lib/lp/registry/doc/distribution-mirror.txt 2009-12-09 10:03:20 +0000 |
945 | +++ lib/lp/registry/doc/distribution-mirror.txt 2010-04-05 20:44:34 +0000 |
946 | @@ -8,7 +8,7 @@ |
947 | >>> from canonical.launchpad.interfaces import ( |
948 | ... ICountrySet, IDistributionSet, IDistributionMirrorSet, |
949 | ... IDistroArchSeriesSet, IDistroSeriesSet, ILibraryFileAliasSet, |
950 | - ... IPersonSet, MirrorContent, MirrorSpeed) |
951 | + ... IPersonSet, MirrorContent, MirrorSpeed, MirrorStatus) |
952 | >>> from lp.registry.interfaces.pocket import PackagePublishingPocket |
953 | >>> mirrorset = getUtility(IDistributionMirrorSet) |
954 | >>> distroset = getUtility(IDistributionSet) |
955 | @@ -814,3 +814,121 @@ |
956 | >>> mirrorset.getByName('invalid-mirror') is None |
957 | True |
958 | |
959 | +Country DNS mirrors |
960 | +------------------- |
961 | + |
962 | +Country DNS mirrors are mirrors which have been assigned |
963 | +$CC.archive.ubuntu.com or $CC.releases.ubuntu.com. These assignments are |
964 | +tracked in Launchpad. |
965 | + |
966 | + >>> login('admin@canonical.com') |
967 | + >>> ubuntu_distro = getUtility(IDistributionSet).getByName('ubuntu') |
968 | + >>> de_archive_mirror = factory.makeMirror(ubuntu_distro, |
969 | + ... "Technische Universitaet Dresden", country=82, |
970 | + ... http_url="http://ubuntu.mirror.tudos.de/ubuntu/", |
971 | + ... official_candidate=True) |
972 | + >>> davis_station_archive = factory.makeMirror(ubuntu_distro, |
973 | + ... "Davis Station", country=9, |
974 | + ... http_url="http://mirror.davis.antarctica.org/ubuntu", |
975 | + ... official_candidate=True) |
976 | + >>> de_archive_mirror.status = MirrorStatus.OFFICIAL |
977 | + >>> de_archive_prober_log = factory.makeMirrorProbeRecord(de_archive_mirror) |
978 | + >>> logout() |
979 | + |
980 | +Normal users can access country_dns_mirror, can see if a mirror is eligible |
981 | +for the status, however, they may not change it: |
982 | + |
983 | + >>> login('test@canonical.com') |
984 | + >>> de_archive_mirror.canTransitionToCountryMirror() |
985 | + True |
986 | + >>> de_archive_mirror.transitionToCountryMirror(True) |
987 | + Traceback (most recent call last): |
988 | + ... |
989 | + Unauthorized: (<DistributionMirror at ...>, 'transitionToCountryMirror', |
990 | + 'launchpad.Admin') |
991 | + >>> logout() |
992 | + |
993 | +Mirror listing administrators may change the status however: |
994 | + |
995 | + >>> login('karl@canonical.com') |
996 | + >>> de_archive_mirror.transitionToCountryMirror(True) |
997 | + |
998 | +Mirrors which are already set as country mirrors can't be 'set' as such |
999 | +again: |
1000 | + |
1001 | + >>> de_archive_mirror.canTransitionToCountryMirror() |
1002 | + False |
1003 | + >>> de_archive_mirror.transitionToCountryMirror(True) |
1004 | + >>> logout() |
1005 | + |
1006 | +There cannot be multiple country mirrors of one type for one country: |
1007 | + |
1008 | + >>> login('karl@canonical.com') |
1009 | + >>> proberecord = factory.makeMirrorProbeRecord(davis_station_archive) |
1010 | + |
1011 | + >>> print davis_station_archive.content.name |
1012 | + ARCHIVE |
1013 | + >>> print davis_station_archive.country_dns_mirror |
1014 | + False |
1015 | + >>> print davis_station_archive.country.name |
1016 | + Antarctica |
1017 | + |
1018 | + >>> archive_mirror2 = getUtility(IDistributionMirrorSet).getByName( |
1019 | + ... 'archive-mirror2') |
1020 | + >>> print archive_mirror2.content.name |
1021 | + ARCHIVE |
1022 | + >>> print archive_mirror2.country_dns_mirror |
1023 | + False |
1024 | + >>> print archive_mirror2.country.name |
1025 | + Antarctica |
1026 | + |
1027 | + >>> davis_station_archive.status = MirrorStatus.OFFICIAL |
1028 | + |
1029 | + >>> davis_station_archive.transitionToCountryMirror(True) |
1030 | + >>> archive_mirror2.transitionToCountryMirror(True) |
1031 | + Traceback (most recent call last): |
1032 | + ... |
1033 | + CountryMirrorAlreadySet: Antarctica already has a country Archive mirror |
1034 | + set. |
1035 | + |
1036 | +Mirrors which have not been probed may not be marked as country mirrors: |
1037 | + |
1038 | + >>> linux_au_mirror = factory.makeMirror(ubuntu_distro, |
1039 | + ... "Linux.org.au", country=14, |
1040 | + ... http_url="http://mirror.linux.org.au/ubuntu", |
1041 | + ... official_candidate=True) |
1042 | + >>> linux_au_mirror.status = MirrorStatus.OFFICIAL |
1043 | + >>> linux_au_mirror.transitionToCountryMirror(True) |
1044 | + Traceback (most recent call last): |
1045 | + ... |
1046 | + MirrorNotProbed: This mirror may not be set as a country mirror as it has |
1047 | + not been probed. |
1048 | + >>> logout() |
1049 | + |
1050 | +Mirrors which are not official or do not have an HTTP URL may not be set as |
1051 | +country mirrors: |
1052 | + |
1053 | + >>> login('admin@canonical.com') |
1054 | + >>> osuosl_mirror = factory.makeMirror(ubuntu_distro, |
1055 | + ... "OSU Open Source Lab", country=226, |
1056 | + ... ftp_url="ftp://ubuntu.osuosl.org/pub/ubuntu/", |
1057 | + ... official_candidate=True) |
1058 | + >>> osuosl_mirror.status = MirrorStatus.OFFICIAL |
1059 | + >>> print osuosl_mirror.http_base_url |
1060 | + None |
1061 | + |
1062 | + >>> osuosl_mirror.canTransitionToCountryMirror() |
1063 | + False |
1064 | + |
1065 | + >>> osuosl_mirror.transitionToCountryMirror(None) |
1066 | + Traceback (most recent call last): |
1067 | + ... |
1068 | + NoneError: None isn't acceptable as a value for |
1069 | + DistributionMirror.country_dns_mirror |
1070 | + |
1071 | + >>> osuosl_mirror.transitionToCountryMirror(True) |
1072 | + Traceback (most recent call last): |
1073 | + ... |
1074 | + MirrorHasNoHTTPURL: This mirror may not be set as a country mirror as it |
1075 | + does not have an HTTP URL set. |
1076 | + >>> logout() |
1077 | |
1078 | === modified file 'lib/lp/registry/interfaces/distribution.py' |
1079 | --- lib/lp/registry/interfaces/distribution.py 2010-03-24 21:59:58 +0000 |
1080 | +++ lib/lp/registry/interfaces/distribution.py 2010-04-05 20:44:34 +0000 |
1081 | @@ -25,7 +25,7 @@ |
1082 | |
1083 | from lazr.restful.fields import CollectionField, Reference |
1084 | from lazr.restful.declarations import ( |
1085 | - collection_default_content, export_as_webservice_collection, |
1086 | + collection_default_content, copy_field, export_as_webservice_collection, |
1087 | export_as_webservice_entry, export_operation_as, |
1088 | export_read_operation, exported, operation_parameters, |
1089 | operation_returns_collection_of, operation_returns_entry, |
1090 | @@ -321,6 +321,14 @@ |
1091 | if it's not found. |
1092 | """ |
1093 | |
1094 | + @operation_parameters( |
1095 | + country=copy_field(IDistributionMirror['country'], required=True), |
1096 | + mirror_type=copy_field(IDistributionMirror['content'], required=True)) |
1097 | + @operation_returns_entry(IDistributionMirror) |
1098 | + @export_read_operation() |
1099 | + def getCountryMirror(country, mirror_type): |
1100 | + """Return the country DNS mirror for a country and content type.""" |
1101 | + |
1102 | def newMirror(owner, speed, country, content, displayname=None, |
1103 | description=None, http_base_url=None, |
1104 | ftp_base_url=None, rsync_base_url=None, enabled=False, |
1105 | |
1106 | === modified file 'lib/lp/registry/interfaces/distributionmirror.py' |
1107 | --- lib/lp/registry/interfaces/distributionmirror.py 2010-02-22 15:50:06 +0000 |
1108 | +++ lib/lp/registry/interfaces/distributionmirror.py 2010-04-05 20:44:34 +0000 |
1109 | @@ -6,21 +6,24 @@ |
1110 | __metaclass__ = type |
1111 | |
1112 | __all__ = [ |
1113 | -'IDistributionMirror', |
1114 | -'IDistributionMirrorAdminRestricted', |
1115 | -'IDistributionMirrorEditRestricted', |
1116 | -'IDistributionMirrorPublic', |
1117 | -'IMirrorDistroArchSeries', |
1118 | -'IMirrorDistroSeriesSource', |
1119 | -'IMirrorProbeRecord', |
1120 | -'IDistributionMirrorSet', |
1121 | -'IMirrorCDImageDistroSeries', |
1122 | -'PROBE_INTERVAL', |
1123 | -'UnableToFetchCDImageFileList', |
1124 | -'MirrorContent', |
1125 | -'MirrorFreshness', |
1126 | -'MirrorSpeed', |
1127 | -'MirrorStatus'] |
1128 | + 'CannotTransitionToCountryMirror', |
1129 | + 'CountryMirrorAlreadySet', |
1130 | + 'IDistributionMirror', |
1131 | + 'IMirrorDistroArchSeries', |
1132 | + 'IMirrorDistroSeriesSource', |
1133 | + 'IMirrorProbeRecord', |
1134 | + 'IDistributionMirrorSet', |
1135 | + 'IMirrorCDImageDistroSeries', |
1136 | + 'PROBE_INTERVAL', |
1137 | + 'MirrorContent', |
1138 | + 'MirrorFreshness', |
1139 | + 'MirrorHasNoHTTPURL', |
1140 | + 'MirrorNotOfficial', |
1141 | + 'MirrorNotProbed', |
1142 | + 'MirrorSpeed', |
1143 | + 'MirrorStatus', |
1144 | + 'UnableToFetchCDImageFileList', |
1145 | + ] |
1146 | |
1147 | from cgi import escape |
1148 | |
1149 | @@ -31,8 +34,11 @@ |
1150 | from zope.component import getUtility |
1151 | from lazr.enum import DBEnumeratedType, DBItem |
1152 | from lazr.restful.declarations import ( |
1153 | - export_as_webservice_entry, export_read_operation, exported) |
1154 | + export_as_webservice_entry, export_read_operation, |
1155 | + export_write_operation, exported, mutator_for, operation_parameters, |
1156 | + webservice_error) |
1157 | from lazr.restful.fields import Reference, ReferenceChoice |
1158 | +from lazr.restful.interface import copy_field |
1159 | |
1160 | from canonical.launchpad import _ |
1161 | from canonical.launchpad.fields import ( |
1162 | @@ -47,6 +53,43 @@ |
1163 | PROBE_INTERVAL = 23 |
1164 | |
1165 | |
1166 | +class CannotTransitionToCountryMirror(Exception): |
1167 | + """Root exception for transitions to country mirrors.""" |
1168 | + webservice_error(400) |
1169 | + |
1170 | + |
1171 | +class CountryMirrorAlreadySet(CannotTransitionToCountryMirror): |
1172 | + """Distribution mirror cannot be set as a country mirror. |
1173 | + |
1174 | + Raised when a user tries to change set a distribution mirror as a country |
1175 | + mirror, however there is already one set for that country. |
1176 | + """ |
1177 | + |
1178 | + |
1179 | +class MirrorNotOfficial(CannotTransitionToCountryMirror): |
1180 | + """Distribution mirror is not permitted to become a country mirror. |
1181 | + |
1182 | + Raised when a user tries to change set a distribution mirror as a country |
1183 | + mirror, however the mirror in question is not official. |
1184 | + """ |
1185 | + |
1186 | + |
1187 | +class MirrorHasNoHTTPURL(CannotTransitionToCountryMirror): |
1188 | + """Distribution mirror has no HTTP URL. |
1189 | + |
1190 | + Raised when a user tries to make an official mirror a country mirror, |
1191 | + however the mirror has not HTTP URL set. |
1192 | + """ |
1193 | + |
1194 | + |
1195 | +class MirrorNotProbed(CannotTransitionToCountryMirror): |
1196 | + """Distribution mirror has not been probed. |
1197 | + |
1198 | + Raised when a user tries to set an official mirror as a country mirror, |
1199 | + however the mirror has not been probed yet. |
1200 | + """ |
1201 | + |
1202 | + |
1203 | class MirrorContent(DBEnumeratedType): |
1204 | """The content that is mirrored.""" |
1205 | |
1206 | @@ -284,33 +327,10 @@ |
1207 | def getMirrorByURI(self, url): |
1208 | return getUtility(IDistributionMirrorSet).getByRsyncUrl(url) |
1209 | |
1210 | -class IDistributionMirrorAdminRestricted(Interface): |
1211 | - """IDistributionMirror properties requiring launchpad.Admin permission.""" |
1212 | - |
1213 | - reviewer = exported(PublicPersonChoice( |
1214 | - title=_('Reviewer'), required=False, readonly=True, |
1215 | - vocabulary='ValidPersonOrTeam', description=_( |
1216 | - "The person who last reviewed this mirror."))) |
1217 | - date_reviewed = exported(Datetime( |
1218 | - title=_('Date reviewed'), required=False, readonly=True, |
1219 | - description=_( |
1220 | - "The date on which this mirror was last reviewed by a mirror admin."))) |
1221 | - |
1222 | - |
1223 | -class IDistributionMirrorEditRestricted(Interface): |
1224 | - """IDistributionMirror properties requiring launchpad.Edit permission.""" |
1225 | - |
1226 | - official_candidate = exported(Bool( |
1227 | - title=_('Apply to be an official mirror of this distribution'), |
1228 | - required=False, readonly=False, default=True)) |
1229 | - whiteboard = exported(Whiteboard( |
1230 | - title=_('Whiteboard'), required=False, readonly=False, |
1231 | - description=_("Notes on the current status of the mirror (only " |
1232 | - "visible to admins and the mirror's registrant)."))) |
1233 | - |
1234 | - |
1235 | -class IDistributionMirrorPublic(Interface): |
1236 | - """Public IDistributionMirror properties.""" |
1237 | + |
1238 | +class IDistributionMirror(Interface): |
1239 | + """A mirror of a given distribution.""" |
1240 | + export_as_webservice_entry() |
1241 | |
1242 | id = Int(title=_('The unique id'), required=True, readonly=True) |
1243 | owner = exported(PublicPersonChoice( |
1244 | @@ -386,6 +406,39 @@ |
1245 | date_created = exported(Datetime( |
1246 | title=_('Date Created'), required=True, readonly=True, |
1247 | description=_("The date on which this mirror was registered."))) |
1248 | + country_dns_mirror = exported(Bool( |
1249 | + title=_('Country DNS Mirror'), |
1250 | + description=_('Whether this is a country mirror in DNS.'), |
1251 | + required=False, readonly=True, default=False)) |
1252 | + |
1253 | + reviewer = exported(PublicPersonChoice( |
1254 | + title=_('Reviewer'), required=False, readonly=True, |
1255 | + vocabulary='ValidPersonOrTeam', description=_( |
1256 | + "The person who last reviewed this mirror."))) |
1257 | + date_reviewed = exported(Datetime( |
1258 | + title=_('Date reviewed'), required=False, readonly=True, |
1259 | + description=_( |
1260 | + "The date on which this mirror was last reviewed by a mirror " |
1261 | + "admin."))) |
1262 | + |
1263 | + official_candidate = exported(Bool( |
1264 | + title=_('Apply to be an official mirror of this distribution'), |
1265 | + required=False, readonly=False, default=True)) |
1266 | + whiteboard = exported(Whiteboard( |
1267 | + title=_('Whiteboard'), required=False, readonly=False, |
1268 | + description=_("Notes on the current status of the mirror (only " |
1269 | + "visible to admins and the mirror's registrant)."))) |
1270 | + |
1271 | + @export_read_operation() |
1272 | + def canTransitionToCountryMirror(): |
1273 | + """Verify if a mirror can be set as a country mirror or return |
1274 | + False.""" |
1275 | + |
1276 | + @mutator_for(country_dns_mirror) |
1277 | + @operation_parameters(country_dns_mirror=copy_field(country_dns_mirror)) |
1278 | + @export_write_operation() |
1279 | + def transitionToCountryMirror(country_dns_mirror): |
1280 | + """Method run on changing country_dns_mirror.""" |
1281 | |
1282 | @invariant |
1283 | def mirrorMustHaveHTTPOrFTPURL(mirror): |
1284 | @@ -521,10 +574,6 @@ |
1285 | Sources.gz file refer to and the path to the file itself. |
1286 | """ |
1287 | |
1288 | -class IDistributionMirror(IDistributionMirrorAdminRestricted, |
1289 | - IDistributionMirrorEditRestricted, IDistributionMirrorPublic): |
1290 | - """A mirror of a given distribution.""" |
1291 | - export_as_webservice_entry() |
1292 | |
1293 | |
1294 | class UnableToFetchCDImageFileList(Exception): |
1295 | |
1296 | === modified file 'lib/lp/registry/model/distribution.py' |
1297 | --- lib/lp/registry/model/distribution.py 2010-03-24 02:53:42 +0000 |
1298 | +++ lib/lp/registry/model/distribution.py 2010-04-05 20:44:34 +0000 |
1299 | @@ -402,6 +402,17 @@ |
1300 | """See `IDistribution`.""" |
1301 | return DistributionMirror.selectOneBy(distribution=self, name=name) |
1302 | |
1303 | + def getCountryMirror(self, country, mirror_type): |
1304 | + """See `IDistribution`.""" |
1305 | + store = Store.of(self) |
1306 | + results = store.find( |
1307 | + DistributionMirror, |
1308 | + DistributionMirror.distribution == self, |
1309 | + DistributionMirror.country == country, |
1310 | + DistributionMirror.content == mirror_type, |
1311 | + DistributionMirror.country_dns_mirror == True) |
1312 | + return results.one() |
1313 | + |
1314 | def newMirror(self, owner, speed, country, content, displayname=None, |
1315 | description=None, http_base_url=None, |
1316 | ftp_base_url=None, rsync_base_url=None, |
1317 | |
1318 | === modified file 'lib/lp/registry/model/distributionmirror.py' |
1319 | --- lib/lp/registry/model/distributionmirror.py 2010-03-23 20:42:23 +0000 |
1320 | +++ lib/lp/registry/model/distributionmirror.py 2010-04-05 20:44:34 +0000 |
1321 | @@ -45,9 +45,11 @@ |
1322 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1323 | from lp.soyuz.interfaces.publishing import PackagePublishingStatus |
1324 | from lp.registry.interfaces.distributionmirror import ( |
1325 | + CannotTransitionToCountryMirror, CountryMirrorAlreadySet, |
1326 | IDistributionMirror, IDistributionMirrorSet, IMirrorCDImageDistroSeries, |
1327 | IMirrorDistroArchSeries, IMirrorDistroSeriesSource, IMirrorProbeRecord, |
1328 | - MirrorContent, MirrorFreshness, MirrorSpeed, MirrorStatus, PROBE_INTERVAL) |
1329 | + MirrorContent, MirrorFreshness, MirrorHasNoHTTPURL, MirrorNotOfficial, |
1330 | + MirrorNotProbed, MirrorSpeed, MirrorStatus, PROBE_INTERVAL) |
1331 | from lp.registry.interfaces.distroseries import IDistroSeries |
1332 | from lp.registry.interfaces.sourcepackage import SourcePackageFileType |
1333 | from canonical.launchpad.mail import simple_sendmail, format_address |
1334 | @@ -100,6 +102,8 @@ |
1335 | date_reviewed = UtcDateTimeCol(default=None) |
1336 | whiteboard = StringCol( |
1337 | notNull=False, default=None) |
1338 | + country_dns_mirror = BoolCol( |
1339 | + notNull=True, default=False) |
1340 | |
1341 | @property |
1342 | def base_url(self): |
1343 | @@ -145,6 +149,64 @@ |
1344 | "This mirror has been probed and thus can't be removed.") |
1345 | SQLBase.destroySelf(self) |
1346 | |
1347 | + def verifyTransitionToCountryMirror(self): |
1348 | + """Verify that a mirror can be set as a country mirror. |
1349 | + |
1350 | + Return True if valid, otherwise raise a subclass of |
1351 | + CannotTransitionToCountryMirror. |
1352 | + """ |
1353 | + |
1354 | + current_country_mirror = self.distribution.getCountryMirror( |
1355 | + self.country, self.content) |
1356 | + |
1357 | + if current_country_mirror is not None: |
1358 | + # Country already has a country mirror. |
1359 | + raise CountryMirrorAlreadySet( |
1360 | + "%s already has a country %s mirror set." % ( |
1361 | + self.country.name, self.content)) |
1362 | + |
1363 | + if not self.isOfficial(): |
1364 | + # Only official mirrors may be set as country mirrors. |
1365 | + raise MirrorNotOfficial( |
1366 | + "This mirror may not be set as a country mirror as it is not " |
1367 | + "an official mirror.") |
1368 | + |
1369 | + if self.http_base_url is None: |
1370 | + # Country mirrors must have HTTP URLs set. |
1371 | + raise MirrorHasNoHTTPURL( |
1372 | + "This mirror may not be set as a country mirror as it does " |
1373 | + "not have an HTTP URL set.") |
1374 | + |
1375 | + if not self.last_probe_record: |
1376 | + # Only mirrors which have been probed may be set as country |
1377 | + # mirrors. |
1378 | + raise MirrorNotProbed( |
1379 | + "This mirror may not be set as a country mirror as it has " |
1380 | + "not been probed.") |
1381 | + |
1382 | + # Verification done. |
1383 | + return True |
1384 | + |
1385 | + def canTransitionToCountryMirror(self): |
1386 | + """See `IDistributionMirror`.""" |
1387 | + try: |
1388 | + return self.verifyTransitionToCountryMirror() |
1389 | + except CannotTransitionToCountryMirror: |
1390 | + return False |
1391 | + |
1392 | + def transitionToCountryMirror(self, country_dns_mirror): |
1393 | + """See `IDistributionMirror`.""" |
1394 | + |
1395 | + # country_dns_mirror has not been changed, do nothing. |
1396 | + if self.country_dns_mirror == country_dns_mirror: |
1397 | + return |
1398 | + |
1399 | + # Environment sanity checks. |
1400 | + if country_dns_mirror: |
1401 | + self.verifyTransitionToCountryMirror() |
1402 | + |
1403 | + self.country_dns_mirror = country_dns_mirror |
1404 | + |
1405 | def getOverallFreshness(self): |
1406 | """See IDistributionMirror""" |
1407 | # XXX Guilherme Salgado 2006-08-16: |
1408 | |
1409 | === modified file 'lib/lp/registry/model/person.py' |
1410 | --- lib/lp/registry/model/person.py 2010-03-24 23:19:52 +0000 |
1411 | +++ lib/lp/registry/model/person.py 2010-04-05 20:44:34 +0000 |
1412 | @@ -124,7 +124,7 @@ |
1413 | TeamMembershipStatus) |
1414 | from lp.registry.interfaces.wikiname import IWikiName, IWikiNameSet |
1415 | from canonical.launchpad.webapp.interfaces import ( |
1416 | - AUTH_STORE, ILaunchBag, IStoreSelector, MASTER_FLAVOR) |
1417 | + ILaunchBag, IStoreSelector, MASTER_FLAVOR) |
1418 | |
1419 | from lp.soyuz.model.archive import Archive |
1420 | from lp.registry.model.codeofconduct import SignedCodeOfConduct |
1421 | @@ -263,22 +263,7 @@ |
1422 | return '<Person at 0x%x %s (%s)>' % ( |
1423 | id(self), self.name, self.displayname) |
1424 | |
1425 | - def _sync_displayname(self, attr, value): |
1426 | - """Update any related Account.displayname. |
1427 | - |
1428 | - We can't do this in a DB trigger as soon the Account table will |
1429 | - in a separate database to the Person table. |
1430 | - """ |
1431 | - if self.accountID is not None: |
1432 | - auth_store = getUtility(IStoreSelector).get( |
1433 | - AUTH_STORE, MASTER_FLAVOR) |
1434 | - account = auth_store.get(Account, self.accountID) |
1435 | - if account.displayname != value: |
1436 | - account.displayname = value |
1437 | - return value |
1438 | - |
1439 | - displayname = StringCol(dbName='displayname', notNull=True, |
1440 | - storm_validator=_sync_displayname) |
1441 | + displayname = StringCol(dbName='displayname', notNull=True) |
1442 | |
1443 | teamdescription = StringCol(dbName='teamdescription', default=None) |
1444 | homepage_content = StringCol(default=None) |
1445 | |
1446 | === modified file 'lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt' |
1447 | --- lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt 2009-12-10 23:32:13 +0000 |
1448 | +++ lib/lp/registry/stories/distributionmirror/xx-distribution-mirrors.txt 2010-04-05 20:44:34 +0000 |
1449 | @@ -40,13 +40,14 @@ |
1450 | >>> print browser.title |
1451 | Mirrors of Ubuntu Linux... |
1452 | >>> print_mirrors_by_countries(browser.contents) |
1453 | - Antarctica: |
1454 | - [(u'Archive-mirror2', u'http', u'128 Kbps', u'Six hours behind')] |
1455 | + Antarctica: [(u'Archive-mirror2', u'http', u'128 Kbps', |
1456 | + u'Six hours behind')] |
1457 | France: |
1458 | [(u'Archive-404-mirror', u'http', u'512 Kbps', u'Last update unknown'), |
1459 | - (u'Archive-mirror', u'http', u'128 Kbps', u'Last update unknown')] |
1460 | - United Kingdom: |
1461 | - [(u'Canonical-archive', u'http', u'100 Mbps', u'Last update unknown')] |
1462 | + (u'Archive-mirror', u'http', u'128 Kbps', u'Last update unknown')] |
1463 | + United Kingdom: [(u'Canonical-archive', u'http', u'100 Mbps', |
1464 | + u'Last update unknown')] |
1465 | + |
1466 | >>> find_tags_by_class(browser.contents, 'distromirrorstatusSIXHOURSBEHIND') |
1467 | [<span class="distromirrorstatusSIXHOURSBEHIND">Six hours behind</span>] |
1468 | >>> find_tags_by_class(browser.contents, 'distromirrorstatusUNKNOWN')[0] |
1469 | @@ -59,13 +60,12 @@ |
1470 | >>> browser.url |
1471 | 'http://launchpad.dev/ubuntu/+cdmirrors' |
1472 | >>> print_mirrors_by_countries(browser.contents) |
1473 | - France: |
1474 | - [(u'Releases-mirror', u'http', u'2 Mbps'), |
1475 | + France: |
1476 | + [(u'Releases-mirror', u'http', u'2 Mbps'), |
1477 | (u'Unreachable-mirror', u'http', u'512 Kbps')] |
1478 | - Germany: |
1479 | - [(u'Releases-mirror2', u'http', u'2 Mbps')] |
1480 | - United Kingdom: |
1481 | - [(u'Canonical-releases', u'http', u'100 Mbps')] |
1482 | + Germany: [(u'Releases-mirror2', u'http', u'2 Mbps')] |
1483 | + United Kingdom: [(u'Canonical-releases', u'http', u'100 Mbps')] |
1484 | + |
1485 | |
1486 | === Disabled mirrors === |
1487 | |
1488 | |
1489 | === modified file 'lib/lp/registry/stories/person/xx-admin-person-review.txt' |
1490 | --- lib/lp/registry/stories/person/xx-admin-person-review.txt 2010-01-30 22:39:54 +0000 |
1491 | +++ lib/lp/registry/stories/person/xx-admin-person-review.txt 2010-04-05 20:44:34 +0000 |
1492 | @@ -63,7 +63,7 @@ |
1493 | >>> print admin_browser.title |
1494 | The one and only Salgado does not use Launchpad |
1495 | >>> print get_feedback_messages(admin_browser.contents)[0] |
1496 | - The account "The one and only Salgado" has been suspended. |
1497 | + The account "Guilherme Salgado" has been suspended. |
1498 | |
1499 | The admin can see the account information of a user that does not use |
1500 | Launchpad, and can change the account too. Note that all pages that belong |
1501 | |
1502 | === modified file 'lib/lp/registry/stories/webservice/xx-distribution-mirror.txt' |
1503 | --- lib/lp/registry/stories/webservice/xx-distribution-mirror.txt 2010-02-23 19:40:45 +0000 |
1504 | +++ lib/lp/registry/stories/webservice/xx-distribution-mirror.txt 2010-04-05 20:44:34 +0000 |
1505 | @@ -8,10 +8,12 @@ |
1506 | >>> distro = distros['entries'][0] |
1507 | >>> ubuntu = webservice.get(distro['self_link']).jsonBody() |
1508 | >>> ubuntu_archive_mirrors = webservice.get(ubuntu['archive_mirrors_collection_link']).jsonBody() |
1509 | - >>> canonical_archive = ubuntu_archive_mirrors['entries'][0] |
1510 | - >>> canonical_archive_json = webservice.get(canonical_archive['self_link']).jsonBody() |
1511 | - >>> pprint_entry(canonical_archive_json) |
1512 | + >>> canonical_archive = webservice.named_get( |
1513 | + ... ubuntu['self_link'], 'getMirrorByName', |
1514 | + ... name='canonical-archive').jsonBody() |
1515 | + >>> pprint_entry(canonical_archive) |
1516 | content: u'Archive' |
1517 | + country_dns_mirror: False |
1518 | country_link: u'http://.../+countries/GB' |
1519 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1520 | date_reviewed: None |
1521 | @@ -39,6 +41,7 @@ |
1522 | >>> canonical_releases_json = webservice.get(canonical_releases['self_link']).jsonBody() |
1523 | >>> pprint_entry(canonical_releases_json) |
1524 | content: u'CD Image' |
1525 | + country_dns_mirror: False |
1526 | country_link: u'http://.../+countries/GB' |
1527 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1528 | date_reviewed: None |
1529 | @@ -73,12 +76,12 @@ |
1530 | >>> karl_db = getUtility(IPersonSet).getByName('karl') |
1531 | >>> test_db = getUtility(IPersonSet).getByName('name12') |
1532 | >>> no_priv_db = getUtility(IPersonSet).getByName('no-priv') |
1533 | - >>> karl_webservice = webservice_for_person(karl_db, |
1534 | - ... permission=OAuthPermission.WRITE_PUBLIC) |
1535 | - >>> test_webservice = webservice_for_person(test_db, |
1536 | - ... permission=OAuthPermission.WRITE_PUBLIC) |
1537 | - >>> no_priv_webservice = webservice_for_person(no_priv_db, |
1538 | - ... permission=OAuthPermission.READ_PUBLIC) |
1539 | + >>> karl_webservice = webservice_for_person( |
1540 | + ... karl_db, permission=OAuthPermission.WRITE_PUBLIC) |
1541 | + >>> test_webservice = webservice_for_person( |
1542 | + ... test_db, permission=OAuthPermission.WRITE_PUBLIC) |
1543 | + >>> no_priv_webservice = webservice_for_person( |
1544 | + ... no_priv_db, permission=OAuthPermission.READ_PUBLIC) |
1545 | >>> logout() |
1546 | |
1547 | Ensure that anonymous API sessions can view mirror listings; archive/releases. |
1548 | @@ -97,7 +100,9 @@ |
1549 | |
1550 | One must have special permissions to access certain attributes: |
1551 | |
1552 | - >>> archive_404_mirror = ubuntu_archive_mirrors['entries'][1] |
1553 | + >>> archive_404_mirror = webservice.named_get( |
1554 | + ... ubuntu['self_link'], 'getMirrorByName', |
1555 | + ... name="archive-404-mirror").jsonBody() |
1556 | >>> response = no_priv_webservice.get( |
1557 | ... archive_404_mirror['self_link']).jsonBody() |
1558 | >>> pprint_entry(response) |
1559 | @@ -128,6 +133,7 @@ |
1560 | ... archive_404_mirror['self_link']).jsonBody() |
1561 | >>> pprint_entry(response) |
1562 | content: u'Archive' |
1563 | + country_dns_mirror: False |
1564 | country_link: u'http://.../+countries/FR' |
1565 | date_created: u'2006-10-16T18:31:43.438573+00:00' |
1566 | date_reviewed: None |
1567 | @@ -209,6 +215,7 @@ |
1568 | ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody() |
1569 | >>> pprint_entry(response) |
1570 | content: u'CD Image' |
1571 | + country_dns_mirror: False |
1572 | country_link: u'http://.../+countries/GL' |
1573 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1574 | date_reviewed: None |
1575 | @@ -244,7 +251,9 @@ |
1576 | "getOverallFreshness" returns the freshness of the mirror determined by the |
1577 | mirror prober from the mirror's last probe. |
1578 | |
1579 | - >>> releases_mirror2 = ubuntu_cd_mirrors['entries'][2] |
1580 | + >>> releases_mirror2 = webservice.named_get( |
1581 | + ... ubuntu['self_link'], 'getMirrorByName', |
1582 | + ... name='releases-mirror2').jsonBody() |
1583 | >>> freshness = webservice.named_get(releases_mirror2['self_link'], |
1584 | ... 'getOverallFreshness').jsonBody() |
1585 | >>> print freshness |
1586 | |
1587 | === modified file 'lib/lp/registry/stories/webservice/xx-distribution.txt' |
1588 | --- lib/lp/registry/stories/webservice/xx-distribution.txt 2010-02-23 17:36:27 +0000 |
1589 | +++ lib/lp/registry/stories/webservice/xx-distribution.txt 2010-04-05 20:44:34 +0000 |
1590 | @@ -123,6 +123,7 @@ |
1591 | ... name='canonical-releases').jsonBody() |
1592 | >>> pprint_entry(canonical_releases) |
1593 | content: u'CD Image' |
1594 | + country_dns_mirror: False |
1595 | country_link: u'http://.../+countries/GB' |
1596 | date_created: u'2006-10-16T18:31:43.434567+00:00' |
1597 | date_reviewed: None |
1598 | @@ -142,3 +143,76 @@ |
1599 | speed: u'100 Mbps' |
1600 | status: u'Official' |
1601 | whiteboard: None |
1602 | + |
1603 | +"getCountryMirror" returns the country DNS mirror for a given country; |
1604 | +returning None if there isn't one. |
1605 | + |
1606 | + >>> # Prepare stuff. |
1607 | + >>> from lp.registry.interfaces.distribution import IDistributionSet |
1608 | + >>> from zope.component import getUtility |
1609 | + >>> from canonical.launchpad.testing.pages import webservice_for_person |
1610 | + >>> from canonical.launchpad.webapp.interfaces import OAuthPermission |
1611 | + >>> from lp.registry.interfaces.person import IPersonSet |
1612 | + >>> from simplejson import dumps |
1613 | + |
1614 | + >>> login('admin@canonical.com') |
1615 | + >>> ubuntu_distro = getUtility(IDistributionSet).getByName('ubuntu') |
1616 | + >>> showa_station = factory.makeMirror(ubuntu_distro, |
1617 | + ... "Showa Station", country=9, |
1618 | + ... http_url="http://mirror.showa.antarctica.org/ubuntu", |
1619 | + ... official_candidate=True) |
1620 | + >>> showa_station_log = factory.makeMirrorProbeRecord(showa_station) |
1621 | + >>> logout() |
1622 | + |
1623 | + >>> login(ANONYMOUS) |
1624 | + >>> karl_db = getUtility(IPersonSet).getByName('karl') |
1625 | + >>> karl_webservice = webservice_for_person(karl_db, |
1626 | + ... permission=OAuthPermission.WRITE_PUBLIC) |
1627 | + >>> logout() |
1628 | + |
1629 | + >>> # Mark new mirror as official and a country mirror. |
1630 | + >>> patch = { |
1631 | + ... u'status': 'Official', |
1632 | + ... u'country_dns_mirror': True |
1633 | + ... } |
1634 | + |
1635 | + >>> antarctica_patch_target = webservice.named_get( |
1636 | + ... ubuntu['self_link'], 'getMirrorByName', |
1637 | + ... name='mirror.showa.antarctica.org-archive').jsonBody() |
1638 | + ... ) |
1639 | + |
1640 | + >>> response = karl_webservice.patch( |
1641 | + ... antarctica_patch_target['self_link'], 'application/json', |
1642 | + ... dumps(patch)) |
1643 | + |
1644 | + >>> antarctica = webservice.get("/+countries/AQ").jsonBody() |
1645 | + >>> antarctica_country_mirror_archive = webservice.named_get( |
1646 | + ... ubuntu['self_link'], 'getCountryMirror', |
1647 | + ... country=antarctica['self_link'], |
1648 | + ... mirror_type="Archive").jsonBody() |
1649 | + >>> pprint_entry(antarctica_country_mirror_archive) |
1650 | + content: u'Archive' |
1651 | + country_dns_mirror: True |
1652 | + country_link: u'http://.../+countries/AQ' |
1653 | + ... |
1654 | + |
1655 | + >>> uk = webservice.get("/+countries/GB").jsonBody() |
1656 | + >>> uk_country_mirror_archive = webservice.named_get( |
1657 | + ... ubuntu['self_link'], 'getCountryMirror', |
1658 | + ... country=uk['self_link'], |
1659 | + ... mirror_type="Archive") |
1660 | + >>> print uk_country_mirror_archive.jsonBody() |
1661 | + None |
1662 | + |
1663 | +For "getCountryMirror", the mirror_type parameter must be "Archive" or |
1664 | +"CD Images": |
1665 | + |
1666 | + >>> uk_country_mirror_archive = webservice.named_get( |
1667 | + ... ubuntu['self_link'], 'getCountryMirror', |
1668 | + ... country=uk['self_link'], |
1669 | + ... mirror_type="Bogus") |
1670 | + >>> print uk_country_mirror_archive.jsonBody() |
1671 | + Traceback (most recent call last): |
1672 | + ... |
1673 | + ValueError: mirror_type: Invalid value "Bogus". Acceptable values are: |
1674 | + Archive, CD Image |
1675 | |
1676 | === modified file 'lib/lp/registry/tests/test_distributionmirror.py' |
1677 | --- lib/lp/registry/tests/test_distributionmirror.py 2009-10-26 18:40:04 +0000 |
1678 | +++ lib/lp/registry/tests/test_distributionmirror.py 2010-04-05 20:44:34 +0000 |
1679 | @@ -3,7 +3,6 @@ |
1680 | |
1681 | __metaclass__ = type |
1682 | |
1683 | -from StringIO import StringIO |
1684 | import unittest |
1685 | |
1686 | import transaction |
1687 | @@ -17,19 +16,19 @@ |
1688 | from lp.registry.interfaces.distributionmirror import ( |
1689 | IDistributionMirrorSet, MirrorContent, MirrorFreshness) |
1690 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
1691 | -from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet |
1692 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
1693 | from lp.registry.interfaces.distribution import IDistributionSet |
1694 | from lp.services.mail import stub |
1695 | +from lp.testing.factory import LaunchpadObjectFactory |
1696 | |
1697 | from canonical.testing import LaunchpadFunctionalLayer |
1698 | |
1699 | - |
1700 | class TestDistributionMirror(unittest.TestCase): |
1701 | layer = LaunchpadFunctionalLayer |
1702 | |
1703 | def setUp(self): |
1704 | login('test@canonical.com') |
1705 | + self.factory = LaunchpadObjectFactory() |
1706 | mirrorset = getUtility(IDistributionMirrorSet) |
1707 | self.cdimage_mirror = getUtility(IDistributionMirrorSet).getByName( |
1708 | 'releases-mirror') |
1709 | @@ -132,15 +131,6 @@ |
1710 | self.archive_mirror.getOverallFreshness(), |
1711 | MirrorFreshness.TWODAYSBEHIND) |
1712 | |
1713 | - def _create_probe_record(self, mirror): |
1714 | - log_file = StringIO() |
1715 | - log_file.write("Fake probe, nothing useful here.") |
1716 | - log_file.seek(0) |
1717 | - library_alias = getUtility(ILibraryFileAliasSet).create( |
1718 | - name='foo', size=len(log_file.getvalue()), |
1719 | - file=log_file, contentType='text/plain') |
1720 | - proberecord = mirror.newProbeRecord(library_alias) |
1721 | - |
1722 | def test_disabling_mirror_and_notifying_owner(self): |
1723 | login('karl@canonical.com') |
1724 | |
1725 | @@ -148,7 +138,7 @@ |
1726 | # If a mirror has been probed only once, the owner will always be |
1727 | # notified when it's disabled --it doesn't matter whether it was |
1728 | # previously enabled or disabled. |
1729 | - self._create_probe_record(mirror) |
1730 | + self.factory.makeMirrorProbeRecord(mirror) |
1731 | self.failUnless(mirror.enabled) |
1732 | log = 'Got a 404 on http://foo/baz' |
1733 | mirror.disable(notify_owner=True, log=log) |
1734 | @@ -166,7 +156,7 @@ |
1735 | |
1736 | # For mirrors that have been probed more than once, we'll only notify |
1737 | # the owner if the mirror was previously enabled. |
1738 | - self._create_probe_record(mirror) |
1739 | + self.factory.makeMirrorProbeRecord(mirror) |
1740 | mirror.enabled = True |
1741 | mirror.disable(notify_owner=True, log=log) |
1742 | # A notification was sent to the owner and other to the mirror admins. |
1743 | |
1744 | === modified file 'lib/lp/registry/tests/test_personset.py' |
1745 | --- lib/lp/registry/tests/test_personset.py 2010-03-11 20:54:36 +0000 |
1746 | +++ lib/lp/registry/tests/test_personset.py 2010-04-05 20:44:34 +0000 |
1747 | @@ -99,23 +99,19 @@ |
1748 | # Person in question. |
1749 | |
1750 | # Create a testing `Account` and a testing `Person` directly, |
1751 | - # linked. However the `Account` email is not linked to the |
1752 | - # `Person`. |
1753 | + # linked. |
1754 | testing_account = self.factory.makeAccount( |
1755 | self.displayname, email=self.email_address) |
1756 | testing_person = removeSecurityProxy( |
1757 | testing_account).createPerson(self.rationale) |
1758 | - self.assertIs(None, testing_account.preferredemail.person) |
1759 | - self.assertIs(None, testing_person.preferredemail) |
1760 | + self.assertEqual( |
1761 | + testing_person, testing_account.preferredemail.person) |
1762 | |
1763 | + # Since there's an existing Person for the given email address, |
1764 | + # IPersonSet.ensurePerson() will just return it. |
1765 | ensured_person = self.person_set.ensurePerson( |
1766 | self.email_address, self.displayname, self.rationale) |
1767 | - |
1768 | - # The existing Person was retrieved and the Account |
1769 | - # 'preferredemail' is also bound to the existing Person. |
1770 | - self.assertEquals(testing_person.id, ensured_person.id) |
1771 | - self.assertEquals(testing_account.preferredemail.id, |
1772 | - ensured_person.preferredemail.id) |
1773 | + self.assertEqual(testing_person, ensured_person) |
1774 | |
1775 | |
1776 | class TestPersonSetMerge(TestCaseWithFactory): |
1777 | |
1778 | === modified file 'lib/lp/testing/factory.py' |
1779 | --- lib/lp/testing/factory.py 2010-03-26 21:03:30 +0000 |
1780 | +++ lib/lp/testing/factory.py 2010-04-05 20:44:34 +0000 |
1781 | @@ -1992,8 +1992,22 @@ |
1782 | team_list = self.makeMailingList(team, owner) |
1783 | return team, team_list |
1784 | |
1785 | + def makeMirrorProbeRecord(self, mirror): |
1786 | + """Create a probe record for a mirror of a distribution.""" |
1787 | + log_file = StringIO() |
1788 | + log_file.write("Fake probe, nothing useful here.") |
1789 | + log_file.seek(0) |
1790 | + |
1791 | + library_alias = getUtility(ILibraryFileAliasSet).create( |
1792 | + name='foo', size=len(log_file.getvalue()), |
1793 | + file=log_file, contentType='text/plain') |
1794 | + |
1795 | + proberecord = mirror.newProbeRecord(library_alias) |
1796 | + return proberecord |
1797 | + |
1798 | def makeMirror(self, distribution, displayname, country=None, |
1799 | - http_url=None, ftp_url=None, rsync_url=None): |
1800 | + http_url=None, ftp_url=None, rsync_url=None, |
1801 | + official_candidate=False): |
1802 | """Create a mirror for the distribution.""" |
1803 | # If no URL is specified create an HTTP URL. |
1804 | if http_url is None and ftp_url is None and rsync_url is None: |
1805 | @@ -2012,7 +2026,7 @@ |
1806 | http_base_url=http_url, |
1807 | ftp_base_url=ftp_url, |
1808 | rsync_base_url=rsync_url, |
1809 | - official_candidate=False) |
1810 | + official_candidate=official_candidate) |
1811 | return mirror |
1812 | |
1813 | def makeUniqueRFC822MsgId(self): |
1814 | |
1815 | === modified file 'lib/lp/testopenid/browser/server.py' |
1816 | --- lib/lp/testopenid/browser/server.py 2010-02-24 12:52:08 +0000 |
1817 | +++ lib/lp/testopenid/browser/server.py 2010-04-05 20:44:34 +0000 |
1818 | @@ -6,7 +6,7 @@ |
1819 | __all__ = [ |
1820 | 'PersistentIdentityView', |
1821 | 'TestOpenIDApplicationNavigation', |
1822 | - 'TestOpenIDIndexView' |
1823 | + 'TestOpenIDIndexView', |
1824 | 'TestOpenIDLoginView', |
1825 | 'TestOpenIDRootUrlData', |
1826 | 'TestOpenIDView', |
1827 | @@ -21,6 +21,7 @@ |
1828 | from zope.security.proxy import isinstance as zisinstance |
1829 | from zope.session.interfaces import ISession |
1830 | |
1831 | +from openid import oidutil |
1832 | from openid.server.server import CheckIDRequest, Server |
1833 | from openid.store.memstore import MemoryStore |
1834 | |
1835 | @@ -47,6 +48,10 @@ |
1836 | openid_store = MemoryStore() |
1837 | |
1838 | |
1839 | +# Shut up noisy OpenID library |
1840 | +oidutil.log = lambda message, level=0: None |
1841 | + |
1842 | + |
1843 | class TestOpenIDRootUrlData: |
1844 | """`ICanonicalUrlData` for the test OpenID provider.""" |
1845 | |
1846 | |
1847 | === modified file 'utilities/sourcedeps.conf' |
1848 | --- utilities/sourcedeps.conf 2010-03-26 17:53:41 +0000 |
1849 | +++ utilities/sourcedeps.conf 2010-04-05 20:44:34 +0000 |
1850 | @@ -15,5 +15,4 @@ |
1851 | subunit lp:~launchpad-pqm/subunit/trunk;revno=61 |
1852 | subvertpy lp:~launchpad-pqm/subvertpy/trunk;revno=2040 |
1853 | testresources lp:~launchpad-pqm/testresources/dev;revno=16 |
1854 | -canonical-identity-provider lp:~launchpad-pqm/canonical-identity-provider/trunk;revno=8903 optional |
1855 | shipit lp:~launchpad-pqm/shipit/trunk;revno=8903 optional |
This branch exports a bug's message_count in the API.