Merge ~andrey-fedoseev/launchpad:uct-upstream into launchpad:master

Proposed by Andrey Fedoseev
Status: Merged
Approved by: Andrey Fedoseev
Approved revision: f256964a574a9d5183f0bad34c142472b2b0ad1f
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~andrey-fedoseev/launchpad:uct-upstream
Merge into: launchpad:master
Prerequisite: ~andrey-fedoseev/launchpad:uct-export
Diff against target: 879 lines (+329/-116)
5 files modified
lib/lp/bugs/scripts/tests/test_uct.py (+135/-50)
lib/lp/bugs/scripts/uct/models.py (+123/-54)
lib/lp/bugs/scripts/uct/uctexport.py (+22/-0)
lib/lp/bugs/scripts/uct/uctimport.py (+46/-12)
lib/lp/registry/model/distributionsourcepackage.py (+3/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+428893@code.launchpad.net

Commit message

UCT import/export: handle upstream package status and ESM packages

Description of the change

This also includes the changes related to UCT export that were added in https://code.launchpad.net/~andrey-fedoseev/launchpad/+git/launchpad/+merge/428152

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

I'm OK with starting with this, but at some point soon we're going to need to change the package → product mapping to use the `Packaging` table rather than assuming that the source package name is equal to the product name, since that's often not the case. As discussed on Mattermost, this may require tightening up permissions on `Packaging` edits first, which to be fair we've wanted an excuse to do for a long time.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/bugs/scripts/tests/test_uct.py b/lib/lp/bugs/scripts/tests/test_uct.py
2index 47ff535..00f995d 100644
3--- a/lib/lp/bugs/scripts/tests/test_uct.py
4+++ b/lib/lp/bugs/scripts/tests/test_uct.py
5@@ -91,20 +91,20 @@ class TestUCTRecord(TestCase):
6 UCTRecord.Package(
7 name="linux",
8 statuses=[
9- UCTRecord.DistroSeriesPackageStatus(
10- distroseries="upstream",
11+ UCTRecord.SeriesPackageStatus(
12+ series="upstream",
13 status=UCTRecord.PackageStatus.RELEASED,
14 reason="5.17~rc1",
15 priority=None,
16 ),
17- UCTRecord.DistroSeriesPackageStatus(
18- distroseries="impish",
19+ UCTRecord.SeriesPackageStatus(
20+ series="impish",
21 status=UCTRecord.PackageStatus.RELEASED,
22 reason="5.13.0-37.42",
23 priority=UCTRecord.Priority.MEDIUM,
24 ),
25- UCTRecord.DistroSeriesPackageStatus(
26- distroseries="devel",
27+ UCTRecord.SeriesPackageStatus(
28+ series="devel",
29 status=UCTRecord.PackageStatus.NOT_AFFECTED,
30 reason="5.15.0-25.25",
31 priority=UCTRecord.Priority.MEDIUM,
32@@ -126,20 +126,20 @@ class TestUCTRecord(TestCase):
33 UCTRecord.Package(
34 name="linux-hwe",
35 statuses=[
36- UCTRecord.DistroSeriesPackageStatus(
37- distroseries="upstream",
38+ UCTRecord.SeriesPackageStatus(
39+ series="upstream",
40 status=UCTRecord.PackageStatus.RELEASED,
41 reason="5.17~rc1",
42 priority=None,
43 ),
44- UCTRecord.DistroSeriesPackageStatus(
45- distroseries="impish",
46+ UCTRecord.SeriesPackageStatus(
47+ series="impish",
48 status=UCTRecord.PackageStatus.DOES_NOT_EXIST,
49 reason="",
50 priority=None,
51 ),
52- UCTRecord.DistroSeriesPackageStatus(
53- distroseries="devel",
54+ UCTRecord.SeriesPackageStatus(
55+ series="devel",
56 status=UCTRecord.PackageStatus.DOES_NOT_EXIST,
57 reason="",
58 priority=None,
59@@ -186,8 +186,14 @@ class TextCVE(TestCaseWithFactory):
60 status=SeriesStatus.DEVELOPMENT,
61 name="kinetic",
62 )
63- dsp1 = self.factory.makeDistributionSourcePackage(distribution=ubuntu)
64- dsp2 = self.factory.makeDistributionSourcePackage(distribution=ubuntu)
65+ product_1 = self.factory.makeProduct()
66+ product_2 = self.factory.makeProduct()
67+ dsp1 = self.factory.makeDistributionSourcePackage(
68+ sourcepackagename=product_1.name, distribution=ubuntu
69+ )
70+ dsp2 = self.factory.makeDistributionSourcePackage(
71+ sourcepackagename=product_2.name, distribution=ubuntu
72+ )
73 assignee = self.factory.makePerson()
74
75 self.uct_record = UCTRecord(
76@@ -224,24 +230,30 @@ class TextCVE(TestCaseWithFactory):
77 UCTRecord.Package(
78 name=dsp1.sourcepackagename.name,
79 statuses=[
80- UCTRecord.DistroSeriesPackageStatus(
81- distroseries=supported_series.name,
82+ UCTRecord.SeriesPackageStatus(
83+ series=supported_series.name,
84 status=UCTRecord.PackageStatus.NOT_AFFECTED,
85 reason="reason 1",
86 priority=UCTRecord.Priority.MEDIUM,
87 ),
88- UCTRecord.DistroSeriesPackageStatus(
89- distroseries=current_series.name,
90+ UCTRecord.SeriesPackageStatus(
91+ series=current_series.name,
92 status=UCTRecord.PackageStatus.RELEASED,
93 reason="reason 2",
94 priority=UCTRecord.Priority.MEDIUM,
95 ),
96- UCTRecord.DistroSeriesPackageStatus(
97- distroseries="devel",
98+ UCTRecord.SeriesPackageStatus(
99+ series="devel",
100 status=UCTRecord.PackageStatus.RELEASED,
101 reason="reason 3",
102 priority=None,
103 ),
104+ UCTRecord.SeriesPackageStatus(
105+ series="upstream",
106+ status=UCTRecord.PackageStatus.RELEASED,
107+ reason="reason 4",
108+ priority=None,
109+ ),
110 ],
111 priority=None,
112 tags=set(),
113@@ -250,20 +262,26 @@ class TextCVE(TestCaseWithFactory):
114 UCTRecord.Package(
115 name=dsp2.sourcepackagename.name,
116 statuses=[
117- UCTRecord.DistroSeriesPackageStatus(
118- distroseries=supported_series.name,
119+ UCTRecord.SeriesPackageStatus(
120+ series=supported_series.name,
121 status=UCTRecord.PackageStatus.DOES_NOT_EXIST,
122 reason="",
123 priority=None,
124 ),
125- UCTRecord.DistroSeriesPackageStatus(
126- distroseries=current_series.name,
127+ UCTRecord.SeriesPackageStatus(
128+ series=current_series.name,
129 status=UCTRecord.PackageStatus.DOES_NOT_EXIST,
130 reason="",
131 priority=None,
132 ),
133- UCTRecord.DistroSeriesPackageStatus(
134- distroseries="devel",
135+ UCTRecord.SeriesPackageStatus(
136+ series="devel",
137+ status=UCTRecord.PackageStatus.RELEASED,
138+ reason="",
139+ priority=None,
140+ ),
141+ UCTRecord.SeriesPackageStatus(
142+ series="upstream",
143 status=UCTRecord.PackageStatus.RELEASED,
144 reason="",
145 priority=None,
146@@ -353,6 +371,20 @@ class TextCVE(TestCaseWithFactory):
147 status_explanation="",
148 ),
149 ],
150+ upstream_packages=[
151+ CVE.UpstreamPackage(
152+ package=product_1,
153+ importance=None,
154+ status=BugTaskStatus.FIXRELEASED,
155+ status_explanation="reason 4",
156+ ),
157+ CVE.SeriesPackage(
158+ package=product_2,
159+ importance=None,
160+ status=BugTaskStatus.FIXRELEASED,
161+ status_explanation="",
162+ ),
163+ ],
164 importance=BugTaskImportance.CRITICAL,
165 status=VulnerabilityStatus.ACTIVE,
166 assignee=assignee,
167@@ -393,7 +425,7 @@ class TestUCTImporterExporter(TestCaseWithFactory):
168 super().setUp(*args, **kwargs)
169 celebrities = getUtility(ILaunchpadCelebrities)
170 self.ubuntu = celebrities.ubuntu
171- self.esm = self.factory.makeDistribution("esm")
172+ self.esm = self.factory.makeDistribution("ubuntu-esm")
173 self.bug_importer = celebrities.bug_importer
174 self.ubuntu_supported_series = self.factory.makeDistroSeries(
175 distribution=self.ubuntu,
176@@ -418,11 +450,13 @@ class TestUCTImporterExporter(TestCaseWithFactory):
177 status=SeriesStatus.CURRENT,
178 name="trusty",
179 )
180+ self.product_1 = self.factory.makeProduct()
181+ self.product_2 = self.factory.makeProduct()
182 self.ubuntu_package = self.factory.makeDistributionSourcePackage(
183- distribution=self.ubuntu
184+ sourcepackagename=self.product_1.name, distribution=self.ubuntu
185 )
186 self.esm_package = self.factory.makeDistributionSourcePackage(
187- distribution=self.esm
188+ sourcepackagename=self.product_2.name, distribution=self.esm
189 )
190 for series in (
191 self.ubuntu_supported_series,
192@@ -446,7 +480,9 @@ class TestUCTImporterExporter(TestCaseWithFactory):
193 )
194
195 self.lp_cve = self.factory.makeCVE("2022-23222")
196- self.now = datetime.datetime.now(datetime.timezone.utc)
197+ self.now = datetime.datetime.now(datetime.timezone.utc).replace(
198+ microsecond=0
199+ )
200 self.cve = CVE(
201 sequence="CVE-2022-23222",
202 crd=None,
203@@ -509,6 +545,20 @@ class TestUCTImporterExporter(TestCaseWithFactory):
204 status_explanation="needs triage",
205 ),
206 ],
207+ upstream_packages=[
208+ CVE.UpstreamPackage(
209+ package=self.product_1,
210+ importance=BugTaskImportance.HIGH,
211+ status=BugTaskStatus.FIXRELEASED,
212+ status_explanation="fix released",
213+ ),
214+ CVE.UpstreamPackage(
215+ package=self.product_2,
216+ importance=BugTaskImportance.LOW,
217+ status=BugTaskStatus.WONTFIX,
218+ status_explanation="ignored",
219+ ),
220+ ],
221 importance=BugTaskImportance.MEDIUM,
222 status=VulnerabilityStatus.ACTIVE,
223 assignee=self.factory.makePerson(),
224@@ -552,7 +602,10 @@ class TestUCTImporterExporter(TestCaseWithFactory):
225 bug_tasks = bug.bugtasks # type: List[BugTask]
226
227 self.assertEqual(
228- len(cve.distro_packages) + len(cve.series_packages), len(bug_tasks)
229+ len(cve.distro_packages)
230+ + len(cve.series_packages)
231+ + len(cve.upstream_packages),
232+ len(bug_tasks),
233 )
234 bug_tasks_by_target = {t.target: t for t in bug_tasks}
235
236@@ -563,7 +616,7 @@ class TestUCTImporterExporter(TestCaseWithFactory):
237 t = bug_tasks_by_target[distro_package.package]
238 package_importance = distro_package.importance or cve.importance
239 package_importances[
240- distro_package.package.sourcepackagename
241+ distro_package.package.sourcepackagename.name
242 ] = package_importance
243 conjoined_primary = t.conjoined_primary
244 if conjoined_primary:
245@@ -580,7 +633,7 @@ class TestUCTImporterExporter(TestCaseWithFactory):
246 self.assertIn(series_package.package, bug_tasks_by_target)
247 t = bug_tasks_by_target[series_package.package]
248 package_importance = package_importances[
249- series_package.package.sourcepackagename
250+ series_package.package.sourcepackagename.name
251 ]
252 sp_importance = series_package.importance or package_importance
253 self.assertEqual(sp_importance, t.importance)
254@@ -589,6 +642,19 @@ class TestUCTImporterExporter(TestCaseWithFactory):
255 series_package.status_explanation, t.status_explanation
256 )
257
258+ for upstream_package in cve.upstream_packages:
259+ self.assertIn(upstream_package.package, bug_tasks_by_target)
260+ t = bug_tasks_by_target[upstream_package.package]
261+ package_importance = package_importances[
262+ upstream_package.package.name
263+ ]
264+ sp_importance = upstream_package.importance or package_importance
265+ self.assertEqual(sp_importance, t.importance)
266+ self.assertEqual(upstream_package.status, t.status)
267+ self.assertEqual(
268+ upstream_package.status_explanation, t.status_explanation
269+ )
270+
271 for t in bug_tasks:
272 self.assertEqual(cve.assignee, t.assignee)
273
274@@ -625,6 +691,32 @@ class TestUCTImporterExporter(TestCaseWithFactory):
275 lp_cve.cvss,
276 )
277
278+ def checkCVE(self, expected: CVE, actual: CVE):
279+ self.assertEqual(expected.sequence, actual.sequence)
280+ self.assertEqual(expected.crd, actual.crd)
281+ self.assertEqual(expected.public_date, actual.public_date)
282+ self.assertEqual(
283+ expected.public_date_at_USN, actual.public_date_at_USN
284+ )
285+ self.assertListEqual(expected.distro_packages, actual.distro_packages)
286+ self.assertListEqual(expected.series_packages, actual.series_packages)
287+ self.assertListEqual(
288+ expected.upstream_packages, actual.upstream_packages
289+ )
290+ self.assertEqual(expected.importance, actual.importance)
291+ self.assertEqual(expected.status, actual.status)
292+ self.assertEqual(expected.assignee, actual.assignee)
293+ self.assertEqual(expected.discovered_by, actual.discovered_by)
294+ self.assertEqual(expected.description, actual.description)
295+ self.assertEqual(
296+ expected.ubuntu_description, actual.ubuntu_description
297+ )
298+ self.assertListEqual(expected.bug_urls, actual.bug_urls)
299+ self.assertListEqual(expected.references, actual.references)
300+ self.assertEqual(expected.notes, actual.notes)
301+ self.assertEqual(expected.mitigation, actual.mitigation)
302+ self.assertListEqual(expected.cvss, actual.cvss)
303+
304 def test_create_bug(self):
305 bug = self.importer.create_bug(self.cve, self.lp_cve)
306
307@@ -891,20 +983,13 @@ class TestUCTImporterExporter(TestCaseWithFactory):
308 self.importer.import_cve(self.cve)
309 bug = self.importer._find_existing_bug(self.cve, self.lp_cve)
310 cve = self.exporter._make_cve_from_bug(bug)
311- self.assertEqual(self.cve.sequence, cve.sequence)
312- self.assertEqual(self.cve.crd, cve.crd)
313- self.assertEqual(self.cve.public_date, cve.public_date)
314- self.assertEqual(self.cve.public_date_at_USN, cve.public_date_at_USN)
315- self.assertListEqual(self.cve.distro_packages, cve.distro_packages)
316- self.assertListEqual(self.cve.series_packages, cve.series_packages)
317- self.assertEqual(self.cve.importance, cve.importance)
318- self.assertEqual(self.cve.status, cve.status)
319- self.assertEqual(self.cve.assignee, cve.assignee)
320- self.assertEqual(self.cve.discovered_by, cve.discovered_by)
321- self.assertEqual(self.cve.description, cve.description)
322- self.assertEqual(self.cve.ubuntu_description, cve.ubuntu_description)
323- self.assertListEqual(self.cve.bug_urls, cve.bug_urls)
324- self.assertListEqual(self.cve.references, cve.references)
325- self.assertEqual(self.cve.notes, cve.notes)
326- self.assertEqual(self.cve.mitigation, cve.mitigation)
327- self.assertListEqual(self.cve.cvss, cve.cvss)
328+ self.checkCVE(self.cve, cve)
329+
330+ def test_export_bug_to_uct_file(self):
331+ self.importer.import_cve(self.cve)
332+ bug = self.importer._find_existing_bug(self.cve, self.lp_cve)
333+ output_dir = Path(self.makeTemporaryDirectory())
334+ cve_path = self.exporter.export_bug_to_uct_file(bug.id, output_dir)
335+ uct_record = UCTRecord.load(cve_path)
336+ cve = CVE.make_from_uct_record(uct_record)
337+ self.checkCVE(self.cve, cve)
338diff --git a/lib/lp/bugs/scripts/uct/models.py b/lib/lp/bugs/scripts/uct/models.py
339index 6c87f86..440f79d 100644
340--- a/lib/lp/bugs/scripts/uct/models.py
341+++ b/lib/lp/bugs/scripts/uct/models.py
342@@ -2,7 +2,7 @@
343 # GNU Affero General Public License version 3 (see the file LICENSE).
344
345 import logging
346-from collections import defaultdict
347+from collections import OrderedDict, defaultdict
348 from datetime import datetime
349 from enum import Enum
350 from pathlib import Path
351@@ -13,11 +13,12 @@ import dateutil.parser
352 from contrib.cve_lib import load_cve
353 from zope.component import getUtility
354
355-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
356 from lp.bugs.enums import VulnerabilityStatus
357 from lp.bugs.interfaces.bugtask import BugTaskImportance, BugTaskStatus
358+from lp.registry.interfaces.distribution import IDistributionSet
359 from lp.registry.interfaces.distroseries import IDistroSeriesSet
360 from lp.registry.interfaces.person import IPersonSet
361+from lp.registry.interfaces.product import IProductSet
362 from lp.registry.interfaces.series import SeriesStatus
363 from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
364 from lp.registry.model.distribution import Distribution
365@@ -26,6 +27,7 @@ from lp.registry.model.distributionsourcepackage import (
366 )
367 from lp.registry.model.distroseries import DistroSeries
368 from lp.registry.model.person import Person
369+from lp.registry.model.product import Product
370 from lp.registry.model.sourcepackage import SourcePackage
371 from lp.registry.model.sourcepackagename import SourcePackageName
372 from lp.services.propertycache import cachedproperty
373@@ -73,10 +75,10 @@ class UCTRecord:
374 NEEDED = "needed"
375 PENDING = "pending"
376
377- DistroSeriesPackageStatus = NamedTuple(
378- "DistroSeriesPackageStatus",
379+ SeriesPackageStatus = NamedTuple(
380+ "SeriesPackageStatus",
381 (
382- ("distroseries", str),
383+ ("series", str),
384 ("status", PackageStatus),
385 ("reason", str),
386 ("priority", Optional[Priority]),
387@@ -95,7 +97,7 @@ class UCTRecord:
388 "Package",
389 (
390 ("name", str),
391- ("statuses", List[DistroSeriesPackageStatus]),
392+ ("statuses", List[SeriesPackageStatus]),
393 ("priority", Optional[Priority]),
394 ("tags", Set[str]),
395 ("patches", List[Patch]),
396@@ -167,23 +169,23 @@ class UCTRecord:
397 cve_data, "pkgs"
398 ).items():
399 statuses = []
400- for distroseries, (status, reason) in statuses_dict.items():
401- distroseries_priority = cls._pop_cve_property(
402+ for series, (status, reason) in statuses_dict.items():
403+ series_priority = cls._pop_cve_property(
404 cve_data,
405- "Priority_{package}_{distroseries}".format(
406+ "Priority_{package}_{series}".format(
407 package=package,
408- distroseries=distroseries,
409+ series=series,
410 ),
411 required=False,
412 )
413 statuses.append(
414- cls.DistroSeriesPackageStatus(
415- distroseries=distroseries,
416+ cls.SeriesPackageStatus(
417+ series=series,
418 status=cls.PackageStatus(status),
419 reason=reason,
420 priority=(
421- cls.Priority(distroseries_priority)
422- if distroseries_priority
423+ cls.Priority(series_priority)
424+ if series_priority
425 else None
426 ),
427 )
428@@ -334,7 +336,7 @@ class UCTRecord:
429 )
430 for status in package.statuses:
431 self._write_field(
432- "{}_{}".format(status.distroseries, package.name),
433+ "{}_{}".format(status.series, package.name),
434 (
435 "{} ({})".format(status.status.value, status.reason)
436 if status.reason
437@@ -351,9 +353,7 @@ class UCTRecord:
438 for status in package.statuses:
439 if status.priority:
440 self._write_field(
441- "Priority_{}_{}".format(
442- package.name, status.distroseries
443- ),
444+ "Priority_{}_{}".format(package.name, status.series),
445 status.priority.value,
446 output,
447 )
448@@ -436,6 +436,16 @@ class CVE:
449 ),
450 )
451
452+ UpstreamPackage = NamedTuple(
453+ "UpstreamPackage",
454+ (
455+ ("package", Product),
456+ ("importance", Optional[BugTaskImportance]),
457+ ("status", BugTaskStatus),
458+ ("status_explanation", str),
459+ ),
460+ )
461+
462 PRIORITY_MAP = {
463 UCTRecord.Priority.CRITICAL: BugTaskImportance.CRITICAL,
464 UCTRecord.Priority.HIGH: BugTaskImportance.HIGH,
465@@ -478,6 +488,7 @@ class CVE:
466 public_date_at_USN: Optional[datetime],
467 distro_packages: List[DistroPackage],
468 series_packages: List[SeriesPackage],
469+ upstream_packages: List[UpstreamPackage],
470 importance: BugTaskImportance,
471 status: VulnerabilityStatus,
472 assignee: Optional[Person],
473@@ -496,6 +507,7 @@ class CVE:
474 self.public_date_at_USN = public_date_at_USN
475 self.distro_packages = distro_packages
476 self.series_packages = series_packages
477+ self.upstream_packages = upstream_packages
478 self.importance = importance
479 self.status = status
480 self.assignee = assignee
481@@ -518,6 +530,7 @@ class CVE:
482
483 distro_packages = []
484 series_packages = []
485+ upstream_packages = []
486
487 spn_set = getUtility(ISourcePackageNameSet)
488
489@@ -530,11 +543,6 @@ class CVE:
490 )
491
492 for uct_package_status in uct_package.statuses:
493- distro_series = cls.get_distro_series(
494- uct_package_status.distroseries
495- )
496- if distro_series is None:
497- continue
498
499 if uct_package_status.status not in cls.BUG_TASK_STATUS_MAP:
500 logger.warning(
501@@ -543,6 +551,34 @@ class CVE:
502 )
503 continue
504
505+ series_package_importance = (
506+ cls.PRIORITY_MAP[uct_package_status.priority]
507+ if uct_package_status.priority
508+ else None
509+ )
510+
511+ if uct_package_status.series == "upstream":
512+ product = cls.get_product(uct_package.name)
513+ if product is None:
514+ continue
515+ upstream_packages.append(
516+ cls.UpstreamPackage(
517+ package=product,
518+ importance=series_package_importance,
519+ status=cls.BUG_TASK_STATUS_MAP[
520+ uct_package_status.status
521+ ],
522+ status_explanation=uct_package_status.reason,
523+ )
524+ )
525+ continue
526+
527+ distro_series = cls.get_distro_series(
528+ uct_package_status.series
529+ )
530+ if distro_series is None:
531+ continue
532+
533 distro_package = cls.DistroPackage(
534 package=DistributionSourcePackage(
535 distribution=distro_series.distribution,
536@@ -553,12 +589,6 @@ class CVE:
537 if distro_package not in distro_packages:
538 distro_packages.append(distro_package)
539
540- series_package_importance = (
541- cls.PRIORITY_MAP[uct_package_status.priority]
542- if uct_package_status.priority
543- else None
544- )
545-
546 series_packages.append(
547 cls.SeriesPackage(
548 package=SourcePackage(
549@@ -589,6 +619,7 @@ class CVE:
550 public_date_at_USN=uct_record.public_date_at_USN,
551 distro_packages=distro_packages,
552 series_packages=series_packages,
553+ upstream_packages=upstream_packages,
554 importance=cls.PRIORITY_MAP[uct_record.priority],
555 status=cls.infer_vulnerability_status(uct_record),
556 assignee=assignee,
557@@ -616,23 +647,28 @@ class CVE:
558 series_package.package.sourcepackagename
559 ].append(series_package)
560
561- packages = [] # type: List[UCTRecord.Package]
562+ packages_by_name = OrderedDict() # type: Dict[str, UCTRecord.Package]
563 processed_packages = set() # type: Set[SourcePackageName]
564 for distro_package in self.distro_packages:
565 spn = distro_package.package.sourcepackagename
566 if spn in processed_packages:
567 continue
568 processed_packages.add(spn)
569- statuses = [] # type: List[UCTRecord.DistroSeriesPackageStatus]
570+ statuses = [] # type: List[UCTRecord.SeriesPackageStatus]
571 for series_package in series_packages_by_name[spn]:
572 series = series_package.package.distroseries
573 if series.status == SeriesStatus.DEVELOPMENT:
574 series_name = "devel"
575 else:
576 series_name = series.name
577+ distro_name = distro_package.package.distribution.name
578+ if distro_name != "ubuntu":
579+ if distro_name == "ubuntu-esm":
580+ distro_name = "esm"
581+ series_name = "{}/{}".format(series_name, distro_name)
582 statuses.append(
583- UCTRecord.DistroSeriesPackageStatus(
584- distroseries=series_name,
585+ UCTRecord.SeriesPackageStatus(
586+ series=series_name,
587 status=self.BUG_TASK_STATUS_MAP_REVERSE[
588 series_package.status
589 ],
590@@ -647,19 +683,43 @@ class CVE:
591 )
592 )
593
594- packages.append(
595- UCTRecord.Package(
596- name=spn.name,
597- statuses=statuses,
598- priority=(
599- self.PRIORITY_MAP_REVERSE[distro_package.importance]
600- if distro_package.importance
601- else None
602- ),
603+ packages_by_name[spn.name] = UCTRecord.Package(
604+ name=spn.name,
605+ statuses=statuses,
606+ priority=(
607+ self.PRIORITY_MAP_REVERSE[distro_package.importance]
608+ if distro_package.importance
609+ else None
610+ ),
611+ tags=set(),
612+ patches=[],
613+ )
614+
615+ for upstream_package in self.upstream_packages:
616+ status = UCTRecord.SeriesPackageStatus(
617+ series="upstream",
618+ status=self.BUG_TASK_STATUS_MAP_REVERSE[
619+ upstream_package.status
620+ ],
621+ reason=upstream_package.status_explanation,
622+ priority=(
623+ self.PRIORITY_MAP_REVERSE[upstream_package.importance]
624+ if upstream_package.importance
625+ else None
626+ ),
627+ )
628+ package_name = upstream_package.package.name
629+ if package_name in packages_by_name:
630+ packages_by_name[package_name].statuses.append(status)
631+ else:
632+ packages_by_name[package_name] = UCTRecord.Package(
633+ name=package_name,
634+ statuses=[status],
635+ priority=None,
636 tags=set(),
637 patches=[],
638 )
639- )
640+
641 return UCTRecord(
642 parent_dir=self.VULNERABILITY_STATUS_MAP_REVERSE.get(
643 self.status, ""
644@@ -678,7 +738,7 @@ class CVE:
645 priority=self.PRIORITY_MAP_REVERSE[self.importance],
646 references=self.references,
647 ubuntu_description=self.ubuntu_description,
648- packages=packages,
649+ packages=list(packages_by_name.values()),
650 )
651
652 @property
653@@ -722,20 +782,29 @@ class CVE:
654 if "/" in distro_series_name:
655 series_name, distro_name = distro_series_name.split("/", 1)
656 if distro_name == "esm":
657- # TODO: ESM needs special handling
658- pass
659- return
660+ distro_name = "ubuntu-esm"
661 else:
662+ distro_name = "ubuntu"
663 series_name = distro_series_name
664- distribution = getUtility(ILaunchpadCelebrities).ubuntu
665- if series_name == "devel":
666- distro_series = cls.get_devel_series(distribution)
667- else:
668- distro_series = getUtility(IDistroSeriesSet).queryByName(
669- distribution, series_name
670- )
671+ distribution = getUtility(IDistributionSet).getByName(distro_name)
672+ if distribution is None:
673+ logger.warning("Could not find the distribution: %s", distro_name)
674+ return
675+ if series_name == "devel":
676+ distro_series = cls.get_devel_series(distribution)
677+ else:
678+ distro_series = getUtility(IDistroSeriesSet).queryByName(
679+ distribution, series_name
680+ )
681 if not distro_series:
682 logger.warning(
683 "Could not find the distro series: %s", distro_series_name
684 )
685 return distro_series
686+
687+ @classmethod
688+ def get_product(cls, product_name: str) -> Optional[Product]:
689+ product = getUtility(IProductSet).getByName(product_name)
690+ if not product:
691+ logger.warning("Could not find the product: %s", product_name)
692+ return product
693diff --git a/lib/lp/bugs/scripts/uct/uctexport.py b/lib/lp/bugs/scripts/uct/uctexport.py
694index 17dcece..65e274c 100644
695--- a/lib/lp/bugs/scripts/uct/uctexport.py
696+++ b/lib/lp/bugs/scripts/uct/uctexport.py
697@@ -18,6 +18,7 @@ from lp.bugs.scripts.uct.models import CVE, CVSS
698 from lp.registry.model.distributionsourcepackage import (
699 DistributionSourcePackage,
700 )
701+from lp.registry.model.product import Product
702 from lp.registry.model.sourcepackage import SourcePackage
703
704 __all__ = [
705@@ -151,6 +152,26 @@ class UCTExporter:
706 )
707 )
708
709+ upstream_packages = []
710+ for bug_task in bug_tasks:
711+ target = removeSecurityProxy(bug_task.target)
712+ if not isinstance(target, Product):
713+ continue
714+ up_importance = bug_task.importance
715+ package_importance = package_importances.get(target.name)
716+ upstream_packages.append(
717+ CVE.UpstreamPackage(
718+ package=target,
719+ importance=(
720+ up_importance
721+ if up_importance != package_importance
722+ else None
723+ ),
724+ status=bug_task.status,
725+ status_explanation=bug_task.status_explanation,
726+ )
727+ )
728+
729 return CVE(
730 sequence="CVE-{}".format(lp_cve.sequence),
731 crd=None, # TODO: fix this
732@@ -158,6 +179,7 @@ class UCTExporter:
733 public_date_at_USN=None, # TODO: fix this
734 distro_packages=distro_packages,
735 series_packages=series_packages,
736+ upstream_packages=upstream_packages,
737 importance=cve_importance,
738 status=vulnerability.status,
739 assignee=bug_tasks[0].assignee,
740diff --git a/lib/lp/bugs/scripts/uct/uctimport.py b/lib/lp/bugs/scripts/uct/uctimport.py
741index 3bc3512..5f94703 100644
742--- a/lib/lp/bugs/scripts/uct/uctimport.py
743+++ b/lib/lp/bugs/scripts/uct/uctimport.py
744@@ -14,12 +14,21 @@ For each entry in UCT we:
745 3. Create a Bug Task for each distribution/series package in the CVE entry
746 4. Update the statuses of Bug Tasks based on the information in the CVE entry
747 5. Update the information the related Launchpad's `Cve` model, if necessary
748+
749+Three types of bug tags are created:
750+
751+1. Bug tasks with a distribution package as a target - they represent
752+ importance of the package
753+2. Bug tasks with distribution series packages as a target - they represent
754+ importance and status of the package in a particular series
755+3. Bug tasks with a product as a target - they represent importance and
756+ status of the package in upstream.
757 """
758 import logging
759 from datetime import timezone
760 from itertools import chain
761 from pathlib import Path
762-from typing import List, Optional
763+from typing import Dict, List, Optional
764
765 import transaction
766 from zope.component import getUtility
767@@ -156,10 +165,17 @@ class UCTImporter:
768 self._update_external_bug_urls(bug, cve.bug_urls)
769
770 self._create_bug_tasks(
771- bug, cve.distro_packages[1:], cve.series_packages
772+ bug,
773+ cve.distro_packages[1:],
774+ cve.series_packages,
775+ cve.upstream_packages,
776 )
777 self._update_statuses_and_importances(
778- bug, cve.importance, cve.distro_packages, cve.series_packages
779+ bug,
780+ cve.importance,
781+ cve.distro_packages,
782+ cve.series_packages,
783+ cve.upstream_packages,
784 )
785 self._assign_bug_tasks(bug, cve.assignee)
786
787@@ -188,9 +204,18 @@ class UCTImporter:
788 """
789 bug.description = self._make_bug_description(cve)
790
791- self._create_bug_tasks(bug, cve.distro_packages, cve.series_packages)
792+ self._create_bug_tasks(
793+ bug,
794+ cve.distro_packages,
795+ cve.series_packages,
796+ cve.upstream_packages,
797+ )
798 self._update_statuses_and_importances(
799- bug, cve.importance, cve.distro_packages, cve.series_packages
800+ bug,
801+ cve.importance,
802+ cve.distro_packages,
803+ cve.series_packages,
804+ cve.upstream_packages,
805 )
806 self._assign_bug_tasks(bug, cve.assignee)
807 self._update_external_bug_urls(bug, cve.bug_urls)
808@@ -228,6 +253,7 @@ class UCTImporter:
809 bug: BugModel,
810 distro_packages: List[CVE.DistroPackage],
811 series_packages: List[CVE.SeriesPackage],
812+ upstream_packages: List[CVE.UpstreamPackage],
813 ) -> None:
814 """
815 Add bug tasks to the given `Bug` model based on the information
816@@ -246,7 +272,8 @@ class UCTImporter:
817 bug_task_by_target = {t.target: t for t in bug_tasks}
818 bug_task_set = getUtility(IBugTaskSet)
819 for target in (
820- p.package for p in chain(distro_packages, series_packages)
821+ p.package
822+ for p in chain(distro_packages, series_packages, upstream_packages)
823 ):
824 if target not in bug_task_by_target:
825 bug_task_set.createTask(bug, self.bug_importer, target)
826@@ -331,6 +358,7 @@ class UCTImporter:
827 cve_importance: BugTaskImportance,
828 distro_packages: List[CVE.DistroPackage],
829 series_packages: List[CVE.SeriesPackage],
830+ upstream_packages: List[CVE.UpstreamPackage],
831 ) -> None:
832 """
833 Update statuses and importances of bug tasks according to the
834@@ -350,19 +378,25 @@ class UCTImporter:
835 bug_tasks = bug.bugtasks # type: List[BugTask]
836 bug_task_by_target = {t.target: t for t in bug_tasks}
837
838- package_importances = {}
839+ package_importances = {} # type: Dict[str, BugTaskImportance]
840
841 for dp in distro_packages:
842 task = bug_task_by_target[dp.package]
843 dp_importance = dp.importance or cve_importance
844- package_importances[dp.package.sourcepackagename] = dp_importance
845+ package_importances[
846+ dp.package.sourcepackagename.name
847+ ] = dp_importance
848 task.transitionToImportance(dp_importance)
849
850- for sp in series_packages:
851+ for sp in chain(series_packages, upstream_packages):
852 task = bug_task_by_target[sp.package]
853- package_importance = package_importances[
854- sp.package.sourcepackagename
855- ]
856+ if isinstance(sp, CVE.SeriesPackage):
857+ package_name = sp.package.sourcepackagename.name
858+ elif isinstance(sp, CVE.UpstreamPackage):
859+ package_name = sp.package.name
860+ else:
861+ raise AssertionError()
862+ package_importance = package_importances[package_name]
863 sp_importance = sp.importance or package_importance
864 task.transitionToImportance(sp_importance)
865 task.transitionToStatus(sp.status)
866diff --git a/lib/lp/registry/model/distributionsourcepackage.py b/lib/lp/registry/model/distributionsourcepackage.py
867index 6d31eab..fd9da20 100644
868--- a/lib/lp/registry/model/distributionsourcepackage.py
869+++ b/lib/lp/registry/model/distributionsourcepackage.py
870@@ -141,6 +141,9 @@ class DistributionSourcePackage(
871 self.distribution = distribution
872 self.sourcepackagename = sourcepackagename
873
874+ def __repr__(self):
875+ return "<{} '{}'>".format(self.__class__.__name__, self.display_name)
876+
877 @property
878 def name(self):
879 """See `IDistributionSourcePackage`."""

Subscribers

People subscribed via source and target branches

to status/vote changes: