def rebuild_if_not_exists(self, event, errata_id):
        """
        Initiates rebuild of artifacts based on Errata advisory with
        `errata_id` id.

        :rtype: List of ErrataAdvisoryRPMsSignedEvent instances.
        :return: List of extra events generated to initiate the rebuild.
        """

        db_event = db.session.query(Event).filter_by(
            event_type_id=EVENT_TYPES[ErrataAdvisoryRPMsSignedEvent],
            search_key=str(errata_id)).first()
        if (db_event and db_event.state != EventState.FAILED.value and
                not event.manual):
            log.debug("Ignoring Errata advisory %d - it already exists in "
                      "Freshmaker db.", errata_id)
            return []

        # Get additional info from Errata to fill in the needed data.
        errata = Errata()
        advisories = errata.advisories_from_event(event)
        if not advisories:
            log.error("Unknown Errata advisory %d" % errata_id)
            return []

        log.info("Generating ErrataAdvisoryRPMsSignedEvent for Errata "
                 "advisory %d, because its state changed to %s.", errata_id,
                 event.advisory.state)
        advisory = advisories[0]
        new_event = ErrataAdvisoryRPMsSignedEvent(
            event.msg_id + "." + str(advisory.name), advisory)
        new_event.dry_run = event.dry_run
        new_event.manual = event.manual
        return [new_event]
예제 #2
0
    def prepare_yum_repo(self, db_event):
        """
        Request a compose from ODCS for builds included in Errata advisory

        Run a compose in ODCS to contain required RPMs for rebuilding images
        later.

        :param Event db_event: current event being handled that contains errata
            advisory to get builds containing updated RPMs.
        :return: a mapping returned from ODCS that represents the request
            compose.
        :rtype: dict
        """
        errata_id = int(db_event.search_key)

        packages = []
        errata = Errata()
        builds = errata.get_srpm_nvrs(errata_id)
        compose_source = None
        for nvr in builds:
            packages += self._get_packages_for_compose(nvr)
            source = self._get_compose_source(nvr)
            if compose_source and compose_source != source:
                # TODO: Handle this by generating two ODCS composes
                db_event.builds_transition(
                    ArtifactBuildState.FAILED.value, "Packages for errata "
                    "advisory %d found in multiple different tags." %
                    (errata_id))
                return
            else:
                compose_source = source

        if compose_source is None:
            db_event.builds_transition(
                ArtifactBuildState.FAILED.value, 'None of builds %s of '
                'advisory %d is the latest build in its candidate tag.' %
                (builds, errata_id))
            return

        self.handler.log_info(
            'Generating new compose for rebuild: '
            'source: %s, source type: %s, packages: %s', compose_source, 'tag',
            packages)

        if not self.handler.dry_run:
            new_compose = create_odcs_client().new_compose(
                compose_source,
                'tag',
                packages=packages,
                sigkeys=conf.odcs_sigkeys,
                flags=["no_deps"])
        else:
            new_compose = self._fake_odcs_new_compose(compose_source,
                                                      'tag',
                                                      packages=packages)

        return new_compose
예제 #3
0
    def setUp(self):
        super(TestErrataAuthorizedGet, self).setUp()
        self.errata = Errata("https://localhost/")

        self.patcher = helpers.Patcher('freshmaker.errata.')
        self.requests_get = self.patcher.patch("requests.get")
        self.response = MagicMock()
        self.response.json.return_value = {"foo": "bar"}
        self.unlink = self.patcher.patch("os.unlink")
    def _verify_advisory_rpms_in_container_build(self, errata_id,
                                                 container_build_id):
        """
        verify container built on brew has the latest rpms from an advisory
        """
        if self.dry_run:
            return (True, '')

        # Get rpms in advisory. There can be multiple versions of RPMs with
        # the same name, so we group them by a name in `advisory_rpms_by_name`
        # and use set of the nvrs as a value.
        advisory_rpms_by_name = {}
        e = Errata()
        build_nvrs = e.get_builds(errata_id)
        if build_nvrs:
            with koji_service(conf.koji_profile,
                              log,
                              login=False,
                              dry_run=self.dry_run) as session:
                for build_nvr in build_nvrs:
                    build_rpms = session.get_build_rpms(build_nvr)
                    for rpm in build_rpms:
                        if rpm['name'] not in advisory_rpms_by_name:
                            advisory_rpms_by_name[rpm['name']] = set()
                        advisory_rpms_by_name[rpm['name']].add(rpm['nvr'])

        # get rpms in container
        with koji_service(conf.koji_profile,
                          log,
                          login=False,
                          dry_run=self.dry_run) as session:
            container_rpms = session.get_rpms_in_container(container_build_id)
            container_rpms_by_name = {
                rpmlib.parse_nvr(x)['name']: x
                for x in container_rpms
            }

        # For each RPM name in advisory, check that the RPM exists in the
        # built container and its version is the same as one RPM in the
        # advisory.
        unmatched_rpms = []
        for rpm_name, nvrs in advisory_rpms_by_name.items():
            if rpm_name not in container_rpms_by_name:
                continue
            container_rpm_nvr = container_rpms_by_name[rpm_name]
            if container_rpm_nvr not in nvrs:
                unmatched_rpms.append(rpm_name)

        if unmatched_rpms:
            msg = ("The following RPMs in container build (%s) do not match "
                   "with the latest RPMs in advisory (%s):\n%s" %
                   (container_build_id, errata_id, unmatched_rpms))
            return (False, msg)
        return (True, "")
