def test_is_first_fix_any_validate(self): bzapi = None bug = None tr = '4.8.z' expected = True actual = bzutil.is_first_fix_any(bzapi, bug, tr) self.assertEqual(expected, actual) bzapi = None bug = flexmock(depends_on=[]) tr = '4.8.0' expected = True actual = bzutil.is_first_fix_any(bzapi, bug, tr) self.assertEqual(expected, actual)
def test_is_first_fix_any_any(self): tr = '4.8.0' bug_a = BugzillaBug(flexmock( id=1, keywords=constants.TRACKER_BUG_KEYWORDS, whiteboard='component:runc', product=constants.BUGZILLA_PRODUCT_OCP, target_release=['4.7.z'], status='RELEASE_PENDING')) bug_b = BugzillaBug(flexmock( id=2, keywords=constants.TRACKER_BUG_KEYWORDS, whiteboard='component:runc', product=constants.BUGZILLA_PRODUCT_OCP, target_release=[tr], status='ON_QA')) bug_c = BugzillaBug(flexmock( id=3, keywords=constants.TRACKER_BUG_KEYWORDS, whiteboard='component:crio', product=constants.BUGZILLA_PRODUCT_OCP, target_release=[tr], status='ON_QA')) tracker_bug_objs = [bug_a, bug_b, bug_c] tracker_bug_ids = [t.id for t in tracker_bug_objs] flaw_bug = BugzillaBug(flexmock(id=4, depends_on=tracker_bug_ids)) flexmock(BugzillaBugTracker).should_receive("login") bug_tracker = BugzillaBugTracker({}) bug_tracker.should_receive("get_bugs").with_args(tracker_bug_ids).and_return(tracker_bug_objs) expected = True actual = bzutil.is_first_fix_any(bug_tracker, flaw_bug, tr) self.assertEqual(expected, actual)
async def get_flaws(runtime, advisory, bug_tracker, flaw_bug_tracker, noop): # get attached bugs from advisory advisory_bug_ids = bug_tracker.advisory_bug_ids(advisory) if not advisory_bug_ids: runtime.logger.info(f'Found 0 {bug_tracker.type} bugs attached') return [] attached_tracker_bugs: List[Bug] = bug_tracker.get_tracker_bugs(advisory_bug_ids, verbose=runtime.debug) runtime.logger.info(f'Found {len(attached_tracker_bugs)} {bug_tracker.type} tracker bugs attached: ' f'{sorted([b.id for b in attached_tracker_bugs])}') if not attached_tracker_bugs: return [] # validate and get target_release current_target_release = Bug.get_target_release(attached_tracker_bugs) tracker_flaws, flaw_id_bugs = bug_tracker.get_corresponding_flaw_bugs( attached_tracker_bugs, flaw_bug_tracker, strict=True ) runtime.logger.info(f'Found {len(flaw_id_bugs)} {flaw_bug_tracker.type} corresponding flaw bugs:' f' {sorted(flaw_id_bugs.keys())}') # current_target_release is digit.digit.[z|0] # if current_target_release is GA then run first-fix bug filtering # for GA not every flaw bug is considered first-fix # for z-stream every flaw bug is considered first-fix if current_target_release[-1] == 'z': runtime.logger.info("Detected z-stream target release, every flaw bug is considered first-fix") first_fix_flaw_bugs = list(flaw_id_bugs.values()) else: runtime.logger.info("Detected GA release, applying first-fix filtering..") first_fix_flaw_bugs = [ flaw_bug for flaw_bug in flaw_id_bugs.values() if is_first_fix_any(flaw_bug_tracker, flaw_bug, current_target_release) ] runtime.logger.info(f'{len(first_fix_flaw_bugs)} out of {len(flaw_id_bugs)} flaw bugs considered "first-fix"') if not first_fix_flaw_bugs: return [] runtime.logger.info('Associating CVEs with builds') errata_config = runtime.gitdata.load_data(key='erratatool').data errata_api = AsyncErrataAPI(errata_config.get("server", constants.errata_url)) try: await errata_api.login() await associate_builds_with_cves(errata_api, advisory, attached_tracker_bugs, tracker_flaws, flaw_id_bugs, noop) except ValueError as e: runtime.logger.warn(f"Error associating builds with cves: {e}") finally: await errata_api.close() return first_fix_flaw_bugs
def test_is_first_fix_any_no_valid_trackers(self): tr = '4.8.0' tracker_bug_ids = [1, 2] bug_a = BugzillaBug(flexmock( id=1, product=constants.BUGZILLA_PRODUCT_OCP, keywords=['foo']) ) tracker_bug_objs = [bug_a] flaw_bug = BugzillaBug(flexmock(id=6, depends_on=tracker_bug_ids)) flexmock(BugzillaBugTracker).should_receive("login") bug_tracker = BugzillaBugTracker({}) bug_tracker.should_receive("get_bugs").with_args(tracker_bug_ids).and_return(tracker_bug_objs) expected = True actual = bzutil.is_first_fix_any(bug_tracker, flaw_bug, tr) self.assertEqual(expected, actual)
def test_is_first_fix_any_missing_whiteboard_component(self): tr = '4.8.0' tracker_bug_ids = [1, 2] bug_a = BugzillaBug(flexmock( id=1, product=constants.BUGZILLA_PRODUCT_OCP, keywords=constants.TRACKER_BUG_KEYWORDS, whiteboard='', target_release=[tr] )) tracker_bug_objs = [bug_a] flaw_bug = BugzillaBug(flexmock(id=6, depends_on=tracker_bug_ids)) flexmock(BugzillaBugTracker).should_receive("login") bug_tracker = BugzillaBugTracker({}) bug_tracker.should_receive("get_bugs").with_args(tracker_bug_ids).and_return(tracker_bug_objs) expected = False actual = bzutil.is_first_fix_any(bug_tracker, flaw_bug, tr) self.assertEqual(expected, actual)
async def _verify_attached_flaws_for(self, advisory_id: int, attached_trackers: Iterable[Bug], attached_flaws: Iterable[Bug]): # Retrieve flaw bugs in Bugzilla for attached_tracker_bugs tracker_flaws, flaw_id_bugs = self.bug_tracker.get_corresponding_flaw_bugs( attached_trackers) # Find first-fix flaws first_fix_flaw_ids = set() if attached_trackers: current_target_release = bzutil.Bug.get_target_release( attached_trackers) if current_target_release[-1] == 'z': first_fix_flaw_ids = flaw_id_bugs.keys() else: first_fix_flaw_ids = { flaw_bug.id for flaw_bug in flaw_id_bugs.values() if bzutil.is_first_fix_any(self.bug_tracker.client( ), flaw_bug, current_target_release) } # Check if attached flaws match attached trackers attached_flaw_ids = {b.id for b in attached_flaws} missing_flaw_ids = attached_flaw_ids - first_fix_flaw_ids if missing_flaw_ids: self._complain( f"On advisory {advisory_id}, {len(missing_flaw_ids)} flaw bugs are not attached but they are referenced by attached tracker bugs: {', '.join(sorted(map(str, missing_flaw_ids)))}." " You probably need to attach those flaw bugs or drop the corresponding tracker bugs." ) extra_flaw_ids = first_fix_flaw_ids - attached_flaw_ids if extra_flaw_ids: self._complain( f"On advisory {advisory_id}, {len(extra_flaw_ids)} flaw bugs are attached but there are no tracker bugs referencing them: {', '.join(sorted(map(str, extra_flaw_ids)))}." " You probably need to drop those flaw bugs or attach the corresponding tracker bugs." ) # Check if advisory is of the expected type advisory_info = await self.errata_api.get_advisory(advisory_id) advisory_type = next(iter(advisory_info["errata"].keys())).upper( ) # should be one of [RHBA, RHSA, RHEA] if not first_fix_flaw_ids: if advisory_type == "RHSA": self._complain( f"Advisory {advisory_id} is of type {advisory_type} but has no first-fix flaw bugs. It should be converted to RHBA or RHEA." ) return # The remaining checks are not needed for a non-RHSA. if advisory_type != "RHSA": self._complain( f"Advisory {advisory_id} is of type {advisory_type} but has first-fix flaw bugs {first_fix_flaw_ids}. It should be converted to RHSA." ) # Check if flaw bugs are associated with specific builds cve_components_mapping: Dict[str, Set[str]] = {} for tracker in attached_trackers: component_name = tracker.whiteboard_component if not component_name: raise ValueError( f"Tracker bug {tracker.id} doesn't have a valid component name in its whiteboard field." ) flaw_ids = tracker_flaws[tracker.id] for flaw_id in flaw_ids: if len(flaw_id_bugs[flaw_id].alias) != 1: raise ValueError( f"Flaw bug {flaw_id} should have exact 1 alias.") cve = flaw_id_bugs[flaw_id].alias[0] cve_components_mapping.setdefault(cve, set()).add(component_name) current_cve_package_exclusions = await AsyncErrataUtils.get_advisory_cve_package_exclusions( self.errata_api, advisory_id) attached_builds = await self.errata_api.get_builds_flattened( advisory_id) expected_cve_packages_exclusions = AsyncErrataUtils.compute_cve_package_exclusions( attached_builds, cve_components_mapping) extra_cve_package_exclusions, missing_cve_package_exclusions = AsyncErrataUtils.diff_cve_package_exclusions( current_cve_package_exclusions, expected_cve_packages_exclusions) for cve, cve_package_exclusions in extra_cve_package_exclusions.items( ): if cve_package_exclusions: self._complain( f"On advisory {advisory_id}, {cve} is not associated with Brew components {', '.join(sorted(cve_package_exclusions))}." " You may need to associate the CVE with the components in the CVE mapping or drop the tracker bugs." ) for cve, cve_package_exclusions in missing_cve_package_exclusions.items( ): if cve_package_exclusions: self._complain( f"On advisory {advisory_id}, {cve} is associated with Brew components {', '.join(sorted(cve_package_exclusions))} without a tracker bug." " You may need to explictly exclude those Brew components from the CVE mapping or attach the corresponding tracker bugs." ) # Check if flaw bugs match the CVE field of the advisory advisory_cves = advisory_info["content"]["content"]["cve"].split() extra_cves = cve_components_mapping.keys() - advisory_cves if extra_cves: self._complain( f"On advisory {advisory_id}, bugs for the following CVEs are already attached but they are not listed in advisory's `CVE Names` field: {', '.join(sorted(extra_cves))}" ) missing_cves = advisory_cves - cve_components_mapping.keys() if missing_cves: self._complain( f"On advisory {advisory_id}, bugs for the following CVEs are not attached but listed in advisory's `CVE Names` field: {', '.join(sorted(missing_cves))}" )