Example #1
0
    def push_handler(self, msg):
        """
        Handle dist-git push messages by updating the parent-child relationship of commits in Neo4j.

        :param dict msg: a message to be processed
        """
        commits = msg['body']['msg']['commits']
        parent = DistGitCommit.get_or_create({
            'hash_': msg['body']['msg']['oldrev']
        })[0]
        for commit in commits:
            child = DistGitCommit.get_or_create({
                'hash_': commit
            })[0]
            child.parent.connect(parent)
            parent = child
Example #2
0
    def build_handler(self, msg):
        """
        Handle a build state message and update Neo4j if necessary.

        :param dict msg: a message to be processed
        """
        if not msg['body']['msg']['info']['source']:
            return
        commit_hash_pattern = re.compile(r'(?:\#)([0-9a-f]{40})$')
        commit_hash = re.findall(commit_hash_pattern,
                                 msg['body']['msg']['info']['source'])

        # Container builds and rpms have commit hashes, so we want to process them
        if commit_hash:
            commit = DistGitCommit.get_or_create({'hash_': commit_hash[0]})[0]
            build = self.get_or_create_build(msg['body']['msg']['info']['id'])

            if build.__label__ == ModuleKojiBuild.__label__:
                extra_json = msg['body']['msg']['info']['extra']
                module_extra_info = extra_json.get('typeinfo',
                                                   {}).get('module')
                module_build_tag_name = module_extra_info.get(
                    'content_koji_tag')
                if module_build_tag_name:
                    try:
                        tag_info = self.koji_session.getTag(
                            module_build_tag_name)
                    except Exception:
                        log.error('Failed to get tag {0}'.format(
                            module_build_tag_name))
                        raise
                    module_build_tag = KojiTag.create_or_update({
                        'id_':
                        tag_info['id'],
                        'name':
                        module_build_tag_name
                    })[0]

                    module_build_tag.module_builds.connect(build)

                    _, components = self.koji_session.listTaggedRPMS(
                        module_build_tag_name)
                    for component in components:
                        component_build = self.get_or_create_build(component)
                        build.components.connect(component_build)

            build.conditional_connect(build.commit, commit)
Example #3
0
    def build_handler(self, msg):
        """
        Handle a build state message and update Neo4j if necessary.

        :param dict msg: a message to be processed
        """
        if not msg['body']['msg']['info']['source']:
            return
        commit_hash_pattern = re.compile(r'(?:\#)([0-9a-f]{40})$')
        commit_hash = re.findall(commit_hash_pattern,
                                 msg['body']['msg']['info']['source'])

        # Container builds and rpms have commit hashes, so we want to process them
        if commit_hash:
            commit = DistGitCommit.get_or_create({'hash_': commit_hash[0]})[0]
            build = self.get_or_create_build(msg['body']['msg']['info']['id'])

            build.conditional_connect(build.commit, commit)

            if build.__label__ == ModuleKojiBuild.__label__:
                extra_json = msg['body']['msg']['info']['extra']
                module_extra_info = extra_json.get('typeinfo',
                                                   {}).get('module')
                module_build_tag_name = module_extra_info.get(
                    'content_koji_tag')
                if module_build_tag_name:
                    _, components = self.koji_session.listTaggedRPMS(
                        module_build_tag_name)
                    for component in components:
                        component_build = self.get_or_create_build(component)
                        build.components.connect(component_build)

            elif build.__label__ == ContainerKojiBuild.__label__:
                extra_json = msg['body']['msg']['info']['extra']
                build.operator = bool(
                    extra_json.get('typeinfo', {}).get('operator-manifests',
                                                       {}).get('archive'))
                build.save()