예제 #5
0
    def handle(self, event):
        log.info("Finding out all advisories including %s", event.nvr)

        # When get a signed RPM, first step is to find out advisories
        # containing that RPM and ensure all builds are signed.
        errata = Errata()
        advisories = errata.advisories_from_event(event)

        # Filter out advisories which are not allowed by configuration.
        advisories = [
            advisory for advisory in advisories if self.allow_build(
                ArtifactType.IMAGE,
                advisory_name=advisory.name,
                advisory_security_impact=advisory.security_impact,
                advisory_highest_cve_severity=advisory.highest_cve_severity,
                advisory_state=advisory.state)
        ]

        # Filter out advisories which are already in Freshmaker DB.
        advisories = self._filter_out_existing_advisories(advisories)

        if not advisories:
            log.info("No advisories found suitable for rebuilding Docker "
                     "images")
            return []

        if not all((errata.builds_signed(advisory.errata_id)
                    for advisory in advisories)):
            log.info(
                'Not all builds in %s are signed. Do not rebuild any '
                'docker image until signed.', advisories)
            return []

        # Now we know that all advisories with this signed RPM have also other
        # RPMs signed. We can then proceed and generate
        # ErrataAdvisoryRPMsSignedEvent.
        new_events = []
        for advisory in advisories:
            new_event = ErrataAdvisoryRPMsSignedEvent(
                event.msg_id + "." + str(advisory.name), advisory)
            db_event = Event.create(db.session,
                                    new_event.msg_id,
                                    new_event.search_key,
                                    new_event.__class__,
                                    released=False)
            db.session.add(db_event)
            new_events.append(new_event)
        db.session.commit()
        return new_events
    def parse_post_data(self, data):
        """
        Method shared between Frontend and Backend to parse the POST data
        of manual rebuild JSON and generate the BaseEvent representation
        of the rebuild request.

        :param dict data: Dict generated from JSON from HTTP POST or parsed
            from the UMB message sent from Frontend to Backend.
        """
        msg_id = data.get('msg_id', "manual_rebuild_%s" % (str(time.time())))
        dry_run = data.get('dry_run', False)

        if data.get('errata_id', None):
            errata_id = data.get('errata_id')
            errata = Errata()
            advisory = ErrataAdvisory.from_advisory_id(errata, errata_id)

            event = ManualRebuildWithAdvisoryEvent(
                msg_id,
                advisory,
                data.get("container_images", []),
                data.get("metadata", None),
                freshmaker_event_id=data.get('freshmaker_event_id'),
                manual=True,
                dry_run=dry_run,
                requester=data.get('requester', None))
        else:
            event = ManualBundleRebuild(msg_id,
                                        data.get('container_images', []),
                                        data.get('bundle_images'),
                                        data.get('metadata', None),
                                        dry_run=dry_run,
                                        requester=data.get('requester', None))

        return event
