Merge lp:~frankban/launchpad/setuplxc-remove-sleep into lp:~frankban/launchpad/more-integration

Proposed by Francesco Banconi
Status: Rejected
Rejected by: Francesco Banconi
Proposed branch: lp:~frankban/launchpad/setuplxc-remove-sleep
Merge into: lp:~frankban/launchpad/more-integration
Diff against target: 1962 lines (+552/-406)
34 files modified
database/schema/security.cfg (+1/-43)
lib/canonical/launchpad/icing/css/colours.css (+0/-20)
lib/canonical/launchpad/icing/shipit.css (+0/-150)
lib/lp/app/templates/base-layout-macros.pt (+16/-17)
lib/lp/app/templates/base-layout.pt (+15/-28)
lib/lp/app/templates/root-index.pt (+1/-1)
lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt (+10/-6)
lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt (+8/-0)
lib/lp/registry/browser/pillar.py (+3/-3)
lib/lp/registry/browser/tests/test_pillar_sharing.py (+1/-1)
lib/lp/registry/interfaces/accesspolicy.py (+10/-0)
lib/lp/registry/interfaces/accesspolicyservice.py (+11/-10)
lib/lp/registry/javascript/disclosure/observerpicker.js (+13/-13)
lib/lp/registry/javascript/disclosure/observertable.js (+14/-12)
lib/lp/registry/javascript/disclosure/pillarsharingview.js (+36/-8)
lib/lp/registry/javascript/disclosure/tests/test_observerpicker.js (+8/-7)
lib/lp/registry/javascript/disclosure/tests/test_observertable.js (+5/-3)
lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.html (+4/-0)
lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.js (+50/-9)
lib/lp/registry/model/accesspolicy.py (+15/-0)
lib/lp/registry/services/accesspolicyservice.py (+48/-21)
lib/lp/registry/services/tests/test_accesspolicyservice.py (+87/-17)
lib/lp/registry/tests/test_accesspolicy.py (+41/-0)
lib/lp/scripts/garbo.py (+72/-1)
lib/lp/scripts/tests/test_garbo.py (+22/-0)
lib/lp/services/webapp/adapter.py (+3/-3)
lib/lp/services/webapp/errorlog.py (+8/-10)
lib/lp/services/webapp/tests/test_user_requested_oops.py (+19/-12)
lib/lp/soyuz/stories/soyuz/xx-builder-page.txt (+4/-1)
lib/lp/soyuz/stories/soyuz/xx-buildfarm-index.txt (+2/-0)
lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt (+2/-1)
lib/lp/soyuz/stories/soyuz/xx-private-builds.txt (+5/-1)
lib/lp/translations/stories/buildfarm/xx-build-summary.txt (+1/-0)
utilities/setuplxc.py (+17/-8)
To merge this branch: bzr merge lp:~frankban/launchpad/setuplxc-remove-sleep
Reviewer Review Type Date Requested Status
Gary Poster (community) Approve
Review via email: mp+96422@code.launchpad.net

Description of the change

== Changes ==

- Replaced *sleep 30* with an ssh/sleep loop.
- Removed unused function argument in *create_scripts*.

== Notes ==

The diff is against the merge of latest Benji's branch and devel.

To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :

