Merge lp:~twom/launchpad/sometimes-mirrors-are-broken into lp:launchpad

Proposed by Tom Wardill
Status: Merged
Merged at revision: 19045
Proposed branch: lp:~twom/launchpad/sometimes-mirrors-are-broken
Merge into: lp:launchpad
Diff against target: 292 lines (+127/-4)
9 files modified
lib/lp/registry/browser/configure.zcml (+7/-0)
lib/lp/registry/browser/distributionmirror.py (+35/-2)
lib/lp/registry/browser/tests/distributionmirror-views.txt (+27/-1)
lib/lp/registry/configure.zcml (+1/-1)
lib/lp/registry/errors.py (+6/-0)
lib/lp/registry/interfaces/distributionmirror.py (+13/-0)
lib/lp/registry/model/distributionmirror.py (+8/-0)
lib/lp/registry/templates/distributionmirror-resubmit.pt (+18/-0)
lib/lp/registry/tests/test_distributionmirror.py (+12/-0)
To merge this branch: bzr merge lp:~twom/launchpad/sometimes-mirrors-are-broken
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+372015@code.launchpad.net

Commit message

Add a BROKEN state for mirrors and allow them to be resubmitted

Description of the change

A mirror can be submitted for review that obviously does not work, even before probing (DNS failure, incorrect url, etc).
Currently the only option is to mark these as 'UNOFFICIAL' or they clutter the pendingreview view.

This MP adds a 'BROKEN' state, and allows the owner of the archive to resubmit the archive once it is fixed or the details edited.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

This mostly looks good, but needs another pass to tighten things up.

Could you post screenshots somewhere of DistributionMirror:+index and DistributionMirror:+resubmit with these changes? I'd like to see how it'll be presented to mirror admins.

review: Needs Fixing
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Updating diff...