예제 #7
0
    def _create_original_to_rebuilt_nvrs_map(self):
        """
        Creates mapping of original build NVRs to rebuilt NVRs in advisory.
        Including NVRs of the builds from the blocking advisories

        :rtype: dict
        :return: map of the original NVRs as keys and rebuilt NVRs as values
        """
        nvrs_mapping = {}

        # Get builds from all blocking advisories
        blocking_advisories_builds = \
            Errata().get_blocking_advisories_builds(self.event.advisory.errata_id)
        # Get builds NVRs from the advisory attached to the message/event and
        # then get original NVR for every build
        for product_info in self.event.advisory.builds.values():
            for build in product_info['builds']:
                # Search for the first build that triggered the chain of rebuilds
                # for every shipped NVR to get original NVR from it
                original_nvr = self.get_published_original_nvr(build['nvr'])
                if original_nvr is None:
                    continue
                nvrs_mapping[original_nvr] = build['nvr']
                build_nvr = parse_nvr(build['nvr'])

                # Check builds from blocking advisories and add to the mapping
                # all of them, that have overlapping package names
                for block_build in blocking_advisories_builds:
                    block_build_nvr = parse_nvr(block_build)
                    if block_build_nvr['name'] == build_nvr['name'] and \
                            block_build_nvr['version'] == build_nvr['version']:
                        nvrs_mapping[block_build] = build['nvr']
        return nvrs_mapping
예제 #8
0
    def parse(self, topic, msg):
        msg_id = msg.get('msg_id')
        inner_msg = msg.get('msg')
        errata_id = int(inner_msg.get('errata_id'))

        errata = Errata()
        return ErrataAdvisoryStateChangedEvent(
            msg_id, ErrataAdvisory.from_advisory_id(errata, errata_id))
예제 #9
0
class TestErrataAuthorizedGet(helpers.FreshmakerTestCase):
    def setUp(self):
        super(TestErrataAuthorizedGet, self).setUp()
        self.errata = Errata("https://localhost/")

        self.patcher = helpers.Patcher('freshmaker.errata.')
        self.requests_get = self.patcher.patch("requests.get")
        self.response = MagicMock()
        self.response.json.return_value = {"foo": "bar"}
        self.unlink = self.patcher.patch("os.unlink")

    def tearDown(self):
        super(TestErrataAuthorizedGet, self).tearDown()
        self.patcher.unpatch_all()

    def test_errata_authorized_get(self):
        self.requests_get.return_value = self.response
        data = self.errata._errata_authorized_get("http://localhost/test")
        self.assertEqual(data, {"foo": "bar"})

    def test_errata_authorized_get_kerberos_exception(self):
        # Test that MutualAuthenticationError is retried.
        self.requests_get.side_effect = [
            MutualAuthenticationError, self.response
        ]

        data = self.errata._errata_authorized_get("http://localhost/test")

        self.assertEqual(data, {"foo": "bar"})
        self.assertEqual(len(self.requests_get.mock_calls), 2)

    def test_errata_authorized_get_kerberos_exception_401(self):
        # Test that 401 error response is retried with kerberos ccache file
        # removed.
        error_response = MagicMock()
        error_response.status_code = 401
        error_response.raise_for_status.side_effect = HTTPError(
            "Expected exception", response=error_response)
        self.requests_get.side_effect = [error_response, self.response]

        data = self.errata._errata_authorized_get("http://localhost/test")

        self.assertEqual(data, {"foo": "bar"})
        self.assertEqual(len(self.requests_get.mock_calls), 2)
        self.unlink.assert_called_once_with(
            helpers.AnyStringWith("freshmaker_cc"))
예제 #10
0
    def parse(self, topic, msg):
        msg_id = msg.get('msg_id')
        inner_msg = msg.get('msg')
        errata_id = int(inner_msg.get('errata_id'))

        errata = Errata()
        advisory = ErrataAdvisory.from_advisory_id(errata, errata_id)
        # If advisory created by BOTAS and it's shipped,
        # then return BotasErrataShippedEvent event
        if advisory.state == "SHIPPED_LIVE" and \
           advisory.reporter.startswith('botas'):
            event = BotasErrataShippedEvent(msg_id, advisory)
        else:
            event = ErrataAdvisoryStateChangedEvent(msg_id, advisory)
        return event
예제 #11
0
    def parse(self, topic, msg):
        msg_id = msg.get('msg_id')
        inner_msg = msg.get('msg')
        errata_id = int(inner_msg.get('errata_id'))
        new_state = inner_msg.get('to')

        errata = Errata()
        advisory = ErrataAdvisory.from_advisory_id(errata, errata_id)
        # When there is message delay, state change messages can arrive after
        # advisory has been changed to a different state other than the one
        # in message, so we override advisory state with the state in message
        advisory.state = new_state

        # If advisory created by BOTAS and it's shipped,
        # then return BotasErrataShippedEvent event
        if advisory.state == "SHIPPED_LIVE" and advisory.reporter.startswith(
                'botas'):
            event = BotasErrataShippedEvent(msg_id, advisory)
        else:
            event = ErrataAdvisoryStateChangedEvent(msg_id, advisory)
        return event
