Example #1
0
    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)
Example #2
0
    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)
Example #3
0
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
Example #4
0
    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)
Example #5
0
    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))}"
            )