An updated diff will be available in a few minutes. Reload to see the changes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/configure.zcml'
2--- lib/lp/registry/browser/configure.zcml 2017-07-31 11:45:32 +0000
3+++ lib/lp/registry/browser/configure.zcml 2019-09-03 07:12:58 +0000
4@@ -2439,6 +2439,13 @@
5 template="../templates/distributionmirror-edit.pt">
6 </browser:page>
7 <browser:page
8+ name="+resubmit"
9+ for="lp.registry.interfaces.distributionmirror.IDistributionMirror"
10+ class="lp.registry.browser.distributionmirror.DistributionMirrorResubmitView"
11+ permission="launchpad.Edit"
12+ template="../templates/distributionmirror-resubmit.pt">
13+ </browser:page>
14+ <browser:page
15 name="+review"
16 for="lp.registry.interfaces.distributionmirror.IDistributionMirror"
17 class="lp.registry.browser.distributionmirror.DistributionMirrorReviewView"
18
19=== modified file 'lib/lp/registry/browser/distributionmirror.py'
20--- lib/lp/registry/browser/distributionmirror.py 2015-10-13 13:22:08 +0000
21+++ lib/lp/registry/browser/distributionmirror.py 2019-09-03 07:12:58 +0000
22@@ -30,8 +30,12 @@
23 )
24 from lp.archivepublisher.debversion import Version
25 from lp.registry.browser.objectreassignment import ObjectReassignmentView
26+from lp.registry.errors import InvalidMirrorReviewState
27 from lp.registry.interfaces.distribution import IDistributionMirrorMenuMarker
28-from lp.registry.interfaces.distributionmirror import IDistributionMirror
29+from lp.registry.interfaces.distributionmirror import (
30+ IDistributionMirror,
31+ MirrorStatus,
32+ )
33 from lp.services.propertycache import cachedproperty
34 from lp.services.webapp import (
35 canonical_url,
36@@ -52,7 +56,7 @@
37
38 usedfor = IDistributionMirror
39 facet = 'overview'
40- links = ['proberlogs', 'edit', 'review', 'reassign', 'delete']
41+ links = ['proberlogs', 'edit', 'review', 'reassign', 'delete', 'resubmit']
42
43 @enabled_with_permission('launchpad.Edit')
44 def edit(self):
45@@ -81,6 +85,12 @@
46 text = 'Review mirror'
47 return Link('+review', text, icon='edit')
48
49+ @enabled_with_permission('launchpad.Edit')
50+ def resubmit(self):
51+ text = 'Resubmit for review'
52+ enabled = self.context.status == MirrorStatus.BROKEN
53+ return Link('+resubmit', text, icon='edit', enabled=enabled)
54+
55
56 class _FlavoursByDistroSeries:
57 """A simple class to help when rendering a table of series and flavours
58@@ -293,6 +303,29 @@
59 self.next_url = canonical_url(self.context)
60
61
62+class DistributionMirrorResubmitView(LaunchpadEditFormView):
63+
64+ schema = IDistributionMirror
65+ field_names = []
66+
67+ @property
68+ def label(self):
69+ """See `LaunchpadFormView`."""
70+ return 'Resubmit mirror %s' % self.context.title
71+
72+ page_title = label
73+
74+ @action(_("Resubmit"), name="resubmit")
75+ def action_resubmit(self, action, data):
76+ try:
77+ self.context.resubmitForReview()
78+ except InvalidMirrorReviewState:
79+ self.request.response.addInfoNotification(
80+ "The mirror is not in the correct state"
81+ " (broken) and cannot be resubmitted.")
82+ self.next_url = canonical_url(self.context)
83+
84+
85 class DistributionMirrorReassignmentView(ObjectReassignmentView):
86
87 @property
88
89=== modified file 'lib/lp/registry/browser/tests/distributionmirror-views.txt'
90--- lib/lp/registry/browser/tests/distributionmirror-views.txt 2019-05-22 14:57:45 +0000
91+++ lib/lp/registry/browser/tests/distributionmirror-views.txt 2019-09-03 07:12:58 +0000
92@@ -283,6 +283,32 @@
93 False
94
95
96+Resubmit distribution mirror
97+----------------------------
98+
99+The mirror owner can resubmit a 'Broken' mirror for a new review.
100+
101+ >>> login('karl@canonical.com')
102+ >>> review_form['field.status'] = 'BROKEN'
103+ >>> review_form['field.whiteboard'] = 'This site is broken.'
104+ >>> view = create_initialized_view(mirror, '+review', form=review_form)
105+ >>> form['field.actions.resubmit'] = 'Resubmit'
106+ >>> view = create_initialized_view(mirror, '+resubmit', form=form)
107+ >>> print mirror.status.name
108+ PENDING_REVIEW
109+
110+The resubmit view should only be available to people with launchpad.Edit.
111+
112+ >>> login('karl@canonical.com')
113+ >>> view = create_initialized_view(mirror, '+resubmit')
114+ >>> check_permission('launchpad.Edit', view)
115+ True
116+
117+ >>> login('no-priv@canonical.com')
118+ >>> check_permission('launchpad.Edit', view)
119+ False
120+
121+
122 Delete distribution mirror
123 --------------------------
124
125@@ -376,7 +402,7 @@
126 Antarctica
127
128 The page shows which kind of mirror a mirror is:
129-
130+
131 >>> print extract_text(find_tag_by_id(content, 'type'))
132 Type:
133 Archive
134
135=== modified file 'lib/lp/registry/configure.zcml'
136--- lib/lp/registry/configure.zcml 2018-04-22 23:30:37 +0000
137+++ lib/lp/registry/configure.zcml 2019-09-03 07:12:58 +0000
138@@ -2028,7 +2028,7 @@
139 set_attributes="name display_name description whiteboard
140 http_base_url ftp_base_url rsync_base_url enabled
141 speed country content official_candidate owner"
142- attributes="official_candidate whiteboard" />
143+ attributes="official_candidate whiteboard resubmitForReview" />
144 <require
145 permission="launchpad.Admin"
146 set_attributes="status reviewer date_reviewed"
147
148=== modified file 'lib/lp/registry/errors.py'
149--- lib/lp/registry/errors.py 2016-05-24 03:39:36 +0000
150+++ lib/lp/registry/errors.py 2019-09-03 07:12:58 +0000
151@@ -13,6 +13,7 @@
152 'CountryMirrorAlreadySet',
153 'DeleteSubscriptionError',
154 'InvalidFilename',
155+ 'InvalidMirrorReviewState',
156 'InvalidName',
157 'JoinNotAllowed',
158 'MirrorNotOfficial',
159@@ -223,6 +224,11 @@
160 """The information type cannot be changed."""
161
162
163+@error_status(httplib.BAD_REQUEST)
164+class InvalidMirrorReviewState(Exception):
165+ """The mirror is in an invalid state in the review workflow."""
166+
167+
168 class CannotPackageProprietaryProduct(Exception):
169 """Raised when a non-PUBLIC product's series is linked to a package."""
170
171
172=== modified file 'lib/lp/registry/interfaces/distributionmirror.py'
173--- lib/lp/registry/interfaces/distributionmirror.py 2015-10-26 14:54:43 +0000
174+++ lib/lp/registry/interfaces/distributionmirror.py 2019-09-03 07:12:58 +0000
175@@ -244,6 +244,13 @@
176 the official mirrors for its distribution.
177 """)
178
179+ BROKEN = DBItem(40, """
180+ Broken
181+
182+ This mirror has been reviewed and seems to not respond or
183+ is otherwise misconfigured.
184+ """)
185+
186
187 class DistributionMirrorNameField(ContentNameField):
188 errormessage = _("%s is already in use by another distribution mirror.")
189@@ -502,6 +509,12 @@
190 previously enabled or if it was probed only once.
191 """
192
193+ def resubmitForReview():
194+ """Allow the owner (launchpad.Edit) to resubmit for review.
195+
196+ Only allows the transition state from 'Broken' to 'Pending Review'.
197+ """
198+
199 def newProbeRecord(log_file):
200 """Create and return a new MirrorProbeRecord for this mirror."""
201
202
203=== modified file 'lib/lp/registry/model/distributionmirror.py'
204--- lib/lp/registry/model/distributionmirror.py 2015-10-13 13:22:08 +0000
205+++ lib/lp/registry/model/distributionmirror.py 2019-09-03 07:12:58 +0000
206@@ -39,6 +39,7 @@
207 from lp.registry.errors import (
208 CannotTransitionToCountryMirror,
209 CountryMirrorAlreadySet,
210+ InvalidMirrorReviewState,
211 MirrorHasNoHTTPURL,
212 MirrorNotOfficial,
213 MirrorNotProbed,
214@@ -331,6 +332,13 @@
215 return (self.official_candidate
216 and self.status == MirrorStatus.OFFICIAL)
217
218+ def resubmitForReview(self):
219+ """See IDistributionMirror"""
220+ if self.status != MirrorStatus.BROKEN:
221+ raise InvalidMirrorReviewState(
222+ "DistributionMirror.status is not BROKEN")
223+ self.status = MirrorStatus.PENDING_REVIEW
224+
225 def shouldDisable(self, expected_file_count=None):
226 """See IDistributionMirror"""
227 if self.content == MirrorContent.RELEASE:
228
229=== added file 'lib/lp/registry/templates/distributionmirror-resubmit.pt'
230--- lib/lp/registry/templates/distributionmirror-resubmit.pt 1970-01-01 00:00:00 +0000
231+++ lib/lp/registry/templates/distributionmirror-resubmit.pt 2019-09-03 07:12:58 +0000
232@@ -0,0 +1,18 @@
233+<html
234+ xmlns="http://www.w3.org/1999/xhtml"
235+ xmlns:tal="http://xml.zope.org/namespaces/tal"
236+ xmlns:metal="http://xml.zope.org/namespaces/metal"
237+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
238+ metal:use-macro="view/macro:page/main_only"
239+ i18n:domain="launchpad">
240+ <body>
241+ <div metal:fill-slot="main">
242+ <div metal:use-macro="context/@@launchpad_form/form">
243+ <p metal:fill-slot="extra_top" class="documentDescription">
244+ This will resubmit this mirror to the review queue and set
245+ the status to 'Pending review'.
246+ </p>
247+ </div>
248+ </div>
249+ </body>
250+</html>
251
252=== modified file 'lib/lp/registry/tests/test_distributionmirror.py'
253--- lib/lp/registry/tests/test_distributionmirror.py 2018-01-02 10:54:31 +0000
254+++ lib/lp/registry/tests/test_distributionmirror.py 2019-09-03 07:12:58 +0000
255@@ -5,6 +5,7 @@
256
257 import transaction
258 from zope.component import getUtility
259+from zope.security.interfaces import Unauthorized
260 from zope.security.proxy import removeSecurityProxy
261
262 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
263@@ -13,11 +14,13 @@
264 IDistributionMirrorSet,
265 MirrorContent,
266 MirrorFreshness,
267+ MirrorStatus,
268 )
269 from lp.registry.interfaces.pocket import PackagePublishingPocket
270 from lp.registry.model.distributionmirror import DistributionMirror
271 from lp.services.database.sqlbase import flush_database_updates
272 from lp.services.mail import stub
273+from lp.services.webapp.authorization import check_permission
274 from lp.services.worlddata.interfaces.country import ICountrySet
275 from lp.testing import (
276 login,
277@@ -224,6 +227,15 @@
278 transaction.commit()
279 self.assertEqual(len(stub.test_emails), 1)
280
281+ def test_permissions_for_resubmit(self):
282+ self.assertRaises(
283+ Unauthorized, getattr, self.archive_mirror, 'resubmitForReview')
284+ login_as(self.archive_mirror.owner)
285+ self.archive_mirror.status = MirrorStatus.BROKEN
286+ self.archive_mirror.resubmitForReview()
287+ self.assertEqual(
288+ MirrorStatus.PENDING_REVIEW, self.archive_mirror.status)
289+
290
291 class TestDistributionMirrorSet(TestCase):
292 layer = LaunchpadFunctionalLayer