예제 #12
0
    def rebuild_images_depending_on_advisory(self, db_event, errata_id):
        """
        Submits requests to Bob to rebuild the images depending on the
        images updated in the advisory with ID `errata_id`.
        """
        # Get the list of CDN repository names for each build in the advisory
        # as well as the name of tags used for the images.
        errata = Errata()
        repo_tags = errata.get_docker_repo_tags(errata_id)
        if not repo_tags:
            msg = "No CDN repo found for advisory %r" % errata_id
            self.log_info(msg)
            db_event.transition(EventState.FAILED, msg)
            db.session.commit()
            return

        # Use the Pulp to get the Docker repository name from the CDN repository
        # name and store it into `docker_repos` dict.
        pulp = Pulp(conf.pulp_docker_server_url, conf.pulp_docker_username,
                    conf.pulp_docker_password)
        # {docker_repository_name: [list, of, docker, tags], ...}
        docker_repos = {}
        for per_build_repo_tags in repo_tags.values():
            for cdn_repo, docker_repo_tags in per_build_repo_tags.items():
                docker_repo = pulp.get_docker_repository_name(cdn_repo)
                if not docker_repo:
                    self.log_error("No Docker repo found for CDN repo %r",
                                   cdn_repo)
                    continue
                docker_repos[docker_repo] = docker_repo_tags

        self.log_info(
            "Found following Docker repositories updated by the advisory: %r",
            docker_repos.keys())

        # Count the number of impacted builds to show them in state reason
        # when moving the Event to COMPLETE.
        num_impacted = None

        # Submit rebuild request to Bob :).
        for repo_name in docker_repos.keys():
            self.log_info("Requesting Bob rebuild of %s", repo_name)

            parent_build = self.record_build(
                db_event,
                repo_name,
                ArtifactType.IMAGE_REPOSITORY,
                state=ArtifactBuildState.DONE.value)

            bob_url = "%s/update_children/%s" % (
                conf.bob_server_url.rstrip('/'), repo_name)
            headers = {"Authorization": "Bearer %s" % conf.bob_auth_token}
            if self.dry_run:
                self.log_info("DRY RUN: Skipping request to Bob.")
                continue

            r = requests.get(bob_url, headers=headers)
            r.raise_for_status()
            resp = r.json()
            self.log_info("Response: %r", resp)
            if "impacted" in resp:
                if num_impacted is None:
                    num_impacted = 0
                num_impacted += len(resp["impacted"])
                for external_repo_name in resp["impacted"]:
                    self.record_build(db_event,
                                      external_repo_name,
                                      ArtifactType.IMAGE_REPOSITORY,
                                      state=ArtifactBuildState.DONE.value,
                                      dep_on=parent_build)

        msg = "Advisory %s: Informed Bob about update of %d image repositories." % (
            db_event.search_key, len(docker_repos))
        if num_impacted is not None:
            msg += " Bob is rebuilding %d impacted external image repositories." % (
                num_impacted)
        db_event.transition(EventState.COMPLETE, msg)
        db.session.commit()
예제 #13
0
#!/usr/bin/python3
from __future__ import print_function
import os
import sys
from pprint import pprint

# Allow imports from parent directory.
sys.path.insert(1, os.path.join(sys.path[0], '..'))

# Set the FRESHMAKER_DEVELOPER_ENV variable.
os.environ["FRESHMAKER_DEVELOPER_ENV"] = "1"

from freshmaker.errata import Errata
from freshmaker import conf

if len(sys.argv) != 2:
    print("Template for testing freshmaker.Errata class")
    print("Usage: ./errata.py ERRATA_TOOL_SERVER_URL")
    sys.exit(1)

conf.errata_tool_server_url = sys.argv[1]
errata = Errata()

# Example usage:
data = errata.get_builds(42515)
pprint(data)
예제 #14
0
 def setUp(self):
     super(TestErrata, self).setUp()
     self.errata = Errata("https://localhost/")
