From eba425bfda96ea420fd715940819f2aea6e4ebb4 Mon Sep 17 00:00:00 2001 From: Matt Jia Date: Aug 02 2017 05:09:54 +0000 Subject: change the subject of a decision to a list of dicts For the CentOS CI test results there might be no 'item' key at all in ResultsDB, so we have to modify the subject list field in the POST /decision API to accept a list of dicts rather than a list of 'item' key , where each dict contains one or more key-value pairs of 'data' key in ResultsDB. Greenwave will then look up results by passing these key-value pairs directly to the ResultsDB query. --- diff --git a/functional-tests/conftest.py b/functional-tests/conftest.py index 9079cdf..5aaf9df 100644 --- a/functional-tests/conftest.py +++ b/functional-tests/conftest.py @@ -113,7 +113,7 @@ class TestDataBuilder(object): def create_result(self, item, testcase_name, outcome): data = { 'testcase': {'name': testcase_name}, - 'data': {'item': item}, + 'data': {'item': item, 'type': 'koji_build'}, 'outcome': outcome, } response = self.requests_session.post( diff --git a/functional-tests/test_api_v1.py b/functional-tests/test_api_v1.py index 9e6e582..ebdb385 100644 --- a/functional-tests/test_api_v1.py +++ b/functional-tests/test_api_v1.py @@ -87,7 +87,7 @@ TASKTRON_RELEASE_CRITICAL_TASKS = [ def test_cannot_make_decision_without_product_version(requests_session, greenwave_server): data = { 'decision_context': 'errata_newfile_to_qe', - 'subject': ['foo-1.0.0-1.el7'] + 'subject': [{'item': 'foo-1.0.0-1.el7', 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -99,7 +99,7 @@ def test_cannot_make_decision_without_product_version(requests_session, greenwav def test_cannot_make_decision_without_decision_context(requests_session, greenwave_server): data = { 'product_version': 'rhel-7', - 'subject': ['foo-1.0.0-1.el7'] + 'subject': [{'item': 'foo-1.0.0-1.el7', 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -120,11 +120,24 @@ def test_cannot_make_decision_without_subject(requests_session, greenwave_server assert u'Missing required subject' in r.text +def test_cannot_make_decision_with_invalid_subject(requests_session, greenwave_server): + data = { + 'decision_context': 'errata_newfile_to_qe', + 'product_version': 'rhel-7', + 'subject': ['foo-1.0.0-1.el7'], + } + r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', + headers={'Content-Type': 'application/json'}, + data=json.dumps(data)) + assert r.status_code == 400 + assert u'Invalid subject, must be a list of dicts' in r.text + + def test_404_for_inapplicable_policies(requests_session, greenwave_server): data = { 'decision_context': 'dummpy_decision', 'product_version': 'rhel-7', - 'subject': ['foo-1.0.0-1.el7'] + 'subject': [{'item': 'foo-1.0.0-1.el7', 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -142,7 +155,7 @@ def test_make_a_decison_on_passed_result(requests_session, greenwave_server, tes data = { 'decision_context': 'errata_newfile_to_qe', 'product_version': 'rhel-7', - 'subject': [nvr] + 'subject': [{'item': nvr, 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -171,7 +184,7 @@ def test_make_a_decison_on_failed_result_with_waiver( data = { 'decision_context': 'errata_newfile_to_qe', 'product_version': 'rhel-7', - 'subject': [nvr] + 'subject': [{'item': nvr, 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -192,7 +205,7 @@ def test_make_a_decison_on_failed_result(requests_session, greenwave_server, tes data = { 'decision_context': 'errata_newfile_to_qe', 'product_version': 'rhel-7', - 'subject': [nvr] + 'subject': [{'item': nvr, 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -205,14 +218,14 @@ def test_make_a_decison_on_failed_result(requests_session, greenwave_server, tes assert res_data['summary'] == expected_summary expected_unsatisfied_requirements = [ { - 'item': nvr, + 'item': {'item': nvr, 'type': 'koji_build'}, 'result_id': result['id'], 'testcase': 'dist.rpmdiff.comparison.xml_validity', 'type': 'test-result-failed' }, ] + [ { - 'item': nvr, + 'item': {'item': nvr, 'type': 'koji_build'}, 'testcase': name, 'type': 'test-result-missing' } for name in all_rpmdiff_testcase_names if name != 'dist.rpmdiff.comparison.xml_validity' @@ -225,7 +238,7 @@ def test_make_a_decison_on_no_results(requests_session, greenwave_server, testda data = { 'decision_context': 'errata_newfile_to_qe', 'product_version': 'rhel-7', - 'subject': [nvr] + 'subject': [{'item': nvr, 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -238,7 +251,7 @@ def test_make_a_decison_on_no_results(requests_session, greenwave_server, testda assert res_data['summary'] == expected_summary expected_unsatisfied_requirements = [ { - 'item': nvr, + 'item': {'item': nvr, 'type': 'koji_build'}, 'testcase': name, 'type': 'test-result-missing' } for name in all_rpmdiff_testcase_names @@ -252,7 +265,7 @@ def test_unrestricted_policy_is_always_satisfied( data = { 'decision_context': 'errata_newfile_to_qe', 'product_version': 'cdk-2', - 'subject': [nvr] + 'subject': [{'item': nvr, 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, @@ -276,7 +289,7 @@ def test_bodhi_push_update_stable_policy( data = { 'decision_context': 'bodhi_update_push_stable', 'product_version': 'fedora-26', - 'subject': [nvr] + 'subject': [{'item': nvr, 'type': 'koji_build'}] } r = requests_session.post(greenwave_server.url + 'api/v1.0/decision', headers={'Content-Type': 'application/json'}, diff --git a/greenwave/api_v1.py b/greenwave/api_v1.py index 466c054..650bf17 100644 --- a/greenwave/api_v1.py +++ b/greenwave/api_v1.py @@ -33,7 +33,7 @@ def make_decision(): { "decision_context": "bodhi_update_push_stable", "product_version": "fedora-26", - "subject": ["glibc-1.0-1.f26"] + "subject": [{"item": "glibc-1.0-1.f26", "type": "koji_build"}] } @@ -53,13 +53,13 @@ def make_decision(): "applicable_policies": ["1"], "unsatisfied_requirements": [ { - 'item': "glibc-1.0-1.f26", + 'item': {"item": "glibc-1.0-1.f26", "type": "koji_build"}, 'result_id': "123", 'testcase': 'dist.depcheck', 'type': 'test-result-failed' }, { - 'item': "glibc-1.0-1.f26", + 'item': {"item": "glibc-1.0-1.f26", "type": "koji_build"}, 'result_id': "124", 'testcase': 'dist.rpmlint', 'type': 'test-result-missing' @@ -71,8 +71,9 @@ def make_decision(): :jsonparam string decision_context: The decision context string, identified by a free-form string label. It is to be named through coordination between policy author and calling application, for example ``bodhi_update_push_stable``. - :jsonparam array subject: A list of items about which the caller is requesting a decision - used for querying ResultsDB. For example, a list of build NVRs. + :jsonparam list subject: A list of items about which the caller is requesting a decision + used for querying ResultsDB. Each item contains one or more key-value pairs of 'data' key + in ResultsDB API. For example, [{"type": "koji_build", "item": "xscreensaver-5.37-3.fc27"}]. :statuscode 200: A decision was made. :statuscode 400: Invalid data was given. """ @@ -96,14 +97,18 @@ def make_decision(): if policy.applies_to(decision_context, product_version)] if not applicable_policies: raise NotFound('Cannot find any applicable policies for %s' % product_version) - subjects = [item.strip() for item in request.get_json()['subject'] if item] + subjects = [item for item in request.get_json()['subject'] if isinstance(item, dict)] + if not subjects: + raise BadRequest('Invalid subject, must be a list of dicts') answers = [] timeout = current_app.config['REQUESTS_TIMEOUT'] for item in subjects: # XXX make this more efficient than just fetching everything + params = item.copy() + params.update({'limit': '1000'}) response = requests_session.get( current_app.config['RESULTSDB_API_URL'] + '/results', - params={'item': item, 'limit': '1000'}, timeout=timeout) + params=params, timeout=timeout) response.raise_for_status() results = response.json()['data'] if results: diff --git a/greenwave/policies.py b/greenwave/policies.py index cc79bcc..7011efe 100644 --- a/greenwave/policies.py +++ b/greenwave/policies.py @@ -117,8 +117,9 @@ class Rule(yaml.YAMLObject): Evaluate this policy rule for the given item. Args: - item (str): The item we are evaluating ('item' key in ResultsDB, - for example a build NVR). + item (dict): The item we are evaluating (one or more key-value pairs + of 'data' key in ResultsDB, for example {"type": "koji_build", + "item": "xscreensaver-5.37-3.fc27"}). results (list): List of result objects looked up in ResultsDB for this item. waivers (list): List of waiver objects looked up in WaiverDB for the results.