Example #4
0
def test_module_story_node_siblings(client, resource, uid, backward_rel, expected):
    """Tests getting the siblings of an artifact's adjacent node in the module story path."""
    bug = BugzillaBug.get_or_create({
        'classification': 'Red Hat',
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'id_': '12345',
        'modified_time': datetime(2018, 2, 7, 19, 30, 47),
        'priority': 'high',
        'product_name': 'Red Hat Enterprise Linux',
        'product_version': '7.5',
        'resolution': '',
        'severity': 'low',
        'short_description': 'Some description',
        'status': 'VERIFIED',
        'target_milestone': 'rc',
        'votes': 0
    })[0]
    commit = DistGitCommit.get_or_create({
        'author_date': datetime(2017, 4, 26, 11, 44, 38),
        'commit_date': datetime(2017, 4, 26, 11, 44, 38),
        'hash_': '8a63adb248ba633e200067e1ad6dc61931727bad',
        'log_message': 'Related: #12345 - fix xyz'
    })[0]
    build = KojiBuild.get_or_create({
        'completion_time': datetime(2017, 4, 2, 19, 39, 6),
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '2345',
        'name': 'slf4j',
        'release': '4.el7_4',
        'start_time': datetime(2017, 4, 2, 19, 39, 6),
        'state': 1,
        'version': '1.7.4'
    })[0]
    build_two = KojiBuild.get_or_create({
        'completion_time': datetime(2017, 4, 2, 19, 39, 6),
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '3456',
        'name': 'slf3j',
        'release': '4.el6_3',
        'start_time': datetime(2017, 4, 2, 19, 39, 6),
        'state': 2,
        'version': '1.7.1'
    })[0]
    module_build = ModuleKojiBuild.get_or_create({
        'completion_time': datetime(2017, 4, 2, 19, 39, 6),
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '2345',
        'name': '389-ds',
        'context': 'a2037af3',
        'release': '20180805121332.a2037af3',
        'start_time': datetime(2017, 4, 2, 19, 39, 6),
        'mbs_id': 1338,
        'module_name': '389-ds',
        'module_version': '20180805121332',
        'module_stream': '1.4'
    })[0]
    advisory = Advisory.get_or_create({
        'actual_ship_date': datetime(2017, 8, 1, 15, 43, 51),
        'advisory_name': 'RHBA-2017:2251-02',
        'content_types': ['docker'],
        'created_at': datetime(2017, 4, 3, 14, 47, 23),
        'id_': '27825',
        'issue_date': datetime(2017, 8, 1, 5, 59, 34),
        'product_name': 'Red Hat Enterprise Linux',
        'product_short_name': 'RHEL',
        'security_impact': 'None',
        'state': 'SHIPPED_LIVE',
        'status_time': datetime(2017, 8, 1, 15, 43, 51),
        'synopsis': 'cifs-utils bug fix update',
        'update_date': datetime(2017, 8, 1, 7, 16)
    })[0]
    fm_event = FreshmakerEvent.get_or_create({
        'event_type_id': 8,
        'id_': '1180',
        'message_id': 'ID:messaging-devops-broker01.test',
        'state': 2,
        'state_name': 'COMPLETE',
        'state_reason': 'All container images have been rebuilt.',
        'time_created': datetime(2019, 8, 21, 13, 42, 20),
        'time_done': datetime(2099, 8, 21, 13, 42, 20)
    })[0]
    cb = ContainerKojiBuild.get_or_create({
        'completion_time': datetime(2017, 4, 2, 19, 39, 6),
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '710',
        'name': 'slf4j_2',
        'release': '4.el7_4_as',
        'start_time': datetime(2017, 4, 2, 19, 39, 6),
        'state': 1,
        'version': '1.7.4'
    })[0]
    cb_two = ContainerKojiBuild.get_or_create({
        'completion_time': datetime(2018, 4, 2, 19, 39, 6),
        'creation_time': datetime(2018, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '811',
        'name': 'some_build',
        'release': '4.el7_4_as',
        'start_time': datetime(2018, 4, 2, 19, 39, 6),
        'state': 2,
        'version': '1.7.5'
    })[0]
    ca = ContainerAdvisory.get_or_create({
        'actual_ship_date': datetime(2017, 8, 1, 15, 43, 51),
        'advisory_name': 'RHBA-2017:2251-03',
        'content_types': ['docker'],
        'created_at': datetime(2017, 4, 3, 14, 47, 23),
        'id_': '12327',
        'issue_date': datetime(2017, 8, 1, 5, 59, 34),
        'product_name': 'Red Hat Enterprise Linux',
        'product_short_name': 'RHEL',
        'security_impact': 'None',
        'state': 'SHIPPED_LIVE',
        'status_time': datetime(2017, 8, 1, 15, 43, 51),
        'synopsis': 'cifs-utils bug fix update',
        'update_date': datetime(2017, 8, 1, 7, 16)
    })[0]

    commit.resolved_bugs.connect(bug)
    commit.koji_builds.connect(build)
    build.advisories.connect(advisory)
    build_two.advisories.connect(advisory)
    fm_event.triggered_by_advisory.connect(advisory)
    fm_event.successful_koji_builds.connect(cb)
    fm_event.successful_koji_builds.connect(cb_two)
    ca.attached_builds.connect(cb)
    ca.attached_builds.connect(cb_two)
    module_build.components.connect(build)
    module_build.components.connect(build_two)
    module_build.advisories.connect(advisory)

    url = '/api/v1/siblings/{0}/{1}?story_type=module'.format(resource, uid)
    if backward_rel:
        url = '{0}&backward_rel=true'.format(url)
    rv = client.get(url)
    assert rv.status_code == 200
    assert json.loads(rv.data.decode('utf-8')) == expected
Example #5
0
def test_all_stories(client, resource, uid, expected):
    """Test getting all unique stories for an artifact."""
    bug = BugzillaBug.get_or_create({
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'id_': '12345',
        'modified_time': datetime(2018, 2, 7, 19, 30, 47),
        'priority': 'high',
        'product_name': 'Red Hat Enterprise Linux',
        'product_version': '7.5',
        'resolution': '',
        'severity': 'low',
        'short_description': 'Some description',
        'status': 'VERIFIED',
        'target_milestone': 'rc',
    })[0]
    bug_two = BugzillaBug.get_or_create({
        'creation_time': datetime(2017, 4, 1, 17, 41, 4),
        'severity': 'medium',
        'short_description': 'some description',
        'product_version': '7.2',
        'priority': 'unspecified',
        'product_name': 'Red Hat Enterprise Linux 7',
        'resolution': 'DUPLICATE',
        'target_milestone': 'rc',
        'modified_time': datetime(2018, 3, 14, 5, 53, 19),
        'id_': '1245',
        'status': 'CLOSED'
    })[0]
    commit = DistGitCommit.get_or_create({
        'author_date': datetime(2017, 4, 26, 11, 44, 38),
        'commit_date': datetime(2018, 5, 2, 10, 36, 47),
        'hash_': '8a63adb248ba633e200067e1ad6dc61931727bad',
        'log_message': 'Related: #12345 - fix xyz'
    })[0]
    commit_two = DistGitCommit.get_or_create({
        'commit_date': datetime(2018, 3, 14, 5, 52, 19),
        'author_date': datetime(2018, 3, 14, 5, 53, 25),
        'log_message': 'Repo creation',
        'hash_': 'f4dfc64c10a90492303e4f14ad3549a1a2b13575'
    })[0]
    build = KojiBuild.get_or_create({
        'completion_time': datetime(2018, 6, 2, 10, 55, 47),
        'creation_time': datetime(2018, 6, 2, 10, 36, 47),
        'epoch': '0',
        'id_': '2345',
        'name': 'slf4j',
        'release': '4.el7_4',
        'start_time': datetime(2018, 6, 2, 10, 36, 47),
        'state': 1,
        'version': '1.7.4'
    })[0]
    advisory = Advisory.get_or_create({
        'actual_ship_date': datetime(2017, 8, 1, 15, 43, 51),
        'advisory_name': 'RHBA-2017:2251-02',
        'created_at': datetime(2018, 6, 13, 10, 36, 47),
        'id_': '27825',
        'issue_date': datetime(2017, 8, 1, 5, 59, 34),
        'product_name': 'Red Hat Enterprise Linux',
        'security_impact': 'None',
        'state': 'SHIPPED_LIVE',
        'status_time': datetime(2017, 8, 1, 15, 43, 51),
        'synopsis': 'cifs-utils bug fix update',
        'update_date': datetime(2017, 8, 1, 7, 16)
    })[0]
    advisory_two = Advisory.get_or_create({
        'security_impact': 'None',
        'created_at': datetime(2018, 4, 21, 19, 36, 47),
        'synopsis': 'This is a synopsis of a test advisory.',
        'product_name': 'Release End2End Test',
        'update_date': datetime(2018, 4, 21, 19, 36, 47),
        'advisory_name': 'RHBA-2017:27760-01',
        'issue_date': datetime(2018, 3, 14, 5, 53, 25),
        'status_time': datetime(2018, 3, 14, 7, 53, 25),
        'state': 'DROPPED_NO_SHIP',
        'id_': '123456'
    })[0]
    fm_event = FreshmakerEvent.get_or_create({
        'id_': '1180',
        'state_name': 'COMPLETE',
        'state_reason': 'All container images have been rebuilt.',
        'time_created': datetime(2018, 8, 13, 10, 36, 47),
        'time_done': datetime(2018, 8, 13, 12, 45, 47)
    })[0]
    cb = ContainerKojiBuild.get_or_create({
        'completion_time': datetime(2017, 4, 2, 19, 39, 6),
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '710',
        'name': 'slf4j_2',
        'release': '4.el7_4_as',
        'start_time': datetime(2017, 4, 2, 19, 39, 6),
        'state': 1,
        'version': '1.7.4'
    })[0]
    cb_two = ContainerKojiBuild.get_or_create({
        'completion_time': datetime(2018, 8, 17, 13, 55, 47),
        'creation_time': datetime(2018, 8, 17, 8, 32, 47),
        'epoch': '0',
        'id_': '811',
        'name': 'some_build',
        'release': '4.el7_4_as',
        'start_time': datetime(2018, 8, 17, 8, 32, 47),
        'state': 2,
        'version': '1.7.5'
    })[0]

    # Longest story
    commit.resolved_bugs.connect(bug)
    commit.koji_builds.connect(build)
    build.advisories.connect(advisory, {'time_attached': datetime(2018, 6, 13, 10, 36, 47)})
    fm_event.triggered_by_advisory.connect(advisory)
    fm_event.successful_koji_builds.connect(cb)
    fm_event.successful_koji_builds.connect(cb_two)

    # Unique partial stories
    commit_two.resolved_bugs.connect(bug_two)
    commit_two.koji_builds.connect(build)
    build.advisories.connect(advisory_two, {'time_attached': datetime(2018, 4, 21, 19, 36, 47)})

    rv = client.get('/api/v1/allstories/{0}/{1}'.format(resource, uid))
    assert rv.status_code == 200
    assert json.loads(rv.data.decode('utf-8')) == expected
def test_get_resources(client, resource, uid, expected):
    """Test getting a resource from Neo4j with its relationships."""
    tbrady = User.get_or_create({
        'email': '*****@*****.**',
        'username': '******'
    })[0]
    mprahl = User.get_or_create({
        'email': '*****@*****.**',
        'username': '******'
    })[0]
    jsmith = User.get_or_create({
        'email': '*****@*****.**',
        'username': '******'
    })[0]
    commit = DistGitCommit.get_or_create({
        'author_date': datetime(2017, 4, 26, 11, 44, 38),
        'commit_date': datetime(2017, 4, 26, 11, 44, 38),
        'hash_': '8a63adb248ba633e200067e1ad6dc61931727bad',
        'log_message': 'Related: #12345 - fix xyz'
    })[0]
    commit_two = DistGitCommit.get_or_create({
        'author_date': datetime(2017, 4, 27, 11, 44, 38),
        'commit_date': datetime(2017, 4, 27, 11, 44, 38),
        'hash_': '1263adb248ba633e205067e1ad6dc61931727c2d',
        'log_message': 'Related: #12345 - fix xz'
    })[0]
    commit_three = DistGitCommit.get_or_create({
        'author_date': datetime(2017, 4, 27, 11, 44, 38),
        'commit_date': datetime(2017, 4, 27, 11, 44, 38),
        'hash_': '5663adb248ba633e205067e1ad6dc61931727123',
        'log_message': 'Revert: #12345'
    })[0]
    bug = BugzillaBug.get_or_create({
        'classification': 'Red Hat',
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'id_': '12345',
        'modified_time': datetime(2018, 2, 7, 19, 30, 47),
        'priority': 'high',
        'product_name': 'Red Hat Enterprise Linux',
        'product_version': '7.5',
        'resolution': '',
        'severity': 'low',
        'short_description': 'Some description',
        'status': 'VERIFIED',
        'target_milestone': 'rc',
        'votes': 0
    })[0]
    bug_two = BugzillaBug.get_or_create({
        'classification': 'Red Hat',
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'id_': '67890',
        'modified_time': datetime(2018, 2, 7, 19, 30, 47),
        'priority': 'medium',
        'product_name': 'Red Hat Enterprise Linux',
        'product_version': '7.3',
        'resolution': '',
        'severity': 'low',
        'short_description': 'Some description',
        'status': 'VERIFIED',
        'target_milestone': 'rc',
        'votes': 0
    })[0]
    bug_three = BugzillaBug.get_or_create({
        'classification': 'Red Hat',
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'id_': '272895',
        'modified_time': datetime(2018, 2, 7, 19, 30, 47),
        'priority': 'low',
        'product_name': 'Satellite',
        'product_version': '3',
        'resolution': '',
        'severity': 'medium',
        'short_description': 'Some description',
        'status': 'VERIFIED',
        'target_milestone': 'rc',
        'votes': 0
    })[0]
    repo = DistGitRepo.get_or_create({
        'name': 'some_repo',
        'namespace': 'some_namespace',
    })[0]
    branch = DistGitBranch.get_or_create({
        'name': 'some_branch_name',
        'repo_name': 'some_repo_name',
        'repo_namespace': 'some_repo_namespace'
    })[0]
    build = KojiBuild.get_or_create({
        'completion_time': datetime(2017, 4, 2, 19, 39, 6),
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '2345',
        'name': 'slf4j',
        'release': '4.el7_4',
        'start_time': datetime(2017, 4, 2, 19, 39, 6),
        'state': 1,
        'version': '1.7.4'
    })[0]
    tag = KojiTag.get_or_create({
        'id_': '2702',
        'name': 'some_active_tag'
    })[0]
    advisory = Advisory.get_or_create({
        'actual_ship_date': datetime(2017, 8, 1, 15, 43, 51),
        'advisory_name': 'RHBA-2017:2251-02',
        'content_types': ['docker'],
        'created_at': datetime(2017, 4, 3, 14, 47, 23),
        'id_': '27825',
        'issue_date': datetime(2017, 8, 1, 5, 59, 34),
        'product_name': 'Red Hat Enterprise Linux',
        'product_short_name': 'RHEL',
        'security_impact': 'None',
        'state': 'SHIPPED_LIVE',
        'status_time': datetime(2017, 8, 1, 15, 43, 51),
        'synopsis': 'cifs-utils bug fix update',
        'update_date': datetime(2017, 8, 1, 7, 16)
    })[0]
    fm_event = FreshmakerEvent.get_or_create({
        'event_type_id': 8,
        'id_': '1180',
        'message_id': 'ID:messaging-devops-broker01.test',
        'state': 2,
        'state_name': 'COMPLETE',
        'state_reason': 'All container images have been rebuilt',
        'time_created': datetime(2019, 8, 21, 13, 42, 20),
        'time_done': datetime(2099, 8, 21, 13, 42, 20)
    })[0]
    fm_build = FreshmakerBuild.get_or_create({
        'id_': 398,
        'build_id': 15639305,
        'dep_on': "jboss-eap-7-eap70-openshift-docker",
        'name': "metrics-hawkular-metrics-docker",
        'original_nvr': "metrics-hawkular-metrics-docker-v3.7.23-10",
        'rebuilt_nvr': "metrics-hawkular-metrics-docker-v3.7.23-10.1522094767",
        'state': 1,
        'state_name': "DONE",
        'state_reason': "Built successfully.",
        'time_completed': datetime(2017, 4, 2, 19, 39, 6),
        'time_submitted': datetime(2017, 4, 2, 19, 39, 6),
        'type_': 1,
        'type_name': "IMAGE",
        'url': "/api/1/builds/398"
    })[0]
    cb = ContainerKojiBuild.get_or_create({
        'completion_time': datetime(2017, 4, 2, 19, 39, 6),
        'creation_time': datetime(2017, 4, 2, 19, 39, 6),
        'epoch': '0',
        'id_': '710',
        'name': 'slf4j_2',
        'release': '4.el7_4_as',
        'start_time': datetime(2017, 4, 2, 19, 39, 6),
        'state': 1,
        'version': '1.7.4'
    })[0]

    if resource == 'bugzillabug':
        bug.assignee.connect(mprahl)
        bug.qa_contact.connect(jsmith)
        bug.reporter.connect(tbrady)
        commit.resolved_bugs.connect(bug)
        commit_two.resolved_bugs.connect(bug)
        commit_three.reverted_bugs.connect(bug)
        advisory.attached_bugs.connect(bug)

    if resource == 'distgitcommit':
        commit.author.connect(tbrady)
        commit.parent.connect(commit_two)
        commit_three.parent.connect(commit)
        commit.related_bugs.connect(bug)
        commit.related_bugs.connect(bug_three)
        commit.reverted_bugs.connect(bug_two)
        repo.commits.connect(commit)
        branch.commits.connect(commit)
        commit.resolved_bugs.connect(bug)
        commit.resolved_bugs.connect(bug_two)

    if resource == 'kojibuild':
        build.owner.connect(mprahl)
        build.commit.connect(commit_two)
        tag.builds.connect(build)

    if resource == 'advisory':
        advisory.assigned_to.connect(mprahl)
        advisory.reporter.connect(jsmith)
        advisory.attached_builds.connect(build)
        advisory.attached_bugs.connect(bug)

    if resource == 'freshmakerevent':
        fm_event.triggered_by_advisory.connect(advisory)
        fm_event.successful_koji_builds.connect(cb)
        fm_event.requested_builds.connect(fm_build)

    if resource == 'containerbuild':
        fm_event.successful_koji_builds.connect(cb)

    rv = client.get('/api/v1/{0}/{1}'.format(resource, uid))
    assert rv.status_code == 200
    assert json.loads(rv.data.decode('utf-8')) == expected
Example #7
0
def test_get_recent_nodes(client):
    """Test the get_recent_nodes function."""
    id_dict = {
        FreshmakerEvent.__label__: 'id',
        BugzillaBug.__label__: 'id',
        DistGitCommit.__label__: 'hash',
        KojiBuild.__label__: 'id',
        Advisory.__label__: 'id'
    }
    timestamp_dict = {
        FreshmakerEvent.__label__: 'time_created',
        BugzillaBug.__label__: 'modified_time',
        DistGitCommit.__label__: 'commit_date',
        KojiBuild.__label__: 'completion_time',
        Advisory.__label__: 'update_date'
    }
    expected = {
        'data': {
            'Advisory': [{
                'advisory_name': 'RHBA-2017:27760-01',
                'update_date': '2017-05-30T11:44:38Z',
                'issue_date': None,
                'created_at': None,
                'state': None,
                'product_name': None,
                'security_sla': None,
                'synopsis': None,
                'security_impact': None,
                'status_time': None,
                'actual_ship_date': None,
                'release_date': None,
                'id': '66666',
                'display_name': 'RHBA-2017:27760-01',
                'resource_type': 'Advisory',
                'assigned_to': None,
                'attached_bugs': [],
                'attached_builds': [],
                'triggered_freshmaker_event': [],
                'reporter': None
            }],
            'DistGitCommit': [{
                'log_message': None,
                'author': None,
                'author_date': None,
                'koji_builds': [],
                'hash': '55555',
                'commit_date': '2017-05-02T11:44:38Z',
                'display_name': 'commit #55555',
                'resource_type': 'DistGitCommit',
                'related_bugs': [],
                'repos': [],
                'resolved_bugs': [],
                'reverted_bugs': []
            }],
            'FreshmakerEvent': [{
                'state_reason': None,
                'state_name': None,
                'time_created': '2017-05-30T11:44:38Z',
                'time_done': None,
                'id': '77777',
                'display_name': 'Freshmaker event 77777',
                'resource_type': 'FreshmakerEvent',
                'requested_builds': [],
                'successful_koji_builds': [],
                'triggered_by_advisory': None,
            }],
            'KojiBuild': [{
                'name': 'slf4j',
                'start_time': None,
                'creation_time': None,
                'state': None,
                'completion_time': '2017-05-27T11:44:38Z',
                'epoch': None,
                'version': '1.7.4',
                'release': '4.el7_4',
                'id': '44444',
                'display_name': 'slf4j-1.7.4-4.el7_4',
                'resource_type': 'KojiBuild',
                'module_builds': [],
                'owner': None,
                'commit': None,
                'advisories': []
            }],
            'BugzillaBug': [{
                'status': None,
                'severity': None,
                'resolution': None,
                'product_version': None,
                'creation_time': None,
                'modified_time': '2017-06-26T11:44:38Z',
                'product_name': None,
                'priority': None,
                'short_description': None,
                'target_milestone': None,
                'id': '22222',
                'display_name': 'RHBZ#22222',
                'resource_type': 'BugzillaBug',
                'assignee': None,
                'attached_advisories': [],
                'qa_contact': None,
                'related_by_commits': [],
                'reporter': None,
                'resolved_by_commits': [],
                'reverted_by_commits': []
            }, {
                'status': None,
                'severity': None,
                'resolution': None,
                'product_version': None,
                'creation_time': None,
                'modified_time': '2017-05-26T11:44:38Z',
                'product_name': None,
                'priority': None,
                'short_description': None,
                'target_milestone': None,
                'id': '33333',
                'display_name': 'RHBZ#33333',
                'resource_type': 'BugzillaBug',
                'assignee': None,
                'attached_advisories': [],
                'qa_contact': None,
                'related_by_commits': [],
                'reporter': None,
                'resolved_by_commits': [],
                'reverted_by_commits': []
            }, {
                'status': None,
                'severity': None,
                'resolution': None,
                'product_version': None,
                'creation_time': None,
                'modified_time': '2017-04-26T11:44:38Z',
                'product_name': None,
                'priority': None,
                'short_description': None,
                'target_milestone': None,
                'id': '11111',
                'display_name': 'RHBZ#11111',
                'resource_type': 'BugzillaBug',
                'assignee': None,
                'attached_advisories': [],
                'qa_contact': None,
                'related_by_commits': [],
                'reporter': None,
                'resolved_by_commits': [],
                'reverted_by_commits': []
            }]
        },
        'metadata': {
            'id_keys': id_dict,
            'timestamp_keys': timestamp_dict
        }
    }

    BugzillaBug.get_or_create({
        'id_':
        '11111',
        'modified_time':
        datetime(2017, 4, 26, 11, 44, 38)
    })
    BugzillaBug.get_or_create({
        'id_':
        '22222',
        'modified_time':
        datetime(2017, 6, 26, 11, 44, 38)
    })
    BugzillaBug.get_or_create({
        'id_':
        '33333',
        'modified_time':
        datetime(2017, 5, 26, 11, 44, 38)
    })
    KojiBuild.get_or_create({
        'id_':
        '44444',
        'completion_time':
        datetime(2017, 5, 27, 11, 44, 38),
        'name':
        'slf4j',
        'version':
        '1.7.4',
        'release':
        '4.el7_4'
    })
    DistGitCommit.get_or_create({
        'hash_': '55555',
        'commit_date': datetime(2017, 5, 2, 11, 44, 38)
    })
    Advisory.get_or_create({
        'id_': '66666',
        'update_date': datetime(2017, 5, 30, 11, 44, 38),
        'advisory_name': 'RHBA-2017:27760-01'
    })
    FreshmakerEvent.get_or_create({
        'id_':
        '77777',
        'time_created':
        datetime(2017, 5, 30, 11, 44, 38),
    })

    rv = client.get('/api/v1/recents')
    assert rv.status_code == 200
    assert json.loads(rv.data.decode('utf-8')) == expected
Example #8
0
    def update_neo4j(self, results):
        """
        Update Neo4j with the dist-git commit and push information from Teiid.

        :param list results: a list of dictionaries
        """
        pool = Pool(processes=8)
        counter = 0
        for result in results:
            if counter % 200 == 0:
                until = counter + 200
                if until > len(results):
                    until = len(results)
                # Because of the joins in the SQL query, we end up with several rows with the same
                # commit hash and we only want to query cgit once per commit
                unique_commits = set([(c['module'], c['sha'])
                                      for c in results[counter:until]])
                log.debug(
                    'Getting the author and committer email addresses from cgit in parallel '
                    'for results {0} to {1}'.format(counter, until))
                repos_info = {}
                for _r in pool.map(DistGitScraper._get_repo_info,
                                   unique_commits):
                    r = json.loads(_r)
                    repos_info[r['commit']] = r
                # This is no longer needed so it can be cleared to save RAM
                del unique_commits
                # A lot of RAM was allocated or used up, so let's call gc.collect() to ensure it
                # is removed
                gc.collect()
            counter += 1
            log.info('Processing commit and push entry {0}/{1}'.format(
                str(counter), str(len(results))))
            repo_info = repos_info[result['sha']]
            if not repo_info.get('namespace'):
                log.info(
                    'Skipping nodes creation with commit ID {0} and push ID {1}'
                    .format(result['commit_id'], result['push_id']))
                continue

            log.debug(
                'Creating nodes associated with commit ID {0} and push ID {1}'.
                format(result['commit_id'], result['push_id']))
            repo = DistGitRepo.get_or_create({
                'namespace':
                repo_info['namespace'],
                'name':
                result['module']
            })[0]
            branch_name = result['ref'].rsplit('/', 1)[1]
            branch = DistGitBranch.get_or_create({
                'name':
                branch_name,
                'repo_namespace':
                repo_info['namespace'],
                'repo_name':
                result['module']
            })[0]
            commit = DistGitCommit.create_or_update({
                'author_date':
                result['author_date'],
                'commit_date':
                result['commit_date'],
                'hash_':
                result['sha'],
                # In case we get unicode characters in Python 2
                'log_message':
                bytes(result['log_message'], 'utf-8').decode()
            })[0]
            push = DistGitPush.get_or_create({
                'id_': result['push_id'],
                'push_date': result['push_date'],
                'push_ip': result['push_ip']
            })[0]
            bug = BugzillaBug.get_or_create({'id_': result['bugzilla_id']})[0]

            log.debug(
                'Creating the user nodes associated with commit ID {0} and push ID {1}'
                .format(result['commit_id'], result['push_id']))
            author = User.create_or_update({
                'username':
                repo_info['author_username'],
                'email':
                repo_info['author_email']
            })[0]
            committer = User.create_or_update({
                'username':
                repo_info['committer_username'],
                'email':
                repo_info['committer_email']
            })[0]
            pusher = User.get_or_create({'username': result['pusher']})[0]

            log.debug(
                'Creating the relationships associated with commit ID {0} and push ID {1}'
                .format(result['commit_id'], result['push_id']))
            repo.contributors.connect(author)
            repo.contributors.connect(committer)
            repo.contributors.connect(pusher)
            repo.commits.connect(commit)
            repo.pushes.connect(push)
            repo.branches.connect(branch)

            branch.contributors.connect(author)
            branch.contributors.connect(committer)
            branch.contributors.connect(pusher)
            branch.commits.connect(commit)
            branch.pushes.connect(push)

            push.conditional_connect(push.pusher, pusher)
            push.commits.connect(commit)

            commit.conditional_connect(commit.author, author)
            commit.conditional_connect(commit.committer, committer)

            if repo_info['parent']:
                parent_commit = DistGitCommit.get_or_create(
                    {'hash_': repo_info['parent']})[0]
                commit.conditional_connect(commit.parent, parent_commit)

            if result['bugzilla_type'] == 'related':
                commit.related_bugs.connect(bug)
            elif result['bugzilla_type'] == 'resolves':
                commit.resolved_bugs.connect(bug)
            elif result['bugzilla_type'] == 'reverted':
                commit.reverted_bugs.connect(bug)
            # This is no longer needed so it can be cleared to save RAM
            del repo_info
Example #9
0
    def update_neo4j(self, builds):
        """
        Update Neo4j with Koji build information from Teiid.

        :param list builds: a list of dictionaries
        """
        # Uploads builds data to their respective nodes
        log.info('Beginning to upload data to Neo4j')
        count = 0

        for build_dict in builds:
            build_params = dict(
                id_=build_dict['id'],
                epoch=build_dict['epoch'],
                state=build_dict['state'],
                creation_time=build_dict['creation_time'],
                start_time=build_dict['start_time'],
                completion_time=build_dict['completion_time'],
                extra=build_dict['extra'],
                name=build_dict['package_name'],
                version=build_dict['version'],
                release=build_dict['release']
            )

            package_name = build_dict['package_name']
            try:
                extra_json = json.loads(build_dict['extra'])
            except (ValueError, TypeError):
                extra_json = {}

            container_build = False
            # Checking a heuristic for determining if a build is a container build since, currently
            # there is no definitive way to do it.
            if extra_json and extra_json.get('container_koji_build_id'):
                container_build = True
            # Checking another heuristic for determining if a build is a container build since
            # currently there is no definitive way to do it.
            elif (package_name.endswith('-container') or package_name.endswith('-docker')):
                container_build = True

            if container_build:
                build = ContainerKojiBuild.create_or_update(build_params)[0]
            else:
                build = KojiBuild.create_or_update(build_params)[0]

            if build_dict['owner_username']:
                username = build_dict['owner_username'].split('@')[0]
            else:
                username = build_dict['owner_name']
            user = User.get_or_create(dict(username=username))[0]
            build.conditional_connect(build.owner, user)

            tags = self.get_build_tags(build_dict['id'])
            current_tag_ids = set()
            for _tag in tags:
                current_tag_ids.add(_tag['tag_id'])
                tag = KojiTag.create_or_update(dict(
                    id_=_tag['tag_id'],
                    name=_tag['tag_name']
                ))[0]

                tag.builds.connect(build)

            # _tag.id_ must be cast as an int because it is stored as a string in Neo4j since
            # it's a UniqueIdProperty
            connected_tags = {int(_tag.id_): _tag for _tag in build.tags.all()}
            extra_connected_tag_ids = set(connected_tags.keys()) - current_tag_ids
            for tag_id in extra_connected_tag_ids:
                build.tags.disconnect(connected_tags[tag_id])

            count += 1
            log.info('Uploaded {0} builds out of {1}'.format(count, len(builds)))

            try:
                extra_json = json.loads(build_dict['extra'])
            except (ValueError, TypeError):
                extra_json = {}

            container_koji_task_id = extra_json.get('container_koji_task_id')
            if build_dict['task_id']:
                task_id = build_dict['task_id']
            elif container_koji_task_id:
                task_id = container_koji_task_id
            else:
                # Continue if the task_id is None
                continue
            # Getting task related to the current build
            task_dict = self.get_task(task_id)[0]
            xml_root = ET.fromstring(task_dict['request'])
            commit_hash = None
            for child in xml_root.iter('string'):
                if child.text and child.text.startswith('git'):
                    commit_hash = child.text.rsplit('#', 1)[1]
                    break

            if not task_dict:
                # Continue if no corresponding task found
                continue

            task = KojiTask.create_or_update(dict(
                id_=task_dict['id'],
                weight=task_dict['weight'],
                create_time=task_dict['create_time'],
                start_time=task_dict['start_time'],
                completion_time=task_dict['completion_time'],
                state=task_dict['state'],
                priority=task_dict['priority'],
                arch=task_dict['arch'],
                method=task_dict['method']
            ))[0]

            # Defining Relationships
            task.builds.connect(build)
            task.conditional_connect(task.owner, user)
            if commit_hash:
                commit = DistGitCommit.get_or_create(dict(hash_=commit_hash))[0]
                build.conditional_connect(build.commit, commit)

            child_tasks = self.get_task_children(task_dict['id'])

            if not child_tasks:
                # Continue if no corresponding child task found
                continue

            for child_task_dict in child_tasks:
                child_task = KojiTask.create_or_update(dict(
                    id_=child_task_dict['id'],
                    weight=child_task_dict['weight'],
                    create_time=child_task_dict['create_time'],
                    start_time=child_task_dict['start_time'],
                    completion_time=child_task_dict['completion_time'],
                    state=child_task_dict['state'],
                    priority=child_task_dict['priority'],
                    arch=child_task_dict['arch'],
                    method=child_task_dict['method']
                ))[0]
                child_task.conditional_connect(child_task.parent, task)
Example #10
0
def test_get_recent_nodes():
    """Test the get_recent_nodes function."""
    BugzillaBug.get_or_create({
        'id_':
        '11111',
        'modified_time':
        datetime(2017, 4, 26, 11, 44, 38)
    })
    BugzillaBug.get_or_create({
        'id_':
        '22222',
        'modified_time':
        datetime(2017, 6, 26, 11, 44, 38)
    })
    BugzillaBug.get_or_create({
        'id_':
        '33333',
        'modified_time':
        datetime(2017, 5, 26, 11, 44, 38)
    })
    KojiBuild.get_or_create({
        'id_':
        '44444',
        'completion_time':
        datetime(2017, 5, 27, 11, 44, 38)
    })
    DistGitCommit.get_or_create({
        'hash_': '55555',
        'commit_date': datetime(2017, 5, 2, 11, 44, 38)
    })
    Advisory.get_or_create({
        'id_': '66666',
        'update_date': datetime(2017, 5, 30, 11, 44, 38)
    })
    Advisory.get_or_create({'id_': '66666', 'update_date': None})
    FreshmakerEvent.get_or_create({
        'id_':
        '77777',
        'time_created':
        datetime(2017, 5, 30, 11, 44, 38),
    })

    nodes, meta = estuary.utils.recents.get_recent_nodes()
    assert nodes['Advisory'][0]['id'] == '66666'
    assert nodes['DistGitCommit'][0]['hash'] == '55555'
    assert nodes['FreshmakerEvent'][0]['id'] == '77777'
    assert nodes['KojiBuild'][0]['id'] == '44444'
    assert nodes['BugzillaBug'][0]['id'] == '22222'
    assert nodes['BugzillaBug'][1]['id'] == '33333'
    assert nodes['BugzillaBug'][2]['id'] == '11111'

    id_dict = {
        FreshmakerEvent.__label__: 'id',
        BugzillaBug.__label__: 'id',
        DistGitCommit.__label__: 'hash',
        KojiBuild.__label__: 'id',
        Advisory.__label__: 'id'
    }
    timestamp_dict = {
        FreshmakerEvent.__label__: 'time_created',
        BugzillaBug.__label__: 'modified_time',
        DistGitCommit.__label__: 'commit_date',
        KojiBuild.__label__: 'completion_time',
        Advisory.__label__: 'update_date'
    }

    assert meta['id_keys'] == id_dict
    assert meta['timestamp_keys'] == timestamp_dict
Example #11
0
    def _update_neo4j(neo4j_url, total_results, counter_and_results):
        """
        Update Neo4j results via mapping with multiprocessing.

        :param str neo4j_url: database url for Neo4j
        :param int total_results: the total number of results that will be processed. This is used
        for a logging statement about progress.
        :param tuple counter_and_results: a tuple where the first index is the current counter and
        the second index is a list of dictionaries representing results from Teiid
        """
        try:
            previous_total = counter_and_results[0]
            results = counter_and_results[1]
            # Since _update_neo4j will be run in a separate process, we must configure the database
            # URL every time the method is run.
            neomodel_config.DATABASE_URL = neo4j_url
            # Create a thread pool with 4 threads to speed up queries to cgit
            pool = ThreadPool(4)
            counter = 0
            for result in results:
                if counter % 200 == 0:
                    until = counter + 200
                    if until > len(results):
                        until = len(results)
                    # Because of the joins in the SQL query, we end up with several rows with the
                    # same commit hash and we only want to query cgit once per commit
                    unique_commits = set([(c['module'], c['sha']) for c in results[counter:until]])
                    log.debug('Getting the author email addresses from cgit in parallel '
                              'for results {0} to {1}'.format(counter, until))
                    repos_info = {r['commit']: r for r in pool.map(
                        DistGitScraper._get_repo_info, unique_commits)}
                    # This is no longer needed so it can be cleared to save RAM
                    del unique_commits
                counter += 1
                log.info('Processing commit entry {0}/{1}'.format(
                    previous_total + counter, total_results))
                repo_info = repos_info[result['sha']]
                if not repo_info.get('namespace'):
                    log.info('Skipping nodes creation with commit ID {0}'.format(
                        result['commit_id']))
                    continue

                log.debug('Creating nodes associated with commit ID {0}'.format(
                    result['commit_id']))
                repo = DistGitRepo.get_or_create({
                    'namespace': repo_info['namespace'],
                    'name': result['module']
                })[0]
                branch_name = result['ref'].rsplit('/', 1)[1]
                branch = DistGitBranch.get_or_create({
                    'name': branch_name,
                    'repo_namespace': repo_info['namespace'],
                    'repo_name': result['module']
                })[0]
                commit = DistGitCommit.create_or_update({
                    'author_date': result['author_date'],
                    'commit_date': result['commit_date'],
                    'hash_': result['sha'],
                    # In case we get unicode characters in Python 2
                    'log_message': bytes(result['log_message'], 'utf-8').decode()
                })[0]
                bug = BugzillaBug.get_or_create({'id_': result['bugzilla_id']})[0]

                log.debug('Creating the user nodes associated with commit ID {0}'
                          .format(result['commit_id']))
                author = User.create_or_update({
                    'username': repo_info['author_username'],
                    'email': repo_info['author_email']
                })[0]

                log.debug('Creating the relationships associated with commit ID {0}'
                          .format(result['commit_id']))
                repo.contributors.connect(author)
                repo.commits.connect(commit)
                repo.branches.connect(branch)

                branch.contributors.connect(author)
                branch.commits.connect(commit)

                commit.conditional_connect(commit.author, author)

                if repo_info['parent']:
                    parent_commit = DistGitCommit.get_or_create({'hash_': repo_info['parent']})[0]
                    commit.conditional_connect(commit.parent, parent_commit)

                if result['bugzilla_type'] == 'related':
                    commit.related_bugs.connect(bug)
                elif result['bugzilla_type'] == 'resolves':
                    commit.resolved_bugs.connect(bug)
                elif result['bugzilla_type'] == 'reverted':
                    commit.reverted_bugs.connect(bug)
                # This is no longer needed so it can be cleared to save RAM
                del repo_info
        finally:
            # Close the DB connection after this is done processing
            db.driver.close()
Example #12
0
    def update_neo4j(self, builds):
        """
        Update Neo4j with Koji build information from Teiid.

        :param list builds: a list of dictionaries
        """
        # Uploads builds data to their respective nodes
        log.info('Beginning to upload data to Neo4j')
        count = 0

        for build_dict in builds:
            build_params = dict(
                id_=build_dict['id'],
                epoch=build_dict['epoch'],
                state=build_dict['state'],
                creation_time=build_dict['creation_time'],
                start_time=build_dict['start_time'],
                completion_time=build_dict['completion_time'],
                name=build_dict['package_name'],
                version=build_dict['version'],
                release=build_dict['release']
            )

            try:
                extra_json = json.loads(build_dict['extra'])
            except (ValueError, TypeError):
                extra_json = {}

            if self.is_container_build(build_dict):
                build_params['operator'] = bool(
                    extra_json.get('typeinfo', {}).get('operator-manifests', {}).get('archive')
                )
                try:
                    build = ContainerKojiBuild.create_or_update(build_params)[0]
                except neomodel.exceptions.ConstraintValidationFailed:
                    # This must have errantly been created as a KojiBuild instead of a
                    # ContainerKojiBuild, so let's fix that.
                    build = KojiBuild.nodes.get_or_none(id_=build_params['id_'])
                    if not build:
                        # If there was a constraint validation failure and the build isn't just the
                        # wrong label, then we can't recover.
                        raise
                    build.add_label(ContainerKojiBuild.__label__)
                    build = ContainerKojiBuild.create_or_update(build_params)[0]
            elif self.is_module_build(build_dict):
                module_extra_info = extra_json.get('typeinfo', {}).get('module')
                try:
                    build_params['context'] = module_extra_info.get('context')
                    build_params['mbs_id'] = module_extra_info.get('module_build_service_id')
                    build_params['module_name'] = module_extra_info.get('name')
                    build_params['module_stream'] = module_extra_info.get('stream')
                    build_params['module_version'] = module_extra_info.get('version')
                    build = ModuleKojiBuild.create_or_update(build_params)[0]
                except neomodel.exceptions.ConstraintValidationFailed:
                    # This must have errantly been created as a KojiBuild instead of a
                    # ModuleKojiBuild, so let's fix that.
                    build = KojiBuild.nodes.get_or_none(id_=build_params['id_'])
                    if not build:
                        # If there was a constraint validation failure and the build isn't just the
                        # wrong label, then we can't recover.
                        raise
                    build.add_label(ModuleKojiBuild.__label__)
                    build = ModuleKojiBuild.create_or_update(build_params)[0]
            else:
                build = KojiBuild.create_or_update(build_params)[0]

            username = build_dict['owner_name']
            user = User.get_or_create(dict(username=username))[0]
            build.conditional_connect(build.owner, user)

            if build.__label__ == ModuleKojiBuild.__label__:
                module_build_tag_name = module_extra_info.get('content_koji_tag')
                if module_build_tag_name:
                    module_components = self.get_tag_info(module_build_tag_name)
                    # Some modules don't have components
                    if module_components:
                        for item in module_components:
                            module_component = KojiBuild.get_or_create(dict(
                                id_=item['build_id']
                            ))[0]
                            build.components.connect(module_component)

                        component_builds = self.get_build_info(
                            [item['build_id'] for item in module_components])
                        self.update_neo4j(component_builds)

            count += 1
            log.info('Uploaded {0} builds out of {1}'.format(count, len(builds)))

            container_koji_task_id = extra_json.get('container_koji_task_id')
            if build_dict['task_id']:
                task_id = build_dict['task_id']
            elif container_koji_task_id:
                task_id = container_koji_task_id
            else:
                # Continue if the task_id is None
                continue
            # Getting task related to the current build
            try:
                task_dict = self.get_task(task_id)[0]
            except IndexError:
                continue

            commit_hash = None
            # Only look for the commit hash if the build is an RPM or container
            if task_dict['method'] in ('build', 'buildContainer'):
                xml_root = ET.fromstring(task_dict['request'])
                for child in xml_root.iter('string'):
                    if child.text and child.text.startswith('git'):
                        commit_hash = child.text.rsplit('#', 1)[1]
                        break

            if commit_hash:
                commit = DistGitCommit.get_or_create(dict(hash_=commit_hash))[0]
                build.conditional_connect(build.commit, commit)
Example #13
0
def test_get_stories(client, resource, uid, expected):
    """Test getting a resource story from Neo4j with its relationships."""
    commit = DistGitCommit.get_or_create({
        'author_date':
        datetime(2017, 4, 26, 11, 44, 38),
        'commit_date':
        datetime(2017, 4, 26, 11, 44, 38),
        'hash_':
        '8a63adb248ba633e200067e1ad6dc61931727bad',
        'log_message':
        'Related: #12345 - fix xyz'
    })[0]
    advisory = Advisory.get_or_create({
        'actual_ship_date':
        datetime(2017, 8, 1, 15, 43, 51),
        'advisory_name':
        'RHBA-2017:2251-02',
        'content_types': ['docker'],
        'created_at':
        datetime(2017, 4, 3, 14, 47, 23),
        'id_':
        '27825',
        'issue_date':
        datetime(2017, 8, 1, 5, 59, 34),
        'product_name':
        'Red Hat Enterprise Linux',
        'product_short_name':
        'RHEL',
        'security_impact':
        'None',
        'state':
        'SHIPPED_LIVE',
        'status_time':
        datetime(2017, 8, 1, 15, 43, 51),
        'synopsis':
        'cifs-utils bug fix update',
        'type_':
        'RHBA',
        'update_date':
        datetime(2017, 8, 1, 7, 16),
        'updated_at':
        datetime(2017, 8, 1, 15, 43, 51)
    })[0]
    bug = BugzillaBug.get_or_create({
        'classification':
        'Red Hat',
        'creation_time':
        datetime(2017, 4, 2, 19, 39, 6),
        'id_':
        '12345',
        'modified_time':
        datetime(2018, 2, 7, 19, 30, 47),
        'priority':
        'high',
        'product_name':
        'Red Hat Enterprise Linux',
        'product_version':
        '7.5',
        'resolution':
        '',
        'severity':
        'low',
        'short_description':
        'Some description',
        'status':
        'VERIFIED',
        'target_milestone':
        'rc',
        'votes':
        0
    })[0]
    bug_two = BugzillaBug.get_or_create({
        'classification':
        'Red Hat',
        'creation_time':
        datetime(2017, 4, 2, 6, 43, 58),
        'id_':
        '5555',
        'modified_time':
        datetime(2017, 12, 5, 10, 12, 47),
        'priority':
        'unspecified',
        'product_name':
        'Red Hat CloudForms Management Engine',
        'product_version':
        '5.7.0',
        'resolution':
        'WORKSFORME',
        'severity':
        'unspecified',
        'short_description':
        'Fail to delete OSP tenant by CFME',
        'status':
        'CLOSED',
        'target_milestone':
        'GA',
        'votes':
        0
    })[0]
    build = KojiBuild.get_or_create({
        'completion_time':
        datetime(2017, 4, 2, 19, 39, 6),
        'creation_time':
        datetime(2017, 4, 2, 19, 39, 6),
        'epoch':
        '0',
        'id_':
        '2345',
        'name':
        'slf4j',
        'release':
        '4.el7_4',
        'start_time':
        datetime(2017, 4, 2, 19, 39, 6),
        'state':
        1,
        'version':
        '1.7.4'
    })[0]
    fm_event = FreshmakerEvent.get_or_create({
        'event_type_id': 8,
        'id_': '1180',
        'message_id': 'ID:messaging-devops-broker01.test',
        'state': 2,
        'state_name': 'COMPLETE',
        'state_reason': 'All container images have been rebuilt.',
        'url': '/api/1/events/1180'
    })[0]
    cb = ContainerKojiBuild.get_or_create({
        'completion_time':
        datetime(2017, 4, 2, 19, 39, 6),
        'creation_time':
        datetime(2017, 4, 2, 19, 39, 6),
        'epoch':
        '0',
        'id_':
        '710',
        'name':
        'slf4j_2',
        'release':
        '4.el7_4_as',
        'start_time':
        datetime(2017, 4, 2, 19, 39, 6),
        'state':
        1,
        'version':
        '1.7.4'
    })[0]

    commit.resolved_bugs.connect(bug_two)
    commit.resolved_bugs.connect(bug)
    commit.koji_builds.connect(build)
    build.advisories.connect(advisory)
    advisory.attached_builds.connect(build)
    fm_event.triggered_by_advisory.connect(advisory)
    fm_event.triggered_container_builds.connect(cb)

    rv = client.get('/api/v1/story/{0}/{1}'.format(resource, uid))
    assert rv.status_code == 200
    assert json.loads(rv.data.decode('utf-8')) == expected