예제 #15
0
class TestErrata(helpers.FreshmakerTestCase):
    def setUp(self):
        super(TestErrata, self).setUp()
        self.errata = Errata("https://localhost/")

    def tearDown(self):
        super(TestErrata, self).tearDown()

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_advisories_from_event(self, errata_http_get, errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        event = BrewSignRPMEvent("msgid", "libntirpc-1.4.3-4.el7rhgs")
        advisories = self.errata.advisories_from_event(event)
        self.assertEqual(len(advisories), 1)
        self.assertEqual(advisories[0].errata_id, 28484)
        self.assertEqual(advisories[0].name, "RHSA-2017:28484")
        self.assertEqual(advisories[0].state, "QE")
        self.assertEqual(advisories[0].content_types, ["rpm"])
        self.assertEqual(advisories[0].security_impact, "important")
        self.assertEqual(advisories[0].product_short_name, "product")
        self.assertEqual(advisories[0].cve_list,
                         ["CVE-2015-3253", "CVE-2016-6814"])
        self.assertEqual(advisories[0].has_hightouch_bug, True)

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_advisories_from_event_empty_cve(self, errata_http_get,
                                             errata_rest_get):
        mocked_errata = MockedErrataAPI(errata_rest_get, errata_http_get)
        mocked_errata.advisory_rest_json["content"]["content"]["cve"] = ""
        event = BrewSignRPMEvent("msgid", "libntirpc-1.4.3-4.el7rhgs")
        advisories = self.errata.advisories_from_event(event)
        self.assertEqual(len(advisories), 1)
        self.assertEqual(advisories[0].cve_list, [])

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_advisories_from_event_no_bugs(self, errata_http_get,
                                           errata_rest_get):
        mocked_errata = MockedErrataAPI(errata_rest_get, errata_http_get)
        mocked_errata.bugs = []
        event = BrewSignRPMEvent("msgid", "libntirpc-1.4.3-4.el7rhgs")
        advisories = self.errata.advisories_from_event(event)
        self.assertEqual(len(advisories), 1)
        self.assertEqual(advisories[0].has_hightouch_bug, False)

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_advisories_from_event_empty_bug_flags(self, errata_http_get,
                                                   errata_rest_get):
        mocked_errata = MockedErrataAPI(errata_rest_get, errata_http_get)
        for bug in mocked_errata.bugs:
            bug["flags"] = ""
        event = BrewSignRPMEvent("msgid", "libntirpc-1.4.3-4.el7rhgs")
        advisories = self.errata.advisories_from_event(event)
        self.assertEqual(len(advisories), 1)
        self.assertEqual(advisories[0].has_hightouch_bug, False)

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_advisories_from_event_missing_all_errata(self, errata_http_get,
                                                      errata_rest_get):
        mocked_errata = MockedErrataAPI(errata_rest_get, errata_http_get)
        del mocked_errata.builds["libntirpc-1.4.3-4.el7rhgs"]["all_errata"]

        event = BrewSignRPMEvent("msgid", "libntirpc-1.4.3-4.el7rhgs")
        advisories = self.errata.advisories_from_event(event)
        self.assertEqual(len(advisories), 0)

    def test_advisories_from_event_unsupported_event(self):
        event = GitRPMSpecChangeEvent("msgid", "libntirpc", "master", "foo")
        with self.assertRaises(ValueError):
            self.errata.advisories_from_event(event)

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_advisories_from_event_errata_state_change_event(
            self, errata_http_get, errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        event = ErrataAdvisoryStateChangedEvent(
            "msgid", ErrataAdvisory(28484, "name", "SHIPPED_LIVE", ['rpm']))
        advisories = self.errata.advisories_from_event(event)
        self.assertEqual(len(advisories), 1)
        self.assertEqual(advisories[0].errata_id, 28484)

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_builds_signed_all_signed(self, errata_http_get, errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        self.assertTrue(self.errata.builds_signed(28484))

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_builds_signed_some_unsigned(self, errata_http_get,
                                         errata_rest_get):
        mocked_errata = MockedErrataAPI(errata_rest_get, errata_http_get)
        mocked_errata.builds["libntirpc-1.4.3-4.el7rhgs"][
            "rpms_signed"] = False
        self.assertFalse(self.errata.builds_signed(28484))

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_builds_signed_missing_data(self, errata_http_get,
                                        errata_rest_get):
        mocked_errata = MockedErrataAPI(errata_rest_get, errata_http_get)
        mocked_errata.builds["libntirpc-1.4.3-4.el7rhgs"] = {}
        self.assertFalse(self.errata.builds_signed(28484))

    @patch('freshmaker.errata.requests.get')
    def test_get_errata_repo_ids(self, get):
        get.return_value.json.return_value = {
            'rhel-6-server-eus-source-rpms__6_DOT_7__x86_64': [],
            'rhel-6-server-eus-optional-debug-rpms__6_DOT_7__i386': [
                '/path/to/package.rpm',
                '/path/to/package1.rpm',
                '/path/to/package2.rpm',
            ],
            'rhel-6-server-eus-rpms__6_DOT_7__x86_64': [],
        }

        repo_ids = self.errata.get_pulp_repository_ids(25718)

        self.assertEqual(
            set([
                'rhel-6-server-eus-source-rpms__6_DOT_7__x86_64',
                'rhel-6-server-eus-optional-debug-rpms__6_DOT_7__i386',
                'rhel-6-server-eus-rpms__6_DOT_7__x86_64'
            ]), set(repo_ids))

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_rhel_release_from_product_version(self, errata_http_get,
                                               errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        ret = self.errata._rhel_release_from_product_version(
            28484, "PRODUCT1-3.2-NFS")
        self.assertEqual(ret, "RHEL-6-foobar")

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_rhel_release_from_product_version_unknown_product_ver(
            self, errata_http_get, errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        with self.assertRaises(ValueError):
            self.errata._rhel_release_from_product_version(
                28484, "PRODUCT1-2.9-NFS")

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_get_nvrs(self, errata_http_get, errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        srpms = self.errata.get_srpm_nvrs(28484, "")
        binary_rpms = self.errata.get_binary_rpm_nvrs(28484)
        self.assertEqual(
            set(srpms),
            set(['libntirpc-1.4.3-4.el7rhgs', 'libntirpc-1.4.3-4.el6rhs']))
        self.assertEqual(
            set(binary_rpms),
            set([
                'libntirpc-devel-1.4.3-4.el6rhs',
                'libntirpc-devel-1.4.3-4.el7rhgs'
            ]))

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_get_binary_rpms_rhel_7(self, errata_http_get, errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        ret = self.errata.get_binary_rpm_nvrs(28484, "RHEL-7")
        self.assertEqual(ret, ['libntirpc-devel-1.4.3-4.el7rhgs'])

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_get_srpm_nvrs_empty(self, errata_http_get, errata_rest_get):
        api = MockedErrataAPI(errata_rest_get, errata_http_get)
        api.builds_json = {
            "PRODUCT1": [{
                "libntirpc-1.4.3-4.el7rhgs": {
                    "PRODUCT2-3.2-NFS": {
                        "x86_64":
                        ["libntirpc-devel-1.4.3-4.el7rhgs.x86_64.rpm"]
                    }
                }
            }]
        }
        ret = self.errata.get_srpm_nvrs(28484, "")
        self.assertEqual(ret, [])

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_get_binary_nvrs_empty(self, errata_http_get, errata_rest_get):
        api = MockedErrataAPI(errata_rest_get, errata_http_get)
        api.builds_json = {
            "PRODUCT1": [{
                "libntirpc-1.4.3-4.el7rhgs": {
                    "PRODUCT2-3.2-NFS": {
                        "SRPMS":
                        ["libntirpc-devel-1.4.3-4.el7rhgs.x86_64.rpm"]
                    }
                }
            }]
        }
        ret = self.errata.get_binary_rpm_nvrs(28484, "")
        self.assertEqual(ret, [])

    @patch.object(Errata, "_errata_rest_get")
    @patch.object(Errata, "_errata_http_get")
    def test_errata_get_cve_affected_rpm_nvrs(self, errata_http_get,
                                              errata_rest_get):
        MockedErrataAPI(errata_rest_get, errata_http_get)
        ret = self.errata.get_cve_affected_rpm_nvrs(28484)
        self.assertEqual(ret, ['libntirpc-1.4.3-4.el6rhs'])

    def test_get_docker_repo_tags(self):
        with patch.object(self.errata, "xmlrpc") as xmlrpc:
            xmlrpc.get_advisory_cdn_docker_file_list.return_value = {
                'foo-container-1-1': {
                    'docker': {
                        'target': {
                            'repos': {
                                'foo-526': {
                                    'tags': ['5.26', 'latest']
                                }
                            }
                        }
                    }
                }
            }
            repo_tags = self.errata.get_docker_repo_tags(28484)

            expected = {'foo-container-1-1': {'foo-526': ['5.26', 'latest']}}
            self.assertEqual(repo_tags, expected)

    def test_get_docker_repo_tags_xmlrpc_exception(self):
        with patch.object(self.errata, "xmlrpc") as xmlrpc:
            xmlrpc.get_advisory_cdn_docker_file_list.side_effect = ValueError(
                "Expected XMLRPC test exception")
            repo_tags = self.errata.get_docker_repo_tags(28484)
            self.assertEqual(repo_tags, None)

    def test_get_docker_repo_tags_xmlrpc_non_returned(self):
        with patch.object(self.errata, "xmlrpc") as xmlrpc:
            xmlrpc.get_advisory_cdn_docker_file_list.return_value = None
            repo_tags = self.errata.get_docker_repo_tags(28484)
            self.assertEqual(repo_tags, None)
예제 #16
0
    def _find_images_to_rebuild(self, errata_id):
        """
        Finds docker rebuild images from each build added to specific Errata
        advisory.

        Found images are yielded in proper rebuild order from base images to
        leaf images through the docker build dependency chain.

        :param int errata_id: Errata ID.
        """
        errata = Errata()
        errata_id = int(errata_id)

        # Use the errata_id to find out Pulp repository IDs from Errata Tool
        # and furthermore get content_sets from Pulp where signed RPM will end
        # up eventually when advisories are shipped.
        pulp_repo_ids = list(set(errata.get_pulp_repository_ids(errata_id)))

        pulp = Pulp(server_url=conf.pulp_server_url,
                    username=conf.pulp_username,
                    password=conf.pulp_password)
        content_sets = pulp.get_content_set_by_repo_ids(pulp_repo_ids)

        self.log_info(
            'RPMs from advisory ends up in following content sets: '
            '%s', content_sets)

        # Query images from LightBlue by signed RPM's srpm name and found
        # content sets
        lb = LightBlue(server_url=conf.lightblue_server_url,
                       cert=conf.lightblue_certificate,
                       private_key=conf.lightblue_private_key,
                       event_id=self.event.msg_id)
        # Check if we are allowed to rebuild unpublished images and clear
        # published and release_categories if so.
        if self.event.is_allowed(self, published=True):
            published = True
            release_categories = conf.lightblue_release_categories
        else:
            published = None
            release_categories = None

        # Limit the Lightblue query to particular leaf images if set in Event.
        leaf_container_images = None
        if isinstance(self.event, ManualRebuildWithAdvisoryEvent):
            leaf_container_images = self.event.container_images

        # For each SRPM NVR, find out all the containers which include
        # this SRPM NVR.
        srpm_nvrs = set(errata.get_builds(errata_id))
        # affected_pkgs contains the list of actually affected pkgs from the CVE.
        # We don't need to build images that really don't affect the CVE, even if they are
        # listed in the RHSA. So let's just remove the ones that are not listed in here.
        # In case this is empty we are just going to use the "old" behavior before this
        # change was made, and rebuild everything.
        affected_pkgs = set(
            [pkg['pkg_name'] for pkg in self.event.advisory.affected_pkgs])
        if affected_pkgs:
            tmp_srpm_nvrs = set(srpm_nvrs)
            srpm_nvrs = set()
            for srpm_nvr in tmp_srpm_nvrs:
                srpm_name = kobo.rpmlib.parse_nvr(srpm_nvr)["name"]
                # In case the SRPM NVR is modular, the `affected_pkgs` might contain
                # modules which are in the "module_name:module_stream/pkg_name" format.
                # We need to respect this format and only try to match the package name, it
                # means only the "/pkg_name" part of affected_pkg.
                if is_pkg_modular(srpm_nvr):
                    if any(
                            affected_pkg.endswith(f"/{srpm_name}")
                            for affected_pkg in affected_pkgs):
                        srpm_nvrs.add(srpm_nvr)
                elif srpm_name in affected_pkgs:
                    srpm_nvrs.add(srpm_nvr)
            self.log_info((
                "Not going to rebuild container images with RPMS from these SRPMs "
                "because they're not affected: %r"),
                          tmp_srpm_nvrs.difference(srpm_nvrs))

        self.log_info(
            "Going to find all the container images to rebuild as "
            "result of %r update.", srpm_nvrs)
        batches = lb.find_images_to_rebuild(
            srpm_nvrs,
            content_sets,
            filter_fnc=self._filter_out_not_allowed_builds,
            published=published,
            release_categories=release_categories,
            leaf_container_images=leaf_container_images)
        return batches
예제 #17
0
    def _find_images_to_rebuild(self, errata_id):
        """
        Finds docker rebuild images from each build added to specific Errata
        advisory.

        Found images are yielded in proper rebuild order from base images to
        leaf images through the docker build dependency chain.

        :param int errata_id: Errata ID.
        """
        errata = Errata()
        errata_id = int(errata_id)

        # Use the errata_id to find out Pulp repository IDs from Errata Tool
        # and furthermore get content_sets from Pulp where signed RPM will end
        # up eventually when advisories are shipped.
        pulp_repo_ids = list(set(errata.get_pulp_repository_ids(errata_id)))

        pulp = Pulp(server_url=conf.pulp_server_url,
                    username=conf.pulp_username,
                    password=conf.pulp_password)
        content_sets = pulp.get_content_set_by_repo_ids(pulp_repo_ids)
        # Some container builds declare Pulp repos directly instead of content
        # sets, but they are stored in the same location as content sets so they
        # can be treated the same
        content_sets.extend(pulp_repo_ids)

        self.log_info(
            'RPMs from advisory ends up in following content sets: '
            '%s', content_sets)

        # Query images from LightBlue by signed RPM's srpm name and found
        # content sets
        lb = LightBlue(server_url=conf.lightblue_server_url,
                       cert=conf.lightblue_certificate,
                       private_key=conf.lightblue_private_key,
                       event_id=self.current_db_event_id)
        # Check if we are allowed to rebuild unpublished images and clear
        # published and release_categories if so.
        if self.event.is_allowed(self, published=True):
            published = True
            release_categories = conf.lightblue_release_categories
        else:
            published = None
            release_categories = None

        # Limit the Lightblue query to particular leaf images if set in Event.
        leaf_container_images = None
        if isinstance(self.event, ManualRebuildWithAdvisoryEvent):
            leaf_container_images = self.event.container_images

        # Get binary rpm nvrs which are affected by the CVEs in this advisory
        affected_nvrs = self.event.advisory.affected_rpm_nvrs

        # If there is no CVE affected binary rpms, this can be non-RHSA advisory,
        # just rebuild images that have the builds in this advisory installed
        if not affected_nvrs:
            affected_nvrs = errata.get_binary_rpm_nvrs(errata_id)

        self.log_info(
            "Going to find all the container images to rebuild as "
            "result of %r update.", affected_nvrs)
        batches = lb.find_images_to_rebuild(
            affected_nvrs,
            content_sets,
            filter_fnc=self._filter_out_not_allowed_builds,
            published=published,
            release_categories=release_categories,
            leaf_container_images=leaf_container_images)
        return batches
예제 #18
0
dictConfig(fedmsg_config.get('logging', {'version': 1}))

if len(sys.argv) < 2:
    print("Queries Lightblue to find out all the images Freshmaker rebuilds.")
    print("Usage: ./lightblue.py ERRATA_ID [[CONTAINER_IMAGE], ...]")
    sys.exit(1)

container_images = sys.argv[2:]

app_context = app.app_context()
app_context.__enter__()

db.drop_all()
db.create_all()
db.session.commit()

errata = Errata()
kwargs = {}
if container_images:
    EventClass = ManualRebuildWithAdvisoryEvent
    kwargs['container_images'] = container_images
else:
    EventClass = ErrataAdvisoryStateChangedEvent
event = EventClass(
    "fake_message", ErrataAdvisory.from_advisory_id(errata, sys.argv[1]),
    dry_run=True, **kwargs)

handler = RebuildImagesOnRPMAdvisoryChange()
with patch("freshmaker.consumer.get_global_consumer"):
    handler.handle(event)
예제 #19
0
    def setUp(self):
        super(TestErrata, self).setUp()
        self.errata = Errata("https://localhost/")

        self.patcher = helpers.Patcher('freshmaker.errata.SFM2API.')
        self.patcher.patch("fetch_cve_metadata", return_value=["moderate", {}])
예제 #20
0
#!/usr/bin/python3
from __future__ import print_function
import os
import sys
from pprint import pprint

# Allow imports from parent directory.
sys.path.insert(1, os.path.join(sys.path[0], '..'))

# Set the FRESHMAKER_DEVELOPER_ENV variable.
os.environ["FRESHMAKER_DEVELOPER_ENV"] = "1"

from freshmaker.errata import Errata
from freshmaker import conf

if len(sys.argv) != 2:
    print("Template for testing freshmaker.Errata class")
    print("Usage: ./errata.py ERRATA_TOOL_SERVER_URL")
    sys.exit(1)

conf.errata_tool_server_url = sys.argv[1]
errata = Errata()

# Example usage:
data = errata.get_srpm_nvrs(42515)
pprint(data)