From 7b108793ed97447b8ef298a57e65c67852a45cfc Mon Sep 17 00:00:00 2001 From: Lukas Holecek Date: Jul 19 2018 08:56:26 +0000 Subject: Merge #257 `Make decision about compose for all architectures/variants` --- diff --git a/conf/policies/redhat.yaml b/conf/policies/redhat.yaml index a5200e8..90264bb 100644 --- a/conf/policies/redhat.yaml +++ b/conf/policies/redhat.yaml @@ -19,3 +19,12 @@ rules: "policycoreutils", ] } +--- !Policy +id: "rtt_compose" +product_versions: + - rhel-something +decision_context: rtt_compose_gate +subject_type: compose +blacklist: [] +rules: + - !PassingTestCaseRule {test_case_name: rtt.acceptance.validation} diff --git a/functional-tests/conftest.py b/functional-tests/conftest.py index 47150be..e1b9ae9 100644 --- a/functional-tests/conftest.py +++ b/functional-tests/conftest.py @@ -274,6 +274,18 @@ class TestDataBuilder(object): data['data']['scenario'] = scenario return self._create_result(data) + def create_rtt_compose_result(self, compose_id, outcome, variant, architecture): + data = { + 'testcase': {'name': 'rtt.acceptance.validation'}, + 'outcome': outcome, + 'data': { + 'productmd.compose.id': [compose_id], + 'system_variant': [variant], + 'system_architecture': [architecture], + } + } + return self._create_result(data) + def create_koji_build_result(self, nvr, testcase_name, outcome, type_='koji_build'): data = { 'testcase': {'name': testcase_name}, diff --git a/functional-tests/test_api_v1.py b/functional-tests/test_api_v1.py index 5688ea3..cf5307b 100644 --- a/functional-tests/test_api_v1.py +++ b/functional-tests/test_api_v1.py @@ -36,7 +36,7 @@ def test_inspect_policies(requests_session, greenwave_server): assert r.status_code == 200 body = r.json() policies = body['policies'] - assert len(policies) == 7 + assert len(policies) == 8 assert any(p['id'] == 'taskotron_release_critical_tasks' for p in policies) assert any(p['decision_context'] == 'bodhi_update_push_stable' for p in policies) assert any(p['product_versions'] == ['fedora-26'] for p in policies) @@ -891,3 +891,65 @@ def test_validate_gating_yaml_missing_tag(requests_session, greenwave_server): greenwave_server + 'api/v1.0/validate-gating-yaml', data=gating_yaml) assert result.json().get('message') == "Missing !Policy tag" assert result.status_code == 400 + + +def test_make_a_decision_about_compose_all_variants_architectures( + requests_session, greenwave_server, testdatabuilder): + compose_id = testdatabuilder.unique_compose_id() + + failed_results = testdatabuilder.create_rtt_compose_result( + compose_id=compose_id, + variant='BaseOS', + architecture='ppc64', + outcome='FAILED') + + testdatabuilder.create_rtt_compose_result( + compose_id=compose_id, + variant='BaseOS', + architecture='x86_64', + outcome='PASSED') + + data = { + 'decision_context': 'rtt_compose_gate', + 'product_version': 'rhel-something', + 'subject_type': 'compose', + 'subject_identifier': compose_id, + } + r = requests_session.post(greenwave_server + 'api/v1.0/decision', + headers={'Content-Type': 'application/json'}, + data=json.dumps(data)) + assert r.status_code == 200 + res_data = r.json() + assert not res_data['policies_satisfied'] + assert res_data['unsatisfied_requirements'] == [{ + 'item': {'productmd.compose.id': compose_id}, + 'result_id': failed_results['id'], + 'scenario': None, + 'testcase': 'rtt.acceptance.validation', + 'type': 'test-result-failed' + }] + + +def test_make_a_decision_about_compose_new_variants_architectures( + requests_session, greenwave_server, testdatabuilder): + compose_id = testdatabuilder.unique_compose_id() + + for arch, outcome in [('ppc64', 'FAILED'), ('ppc64', 'PASSED'), ('x86_64', 'PASSED')]: + testdatabuilder.create_rtt_compose_result( + compose_id=compose_id, + variant='BaseOS', + architecture=arch, + outcome=outcome) + + data = { + 'decision_context': 'rtt_compose_gate', + 'product_version': 'rhel-something', + 'subject_type': 'compose', + 'subject_identifier': compose_id, + } + r = requests_session.post(greenwave_server + 'api/v1.0/decision', + headers={'Content-Type': 'application/json'}, + data=json.dumps(data)) + assert r.status_code == 200 + res_data = r.json() + assert res_data['policies_satisfied'] diff --git a/greenwave/policies.py b/greenwave/policies.py index fa0b7b6..9c4b53c 100644 --- a/greenwave/policies.py +++ b/greenwave/policies.py @@ -360,21 +360,32 @@ class PassingTestCaseRule(Rule): return TestResultMissingWaived( subject_type, subject_identifier, self.test_case_name, self.scenario) + # For compose make decisions based on all architectures and variants. + if subject_type == 'compose': + visited_arch_variants = set() + answers = [] + for result in matching_results: + result_data = result['data'] + + # Items under test result "data" are lists which are unhashable + # types in Python. This converts anything that is stored there + # to a string so we don't have to care about the stored value. + arch_variant = ( + str(result_data.get('system_architecture')), + str(result_data.get('system_variant'))) + + if arch_variant not in visited_arch_variants: + visited_arch_variants.add(arch_variant) + answers.append( + self._answer_for_result(result, waivers, subject_type, subject_identifier)) + + return answers + # If we find multiple matching results, we always use the first one which # will be the latest chronologically, because ResultsDB always returns # results ordered by `submit_time` descending. matching_result = matching_results[0] - if matching_result['outcome'] in ['PASSED', 'INFO']: - return TestResultPassed(self.test_case_name, matching_result['id']) - - # XXX limit who is allowed to waive - if any(w['subject'] == dict([(key, value[0]) - for key, value in matching_result['data'].items()]) and - w['testcase'] == matching_result['testcase']['name'] and - w['waived'] for w in waivers): - return TestResultPassed(self.test_case_name, matching_result['id']) - return TestResultFailed(subject_type, subject_identifier, self.test_case_name, - self.scenario, matching_result['id']) + return self._answer_for_result(matching_result, waivers, subject_type, subject_identifier) def to_json(self): return { @@ -383,6 +394,19 @@ class PassingTestCaseRule(Rule): 'scenario': self.scenario, } + def _answer_for_result(self, result, waivers, subject_type, subject_identifier): + if result['outcome'] in ['PASSED', 'INFO']: + return TestResultPassed(self.test_case_name, result['id']) + + # XXX limit who is allowed to waive + if any(w['subject'] == dict([(key, value[0]) + for key, value in result['data'].items()]) and + w['testcase'] == result['testcase']['name'] and + w['waived'] for w in waivers): + return TestResultPassed(self.test_case_name, result['id']) + return TestResultFailed(subject_type, subject_identifier, self.test_case_name, + self.scenario, result['id']) + class PackageSpecificRule(Rule): """