Merge lp:~danilo/launchpad/bug-1000148 into lp:launchpad

Proposed by Данило Шеган
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 15558
Proposed branch: lp:~danilo/launchpad/bug-1000148
Merge into: lp:launchpad
Diff against target: 737 lines (+1/-622)
5 files modified
database/schema/security.cfg (+0/-5)
lib/lp/blueprints/tests/test_workitem_migration.py (+0/-262)
lib/lp/blueprints/workitemmigration.py (+0/-162)
lib/lp/scripts/garbo.py (+0/-105)
lib/lp/scripts/tests/test_garbo.py (+1/-88)
To merge this branch: bzr merge lp:~danilo/launchpad/bug-1000148
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+105962@code.launchpad.net

Commit message

Remove work items migration script from the garbo job.

Description of the change

= Bug 1000148: remove work items migration script =

Work items migration script has served its purpose: work items have been migrated from the whiteboard to the new dedicated DB objects for projects that wanted it (Ubuntu, in particular, didn't want it because they were close to a release, and they decided to just switch to the new fields for the next Ubuntu cycle).

We don't need this script anymore, so I'd like to remove it. This should reduce the maintenance cost for LP significantly.

This basically reverts a few revisions that included work on the migration script: r14893, r14898, r14939.

= Tests =

Full test suite run.

= QA =

Test that the garbo job keeps running correctly.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  database/schema/security.cfg
  lib/lp/scripts/garbo.py
  lib/lp/scripts/tests/test_garbo.py

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

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-07-04 22:38:14 +0000
3+++ database/schema/security.cfg 2012-07-05 11:09:39 +0000
4@@ -2226,13 +2226,11 @@
5 public.codeimportevent = SELECT, DELETE
6 public.codeimporteventdata = SELECT, DELETE
7 public.codeimportresult = SELECT, DELETE
8-public.distribution = SELECT
9 public.emailaddress = SELECT, UPDATE, DELETE
10 public.hwsubmission = SELECT, UPDATE
11 public.job = SELECT, INSERT, DELETE
12 public.logintoken = SELECT, DELETE
13 public.mailinglistsubscription = SELECT, DELETE
14-public.milestone = SELECT
15 public.milestonetag = SELECT
16 public.oauthnonce = SELECT, DELETE
17 public.openidconsumerassociation = SELECT, DELETE
18@@ -2241,14 +2239,11 @@
19 public.pofiletranslator = SELECT, INSERT, UPDATE, DELETE
20 public.potranslation = SELECT, DELETE
21 public.potmsgset = SELECT, DELETE
22-public.product = SELECT
23 public.revisionauthor = SELECT, UPDATE
24 public.revisioncache = SELECT, DELETE
25 public.sourcepackagename = SELECT
26 public.sourcepackagerelease = SELECT
27 public.sourcepackagepublishinghistory = SELECT, UPDATE
28-public.specification = SELECT, UPDATE
29-public.specificationworkitem = SELECT, INSERT
30 public.suggestivepotemplate = INSERT, DELETE
31 public.teamparticipation = SELECT, DELETE
32 public.translationmessage = SELECT, DELETE
33
34=== removed file 'lib/lp/blueprints/tests/test_workitem_migration.py'
35--- lib/lp/blueprints/tests/test_workitem_migration.py 2012-03-22 23:21:24 +0000
36+++ lib/lp/blueprints/tests/test_workitem_migration.py 1970-01-01 00:00:00 +0000
37@@ -1,262 +0,0 @@
38-# Copyright 2012 Canonical Ltd. This software is licensed under the
39-# GNU Affero General Public License version 3 (see the file LICENSE).
40-
41-__metaclass__ = type
42-
43-from textwrap import dedent
44-
45-from testtools.matchers import MatchesStructure
46-
47-from lp.blueprints.enums import SpecificationWorkItemStatus
48-from lp.blueprints.workitemmigration import (
49- extractWorkItemsFromWhiteboard,
50- WorkItemParseError,
51- WorkitemParser,
52- )
53-from lp.testing import (
54- TestCase,
55- TestCaseWithFactory,
56- )
57-from lp.testing.layers import DatabaseFunctionalLayer
58-
59-
60-class FakeSpecification(object):
61- assignee = None
62-
63-
64-class TestWorkItemParser(TestCase):
65-
66- def test_parse_line_basic(self):
67- parser = WorkitemParser(FakeSpecification())
68- assignee, description, status = parser.parse_blueprint_workitem(
69- "A single work item: TODO")
70- self.assertEqual(
71- [None, "A single work item", SpecificationWorkItemStatus.TODO],
72- [assignee, description, status])
73-
74- def test_parse_line_with_assignee(self):
75- parser = WorkitemParser(FakeSpecification())
76- assignee, description, status = parser.parse_blueprint_workitem(
77- "[salgado] A single work item: TODO")
78- self.assertEqual(
79- ["salgado", "A single work item",
80- SpecificationWorkItemStatus.TODO],
81- [assignee, description, status])
82-
83- def test_parse_line_with_missing_closing_bracket_for_assignee(self):
84- parser = WorkitemParser(FakeSpecification())
85- self.assertRaises(
86- WorkItemParseError, parser.parse_blueprint_workitem,
87- "[salgado A single work item: TODO")
88-
89- def test_parse_line_without_status(self):
90- parser = WorkitemParser(FakeSpecification())
91- assignee, description, status = parser.parse_blueprint_workitem(
92- "A single work item")
93- self.assertEqual(
94- [None, "A single work item", SpecificationWorkItemStatus.TODO],
95- [assignee, description, status])
96-
97- def test_parse_line_with_invalid_status(self):
98- parser = WorkitemParser(FakeSpecification())
99- self.assertRaises(
100- WorkItemParseError, parser.parse_blueprint_workitem,
101- "A single work item: FOO")
102-
103- def test_parse_line_without_description(self):
104- parser = WorkitemParser(FakeSpecification())
105- self.assertRaises(
106- WorkItemParseError, parser.parse_blueprint_workitem,
107- " : TODO")
108-
109- def test_parse_line_with_completed_status(self):
110- parser = WorkitemParser(FakeSpecification())
111- assignee, description, status = parser.parse_blueprint_workitem(
112- "A single work item: Completed")
113- self.assertEqual(
114- [None, "A single work item", SpecificationWorkItemStatus.DONE],
115- [assignee, description, status])
116-
117- def test_parse_line_with_inprogress_status(self):
118- parser = WorkitemParser(FakeSpecification())
119- assignee, description, status = parser.parse_blueprint_workitem(
120- "A single work item: INPROGRESS")
121- self.assertEqual(
122- [None, "A single work item",
123- SpecificationWorkItemStatus.INPROGRESS],
124- [assignee, description, status])
125-
126- def test_parse_line_with_postpone_status(self):
127- parser = WorkitemParser(FakeSpecification())
128- assignee, description, status = parser.parse_blueprint_workitem(
129- "A single work item: POSTPONE")
130- self.assertEqual(
131- [None, "A single work item",
132- SpecificationWorkItemStatus.POSTPONED],
133- [assignee, description, status])
134-
135- def test_parse_line_with_drop_status(self):
136- parser = WorkitemParser(FakeSpecification())
137- assignee, description, status = parser.parse_blueprint_workitem(
138- "A single work item: DROP")
139- self.assertEqual(
140- [None, "A single work item",
141- SpecificationWorkItemStatus.POSTPONED],
142- [assignee, description, status])
143-
144- def test_parse_line_with_dropped_status(self):
145- parser = WorkitemParser(FakeSpecification())
146- assignee, description, status = parser.parse_blueprint_workitem(
147- "A single work item: DROPPED")
148- self.assertEqual(
149- [None, "A single work item",
150- SpecificationWorkItemStatus.POSTPONED],
151- [assignee, description, status])
152-
153- def test_parse_empty_line(self):
154- parser = WorkitemParser(FakeSpecification())
155- self.assertRaises(
156- AssertionError, parser.parse_blueprint_workitem, "")
157-
158-
159-class TestSpecificationWorkItemExtractionFromWhiteboard(TestCaseWithFactory):
160- layer = DatabaseFunctionalLayer
161-
162- def test_None_whiteboard(self):
163- spec = self.factory.makeSpecification(whiteboard=None)
164- work_items = extractWorkItemsFromWhiteboard(spec)
165- self.assertEqual([], work_items)
166-
167- def test_empty_whiteboard(self):
168- spec = self.factory.makeSpecification(whiteboard='')
169- work_items = extractWorkItemsFromWhiteboard(spec)
170- self.assertEqual([], work_items)
171-
172- def test_single_work_item(self):
173- whiteboard = dedent("""
174- Work items:
175- A single work item: TODO
176- """)
177- spec = self.factory.makeSpecification(whiteboard=whiteboard)
178- work_items = extractWorkItemsFromWhiteboard(spec)
179- self.assertEqual(1, len(work_items))
180- self.assertThat(work_items[0], MatchesStructure.byEquality(
181- assignee=None, title="A single work item", milestone=None,
182- status=SpecificationWorkItemStatus.TODO, specification=spec))
183-
184- def test_multiple_work_items(self):
185- whiteboard = dedent("""
186- Work items:
187- A single work item: TODO
188- Another work item: DONE
189- """)
190- spec = self.factory.makeSpecification(whiteboard=whiteboard)
191- work_items = extractWorkItemsFromWhiteboard(spec)
192- self.assertEqual(2, len(work_items))
193- self.assertThat(work_items[0], MatchesStructure.byEquality(
194- assignee=None, title="A single work item", milestone=None,
195- status=SpecificationWorkItemStatus.TODO, specification=spec))
196- self.assertThat(work_items[1], MatchesStructure.byEquality(
197- assignee=None, title="Another work item", milestone=None,
198- status=SpecificationWorkItemStatus.DONE, specification=spec))
199-
200- def test_work_item_with_assignee(self):
201- person = self.factory.makePerson()
202- whiteboard = dedent("""
203- Work items:
204- [%s] A single work item: TODO
205- """ % person.name)
206- spec = self.factory.makeSpecification(whiteboard=whiteboard)
207- work_items = extractWorkItemsFromWhiteboard(spec)
208- self.assertEqual(1, len(work_items))
209- self.assertThat(work_items[0], MatchesStructure.byEquality(
210- assignee=person, title="A single work item", milestone=None,
211- status=SpecificationWorkItemStatus.TODO, specification=spec))
212-
213- def test_work_item_with_nonexistent_assignee(self):
214- whiteboard = dedent("""
215- Work items:
216- [nonexistentperson] A single work item: TODO""")
217- spec = self.factory.makeSpecification(whiteboard=whiteboard)
218- self.assertRaises(ValueError, extractWorkItemsFromWhiteboard, spec)
219-
220- def test_work_item_with_milestone(self):
221- milestone = self.factory.makeMilestone()
222- whiteboard = dedent("""
223- Work items for %s:
224- A single work item: TODO
225- """ % milestone.name)
226- spec = self.factory.makeSpecification(
227- whiteboard=whiteboard, product=milestone.product)
228- work_items = extractWorkItemsFromWhiteboard(spec)
229- self.assertEqual(1, len(work_items))
230- self.assertThat(work_items[0], MatchesStructure.byEquality(
231- assignee=None, title="A single work item", milestone=milestone,
232- status=SpecificationWorkItemStatus.TODO, specification=spec))
233-
234- def test_work_item_with_inactive_milestone(self):
235- milestone = self.factory.makeMilestone(active=False)
236- whiteboard = dedent("""
237- Work items for %s:
238- A single work item: TODO
239- """ % milestone.name)
240- spec = self.factory.makeSpecification(
241- whiteboard=whiteboard, product=milestone.product)
242- work_items = extractWorkItemsFromWhiteboard(spec)
243- self.assertEqual(1, len(work_items))
244- self.assertThat(work_items[0], MatchesStructure.byEquality(
245- assignee=None, title="A single work item", milestone=milestone,
246- status=SpecificationWorkItemStatus.TODO, specification=spec))
247-
248- def test_work_item_with_unknown_milestone(self):
249- whiteboard = dedent("""
250- Work items for foo:
251- A single work item: TODO
252- """)
253- spec = self.factory.makeSpecification(whiteboard=whiteboard)
254- self.assertRaises(
255- WorkItemParseError, extractWorkItemsFromWhiteboard, spec)
256-
257- def test_blank_line_signals_end_of_work_item_block(self):
258- whiteboard = dedent("""
259- Work items:
260- A single work item: TODO
261-
262- Some random notes about this BP.
263- * This is what was discussed during UDS
264- * Oh, yeah, we need to do that too
265- """)
266- spec = self.factory.makeSpecification(whiteboard=whiteboard)
267- work_items = extractWorkItemsFromWhiteboard(spec)
268- self.assertEqual(1, len(work_items))
269- self.assertThat(work_items[0], MatchesStructure.byEquality(
270- assignee=None, title="A single work item",
271- status=SpecificationWorkItemStatus.TODO, specification=spec))
272-
273- def test_whiteboard_with_all_possible_sections(self):
274- whiteboard = dedent("""
275- Work items:
276- A single work item: TODO
277-
278- Meta:
279- Headline: Foo bar
280- Acceptance: Baz foo
281-
282- Complexity:
283- [user1] milestone1: 10
284- """)
285- spec = self.factory.makeSpecification(whiteboard=whiteboard)
286- work_items = extractWorkItemsFromWhiteboard(spec)
287- self.assertEqual(1, len(work_items))
288- self.assertThat(work_items[0], MatchesStructure.byEquality(
289- assignee=None, title="A single work item", milestone=None,
290- status=SpecificationWorkItemStatus.TODO, specification=spec))
291-
292- # Now assert that the work items were removed from the whiteboard.
293- self.assertEqual(dedent("""
294- Meta:
295- Headline: Foo bar
296- Acceptance: Baz foo
297-
298- Complexity:
299- [user1] milestone1: 10""").strip(), spec.whiteboard.strip())
300
301=== removed file 'lib/lp/blueprints/workitemmigration.py'
302--- lib/lp/blueprints/workitemmigration.py 2012-04-02 05:42:19 +0000
303+++ lib/lp/blueprints/workitemmigration.py 1970-01-01 00:00:00 +0000
304@@ -1,162 +0,0 @@
305-# Copyright 2012 Canonical Ltd. This software is licensed under the
306-# GNU Affero General Public License version 3 (see the file LICENSE).
307-
308-"""Helper functions for the migration of work items from whiteboards to the
309-SpecificationWorkItem table.
310-
311-This will be removed once the migration is done.
312-"""
313-
314-__metaclass__ = type
315-__all__ = [
316- 'extractWorkItemsFromWhiteboard',
317- ]
318-
319-import re
320-
321-from zope.component import getUtility
322-from zope.security.proxy import removeSecurityProxy
323-
324-from lp.blueprints.enums import SpecificationWorkItemStatus
325-from lp.registry.interfaces.person import IPersonSet
326-
327-
328-class WorkItemParseError(Exception):
329- """An error when parsing a work item line from a blueprint's whiteboard."""
330-
331-
332-class WorkitemParser(object):
333- """A parser to extract work items from Blueprint whiteboards."""
334-
335- def __init__(self, blueprint):
336- self.blueprint = blueprint
337-
338- def _normalize_status(self, status, desc):
339- status = status.strip().lower()
340- if not status:
341- status = SpecificationWorkItemStatus.TODO
342- elif status == u'completed':
343- status = SpecificationWorkItemStatus.DONE
344- elif status in (u'postpone', u'dropped', u'drop'):
345- status = SpecificationWorkItemStatus.POSTPONED
346- else:
347- valid_statuses = SpecificationWorkItemStatus.items
348- if status not in [item.name.lower() for item in valid_statuses]:
349- raise WorkItemParseError('Unknown status: %s' % status)
350- return valid_statuses[status.upper()]
351- return status
352-
353- def _parse_line(self, line):
354- try:
355- desc, status = line.rsplit(':', 1)
356- except ValueError:
357- desc = line
358- status = ""
359- assignee_name = None
360- if desc.startswith('['):
361- if ']' in desc:
362- off = desc.index(']')
363- assignee_name = desc[1:off]
364- desc = desc[off + 1:].strip()
365- else:
366- raise WorkItemParseError('Missing closing "]" for assignee')
367- return assignee_name, desc, status
368-
369- def parse_blueprint_workitem(self, line):
370- line = line.strip()
371- assert line, "Please don't give us an empty line"
372- assignee_name, desc, status = self._parse_line(line)
373- if not desc:
374- raise WorkItemParseError(
375- 'No work item description found on "%s"' % line)
376- status = self._normalize_status(status, desc)
377- return assignee_name, desc, status
378-
379-
380-def milestone_extract(text, valid_milestones):
381- words = text.replace('(', ' ').replace(')', ' ').replace(
382- '[', ' ').replace(']', ' ').replace('<wbr />', '').split()
383- for milestone in valid_milestones:
384- for word in words:
385- if word == milestone.name:
386- return milestone
387- raise WorkItemParseError(
388- "No valid milestones found in %s. Valid milestone names are %s"
389- % (words, [m.name for m in valid_milestones]))
390-
391-
392-def extractWorkItemsFromWhiteboard(spec):
393- work_items = []
394- if not spec.whiteboard:
395- return work_items
396- work_items_re = re.compile('^work items(.*)\s*:\s*$', re.I)
397- meta_re = re.compile('^Meta.*?:$', re.I)
398- complexity_re = re.compile('^Complexity.*?:$', re.I)
399- in_wi_block = False
400- new_whiteboard = []
401-
402- target_milestones = list(spec.target.all_milestones)
403- wi_lines = []
404- # Iterate over all lines in the whiteboard and whenever we find a line
405- # matching work_items_re we 'continue' and store the following lines
406- # until we reach the end of the whiteboard or a line matching meta_re or
407- # complexity_re.
408- for line in spec.whiteboard.splitlines():
409- new_whiteboard.append(line)
410- wi_match = work_items_re.search(line)
411- if wi_match:
412- in_wi_block = True
413- milestone = None
414- milestone_part = wi_match.group(1).strip()
415- if milestone_part:
416- milestone = milestone_extract(
417- milestone_part, target_milestones)
418- new_whiteboard.pop()
419- continue
420- if meta_re.search(line):
421- milestone = None
422- in_wi_block = False
423- continue
424- if complexity_re.search(line):
425- milestone = None
426- in_wi_block = False
427- continue
428-
429- if not in_wi_block:
430- # We only care about work-item lines.
431- continue
432-
433- if line.strip() == '':
434- # An empty line signals the end of the work-item block:
435- # https://wiki.ubuntu.com/WorkItemsHowto.
436- in_wi_block = False
437- milestone = None
438- continue
439-
440- # This is a work-item line, which we don't want in the new
441- # whiteboard because we're migrating them into the
442- # SpecificationWorkItem table.
443- new_whiteboard.pop()
444-
445- wi_lines.append((line, milestone))
446-
447- # Now parse the work item lines and store them in SpecificationWorkItem.
448- parser = WorkitemParser(spec)
449- sequence = 0
450- for line, milestone in wi_lines:
451- assignee_name, title, status = parser.parse_blueprint_workitem(line)
452- if assignee_name is not None:
453- assignee_name = assignee_name.strip()
454- assignee = getUtility(IPersonSet).getByName(assignee_name)
455- if assignee is None:
456- raise ValueError("Unknown person name: %s" % assignee_name)
457- else:
458- assignee = None
459- workitem = removeSecurityProxy(spec).newWorkItem(
460- status=status, title=title, assignee=assignee,
461- milestone=milestone, sequence=sequence)
462- work_items.append(workitem)
463- sequence += 1
464-
465- removeSecurityProxy(spec).whiteboard = "\n".join(new_whiteboard)
466- return work_items
467
468=== modified file 'lib/lp/scripts/garbo.py'
469--- lib/lp/scripts/garbo.py 2012-07-04 22:38:14 +0000
470+++ lib/lp/scripts/garbo.py 2012-07-05 11:09:39 +0000
471@@ -38,8 +38,6 @@
472 from zope.security.proxy import removeSecurityProxy
473
474 from lp.answers.model.answercontact import AnswerContact
475-from lp.blueprints.model.specification import Specification
476-from lp.blueprints.workitemmigration import extractWorkItemsFromWhiteboard
477 from lp.bugs.interfaces.bug import IBugSet
478 from lp.bugs.model.bug import Bug
479 from lp.bugs.model.bugattachment import BugAttachment
480@@ -65,7 +63,6 @@
481 from lp.services.database.lpstorm import IMasterStore
482 from lp.services.database.sqlbase import (
483 cursor,
484- quote_like,
485 session_store,
486 sqlvalues,
487 )
488@@ -995,107 +992,6 @@
489 transaction.commit()
490
491
492-class SpecificationWorkitemMigrator(TunableLoop):
493- """Migrate work-items from Specification.whiteboard to
494- SpecificationWorkItem.
495-
496- Migrating work items from the whiteboard is an all-or-nothing thing; if we
497- encounter any errors when parsing the whiteboard of a spec, we abort the
498- transaction and leave its whiteboard unchanged.
499-
500- On a test with production data, only 100 whiteboards (out of almost 2500)
501- could not be migrated. On 24 of those the assignee in at least one work
502- item is not valid, on 33 the status of a work item is not valid and on 42
503- one or more milestones are not valid.
504- """
505-
506- maximum_chunk_size = 500
507- offset = 0
508- projects_to_migrate = [
509- 'linaro-graphics-misc', 'linaro-powerdebug', 'linaro-mm-sig',
510- 'linaro-patchmetrics', 'linaro-android-mirror', 'u-boot-linaro',
511- 'lava-dashboard-tool', 'lava-celery', 'smartt', 'linaro-power-kernel',
512- 'linaro-django-xmlrpc', 'linaro-multimedia-testcontent',
513- 'linaro-status-website', 'linaro-octo-armhf', 'svammel', 'libmatrix',
514- 'glproxy', 'lava-test', 'cbuild', 'linaro-ci',
515- 'linaro-multimedia-ucm', 'linaro-ubuntu',
516- 'linaro-android-infrastructure', 'linaro-wordpress-registration-form',
517- 'linux-linaro', 'lava-server', 'linaro-android-build-tools',
518- 'linaro-graphics-dashboard', 'linaro-fetch-image', 'unity-gles',
519- 'lava-kernel-ci-views', 'cortex-strings', 'glmark2-extra',
520- 'lava-dashboard', 'linaro-multimedia-speex', 'glcompbench',
521- 'igloocommunity', 'linaro-validation-misc', 'linaro-websites',
522- 'linaro-graphics-tests', 'linaro-android',
523- 'jenkins-plugin-shell-status', 'binutils-linaro',
524- 'linaro-multimedia-project', 'lava-qatracker',
525- 'linaro-toolchain-binaries', 'linaro-image-tools',
526- 'linaro-toolchain-misc', 'qemu-linaro', 'linaro-toolchain-benchmarks',
527- 'lava-dispatcher', 'gdb-linaro', 'lava-android-test', 'libjpeg-turbo',
528- 'lava-scheduler-tool', 'glmark2', 'linaro-infrastructure-misc',
529- 'lava-lab', 'linaro-android-frontend', 'linaro-powertop',
530- 'linaro-license-protection', 'gcc-linaro', 'lava-scheduler',
531- 'linaro-offspring', 'linaro-python-dashboard-bundle',
532- 'linaro-power-qa', 'lava-tool', 'linaro']
533-
534- def __init__(self, log, abort_time=None):
535- super(SpecificationWorkitemMigrator, self).__init__(
536- log, abort_time=abort_time)
537-
538- if not getFeatureFlag('garbo.workitem_migrator.enabled'):
539- self.log.info(
540- "Not migrating work items. Change the "
541- "garbo.workitem_migrator.enabled feature flag if you want "
542- "to enable this.")
543- # This will cause isDone() to return True, thus skipping the work
544- # item migration.
545- self.total = 0
546- return
547-
548- query = ("product in (select id from product where name in %s)"
549- % ",".join(sqlvalues(self.projects_to_migrate)))
550- # Get only the specs which contain "work items" in their whiteboard
551- # and which don't have any SpecificationWorkItems.
552- query += " and whiteboard ilike '%%' || %s || '%%'" % quote_like(
553- 'work items')
554- query += (" and id not in (select distinct specification from "
555- "SpecificationWorkItem)")
556- self.specs = IMasterStore(Specification).find(Specification, query)
557- self.total = self.specs.count()
558- self.log.info(
559- "Migrating work items from the whiteboard of %d specs"
560- % self.total)
561-
562- def getNextBatch(self, chunk_size):
563- end_at = self.offset + int(chunk_size)
564- return self.specs[self.offset:end_at]
565-
566- def isDone(self):
567- """See `TunableLoop`."""
568- return self.offset >= self.total
569-
570- def __call__(self, chunk_size):
571- """See `TunableLoop`."""
572- for spec in self.getNextBatch(chunk_size):
573- try:
574- work_items = extractWorkItemsFromWhiteboard(spec)
575- except Exception as e:
576- self.log.info(
577- "Failed to parse whiteboard of %s: %s" % (
578- spec, unicode(e)))
579- transaction.abort()
580- continue
581-
582- if len(work_items) > 0:
583- self.log.info(
584- "Migrated %d work items from the whiteboard of %s" % (
585- len(work_items), spec))
586- transaction.commit()
587- else:
588- self.log.info(
589- "No work items found on the whiteboard of %s" % spec)
590- self.offset += chunk_size
591-
592-
593 class BugTaskFlattener(TunableLoop):
594 """A `TunableLoop` to populate BugTaskFlat for all bugtasks."""
595
596@@ -1369,7 +1265,6 @@
597 OpenIDConsumerNoncePruner,
598 OpenIDConsumerAssociationPruner,
599 AntiqueSessionPruner,
600- SpecificationWorkitemMigrator,
601 ]
602 experimental_tunable_loops = []
603
604
605=== modified file 'lib/lp/scripts/tests/test_garbo.py'
606--- lib/lp/scripts/tests/test_garbo.py 2012-07-04 22:38:14 +0000
607+++ lib/lp/scripts/tests/test_garbo.py 2012-07-05 11:09:39 +0000
608@@ -12,7 +12,6 @@
609 )
610 import logging
611 from StringIO import StringIO
612-from textwrap import dedent
613 import time
614
615 from pytz import UTC
616@@ -30,14 +29,12 @@
617 from testtools.matchers import (
618 Equals,
619 GreaterThan,
620- MatchesStructure,
621 )
622 import transaction
623 from zope.component import getUtility
624 from zope.security.proxy import removeSecurityProxy
625
626 from lp.answers.model.answercontact import AnswerContact
627-from lp.blueprints.enums import SpecificationWorkItemStatus
628 from lp.bugs.model.bugnotification import (
629 BugNotification,
630 BugNotificationRecipient,
631@@ -55,7 +52,7 @@
632 )
633 from lp.code.model.codeimportevent import CodeImportEvent
634 from lp.code.model.codeimportresult import CodeImportResult
635-from lp.registry.interfaces.distribution import IDistributionSet
636+from lp.registry.enums import InformationType
637 from lp.registry.interfaces.person import IPersonSet
638 from lp.scripts.garbo import (
639 AntiqueSessionPruner,
640@@ -77,7 +74,6 @@
641 UTC_NOW,
642 )
643 from lp.services.database.lpstorm import IMasterStore
644-from lp.services.features import getFeatureFlag
645 from lp.services.features.model import FeatureFlag
646 from lp.services.identity.interfaces.account import AccountStatus
647 from lp.services.identity.interfaces.emailaddress import EmailAddressStatus
648@@ -1025,89 +1021,6 @@
649 self.runHourly()
650 self.assertNotEqual(old_update, naked_bug.heat_last_updated)
651
652- def test_SpecificationWorkitemMigrator_not_enabled_by_default(self):
653- self.assertFalse(getFeatureFlag('garbo.workitem_migrator.enabled'))
654- switch_dbuser('testadmin')
655- whiteboard = dedent("""
656- Work items:
657- A single work item: TODO
658- """)
659- spec = self.factory.makeSpecification(whiteboard=whiteboard)
660- transaction.commit()
661-
662- self.runFrequently()
663-
664- self.assertEqual(whiteboard, spec.whiteboard)
665- self.assertEqual(0, spec.work_items.count())
666-
667- def test_SpecificationWorkitemMigrator(self):
668- # When the migration is successful we remove all work-items from the
669- # whiteboard.
670- switch_dbuser('testadmin')
671- product = self.factory.makeProduct(name='linaro')
672- milestone = self.factory.makeMilestone(product=product)
673- person = self.factory.makePerson()
674- whiteboard = dedent("""
675- Work items for %s:
676- [%s] A single work item: TODO
677-
678- Work items:
679- Another work item: DONE
680- """ % (milestone.name, person.name))
681- spec = self.factory.makeSpecification(
682- product=product, whiteboard=whiteboard)
683- IMasterStore(FeatureFlag).add(FeatureFlag(
684- u'default', 0, u'garbo.workitem_migrator.enabled', u'True'))
685- transaction.commit()
686-
687- self.runFrequently()
688-
689- self.assertEqual('', spec.whiteboard.strip())
690- self.assertEqual(2, spec.work_items.count())
691- self.assertThat(spec.work_items[0], MatchesStructure.byEquality(
692- assignee=person, title="A single work item",
693- status=SpecificationWorkItemStatus.TODO,
694- milestone=milestone, specification=spec))
695- self.assertThat(spec.work_items[1], MatchesStructure.byEquality(
696- assignee=None, title="Another work item",
697- status=SpecificationWorkItemStatus.DONE,
698- milestone=None, specification=spec))
699-
700- def test_SpecificationWorkitemMigrator_skips_ubuntu_blueprints(self):
701- switch_dbuser('testadmin')
702- whiteboard = "Work items:\nA work item: TODO"
703- spec = self.factory.makeSpecification(
704- whiteboard=whiteboard,
705- distribution=getUtility(IDistributionSet)['ubuntu'])
706- IMasterStore(FeatureFlag).add(FeatureFlag(
707- u'default', 0, u'garbo.workitem_migrator.enabled', u'True'))
708- transaction.commit()
709- self.runFrequently()
710-
711- self.assertEqual(whiteboard, spec.whiteboard)
712- self.assertEqual(0, spec.work_items.count())
713-
714- def test_SpecificationWorkitemMigrator_parse_error(self):
715- # When we fail to parse any work items in the whiteboard we leave it
716- # untouched and don't create any SpecificationWorkItem entries.
717- switch_dbuser('testadmin')
718- whiteboard = dedent("""
719- Work items:
720- A work item: TODO
721- Another work item: UNKNOWNSTATUSWILLFAILTOPARSE
722- """)
723- product = self.factory.makeProduct(name='linaro')
724- spec = self.factory.makeSpecification(
725- product=product, whiteboard=whiteboard)
726- IMasterStore(FeatureFlag).add(FeatureFlag(
727- u'default', 0, u'garbo.workitem_migrator.enabled', u'True'))
728- transaction.commit()
729-
730- self.runFrequently()
731-
732- self.assertEqual(whiteboard, spec.whiteboard)
733- self.assertEqual(0, spec.work_items.count())
734-
735 def test_BugTaskFlattener(self):
736 # Bugs without a record in BugTaskFlat get mirrored.
737 # Remove the existing mirrored data.