Thank you!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'database/schema/security.cfg'
2--- database/schema/security.cfg 2012-03-06 05:23:43 +0000
3+++ database/schema/security.cfg 2012-03-08 16:34:46 +0000
4@@ -140,7 +140,6 @@
5 public.bugattachment = SELECT, INSERT, UPDATE, DELETE
6 public.bugbranch = SELECT, INSERT, UPDATE, DELETE
7 public.bugcve = SELECT, INSERT, DELETE
8-public.bugjob = SELECT, INSERT, UPDATE, DELETE
9 public.bugnomination = SELECT, UPDATE
10 public.bugnotification = SELECT, INSERT, UPDATE, DELETE
11 public.bugnotificationattachment = SELECT, INSERT
12@@ -564,7 +563,6 @@
13 public.bugactivity = SELECT, INSERT
14 public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE
15 public.bugcve = SELECT, INSERT
16-public.bugjob = SELECT, INSERT
17 public.bugmessage = SELECT, INSERT, UPDATE
18 public.bugmute = SELECT
19 public.bugnomination = SELECT
20@@ -1124,7 +1122,6 @@
21 public.bug = SELECT, INSERT, UPDATE
22 public.bugactivity = SELECT, INSERT, UPDATE
23 public.bugattachment = SELECT, INSERT, UPDATE
24-public.bugjob = SELECT, INSERT
25 public.bugmessage = SELECT, INSERT, UPDATE
26 public.bugmute = SELECT, INSERT, UPDATE, DELETE
27 public.bugnomination = SELECT, INSERT, UPDATE, DELETE
28@@ -1273,7 +1270,6 @@
29 public.bugactivity = SELECT, INSERT
30 public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE
31 public.bugcve = SELECT, INSERT
32-public.bugjob = SELECT, INSERT
33 public.bugmessage = SELECT, INSERT
34 public.bugmute = SELECT
35 public.bugnomination = SELECT
36@@ -1377,7 +1373,6 @@
37 public.bugactivity = SELECT, INSERT
38 public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE
39 public.bugcve = SELECT, INSERT
40-public.bugjob = SELECT, INSERT
41 public.bugmessage = SELECT, INSERT
42 public.bugmute = SELECT
43 public.bugnomination = SELECT
44@@ -1470,13 +1465,6 @@
45 type=user
46 groups=queued
47
48-[ppad]
49-groups=script
50-public.archive = SELECT
51-public.archivearch = SELECT
52-public.person = SELECT
53-type=user
54-
55 [session]
56 type=user
57
58@@ -1490,7 +1478,6 @@
59 public.bugactivity = SELECT, INSERT
60 public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE
61 public.bugattachment = SELECT
62-public.bugjob = SELECT, INSERT
63 public.bugmessage = SELECT, INSERT
64 public.bugmute = SELECT
65 public.bugnomination = SELECT
66@@ -1641,15 +1628,6 @@
67 public.translationtemplateitem = SELECT
68 type=user
69
70-[oopsprune]
71-groups=script
72-public.bug = SELECT
73-public.bugtask = SELECT
74-public.message = SELECT
75-public.messagechunk = SELECT
76-public.question = SELECT
77-type=user
78-
79 [listteammembers]
80 public.emailaddress = SELECT
81 public.person = SELECT
82@@ -1680,7 +1658,6 @@
83 public.bugattachment = SELECT, INSERT
84 public.bugbranch = SELECT
85 public.bugcve = SELECT, INSERT
86-public.bugjob = SELECT, INSERT
87 public.bugmessage = SELECT, INSERT
88 public.bugmute = SELECT
89 public.bugnomination = SELECT, INSERT, UPDATE
90@@ -1942,7 +1919,6 @@
91 public.account = SELECT, INSERT, UPDATE
92 public.bug = SELECT, INSERT, UPDATE
93 public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE
94-public.bugjob = SELECT, INSERT
95 public.bugmessage = SELECT, INSERT
96 public.bugsubscription = SELECT, INSERT
97 public.bugsubscriptionfilter = SELECT, INSERT
98@@ -2125,13 +2101,9 @@
99 public.wikiname = SELECT, UPDATE
100 type=user
101
102-[weblogstats]
103-public.libraryfilealias = SELECT
104-public.libraryfiledownloadcount = SELECT, INSERT, UPDATE, DELETE
105-type=user
106-
107 [garbo]
108 groups=script,read
109+public.accesspolicy = SELECT, INSERT
110 public.account = SELECT, DELETE
111 public.answercontact = SELECT, DELETE
112 public.branch = SELECT, UPDATE
113@@ -2142,7 +2114,6 @@
114 public.bug = SELECT, UPDATE
115 public.bugaffectsperson = SELECT
116 public.bugattachment = SELECT, DELETE
117-public.bugjob = SELECT, INSERT
118 public.bugmessage = SELECT, UPDATE
119 public.bugnotification = SELECT, DELETE
120 public.bugnotificationfilter = SELECT, DELETE
121@@ -2236,19 +2207,6 @@
122 public.branch = SELECT
123 type=user
124
125-[calculate-bug-heat]
126-groups=script,read
127-public.bug = SELECT, UPDATE
128-public.bugjob = SELECT, DELETE
129-public.distribution = SELECT, UPDATE
130-public.distributionsourcepackage = SELECT, INSERT, UPDATE
131-public.distroseries = SELECT
132-public.job = SELECT, UPDATE, DELETE
133-public.product = SELECT, UPDATE
134-public.productseries = SELECT
135-public.project = SELECT, UPDATE
136-type=user
137-
138 [lagmon]
139 public.update_replication_lag_cache() = EXECUTE
140 type=user
141
142=== modified file 'lib/canonical/launchpad/icing/css/colours.css'
143--- lib/canonical/launchpad/icing/css/colours.css 2012-03-07 00:23:37 +0000
144+++ lib/canonical/launchpad/icing/css/colours.css 2012-03-08 16:34:46 +0000
145@@ -346,23 +346,3 @@
146 .red {
147 color: red;
148 }
149-
150-.accessPolicyPUBLIC, .accessPolicyPUBLIC a {
151- color: green;
152- }
153-
154-.accessPolicyUNEMBARGOEDSECURITY, .accessPolicyUNEMBARGOEDSECURITY a {
155- color: #1f7dff;
156- }
157-
158-.accessPolicyEMBARGOEDSECURITY, .accessPolicyEMBARGOEDSECURITY a {
159- color: #ff4500;
160- }
161-
162-.accessPolicyUSERDATA, .accessPolicyUSERDATA a {
163- color: #800080;
164- }
165-
166-.accessPolicyPROPRIETARY, .accessPolicyPROPRIETARY a {
167- color: red;
168- }
169
170=== removed file 'lib/canonical/launchpad/icing/shipit.css'
171--- lib/canonical/launchpad/icing/shipit.css 2011-12-07 04:57:36 +0000
172+++ lib/canonical/launchpad/icing/shipit.css 1970-01-01 00:00:00 +0000
173@@ -1,150 +0,0 @@
174-/* === Overall presentation === */
175-
176-html {
177- position: relative;
178- font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
179- font-size: 12pt;
180- color: black;
181- background-color: #FFFFFF;
182-}
183-body {margin: 0; padding: 0;}
184-
185-div.body {padding: 0em 1em;}
186-
187-.discreet, .lesser {font-size: small;}
188-
189-/* --- Error, warning, and info messages --- */
190-
191-.error, .warning, .informational {
192- padding: 0.5em;
193-}
194-.message {
195- background: 5px 0.5em no-repeat;
196- font-weight: bold;
197- margin: 1em 0 0.5em;
198- padding: 0.5em 1em 0.5em 30px; /* 30px because the image has a pixel size */
199-}
200-.error .message, .warning .message, .informational .message {
201- margin: 0.25em 0 0;
202- padding: 0 0 0 25px;
203- background: 0 0 no-repeat;
204-}
205-.error {background-color: #f0cccc; color: black;}
206-.error.message, .error .message {
207- background-image: url(/@@/error);
208-}
209-.warning {background-color: #fcdd99; color: black;}
210-.warning.message, .warning .message {
211- background-image: url(/@@/warning);
212-}
213-.informational {background-color: #fcfc99; color: black;}
214-.informational.message, .informational .message {
215- background-image: url(/@@/info);
216-}
217-.debugging {background-color: #666; color: white;}
218-.debugging.message, .debugging .message {
219- background-image: url(/@@/info);
220-}
221-
222-/* --- Form controls --- */
223-
224-form {border: 0; margin: 0; padding: 0;}
225-input, select, textarea {
226- background-color: #fff;
227- color: #656565;
228- font-family: Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
229- margin: 0;
230-}
231-input[type="text"], input[type="submit"], input[type="reset"], textarea {
232- font-size: 1em; /* Forces a readable font size in Safari */
233-}
234-input.urlTextType {width: 100%;}
235-.fieldRequired, .fieldOptional {color: #999;}
236-.formHelp {margin-bottom: 1em;}
237-textarea {width: 100%;}
238-table.form {width: 100%;}
239-
240-/* === Tables === */
241-
242-table.latest th, table.latest td {padding-bottom: 2em;}
243-table.summary {float: right; margin: 0 0 1em 1em;}
244-table.summary caption {font-style: italic; margin-left: 1em;}
245-div.right table.summary {float: none; margin: 1em auto;}
246-table.summary tr {border: dotted #ccc; border-width: 1px 0;}
247-table.summary td {padding-left: 0.5em;}
248-table.summary ul, table.summary ul li {
249- list-style-position: inside;
250- margin: 0;
251- padding: 0;
252-}
253-th, td {padding: 0.25em;}
254-th.icon, td.icon {vertical-align: top; white-space: nowrap; width: 1px;}
255-th.icon.left, td.icon.left {padding-right: 0;}
256-th.icon.right, td.icon.right {padding-left: 0;}
257-th.nowrap, td.nowrap {white-space: nowrap;}
258-tfoot th, table.contributions th {text-align: left;}
259-table.contributions, table.contributions th, table.contributions td {
260- border: 1px solid #e6e6e6;}
261-table.contributions tr.odd {background-color: #fff; color: inherit;}
262-table.contributions tr.even {background-color: #f6f6f6; color: inherit;}
263-table.contributions th, table.contributions td {padding: 0.5em 0.75em;}
264-table.contributions td {vertical-align: middle; width: 16px;}
265-table.headers-right-aligned th {text-align: right;}
266-
267-/* --- Listing tables --- */
268-
269-table.listing {margin: 0; width: 100%;}
270-table.listing, table.listing tbody {border-bottom: 1px solid #d2d2d2;}
271-table.listing thead, table.listing thead th, table.listing tfoot tr {
272- border: 1px solid #d2d2d2;
273- background-color: #fff;
274-}
275-table.listing tfoot td {border: 1px solid #d2d2d2;}
276-table.listing thead td {border: none;}
277-tr.highlight {background-color: #ff9;}
278-table.listing tr.note {font-size: smaller;}
279-tr.amount, td.amount {text-align: right;}
280-table.listing th, table.listing td {padding: 0.25em;}
281-table.listing th {font-size: 1.125em; white-space: nowrap;}
282-table.listing td {border: 1px #d2d2d2; border-style: dotted none none none;}
283-table.listing tr.note td {border-style: none;}
284-table.listing img {vertical-align: middle;}
285-table.listing tr.secondary th, table.listing tr.secondary td {border-top: none;}
286-table.listing table tbody, table.listing table thead,
287-table.listing table thead th, table.listing tfoot tr,
288-table.listing table tfoot td, table.listing table td {
289- border: none;
290-}
291-
292-
293-/* --- Sortable tables --- */
294-
295-table.sortable a.sortheader {
296- color:#666666;
297- font-weight: bold;
298- text-decoration: none;
299- display: block;
300-}
301-table.sortable img.sortarrow {
302- padding-left: 2px;
303-}
304-th.ascending {
305- background-image: url(/@@/arrowDown);
306- background-position: center right;
307- background-repeat: no-repeat;
308-}
309-th.descending {
310- background-image: url(/@@/arrowUp);
311- background-position: center right;
312- background-repeat: no-repeat;
313-}
314-/* Used to indicate a value to be used to sort cells in a row */
315-.sortkey, .revsortkey {display: none;}
316-
317-
318-:link {
319- color: #660000;
320-}
321-:visited {
322- color: #993333;
323-}
324
325=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
326--- lib/lp/app/templates/base-layout-macros.pt 2012-02-15 22:09:43 +0000
327+++ lib/lp/app/templates/base-layout-macros.pt 2012-03-08 16:34:46 +0000
328@@ -474,25 +474,24 @@
329
330
331 <metal:debug-timeline define-macro="debug-timeline">
332- <a name="debug_timeline"
333- id="debug_timeline"
334- class="hidden"
335- >
336- <table
337- tal:condition="request/features/visible_render_time"
338- tal:define="timeline_actions modules/lp.services.webapp.adapter/get_timeline_actions"
339- class="debug-timeline listing"
340- >
341+ <a tal:condition="request/features/visible_render_time"
342+ tal:define="timeline_actions modules/lp.services.webapp.adapter/get_timeline_actions"
343+ id="debug_timeline" class="hidden">
344+ <table class="debug-timeline listing">
345 <thead>
346- <th>Duration</th>
347- <th>Action</th>
348+ <tr>
349+ <th>Duration</th>
350+ <th>Action</th>
351+ </tr>
352 </thead>
353- <tr tal:repeat="action timeline_actions">
354- <td class="amount" tal:content="action/duration/fmt:millisecondduration"/>
355- <td style="font-family: monospace; text-align: left;">
356- <pre class="wrap"><span class="action-category" tal:content="action/category"/>: <span class="action-details" tal:content="action/detail"/></pre>
357- </td>
358- </tr>
359+ <tbody>
360+ <tr tal:repeat="action timeline_actions">
361+ <td class="amount" tal:content="action/duration/fmt:millisecondduration"/>
362+ <td style="font-family: monospace; text-align: left;">
363+ <pre class="wrap"><span class="action-category" tal:content="action/category"/>: <span class="action-details" tal:content="action/detail"/></pre>
364+ </td>
365+ </tr>
366+ </tbody>
367 </table>
368 </a>
369 </metal:debug-timeline>
370
371=== modified file 'lib/lp/app/templates/base-layout.pt'
372--- lib/lp/app/templates/base-layout.pt 2012-02-24 04:54:53 +0000
373+++ lib/lp/app/templates/base-layout.pt 2012-03-08 16:34:46 +0000
374@@ -43,15 +43,6 @@
375 html, body {background-image: url(/@@/demo) !important;}
376 </style>
377
378- <tal:comment condition="nothing">
379- Removing the below <noscript></noscript> snippet makes two tests fail
380- with redirection problems:
381- xx-ubuntu-filebug.txt
382- xx-bug-reporting-tools.txt
383- No time to look into it now, so keeping it in. XXX Danilo 20110715
384- </tal:comment>
385- <noscript></noscript>
386-
387 <tal:view condition="not: view/macro:is-page-contentless">
388 <meta name="description"
389 tal:condition="view/page_description | nothing"
390@@ -176,6 +167,21 @@
391
392 <metal:lp-client-cache
393 use-macro="context/@@+base-layout-macros/lp-client-cache" />
394+ <metal:debug-timeline
395+ use-macro="context/@@+base-layout-macros/debug-timeline" />
396+ <tal:comment
397+ tal:condition="request/features/visible_render_time"
398+ define="render_time modules/lp.services.webapp.adapter/summarize_requests;"
399+ replace='structure string:&lt;script type="text/javascript"&gt;
400+ var render_time = "${render_time}";
401+ LPJS.use("node", "lp.ajax_log" , function(Y) {
402+ Y.on("domready", function() {
403+ var node = Y.one("#rendertime");
404+ node.set("innerHTML", render_time);
405+ var ajax_log = new Y.lp.ajax_log();
406+ });
407+ });
408+ &lt;/script&gt;' />
409 </body>
410
411 <tal:template>
412@@ -196,24 +202,5 @@
413
414 --&gt;" />
415 </tal:template>
416-
417-
418-<metal:debug-timeline
419- use-macro="context/@@+base-layout-macros/debug-timeline" />
420-
421-<tal:comment
422- tal:condition="request/features/visible_render_time"
423- define="render_time modules/lp.services.webapp.adapter/summarize_requests;"
424- replace='structure string:&lt;script type="text/javascript"&gt;
425- var render_time = "${render_time}";
426- LPJS.use("node", "lp.ajax_log" , function(Y) {
427- Y.on("domready", function() {
428- var node = Y.one("#rendertime");
429- node.set("innerHTML", render_time);
430- var ajax_log = new Y.lp.ajax_log();
431- });
432- });
433-&lt;/script&gt;' />
434-
435 </html>
436 </metal:page>
437
438=== modified file 'lib/lp/app/templates/root-index.pt'
439--- lib/lp/app/templates/root-index.pt 2012-02-24 04:44:55 +0000
440+++ lib/lp/app/templates/root-index.pt 2012-03-08 16:34:46 +0000
441@@ -136,7 +136,7 @@
442 xml:lang="en" lang="en" dir="ltr"
443 tal:attributes="action string:${rooturl}+search"
444 method="get" accept-charset="UTF-8">
445- <input id="text" type="text" name="field.text" size="25%" />
446+ <input id="text" type="text" name="field.text" size="25" />
447 <input id="search" type="submit" value="Search Launchpad" />
448 </form>
449 <script type="text/javascript">
450
451=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt'
452--- lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt 2011-12-29 05:29:36 +0000
453+++ lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt 2012-03-08 16:34:46 +0000
454@@ -61,22 +61,26 @@
455 The most common case will be that the user is sent to the guided
456 +filebug page and the user goes through the workflow there.
457
458- >>> filebug_url = (
459- ... 'http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug/'
460- ... '%s' % blob_token)
461- >>> user_browser.open(filebug_url)
462+ >>> filebug_host = 'launchpad.dev'
463+ >>> filebug_path = (
464+ ... '/ubuntu/+source/mozilla-firefox/+filebug/%s' % blob_token)
465+ >>> filebug_url = 'http://%s%s' % (filebug_host, filebug_path)
466+ >>> contents = str(http(
467+ ... "GET %s HTTP/1.1\nHostname: %s\n"
468+ ... "Authorization: Basic test@canonical.com:test\n\n"
469+ ... % (filebug_path, filebug_host)))
470
471 At first, the user will be shown a message telling them that the extra
472 data is being processed.
473
474- >>> for message in find_tags_by_class(user_browser.contents, 'message'):
475+ >>> for message in find_tags_by_class(contents, 'message'):
476 ... print message.renderContents()
477 Please wait while bug data is processed. This page will refresh
478 every 10 seconds until processing is complete.
479
480 The page header contains a 10-second meta refresh tag.
481
482- >>> '<meta http-equiv="refresh" content="10"' in user_browser.contents
483+ >>> '<meta http-equiv="refresh" content="10"' in contents
484 True
485
486 Once the data has been processed, the +filebug process can continue as
487
488=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt'
489--- lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt 2011-12-29 05:29:36 +0000
490+++ lib/lp/bugs/stories/guided-filebug/xx-ubuntu-filebug.txt 2012-03-08 16:34:46 +0000
491@@ -71,6 +71,14 @@
492 ... extra_filebug_data, 'not/important', 'not.important')
493 >>> anon_browser.getControl(name='FORM_SUBMIT').click()
494 >>> blob_token = anon_browser.headers['X-Launchpad-Blob-Token']
495+ >>> from lp.bugs.interfaces.apportjob import IProcessApportBlobJobSource
496+ >>> login('foo.bar@canonical.com')
497+ >>> job = getUtility(IProcessApportBlobJobSource).getByBlobUUID(
498+ ... blob_token)
499+ >>> job.job.start()
500+ >>> job.run()
501+ >>> job.job.complete()
502+ >>> logout()
503 >>> filebug_url = (
504 ... 'http://launchpad.dev/ubuntu/+source/mozilla-firefox/+filebug/'
505 ... '%s' % blob_token)
506
507=== modified file 'lib/lp/registry/browser/pillar.py'
508--- lib/lp/registry/browser/pillar.py 2012-03-05 01:20:59 +0000
509+++ lib/lp/registry/browser/pillar.py 2012-03-08 16:34:46 +0000
510@@ -240,8 +240,8 @@
511 return getUtility(IService, 'accesspolicy')
512
513 @property
514- def access_policies(self):
515- return self._getAccessPolicyService().getAccessPolicies(self.context)
516+ def information_types(self):
517+ return self._getAccessPolicyService().getInformationTypes(self.context)
518
519 @property
520 def sharing_permissions(self):
521@@ -279,6 +279,6 @@
522 if not getFeatureFlag('disclosure.enhanced_sharing.enabled'):
523 raise Unauthorized("This feature is not yet available.")
524 cache = IJSONRequestCache(self.request)
525- cache.objects['access_policies'] = self.access_policies
526+ cache.objects['information_types'] = self.information_types
527 cache.objects['sharing_permissions'] = self.sharing_permissions
528 cache.objects['observer_data'] = self.observer_data
529
530=== modified file 'lib/lp/registry/browser/tests/test_pillar_sharing.py'
531--- lib/lp/registry/browser/tests/test_pillar_sharing.py 2012-03-03 04:28:09 +0000
532+++ lib/lp/registry/browser/tests/test_pillar_sharing.py 2012-03-08 16:34:46 +0000
533@@ -81,7 +81,7 @@
534 with FeatureFixture(FLAG):
535 view = create_initialized_view(self.pillar, name='+sharing')
536 cache = IJSONRequestCache(view.request)
537- self.assertIsNotNone(cache.objects.get('access_policies'))
538+ self.assertIsNotNone(cache.objects.get('information_types'))
539 self.assertIsNotNone(cache.objects.get('sharing_permissions'))
540 aps = getUtility(IService, 'accesspolicy')
541 observers = aps.getPillarObservers(self.pillar)
542
543=== modified file 'lib/lp/registry/interfaces/accesspolicy.py'
544--- lib/lp/registry/interfaces/accesspolicy.py 2012-03-07 01:24:33 +0000
545+++ lib/lp/registry/interfaces/accesspolicy.py 2012-03-08 16:34:46 +0000
546@@ -153,6 +153,9 @@
547 def findByPolicy(policies):
548 """Return all `IAccessPolicyArtifact` objects for the policies."""
549
550+ def deleteByArtifact(artifacts):
551+ """Delete all `IAccesyPolicyArtifact` objects for the artifacts."""
552+
553
554 class IAccessPolicySource(Interface):
555
556@@ -218,3 +221,10 @@
557
558 :param policies: a collection of `IAccesPolicy`s.
559 """
560+
561+ def findArtifactsByGrantee(grantee, policies):
562+ """Find the `IAccessArtifact`s for grantee and policies.
563+
564+ :param grantee: the access artifact grantee.
565+ :param policies: a collection of `IAccesPolicy`s.
566+ """
567
568=== modified file 'lib/lp/registry/interfaces/accesspolicyservice.py'
569--- lib/lp/registry/interfaces/accesspolicyservice.py 2012-03-07 02:04:23 +0000
570+++ lib/lp/registry/interfaces/accesspolicyservice.py 2012-03-08 16:34:46 +0000
571@@ -39,11 +39,11 @@
572 # version 'devel'
573 export_as_webservice_entry(publish_web_link=False, as_of='beta')
574
575- def getAccessPolicies(pillar):
576- """Return the allowed access policy types for the given pillar."""
577+ def getInformationTypes(pillar):
578+ """Return the allowed information types for the given pillar."""
579
580 def getSharingPermissions():
581- """Return the access policy sharing permissions."""
582+ """Return the information sharing permissions."""
583
584 @export_read_operation()
585 @operation_parameters(
586@@ -57,22 +57,23 @@
587 @operation_parameters(
588 pillar=Reference(IPillar, title=_('Pillar'), required=True),
589 observer=Reference(IPerson, title=_('Observer'), required=True),
590- access_policy_types=List(Choice(vocabulary=InformationType)))
591+ information_types=List(Choice(vocabulary=InformationType)))
592 @operation_for_version('devel')
593- def updatePillarObserver(pillar, observer, access_policy_types, user):
594- """Ensure observer has the grants for access policies on a pillar."""
595+ def updatePillarObserver(pillar, observer, information_types, user):
596+ """Ensure observer has the grants for information types on a pillar."""
597
598 @export_write_operation()
599 @operation_parameters(
600 pillar=Reference(IPillar, title=_('Pillar'), required=True),
601 observer=Reference(IPerson, title=_('Observer'), required=True),
602- access_policy_type=Choice(vocabulary=InformationType, required=False))
603+ information_types=List(
604+ Choice(vocabulary=InformationType), required=False))
605 @operation_for_version('devel')
606- def deletePillarObserver(pillar, observer, access_policy_type):
607+ def deletePillarObserver(pillar, observer, information_types):
608 """Remove an observer from a pillar.
609
610 :param pillar: the pillar from which to remove access
611 :param observer: the person or team to remove
612- :param access_policy_type: if None, remove all access, otherwise just
613- remove the specified access_policy
614+ :param information_types: if None, remove all access, otherwise just
615+ remove the specified access_policies
616 """
617
618=== modified file 'lib/lp/registry/javascript/disclosure/observerpicker.js'
619--- lib/lp/registry/javascript/disclosure/observerpicker.js 2012-03-06 20:58:51 +0000
620+++ lib/lp/registry/javascript/disclosure/observerpicker.js 2012-03-08 16:34:46 +0000
621@@ -18,13 +18,13 @@
622
623 ObserverPicker.ATTRS = {
624 /**
625- * The value, in percentage, of the progress bar.
626+ * The available information types.
627 *
628- * @attribute access_policies
629+ * @attribute information_types
630 * @type Object
631 * @default []
632 */
633- access_policies: {
634+ information_types: {
635 value: []
636 },
637 // Override for testing
638@@ -37,13 +37,13 @@
639 Y.extend(ObserverPicker, Y.lazr.picker.Picker, {
640 initializer: function(config) {
641 ObserverPicker.superclass.initializer.apply(this, arguments);
642- var access_policies = [];
643+ var information_types = [];
644 if (config !== undefined) {
645- if (config.access_policies !== undefined) {
646- access_policies = config.access_policies;
647+ if (config.information_types !== undefined) {
648+ information_types = config.information_types;
649 }
650 }
651- this.set('access_policies', access_policies);
652+ this.set('information_types', information_types);
653 var self = this;
654 this.subscribe('save', function (e) {
655 e.preventDefault();
656@@ -98,7 +98,7 @@
657 },
658
659 _display_step_two: function(data) {
660- var title = Y.Lang.substitute('Select access policy for {name}',
661+ var title = Y.Lang.substitute('Select sharing policy for {name}',
662 {name: data.title});
663 this.set('steptitle', title);
664 this.set('progress', 75);
665@@ -132,17 +132,17 @@
666 },
667
668 _publish_result: function(data) {
669- // Determine the chosen access policy type. data already contains the
670+ // Determine the chosen information type. data already contains the
671 // selected person due to the base picker behaviour.
672 var contentBox = this.get('contentBox');
673- var selected_access_policies = [];
674+ var selected_info_types = [];
675 contentBox.all('input[name=field.visibility]')
676 .each(function(node) {
677 if (node.get('checked')) {
678- selected_access_policies.push(node.get('value'));
679+ selected_info_types.push(node.get('value'));
680 }
681 });
682- data.access_policies = selected_access_policies;
683+ data.information_types = selected_info_types;
684 // Publish the result with step_nr 0 to indicate we have finished.
685 this.fire('save', data, 0);
686 },
687@@ -173,7 +173,7 @@
688 '{{/policies}}',
689 '</tbody></table></div>'
690 ].join(''), {
691- policies: this.get('access_policies')
692+ policies: this.get('information_types')
693 });
694 return Y.Node.create(html);
695 },
696
697=== modified file 'lib/lp/registry/javascript/disclosure/observertable.js'
698--- lib/lp/registry/javascript/disclosure/observertable.js 2012-03-06 05:22:53 +0000
699+++ lib/lp/registry/javascript/disclosure/observertable.js 2012-03-08 16:34:46 +0000
700@@ -33,8 +33,8 @@
701 observers: {
702 value: []
703 },
704- // The access policy types: public, publicsecurity, userdata etc.
705- access_policy_types: {
706+ // The information types: public, embargoedsecurity, userdata etc.
707+ information_types: {
708 value: {}
709 },
710 // The sharing permission choices: all, some, nothing etc.
711@@ -109,7 +109,8 @@
712 '<td id="remove-{{name}}">',
713 ' <a title="Share nothing with this user"',
714 ' href="#" class="sprite remove"' +
715- ' data-self_link="{{self_link}}">',
716+ ' data-self_link="{{self_link}}"' +
717+ ' data-person_name="{{display_name}}">',
718 ' </a>',
719 '</td>',
720 '<td id="td-permission-{{name}}">',
721@@ -126,19 +127,19 @@
722
723 _observer_policy_template: function() {
724 return [
725- '{{#access_policies}}',
726+ '{{#information_types}}',
727 '<li><span id="{{policy}}-permission-{{observer_name}}">',
728 ' <span class="value"></span>',
729 ' <a class="editicon sprite edit" href="#">&nbsp;</a>',
730 '</span></li>',
731- '{{/access_policies}}'].join(' ');
732+ '{{/information_types}}'].join(' ');
733 },
734
735 // Render the popup widget to pick the sharing permission for an
736 // access policy.
737 render_observer_policy: function(
738 observer, policy, current_value) {
739- var access_policy_types = this.get('access_policy_types');
740+ var information_types = this.get('information_types');
741 var sharing_permissions = this.get('sharing_permissions');
742 var choice_items = [];
743 Y.Array.forEach(sharing_permissions, function(permission) {
744@@ -148,7 +149,7 @@
745 value: permission.value,
746 name: permission.title,
747 source_name: Y.Lang.substitute(source_name,
748- {policy_name: access_policy_types[policy],
749+ {policy_name: information_types[policy],
750 permission_name: permission.title})
751 });
752 });
753@@ -165,7 +166,7 @@
754 value_location: value_location,
755 editicon: editicon,
756 value: current_value,
757- title: "Share " + access_policy_types[policy] + " with "
758+ title: "Share " + information_types[policy] + " with "
759 + observer.display_name,
760 items: choice_items,
761 elementToFlash: contentBox,
762@@ -215,7 +216,8 @@
763 e.preventDefault();
764 var delete_link = e.currentTarget;
765 var observer_link = delete_link.getAttribute('data-self_link');
766- self.fire(REMOVE_OBSERVER, delete_link, observer_link);
767+ var person_name = delete_link.getAttribute('data-person_name');
768+ self.fire(REMOVE_OBSERVER, delete_link, observer_link, person_name);
769 }, 'td[id^=remove-] a');
770 },
771
772@@ -226,12 +228,12 @@
773 _prepareObserverDisplayData: function(observers) {
774 Y.Array.forEach(observers, function(observer) {
775 var observer_policies = observer.permissions;
776- var policy_values = [];
777+ var info_types = [];
778 Y.each(observer_policies, function(policy_value, policy) {
779- policy_values.push({policy: policy,
780+ info_types.push({policy: policy,
781 observer_name: observer.name});
782 });
783- observer.access_policies = policy_values;
784+ observer.information_types = info_types;
785 });
786 },
787
788
789=== modified file 'lib/lp/registry/javascript/disclosure/pillarsharingview.js'
790--- lib/lp/registry/javascript/disclosure/pillarsharingview.js 2012-03-06 12:03:09 +0000
791+++ lib/lp/registry/javascript/disclosure/pillarsharingview.js 2012-03-08 16:34:46 +0000
792@@ -54,7 +54,7 @@
793 headerContent: Y.Node.create("<h2></h2>").set('text', header),
794 zIndex: 1000,
795 visible: false,
796- access_policies: LP.cache.access_policies,
797+ information_types: LP.cache.information_types,
798 save: function(result) {
799 self.save_sharing_selection(result);
800 }
801@@ -68,9 +68,9 @@
802 destructor: function() { },
803
804 renderUI: function() {
805- var access_policy_types = {};
806- Y.Array.each(LP.cache.access_policies, function(policy) {
807- access_policy_types[policy.value] = policy.title;
808+ var information_types = {};
809+ Y.Array.each(LP.cache.information_types, function(info_type) {
810+ information_types[info_type.value] = info_type.title;
811 });
812 var sharing_permissions = LP.cache.sharing_permissions;
813 var observer_data = LP.cache.observer_data;
814@@ -78,7 +78,7 @@
815 var observer_table = new otns.ObserverTableWidget({
816 observers: observer_data,
817 sharing_permissions: sharing_permissions,
818- access_policy_types: access_policy_types
819+ information_types: information_types
820 });
821 this.set('observer_table', observer_table);
822 observer_table.render();
823@@ -95,7 +95,8 @@
824 var otns = Y.lp.registry.disclosure.observertable;
825 observer_table.subscribe(
826 otns.ObserverTableWidget.REMOVE_OBSERVER, function(e) {
827- self.perform_remove_observer(e.details[0], e.details[1]);
828+ self.confirm_observer_removal(
829+ e.details[0], e.details[1], e.details[2]);
830 });
831 },
832
833@@ -129,6 +130,32 @@
834 },
835
836 /**
837+ * Prompt the user to confirm the removal of the selected observer.
838+ *
839+ * @method confirm_observer_removal
840+ */
841+ confirm_observer_removal: function(delete_link, person_uri, person_name) {
842+ var confirm_text_template = [
843+ '<p class="large-warning" style="padding:2px 2px 0 36px;">',
844+ ' Do you really want to stop sharing',
845+ ' "{pillar}" with {person_name}?<br><br>',
846+ '</p>'
847+ ].join('');
848+ var confirm_text = Y.Lang.sub(confirm_text_template,
849+ {pillar: LP.cache.context.display_name,
850+ person_name: person_name});
851+ var self = this;
852+ var co = new Y.lp.app.confirmationoverlay.ConfirmationOverlay({
853+ submit_fn: function() {
854+ self.perform_remove_observer(delete_link, person_uri);
855+ },
856+ form_content: confirm_text,
857+ headerContent: '<h2>Stop sharing</h2>'
858+ });
859+ co.show();
860+ },
861+
862+ /**
863 * The server call to remove the specified observer has succeeded.
864 * Update the model and view.
865 * @method remove_observer_success
866@@ -236,7 +263,7 @@
867 parameters: {
868 pillar: pillar_uri,
869 observer: person_uri,
870- access_policy_types: selection_result.access_policies
871+ information_types: selection_result.information_types
872 }
873 };
874 this.get('lp_client').named_post(
875@@ -250,5 +277,6 @@
876 }, "0.1", { "requires": [
877 'node', 'lp.client', 'lp.mustache', 'lazr.picker', 'lp.app.picker',
878 'lp.mustache', 'lp.registry.disclosure.observerpicker',
879- 'lp.registry.disclosure.observertable'] });
880+ 'lp.registry.disclosure.observertable', 'lp.app.confirmationoverlay'
881+ ]});
882
883
884=== modified file 'lib/lp/registry/javascript/disclosure/tests/test_observerpicker.js'
885--- lib/lp/registry/javascript/disclosure/tests/test_observerpicker.js 2012-03-06 12:03:09 +0000
886+++ lib/lp/registry/javascript/disclosure/tests/test_observerpicker.js 2012-03-08 16:34:46 +0000
887@@ -18,7 +18,7 @@
888 "description": "frieda@example.com", "api_uri": "~/frieda",
889 "metadata": "team"}
890 ];
891- this.access_policies = [
892+ this.information_types = [
893 {index: '0', value: 'P1', title: 'Policy 1',
894 description: 'Policy 1 description'},
895 {index: '1', value: 'P2', title: 'Policy 2',
896@@ -46,7 +46,7 @@
897
898 _create_picker: function(overrides) {
899 var config = {
900- anim_duratrion: 0,
901+ anim_duration: 0,
902 progressbar: true,
903 progress: 50,
904 headerContent: "<h2>Grant access</h2>",
905@@ -54,7 +54,7 @@
906 "with whom to share",
907 zIndex: 1000,
908 visible: false,
909- access_policies: this.access_policies
910+ information_types: this.information_types
911 };
912 if (overrides !== undefined) {
913 config = Y.merge(config, overrides);
914@@ -103,15 +103,16 @@
915 // The step title should be updated according to the selected
916 // person.
917 steptitle = cb.one('.contains-steptitle h2').getContent();
918- Y.Assert.areEqual('Select access policy for Fred', steptitle);
919+ Y.Assert.areEqual(
920+ 'Select sharing policy for Fred', steptitle);
921 // The second step ui should be visible.
922 var step_two_content = cb.one('.picker-content-two');
923 Y.Assert.isFalse(step_two_content.hasClass('unseen'));
924 // The second step ui should contain input buttons for each access
925 // policy type.
926- Y.Array.each(this.access_policies, function(policy) {
927+ Y.Array.each(this.information_types, function(info_type) {
928 var rb = step_two_content.one(
929- 'input[value="' + policy.title + '"]');
930+ 'input[value="' + info_type.title + '"]');
931 Y.Assert.isNotNull(rb);
932 });
933 // There should be a link back to previous step.
934@@ -170,7 +171,7 @@
935 var select_link = step_two_content.one('a.next');
936 select_link.simulate('click');
937 Y.ArrayAssert.itemsAreEqual(
938- ['Policy 2'], selected_result.access_policies);
939+ ['Policy 2'], selected_result.information_types);
940 Y.Assert.areEqual('~/fred', selected_result.api_uri);
941 }
942 }));
943
944=== modified file 'lib/lp/registry/javascript/disclosure/tests/test_observertable.js'
945--- lib/lp/registry/javascript/disclosure/tests/test_observertable.js 2012-03-06 05:22:53 +0000
946+++ lib/lp/registry/javascript/disclosure/tests/test_observertable.js 2012-03-08 16:34:46 +0000
947@@ -25,7 +25,7 @@
948 {'value': 's2', 'title': 'S2',
949 'description': 'Sharing 2'}
950 ];
951- this.access_policies = {
952+ this.information_types = {
953 'P1': 'Policy 1',
954 'P2': 'Policy 2',
955 'P3': 'Policy 3'
956@@ -42,7 +42,7 @@
957 anim_duration: 0.001,
958 observers: this.observer_data,
959 sharing_permissions: this.sharing_permissions,
960- access_policy_types: this.access_policies
961+ information_types: this.information_types
962 });
963 },
964
965@@ -70,7 +70,7 @@
966 Y.Assert.isNotNull(
967 Y.one('#observer-table td[id=remove-'
968 + observer.name + '] a'));
969- // The access policy sharing permissions
970+ // The sharing permissions
971 var permission;
972 for (permission in observer.permissions) {
973 if (observer.permissions.hasOwnProperty(permission)) {
974@@ -120,7 +120,9 @@
975 ns.ObserverTableWidget.REMOVE_OBSERVER, function(e) {
976 var delete_link = e.details[0];
977 var observer_uri = e.details[1];
978+ var person_name = e.details[2];
979 Y.Assert.areEqual('~fred', observer_uri);
980+ Y.Assert.areEqual('Fred Bloggs', person_name);
981 Y.Assert.areEqual(delete_link_to_click, delete_link);
982 event_fired = true;
983 }
984
985=== modified file 'lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.html'
986--- lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.html 2012-03-01 05:46:50 +0000
987+++ lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.html 2012-03-08 16:34:46 +0000
988@@ -33,12 +33,16 @@
989 <script type="text/javascript"
990 src="../../../../../../build/js/lp/app/lp.js"></script>
991 <script type="text/javascript"
992+ src="../../../../../../build/js/lp/app/confirmationoverlay/confirmationoverlay.js"></script>
993+ <script type="text/javascript"
994 src="../../../../../../build/js/lp/app/mustache.js"></script>
995 <script type="text/javascript"
996 src="../../../../../../build/js/lp/app/activator/activator.js"></script>
997 <script type="text/javascript"
998 src="../../../../../../build/js/lp/app/choiceedit/choiceedit.js"></script>
999 <script type="text/javascript"
1000+ src="../../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
1001+ <script type="text/javascript"
1002 src="../../../../../../build/js/lp/app/overlay/overlay.js"></script>
1003 <script type="text/javascript"
1004 src="../../../../../../build/js/lp/app/picker/picker.js"></script>
1005
1006=== modified file 'lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.js'
1007--- lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.js 2012-03-06 12:03:09 +0000
1008+++ lib/lp/registry/javascript/disclosure/tests/test_pillarsharingview.js 2012-03-08 16:34:46 +0000
1009@@ -31,7 +31,7 @@
1010 {'value': 's2', 'title': 'S2',
1011 'description': 'Sharing 2'}
1012 ],
1013- access_policies: [
1014+ information_types: [
1015 {index: '0', value: 'P1', title: 'Policy 1',
1016 description: 'Policy 1 description'},
1017 {index: '1', value: 'P2', title: 'Policy 2',
1018@@ -88,24 +88,65 @@
1019 .hasClass('yui3-observer_picker-hidden'));
1020 },
1021
1022- // Clicking a delete observer link calls the perform_remove_observer
1023+ // Clicking a delete observer link calls the confirm_observer_removal
1024 // method with the correct parameters.
1025 test_delete_observer_click: function() {
1026 this.view = this._create_Widget();
1027 this.view.render();
1028+ var confirmRemove_called = false;
1029+ this.view.confirm_observer_removal = function(
1030+ delete_link, person_uri, person_name) {
1031+ Y.Assert.areEqual('~fred', person_uri);
1032+ Y.Assert.areEqual('Fred Bloggs', person_name);
1033+ Y.Assert.areEqual(delete_link_to_click, delete_link);
1034+ confirmRemove_called = true;
1035+
1036+ };
1037+ var delete_link_to_click =
1038+ Y.one('#observer-table td[id=remove-fred] a');
1039+ delete_link_to_click.simulate('click');
1040+ Y.Assert.isTrue(confirmRemove_called);
1041+ },
1042+
1043+ //Test the behaviour of the removal confirmation dialog.
1044+ _test_confirm_observer_removal: function(click_ok) {
1045+ this.view = this._create_Widget();
1046+ this.view.render();
1047 var performRemove_called = false;
1048- var self = this;
1049 this.view.perform_remove_observer = function(
1050 delete_link, person_uri) {
1051 Y.Assert.areEqual('~fred', person_uri);
1052- Y.Assert.areEqual(delete_link_to_click, delete_link);
1053+ Y.Assert.areEqual(delete_link, delete_link);
1054 performRemove_called = true;
1055
1056 };
1057- var delete_link_to_click =
1058+ var delete_link =
1059 Y.one('#observer-table td[id=remove-fred] a');
1060- delete_link_to_click.simulate('click');
1061- Y.Assert.isTrue(performRemove_called);
1062+ this.view.confirm_observer_removal(
1063+ delete_link, '~fred', 'Fred Bloggs');
1064+ var co = Y.one('.yui3-overlay.yui3-lp-app-confirmationoverlay');
1065+ var actions = co.one('.yui3-lazr-formoverlay-actions');
1066+ var btn_style;
1067+ if (click_ok) {
1068+ btn_style = '.ok-btn';
1069+ } else {
1070+ btn_style = '.cancel-btn';
1071+ }
1072+ var button = actions.one(btn_style);
1073+ button.simulate('click');
1074+ Y.Assert.areEqual(click_ok, performRemove_called);
1075+ Y.Assert.isTrue(
1076+ co.hasClass('yui3-lp-app-confirmationoverlay-hidden'));
1077+ },
1078+
1079+ //Test the remove confirmation dialog when the user clicks Ok.
1080+ test_confirm_observer_removal_ok: function() {
1081+ this._test_confirm_observer_removal(true);
1082+ },
1083+
1084+ //Test the remove confirmation dialog when the user clicks Cancel.
1085+ test_confirm_observer_removal_cancel: function() {
1086+ this._test_confirm_observer_removal(false);
1087 },
1088
1089 // The perform_remove_observer method makes the expected XHR calls.
1090@@ -174,7 +215,7 @@
1091 Y.Assert.areEqual('joe', observer.name);
1092 save_sharing_selection_success_called = true;
1093 };
1094- // Use the picker to select a new observer and access policy.
1095+ // Use the picker to select a new observer and information type.
1096 var observer_picker = this.view.get('observer_picker');
1097 var picker_results = [
1098 {"value": "joe", "title": "Joe", "css": "sprite-person",
1099@@ -203,7 +244,7 @@
1100 expected_url = Y.lp.client.append_qs(
1101 expected_url, 'observer', person_uri);
1102 expected_url = Y.lp.client.append_qs(
1103- expected_url, 'access_policy_types', ['Policy 2']);
1104+ expected_url, 'information_types', ['Policy 2']);
1105 Y.Assert.areEqual(expected_url, mockio.last_request.config.data);
1106 mockio.last_request.successJSON({
1107 'resource_type_link': 'entity',
1108
1109=== modified file 'lib/lp/registry/model/accesspolicy.py'
1110--- lib/lp/registry/model/accesspolicy.py 2012-03-07 01:24:33 +0000
1111+++ lib/lp/registry/model/accesspolicy.py 2012-03-08 16:34:46 +0000
1112@@ -222,6 +222,11 @@
1113 ids = [policy.id for policy in policies]
1114 return IStore(cls).find(cls, cls.policy_id.is_in(ids))
1115
1116+ @classmethod
1117+ def deleteByArtifact(cls, artifacts):
1118+ """See `IAccessPolicyArtifactSource`."""
1119+ cls.findByArtifact(artifacts).remove()
1120+
1121
1122 class AccessArtifactGrant(StormBase):
1123 implements(IAccessArtifactGrant)
1124@@ -330,3 +335,13 @@
1125 ids = [policy.id for policy in policies]
1126 return IStore(cls).find(
1127 Person, Person.id == cls.grantee_id, cls.policy_id.is_in(ids))
1128+
1129+ @classmethod
1130+ def findArtifactsByGrantee(cls, grantee, policies):
1131+ """See `IAccessPolicyGrantFlatSource`."""
1132+ ids = [policy.id for policy in policies]
1133+ return IStore(cls).find(
1134+ AccessArtifact,
1135+ AccessArtifact.id == cls.abstract_artifact_id,
1136+ cls.grantee_id == grantee.id,
1137+ cls.policy_id.is_in(ids))
1138
1139=== modified file 'lib/lp/registry/services/accesspolicyservice.py'
1140--- lib/lp/registry/services/accesspolicyservice.py 2012-03-07 01:24:33 +0000
1141+++ lib/lp/registry/services/accesspolicyservice.py 2012-03-08 16:34:46 +0000
1142@@ -19,7 +19,9 @@
1143 SharingPermission,
1144 )
1145 from lp.registry.interfaces.accesspolicy import (
1146+ IAccessArtifactGrantSource,
1147 IAccessPolicySource,
1148+ IAccessPolicyGrantFlatSource,
1149 IAccessPolicyGrantSource,
1150 )
1151 from lp.registry.interfaces.accesspolicyservice import IAccessPolicyService
1152@@ -29,7 +31,7 @@
1153
1154
1155 class AccessPolicyService:
1156- """Service providing operations for access policies.
1157+ """Service providing operations for adding and removing pillar observers.
1158
1159 Service is accessed via a url of the form
1160 '/services/accesspolicy?ws.op=...
1161@@ -42,27 +44,27 @@
1162 """See `IService`."""
1163 return 'accesspolicy'
1164
1165- def getAccessPolicies(self, pillar):
1166+ def getInformationTypes(self, pillar):
1167 """See `IAccessPolicyService`."""
1168- allowed_policy_types = [
1169+ allowed_types = [
1170 InformationType.EMBARGOEDSECURITY,
1171 InformationType.USERDATA]
1172 # Products with current commercial subscriptions are also allowed to
1173- # have a PROPRIETARY access policy.
1174+ # have a PROPRIETARY information type.
1175 if (IProduct.providedBy(pillar) and
1176 pillar.has_current_commercial_subscription):
1177- allowed_policy_types.append(InformationType.PROPRIETARY)
1178+ allowed_types.append(InformationType.PROPRIETARY)
1179
1180- policies_data = []
1181- for x, policy in enumerate(allowed_policy_types):
1182+ result_data = []
1183+ for x, policy in enumerate(allowed_types):
1184 item = dict(
1185 index=x,
1186 value=policy.name,
1187 title=policy.title,
1188 description=policy.description
1189 )
1190- policies_data.append(item)
1191- return policies_data
1192+ result_data.append(item)
1193+ return result_data
1194
1195 def getSharingPermissions(self):
1196 """See `IAccessPolicyService`."""
1197@@ -96,31 +98,31 @@
1198 person_data = resource.toDataForJSON()
1199 person_data['permissions'] = {}
1200 person_by_id[policy_grant.grantee.id] = person_data
1201+ result.append(person_data)
1202 person_data = person_by_id[policy_grant.grantee.id]
1203 person_data['permissions'][policy_grant.policy.type.name] = (
1204 SharingPermission.ALL.name)
1205- result.append(person_data)
1206 return result
1207
1208 @available_with_permission('launchpad.Edit', 'pillar')
1209- def updatePillarObserver(self, pillar, observer, access_policy_types,
1210+ def updatePillarObserver(self, pillar, observer, information_types,
1211 user):
1212 """See `IAccessPolicyService`."""
1213
1214 # We do not support adding observers to project groups.
1215 assert not IProjectGroup.providedBy(pillar)
1216
1217- pillar_policy_types = [
1218- (pillar, access_policy_type)
1219- for access_policy_type in access_policy_types]
1220+ pillar_info_types = [
1221+ (pillar, information_type)
1222+ for information_type in information_types]
1223
1224 # Create any missing pillar access policies.
1225 policy_source = getUtility(IAccessPolicySource)
1226- pillar_policies = list(policy_source.find(pillar_policy_types))
1227+ pillar_policies = list(policy_source.find(pillar_info_types))
1228 existing_policy_types = [
1229 (pillar, pillar_policy.type) for pillar_policy in pillar_policies]
1230 required_policies = (
1231- set(pillar_policy_types).difference(existing_policy_types))
1232+ set(pillar_info_types).difference(existing_policy_types))
1233 if len(required_policies) > 0:
1234 pillar_policies.extend(policy_source.create(required_policies))
1235
1236@@ -154,13 +156,38 @@
1237 resource = EntryResource(observer, request)
1238 person_data = resource.toDataForJSON()
1239 permissions = {}
1240- for access_policy_type in access_policy_types:
1241- permissions[access_policy_type.name] = SharingPermission.ALL.name
1242+ for information_type in information_types:
1243+ permissions[information_type.name] = SharingPermission.ALL.name
1244 person_data['permissions'] = permissions
1245 return person_data
1246
1247 @available_with_permission('launchpad.Edit', 'pillar')
1248- def deletePillarObserver(self, pillar, observer, access_policy_type):
1249+ def deletePillarObserver(self, pillar, observer,
1250+ information_types=None):
1251 """See `IAccessPolicyService`."""
1252- # TODO - implement this
1253- pass
1254+
1255+ policy_source = getUtility(IAccessPolicySource)
1256+ if information_types is None:
1257+ # We delete all policy grants for the pillar.
1258+ pillar_policies = policy_source.findByPillar([pillar])
1259+ else:
1260+ # We delete selected policy grants for the pillar.
1261+ pillar_policy_types = [
1262+ (pillar, information_type)
1263+ for information_type in information_types]
1264+ pillar_policies = list(policy_source.find(pillar_policy_types))
1265+
1266+ # First delete any access policy grants.
1267+ policy_grant_source = getUtility(IAccessPolicyGrantSource)
1268+ policy_grants = [(policy, observer) for policy in pillar_policies]
1269+ grants = [
1270+ (grant.policy, grant.grantee)
1271+ for grant in policy_grant_source.find(policy_grants)]
1272+ policy_grant_source.revoke(grants)
1273+
1274+ # Second delete any access artifact grants.
1275+ ap_grant_flat = getUtility(IAccessPolicyGrantFlatSource)
1276+ to_delete = ap_grant_flat.findArtifactsByGrantee(
1277+ observer, pillar_policies)
1278+ accessartifact_grant_source = getUtility(IAccessArtifactGrantSource)
1279+ accessartifact_grant_source.revokeByArtifact(to_delete)
1280
1281=== modified file 'lib/lp/registry/services/tests/test_accesspolicyservice.py'
1282--- lib/lp/registry/services/tests/test_accesspolicyservice.py 2012-03-07 01:24:33 +0000
1283+++ lib/lp/registry/services/tests/test_accesspolicyservice.py 2012-03-08 16:34:46 +0000
1284@@ -57,8 +57,8 @@
1285 observer_data['permissions'] = permissions
1286 return observer_data
1287
1288- def _test_getAccessPolicies(self, pillar, expected_policies):
1289- policy_data = self.service.getAccessPolicies(pillar)
1290+ def _test_getInformationTypes(self, pillar, expected_policies):
1291+ policy_data = self.service.getInformationTypes(pillar)
1292 expected_data = []
1293 for x, policy in enumerate(expected_policies):
1294 item = dict(
1295@@ -70,31 +70,31 @@
1296 expected_data.append(item)
1297 self.assertContentEqual(expected_data, policy_data)
1298
1299- def test_getAccessPolicies_product(self):
1300+ def test_getInformationTypes_product(self):
1301 product = self.factory.makeProduct()
1302- self._test_getAccessPolicies(
1303+ self._test_getInformationTypes(
1304 product,
1305 [InformationType.EMBARGOEDSECURITY, InformationType.USERDATA])
1306
1307- def test_getAccessPolicies_expired_commercial_product(self):
1308+ def test_getInformationTypes_expired_commercial_product(self):
1309 product = self.factory.makeProduct()
1310 self.factory.makeCommercialSubscription(product, expired=True)
1311- self._test_getAccessPolicies(
1312+ self._test_getInformationTypes(
1313 product,
1314 [InformationType.EMBARGOEDSECURITY, InformationType.USERDATA])
1315
1316- def test_getAccessPolicies_commercial_product(self):
1317+ def test_getInformationTypes_commercial_product(self):
1318 product = self.factory.makeProduct()
1319 self.factory.makeCommercialSubscription(product)
1320- self._test_getAccessPolicies(
1321+ self._test_getInformationTypes(
1322 product,
1323 [InformationType.EMBARGOEDSECURITY,
1324 InformationType.USERDATA,
1325 InformationType.PROPRIETARY])
1326
1327- def test_getAccessPolicies_distro(self):
1328+ def test_getInformationTypes_distro(self):
1329 distro = self.factory.makeDistribution()
1330- self._test_getAccessPolicies(
1331+ self._test_getInformationTypes(
1332 distro,
1333 [InformationType.EMBARGOEDSECURITY, InformationType.USERDATA])
1334
1335@@ -164,21 +164,21 @@
1336 policy, grantee=observer, grantor=grantor)
1337
1338 # Now call updatePillarObserver will the grants we want.
1339- access_policy_types = [
1340+ information_types = [
1341 InformationType.EMBARGOEDSECURITY,
1342 InformationType.USERDATA]
1343 observer_data = self.service.updatePillarObserver(
1344- pillar, observer, access_policy_types, grantor)
1345+ pillar, observer, information_types, grantor)
1346 policies = getUtility(IAccessPolicySource).findByPillar([pillar])
1347 policy_grant_source = getUtility(IAccessPolicyGrantSource)
1348 grants = policy_grant_source.findByPolicy(policies)
1349- self.assertEqual(grants.count(), len(access_policy_types))
1350+ self.assertEqual(grants.count(), len(information_types))
1351 for grant in grants:
1352 self.assertEqual(grantor, grant.grantor)
1353 self.assertEqual(observer, grant.grantee)
1354- self.assertIn(grant.policy.type, access_policy_types)
1355+ self.assertIn(grant.policy.type, information_types)
1356 expected_observer_data = self._makeObserverData(
1357- observer, access_policy_types)
1358+ observer, information_types)
1359 self.assertEqual(expected_observer_data, observer_data)
1360
1361 def test_updateProjectGroupObserver_not_allowed(self):
1362@@ -209,11 +209,10 @@
1363 # updatePillarObserver raises an Unauthorized exception if the user is
1364 # not permitted to do so.
1365 observer = self.factory.makePerson()
1366- access_policy_type = InformationType.USERDATA
1367 user = self.factory.makePerson()
1368 self.assertRaises(
1369 Unauthorized, self.service.updatePillarObserver,
1370- pillar, observer, [access_policy_type], user)
1371+ pillar, observer, [InformationType.USERDATA], user)
1372
1373 def test_updatePillarObserverAnonymous(self):
1374 # Anonymous users are not allowed.
1375@@ -227,6 +226,77 @@
1376 login_person(self.factory.makePerson())
1377 self._test_updatePillarObserverUnauthorized(product)
1378
1379+ def _test_deletePillarObserver(self, pillar, types_to_delete=None):
1380+ # Make grants for some information types.
1381+ information_types = [
1382+ InformationType.EMBARGOEDSECURITY,
1383+ InformationType.USERDATA]
1384+ access_policies = []
1385+ for info_type in information_types:
1386+ access_policy = self.factory.makeAccessPolicy(
1387+ pillar=pillar, type=info_type)
1388+ access_policies.append(access_policy)
1389+ grantee = self.factory.makePerson()
1390+ # Make some access policy grants for our observer.
1391+ for access_policy in access_policies:
1392+ self.factory.makeAccessPolicyGrant(access_policy, grantee)
1393+ # Make some artifact grants for our observer.
1394+ artifact = self.factory.makeAccessArtifact()
1395+ self.factory.makeAccessArtifactGrant(artifact, grantee)
1396+ for access_policy in access_policies:
1397+ self.factory.makeAccessPolicyArtifact(
1398+ artifact=artifact, policy=access_policy)
1399+ # Make some access policy grants for another observer.
1400+ another = self.factory.makePerson()
1401+ self.factory.makeAccessPolicyGrant(access_policies[0], another)
1402+ # Delete data for a specific information type.
1403+ self.service.deletePillarObserver(pillar, grantee, types_to_delete)
1404+ # Assemble the expected data for the remaining access grants for
1405+ # grantee.
1406+ expected_data = []
1407+ if types_to_delete is not None:
1408+ expected_information_types = (
1409+ set(information_types).difference(types_to_delete))
1410+ remaining_grantee_person_data = self._makeObserverData(
1411+ grantee, expected_information_types)
1412+ expected_data.append(remaining_grantee_person_data)
1413+ # Add the data for the other observer.
1414+ another_person_data = self._makeObserverData(
1415+ another, information_types[:1])
1416+ expected_data.append(another_person_data)
1417+ self.assertContentEqual(
1418+ expected_data, self.service.getPillarObservers(pillar))
1419+
1420+ def test_deleteProductObserverAll(self):
1421+ # Users with launchpad.Edit can delete all access for an observer.
1422+ owner = self.factory.makePerson()
1423+ product = self.factory.makeProduct(owner=owner)
1424+ login_person(owner)
1425+ self._test_deletePillarObserver(product)
1426+
1427+ def test_deleteProductObserverPolicies(self):
1428+ # Users with launchpad.Edit can delete selected policy access for an
1429+ # observer.
1430+ owner = self.factory.makePerson()
1431+ product = self.factory.makeProduct(owner=owner)
1432+ login_person(owner)
1433+ self._test_deletePillarObserver(product, [InformationType.USERDATA])
1434+
1435+ def test_deleteDistroObserverAll(self):
1436+ # Users with launchpad.Edit can delete all access for an observer.
1437+ owner = self.factory.makePerson()
1438+ distro = self.factory.makeDistribution(owner=owner)
1439+ login_person(owner)
1440+ self._test_deletePillarObserver(distro)
1441+
1442+ def test_deleteDistroObserverPolicies(self):
1443+ # Users with launchpad.Edit can delete selected policy access for an
1444+ # observer.
1445+ owner = self.factory.makePerson()
1446+ distro = self.factory.makeDistribution(owner=owner)
1447+ login_person(owner)
1448+ self._test_deletePillarObserver(distro, [InformationType.USERDATA])
1449+
1450
1451 class ApiTestMixin:
1452 """Common tests for launchpadlib and webservice."""
1453
1454=== modified file 'lib/lp/registry/tests/test_accesspolicy.py'
1455--- lib/lp/registry/tests/test_accesspolicy.py 2012-03-07 01:24:33 +0000
1456+++ lib/lp/registry/tests/test_accesspolicy.py 2012-03-08 16:34:46 +0000
1457@@ -264,6 +264,16 @@
1458 grants,
1459 getUtility(IAccessArtifactGrantSource).findByArtifact([artifact]))
1460
1461+ def test_revokeByArtifact(self):
1462+ # revokeByArtifact() removes the relevant grants.
1463+ artifact = self.factory.makeAccessArtifact()
1464+ grant = self.factory.makeAccessArtifactGrant(artifact=artifact)
1465+ other_grant = self.factory.makeAccessArtifactGrant()
1466+ getUtility(IAccessArtifactGrantSource).revokeByArtifact([artifact])
1467+ IStore(grant).invalidate()
1468+ self.assertRaises(LostObjectError, getattr, grant, 'grantor')
1469+ self.assertIsNot(None, other_grant.grantor)
1470+
1471
1472 class TestAccessPolicyArtifact(TestCaseWithFactory):
1473 layer = DatabaseFunctionalLayer
1474@@ -319,6 +329,21 @@
1475 links,
1476 getUtility(IAccessPolicyArtifactSource).findByPolicy([policy]))
1477
1478+ def test_deleteByArtifact(self):
1479+ # deleteByArtifact() removes the relevant grants.
1480+ grant = self.factory.makeAccessPolicyArtifact()
1481+ other_grant = self.factory.makeAccessPolicyArtifact()
1482+ getUtility(IAccessPolicyArtifactSource).deleteByArtifact(
1483+ [grant.abstract_artifact])
1484+ self.assertContentEqual(
1485+ [],
1486+ getUtility(IAccessPolicyArtifactSource).findByArtifact(
1487+ [grant.abstract_artifact]))
1488+ self.assertContentEqual(
1489+ [other_grant],
1490+ getUtility(IAccessPolicyArtifactSource).findByArtifact(
1491+ [other_grant.abstract_artifact]))
1492+
1493
1494 class TestAccessPolicyGrant(TestCaseWithFactory):
1495 layer = DatabaseFunctionalLayer
1496@@ -414,3 +439,19 @@
1497 self.assertContentEqual(
1498 [policy_grant.grantee, artifact_grant.grantee],
1499 apgfs.findGranteesByPolicy([policy]))
1500+
1501+ def test_findArtifactsByGrantee(self):
1502+ # findArtifactsByGrantee() returns the artifacts for grantee for any of
1503+ # the policies.
1504+ apgfs = getUtility(IAccessPolicyGrantFlatSource)
1505+ policy = self.factory.makeAccessPolicy()
1506+ grantee = self.factory.makePerson()
1507+ # Artifacts not linked to the policy do not show up.
1508+ artifact = self.factory.makeAccessArtifact()
1509+ self.factory.makeAccessArtifactGrant(artifact, grantee)
1510+ self.assertContentEqual(
1511+ [], apgfs.findArtifactsByGrantee(grantee, [policy]))
1512+ # Artifacts linked to the policy do show up.
1513+ self.factory.makeAccessPolicyArtifact(artifact=artifact, policy=policy)
1514+ self.assertContentEqual(
1515+ [artifact], apgfs.findArtifactsByGrantee(grantee, [policy]))
1516
1517=== modified file 'lib/lp/scripts/garbo.py'
1518--- lib/lp/scripts/garbo.py 2012-03-07 07:29:31 +0000
1519+++ lib/lp/scripts/garbo.py 2012-03-08 16:34:46 +0000
1520@@ -13,6 +13,7 @@
1521 datetime,
1522 timedelta,
1523 )
1524+import itertools
1525 import logging
1526 import multiprocessing
1527 import os
1528@@ -26,7 +27,12 @@
1529 import iso8601
1530 from psycopg2 import IntegrityError
1531 import pytz
1532-from storm.expr import In
1533+from storm.expr import (
1534+ Exists,
1535+ In,
1536+ Not,
1537+ Select,
1538+ )
1539 from storm.locals import (
1540 Max,
1541 Min,
1542@@ -57,7 +63,12 @@
1543 RevisionCache,
1544 )
1545 from lp.hardwaredb.model.hwdb import HWSubmission
1546+from lp.registry.enums import InformationType
1547+from lp.registry.interfaces.accesspolicy import IAccessPolicySource
1548+from lp.registry.model.accesspolicy import AccessPolicy
1549+from lp.registry.model.distribution import Distribution
1550 from lp.registry.model.person import Person
1551+from lp.registry.model.product import Product
1552 from lp.services.config import config
1553 from lp.services.database import postgresql
1554 from lp.services.database.constants import UTC_NOW
1555@@ -990,6 +1001,64 @@
1556 transaction.commit()
1557
1558
1559+class AccessPolicyDistributionAddition(TunableLoop):
1560+ """A `TunableLoop` to add AccessPolicy for all distributions."""
1561+
1562+ maximum_chunk_size = 5000
1563+
1564+ def __init__(self, log, abort_time=None):
1565+ super(AccessPolicyDistributionAddition, self).__init__(
1566+ log, abort_time)
1567+ self.transaction = transaction
1568+ self.store = IMasterStore(Distribution)
1569+
1570+ def findDistributions(self):
1571+ return self.store.find(
1572+ Distribution,
1573+ Not(Exists(
1574+ Select(AccessPolicy.id,
1575+ tables=[AccessPolicy], where=[
1576+ AccessPolicy.distribution_id == Distribution.id]))))
1577+
1578+ def isDone(self):
1579+ return self.findDistributions().is_empty()
1580+
1581+ def __call__(self, chunk_size):
1582+ policies = itertools.product(
1583+ self.findDistributions()[:chunk_size],
1584+ (InformationType.USERDATA, InformationType.EMBARGOEDSECURITY))
1585+ getUtility(IAccessPolicySource).create(policies)
1586+ self.transaction.commit()
1587+
1588+
1589+class AccessPolicyProductAddition(TunableLoop):
1590+ """A `TunableLoop` to add AccessPolicy for all products."""
1591+
1592+ maximum_chunk_size = 5000
1593+
1594+ def __init__(self, log, abort_time=None):
1595+ super(AccessPolicyProductAddition, self).__init__(log, abort_time)
1596+ self.transaction = transaction
1597+ self.store = IMasterStore(Product)
1598+
1599+ def findProducts(self):
1600+ return self.store.find(
1601+ Product,
1602+ Not(Exists(
1603+ Select(AccessPolicy.id, tables=[AccessPolicy], where=[
1604+ AccessPolicy.product_id == Product.id]))))
1605+
1606+ def isDone(self):
1607+ return self.findProducts().is_empty()
1608+
1609+ def __call__(self, chunk_size):
1610+ policies = itertools.product(
1611+ self.findProducts()[:chunk_size],
1612+ (InformationType.USERDATA, InformationType.EMBARGOEDSECURITY))
1613+ getUtility(IAccessPolicySource).create(policies)
1614+ self.transaction.commit()
1615+
1616+
1617 class SpecificationWorkitemMigrator(TunableLoop):
1618 """Migrate work-items from Specification.whiteboard to
1619 SpecificationWorkItem.
1620@@ -1315,6 +1384,8 @@
1621 UnusedSessionPruner,
1622 DuplicateSessionPruner,
1623 BugHeatUpdater,
1624+ AccessPolicyDistributionAddition,
1625+ AccessPolicyProductAddition,
1626 ]
1627 experimental_tunable_loops = []
1628
1629
1630=== modified file 'lib/lp/scripts/tests/test_garbo.py'
1631--- lib/lp/scripts/tests/test_garbo.py 2012-03-07 07:29:31 +0000
1632+++ lib/lp/scripts/tests/test_garbo.py 2012-03-08 16:34:46 +0000
1633@@ -54,6 +54,8 @@
1634 )
1635 from lp.code.model.codeimportevent import CodeImportEvent
1636 from lp.code.model.codeimportresult import CodeImportResult
1637+from lp.registry.enums import InformationType
1638+from lp.registry.interfaces.accesspolicy import IAccessPolicySource
1639 from lp.registry.interfaces.person import IPersonSet
1640 from lp.scripts.garbo import (
1641 AntiqueSessionPruner,
1642@@ -1019,6 +1021,26 @@
1643 self.runHourly()
1644 self.assertNotEqual(old_update, bug.heat_last_updated)
1645
1646+ def test_AccessPolicyDistributionAddition(self):
1647+ switch_dbuser('testadmin')
1648+ distribution = self.factory.makeDistribution()
1649+ transaction.commit()
1650+ self.runHourly()
1651+ ap = getUtility(IAccessPolicySource).findByPillar((distribution,))
1652+ expected = [
1653+ InformationType.USERDATA, InformationType.EMBARGOEDSECURITY]
1654+ self.assertContentEqual(expected, [policy.type for policy in ap])
1655+
1656+ def test_AccessPolicyProductAddition(self):
1657+ switch_dbuser('testadmin')
1658+ product = self.factory.makeProduct()
1659+ transaction.commit()
1660+ self.runHourly()
1661+ ap = getUtility(IAccessPolicySource).findByPillar((product,))
1662+ expected = [
1663+ InformationType.USERDATA, InformationType.EMBARGOEDSECURITY]
1664+ self.assertContentEqual(expected, [policy.type for policy in ap])
1665+
1666 def test_SpecificationWorkitemMigrator_not_enabled_by_default(self):
1667 self.assertFalse(getFeatureFlag('garbo.workitem_migrator.enabled'))
1668 switch_dbuser('testadmin')
1669
1670=== modified file 'lib/lp/services/webapp/adapter.py'
1671--- lib/lp/services/webapp/adapter.py 2011-12-30 08:13:14 +0000
1672+++ lib/lp/services/webapp/adapter.py 2012-03-08 16:34:46 +0000
1673@@ -226,11 +226,11 @@
1674 timeline = get_request_timeline(request)
1675 from lp.services.webapp.errorlog import (
1676 maybe_record_user_requested_oops)
1677- oopsid = maybe_record_user_requested_oops()
1678- if oopsid is None:
1679+ maybe_record_user_requested_oops()
1680+ if request.oopsid is None:
1681 oops_str = ""
1682 else:
1683- oops_str = " %s" % oopsid
1684+ oops_str = " %s" % request.oopsid
1685 log = "%s queries/external actions issued in %.2f seconds%s" % (
1686 len(timeline.actions), secs, oops_str)
1687 return log
1688
1689=== modified file 'lib/lp/services/webapp/errorlog.py'
1690--- lib/lp/services/webapp/errorlog.py 2012-01-01 02:58:52 +0000
1691+++ lib/lp/services/webapp/errorlog.py 2012-03-08 16:34:46 +0000
1692@@ -542,18 +542,16 @@
1693 def maybe_record_user_requested_oops():
1694 """If an OOPS has been requested, report one.
1695
1696- :return: The oopsid of the requested oops. Returns None if an oops was
1697- not requested, or if there is already an OOPS.
1698+ It will be stored in request.oopsid.
1699 """
1700 request = get_current_browser_request()
1701- # If there is no request, or there is an oops already, then return.
1702- if (request is None or
1703- request.oopsid is not None or
1704- not request.annotations.get(LAZR_OOPS_USER_REQUESTED_KEY, False)):
1705- return None
1706- globalErrorUtility.raising(
1707- (UserRequestOops, UserRequestOops(), None), request)
1708- return request.oopsid
1709+ # If there's a request and no existing OOPS, but an OOPS has been
1710+ # requested, record one.
1711+ if (request is not None
1712+ and request.oopsid is None
1713+ and request.annotations.get(LAZR_OOPS_USER_REQUESTED_KEY, False)):
1714+ globalErrorUtility.raising(
1715+ (UserRequestOops, UserRequestOops(), None), request)
1716
1717
1718 class OopsNamespace(view):
1719
1720=== modified file 'lib/lp/services/webapp/tests/test_user_requested_oops.py'
1721--- lib/lp/services/webapp/tests/test_user_requested_oops.py 2012-01-01 02:58:52 +0000
1722+++ lib/lp/services/webapp/tests/test_user_requested_oops.py 2012-03-08 16:34:46 +0000
1723@@ -37,20 +37,28 @@
1724 def test_none_requested(self):
1725 # If an oops was not requested, then maybe_record_user_requested_oops
1726 # does not record an oops.
1727- oops_id = maybe_record_user_requested_oops()
1728- self.assertIs(None, oops_id)
1729 request = get_current_browser_request()
1730+ maybe_record_user_requested_oops()
1731 self.assertIs(None, request.oopsid)
1732
1733 def test_annotation_key(self):
1734- # The request for an oops is stored in the request annotations. If a
1735- # user request oops is recorded, the oops id is returned, and also
1736- # stored in the request.
1737- request = get_current_browser_request()
1738- request.annotations[LAZR_OOPS_USER_REQUESTED_KEY] = True
1739- oops_id = maybe_record_user_requested_oops()
1740- self.assertIsNot(None, oops_id)
1741- self.assertEqual(oops_id, request.oopsid)
1742+ # The request for an oops is stored in the request annotations.
1743+ # If a user request oops is recorded, the oops id is stored in
1744+ # the request.
1745+ request = get_current_browser_request()
1746+ request.annotations[LAZR_OOPS_USER_REQUESTED_KEY] = True
1747+ self.assertIs(None, request.oopsid)
1748+ maybe_record_user_requested_oops()
1749+ self.assertIsNot(None, request.oopsid)
1750+
1751+ def test_multiple_calls(self):
1752+ # Asking to record the OOPS twice just returns the same ID.
1753+ request = get_current_browser_request()
1754+ request.annotations[LAZR_OOPS_USER_REQUESTED_KEY] = True
1755+ maybe_record_user_requested_oops()
1756+ orig_oops_id = request.oopsid
1757+ maybe_record_user_requested_oops()
1758+ self.assertEqual(orig_oops_id, request.oopsid)
1759
1760 def test_existing_oops_stops_user_requested(self):
1761 # If there is already an existing oops id in the request, then the
1762@@ -58,8 +66,7 @@
1763 request = get_current_browser_request()
1764 request.oopsid = "EXISTING"
1765 request.annotations[LAZR_OOPS_USER_REQUESTED_KEY] = True
1766- oops_id = maybe_record_user_requested_oops()
1767- self.assertIs(None, oops_id)
1768+ maybe_record_user_requested_oops()
1769 self.assertEqual("EXISTING", request.oopsid)
1770
1771 def test_OopsNamespace_traverse(self):
1772
1773=== modified file 'lib/lp/soyuz/stories/soyuz/xx-builder-page.txt'
1774--- lib/lp/soyuz/stories/soyuz/xx-builder-page.txt 2012-01-15 11:06:57 +0000
1775+++ lib/lp/soyuz/stories/soyuz/xx-builder-page.txt 2012-03-08 16:34:46 +0000
1776@@ -4,6 +4,7 @@
1777 builder state. In the sampledata, the builder 'bob' is building
1778 'mozilla-firefox'.
1779
1780+ >>> anon_browser.mech_browser.set_handle_equiv(False)
1781 >>> anon_browser.open("http://launchpad.dev/builders")
1782 >>> anon_browser.getLink("bob").click()
1783
1784@@ -27,6 +28,7 @@
1785 timezone. This way they can easily find out if they are reading
1786 outdated information.
1787
1788+ >>> user_browser.mech_browser.set_handle_equiv(False)
1789 >>> user_browser.open(anon_browser.url)
1790 >>> print extract_text(find_portlet(
1791 ... user_browser.contents, 'View full history Current status'))
1792@@ -68,6 +70,7 @@
1793
1794 >>> cprov_browser = setupBrowser(
1795 ... auth='Basic celso.providelo@canonical.com:test')
1796+ >>> cprov_browser.mech_browser.set_handle_equiv(False)
1797
1798 >>> cprov_browser.open('http://launchpad.dev/builders')
1799 >>> cprov_browser.getLink('bob').click()
1800@@ -193,7 +196,7 @@
1801 when they judge it convenient, for instance, when the builder present
1802 transient failures or is used for another purpose.
1803
1804- >>> cprov_browser.open('http://launchpad.dev/+builds')
1805+ >>> cprov_browser.open('http://launchpad.dev/builders')
1806 >>> cprov_browser.getLink('bob').click()
1807 >>> print backslashreplace(cprov_browser.title)
1808 Bob The Builder : Build Farm
1809
1810=== modified file 'lib/lp/soyuz/stories/soyuz/xx-buildfarm-index.txt'
1811--- lib/lp/soyuz/stories/soyuz/xx-buildfarm-index.txt 2011-12-23 23:44:59 +0000
1812+++ lib/lp/soyuz/stories/soyuz/xx-buildfarm-index.txt 2012-03-08 16:34:46 +0000
1813@@ -4,6 +4,7 @@
1814 link to it yet because we are not yet sure of the benefits of doing
1815 this, since the audience of this page is still restricted.
1816
1817+ >>> anon_browser.mech_browser.set_handle_equiv(False)
1818 >>> anon_browser.open('http://launchpad.dev/+builds')
1819
1820 The BuildFarm contains a list of all builders registered in Launchpad
1821@@ -120,6 +121,7 @@
1822
1823 Administrators can create new builders.
1824
1825+ >>> admin_browser.mech_browser.set_handle_equiv(False)
1826 >>> admin_browser.open("http://launchpad.dev/+builds/+index")
1827
1828 >>> admin_browser.getLink("Register a new build machine").click()
1829
1830=== modified file 'lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt'
1831--- lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt 2012-01-06 11:08:30 +0000
1832+++ lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt 2012-03-08 16:34:46 +0000
1833@@ -69,7 +69,8 @@
1834
1835 For Builder, same as Distribution:
1836
1837- >>> anon_browser.open("http://launchpad.dev/+builds/bob")
1838+ >>> anon_browser.mech_browser.set_handle_equiv(False)
1839+ >>> anon_browser.open("http://launchpad.dev/builders/bob")
1840 >>> anon_browser.getLink("View full history").click()
1841 >>> print anon_browser.title
1842 Build history : Bob The Builder : Build Farm
1843
1844=== modified file 'lib/lp/soyuz/stories/soyuz/xx-private-builds.txt'
1845--- lib/lp/soyuz/stories/soyuz/xx-private-builds.txt 2012-01-15 11:06:57 +0000
1846+++ lib/lp/soyuz/stories/soyuz/xx-private-builds.txt 2012-03-08 16:34:46 +0000
1847@@ -65,6 +65,7 @@
1848 The status shown to an anonymous user hides the private source it is
1849 building.
1850
1851+ >>> anon_browser.mech_browser.set_handle_equiv(False)
1852 >>> anon_browser.open("http://launchpad.dev/+builds/frog")
1853 >>> print extract_text(find_main_content(anon_browser.contents))
1854 The frog builder...
1855@@ -74,6 +75,7 @@
1856
1857 Launchpad Administrators are allowed to see the build:
1858
1859+ >>> admin_browser.mech_browser.set_handle_equiv(False)
1860 >>> admin_browser.open("http://launchpad.dev/+builds/frog")
1861 >>> print extract_text(find_main_content(admin_browser.contents))
1862 The frog builder...
1863@@ -85,6 +87,7 @@
1864 Buildd Administrators are not allowed to see the build in the portlet:
1865
1866 >>> name12_browser = setupBrowser(auth="Basic test@canonical.com:test")
1867+ >>> name12_browser.mech_browser.set_handle_equiv(False)
1868 >>> name12_browser.open("http://launchpad.dev/+builds/frog")
1869 >>> print extract_text(find_main_content(name12_browser.contents))
1870 The frog builder...
1871@@ -96,6 +99,7 @@
1872
1873 >>> cprov_browser = setupBrowser(
1874 ... auth="Basic celso.providelo@canonical.com:test")
1875+ >>> cprov_browser.mech_browser.set_handle_equiv(False)
1876 >>> cprov_browser.open("http://launchpad.dev/+builds/frog")
1877 >>> print extract_text(find_main_content(cprov_browser.contents))
1878 The frog builder...
1879@@ -295,7 +299,6 @@
1880
1881 Now the anonymous user can see the build:
1882
1883- >>> anon_browser = setupBrowser()
1884 >>> anon_browser.open("http://launchpad.dev/+builds")
1885 >>> print extract_text(find_main_content(anon_browser.contents))
1886 The Launchpad build farm
1887@@ -307,6 +310,7 @@
1888
1889 Any other logged-in user will also see the build:
1890
1891+ >>> browser.mech_browser.set_handle_equiv(False)
1892 >>> browser.open("http://launchpad.dev/+builds")
1893 >>> print extract_text(find_main_content(browser.contents))
1894 The Launchpad build farm
1895
1896=== modified file 'lib/lp/translations/stories/buildfarm/xx-build-summary.txt'
1897--- lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2012-02-09 23:09:36 +0000
1898+++ lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2012-03-08 16:34:46 +0000
1899@@ -71,6 +71,7 @@
1900 The job's summary shows that what type of job this is. It also links
1901 to the branch.
1902
1903+ >>> user_browser.mech_browser.set_handle_equiv(False)
1904 >>> user_browser.open(builder_page)
1905 >>> print extract_text(find_build_summary(user_browser))
1906 Working on TranslationTemplatesBuildJob for branch ...
1907
1908=== modified file 'utilities/setuplxc.py'
1909--- utilities/setuplxc.py 2012-03-06 00:11:17 +0000
1910+++ utilities/setuplxc.py 2012-03-08 16:34:46 +0000
1911@@ -761,13 +761,12 @@
1912 LP_SOURCE_DEPS, 'download-cache'])
1913
1914
1915-def create_scripts(user, directory, lxcname, ssh_key_path):
1916+def create_scripts(user, lxcname, ssh_key_path):
1917 """Create scripts to update the Launchpad environment and run tests."""
1918 mapping = {
1919- 'user': user,
1920 'lxcname': lxcname,
1921 'ssh_key_path': ssh_key_path,
1922- 'checkout_dir': os.path.join(directory, LP_CHECKOUT),
1923+ 'user': user,
1924 }
1925 # We need a script that will run the LP build inside LXC. It is run as
1926 # root (see below) but drops root once inside the LXC container.
1927@@ -775,12 +774,22 @@
1928 with open(build_script_file, 'w') as script:
1929 script.write(textwrap.dedent("""\
1930 #!/bin/sh
1931- set -uex
1932+ set -ux
1933 lxc-start -n {lxcname} -d
1934 lxc-wait -n {lxcname} -s RUNNING
1935- sleep 30 # aparently RUNNING isn't quite enough
1936- su {user} -c "/usr/bin/ssh -o StrictHostKeyChecking=no \\
1937- -i '{ssh_key_path}' {lxcname} make -C $PWD schema"
1938+ for i in $(seq 1 30); do
1939+ su {user} -c "/usr/bin/ssh -o StrictHostKeyChecking=no \\
1940+ -i '{ssh_key_path}' {lxcname} make -C $PWD schema"
1941+ if [ ! 255 -eq $? ]; then
1942+ # If ssh returns 255 then its connection failed.
1943+ # Anything else is either success (status 0) or a
1944+ # failure from whatever we ran over the SSH connection.
1945+ # In those cases we want to stop looping, so we break
1946+ # here.
1947+ break;
1948+ fi
1949+ sleep 1
1950+ done
1951 lxc-stop -n {lxcname}
1952 lxc-wait -n {lxcname} -s STOPPED
1953 """.format(**mapping)))
1954@@ -955,7 +964,7 @@
1955 ('initialize_host', (
1956 user, fullname, email, lpuser, private_key, public_key,
1957 ssh_key_path, dependencies_dir, directory)),
1958- ('create_scripts', (user, directory, lxc_name, ssh_key_path)),
1959+ ('create_scripts', (user, lxc_name, ssh_key_path)),
1960 ('create_lxc', (user, lxc_name, ssh_key_path)),
1961 ('initialize_lxc', (
1962 user, dependencies_dir, directory, lxc_name, ssh_key_path)),

Subscribers

People subscribed via source and target branches

to all changes: