From eb2153d131bc3ff21c07b84d2c77c7c1b166b5f8 Mon Sep 17 00:00:00 2001 From: Giulia Naponiello Date: Oct 16 2018 14:29:24 +0000 Subject: Remote policies not considered for decision change Decision change message only respects policies configured locally on the server and ignores RemoteRule rules. If a result changes for a testcase for some Koji build and there is an applicable policy on the server containing RemoteRule, Greenwave should also emit decision change messages for policies defined in gating.yaml in dist-git for the Koji build. --- diff --git a/greenwave/consumers/resultsdb.py b/greenwave/consumers/resultsdb.py index b0ff8c9..cfd0d32 100644 --- a/greenwave/consumers/resultsdb.py +++ b/greenwave/consumers/resultsdb.py @@ -21,6 +21,8 @@ import greenwave.app_factory import greenwave.resources from greenwave.api_v1 import subject_type_identifier_to_list from greenwave.monitoring import publish_decision_exceptions_result_counter +from greenwave.policies import RemoteRule +from greenwave.safe_yaml import SafeYAMLError import xmlrpc.client @@ -223,7 +225,23 @@ class ResultsDBHandler(fedmsg.consumers.FedmsgConsumer): # Build a set of all policies which might apply to this new results applicable_policies = set() + remote_policies = set() for policy in current_app.config['policies']: + for rule in policy.rules: + if isinstance(rule, RemoteRule): + try: + for remote_policy in rule.get_policies(policy, subject_identifier): + remote_policy.product_versions = set( + remote_policy.product_versions).intersection(set( + policy.product_versions)) + if (remote_policy.product_versions and + remote_policy.decision_context == policy.decision_context): + remote_policies = remote_policies.union(set([remote_policy])) + except SafeYAMLError as e: + pass + + tmp_policies = remote_policies.union(current_app.config['policies']) + for policy in tmp_policies: if policy.subject_type in subject_types: testcases = ( getattr(rule, 'test_case_name', None) diff --git a/greenwave/policies.py b/greenwave/policies.py index b570c36..2fcf6d7 100644 --- a/greenwave/policies.py +++ b/greenwave/policies.py @@ -295,7 +295,7 @@ class RemoteRule(Rule): yaml_tag = '!RemoteRule' safe_yaml_attributes = {} - def check(self, policy, product_version, subject_identifier, results_retriever, waivers): + def get_policies(self, policy, subject_identifier): if policy.subject_type != 'koji_build': return [] @@ -312,8 +312,13 @@ class RemoteRule(Rule): # greenwave extension file not found return [] + policies = RemotePolicy.safe_load_all(response) + return policies + + def check(self, policy, product_version, subject_identifier, results_retriever, waivers): + try: - policies = RemotePolicy.safe_load_all(response) + policies = self.get_policies(policy, subject_identifier) except SafeYAMLError as e: if any(waives_invalid_gating_yaml(waiver, policy.subject_type, subject_identifier) for waiver in waivers): diff --git a/greenwave/tests/test_resultsdb_consumer.py b/greenwave/tests/test_resultsdb_consumer.py index 822f63e..c826788 100644 --- a/greenwave/tests/test_resultsdb_consumer.py +++ b/greenwave/tests/test_resultsdb_consumer.py @@ -2,8 +2,11 @@ import mock +from textwrap import dedent + import greenwave.app_factory import greenwave.consumers.resultsdb +from greenwave.policies import Policy def test_announcement_keys_decode_with_list(): @@ -85,3 +88,172 @@ def test_announcement_subjects_for_autocloud_compose(): subjects = list(cls.announcement_subjects(message)) assert subjects == [('compose', 'Fedora-AtomicHost-28_Update-20180723.1839.x86_64.qcow2')] + + +@mock.patch('greenwave.resources.retrieve_update_for_build', return_value=None) +@mock.patch('greenwave.resources.retrieve_decision') +@mock.patch('greenwave.resources.retrieve_scm_from_koji') +@mock.patch('greenwave.resources.retrieve_yaml_remote_rule') +@mock.patch('greenwave.consumers.resultsdb.fedmsg.publish') +def test_remote_rule_decision_change( + mock_fedmsg, + mock_retrieve_yaml_remote_rule, + mock_retrieve_scm_from_koji, + mock_retrieve_decision, + testdatabuilder): + """ + Test publishing decision change message for test cases mentioned in + gating.yaml. + """ + # gating.yaml + gating_yaml = dedent(""" + --- !Policy + product_versions: [fedora-rawhide, notexisting_prodversion] + decision_context: test_context + rules: + - !PassingTestCaseRule {test_case_name: dist.rpmdeplint} + """) + mock_retrieve_yaml_remote_rule.return_value = gating_yaml + + policies = dedent(""" + --- !Policy + id: test_policy + product_versions: [fedora-rawhide] + decision_context: test_context + subject_type: koji_build + rules: + - !RemoteRule {} + """) + + nvr = testdatabuilder.unique_nvr(product_version='rawhide') + result = testdatabuilder.create_result( + item=nvr, testcase_name='dist.rpmdeplint', outcome='PASSED') + + def retrieve_decision(url, data): + if 'ignore_result' in data: + return None + return {} + mock_retrieve_decision.side_effect = retrieve_decision + mock_retrieve_scm_from_koji.return_value = ('rpms', nvr, + 'c3c47a08a66451cb9686c49f040776ed35a0d1bb') + + message = { + 'body': { + 'topic': 'resultsdb.result.new', + 'msg': { + 'id': result['id'], + 'outcome': 'PASSED', + 'testcase': { + 'name': 'dist.rpmdeplint', + }, + 'data': { + 'item': [nvr], + 'type': ['koji_build'], + } + } + } + } + hub = mock.MagicMock() + hub.config = { + 'environment': 'environment', + 'topic_prefix': 'topic_prefix', + } + handler = greenwave.consumers.resultsdb.ResultsDBHandler(hub) + + handler.flask_app.config['policies'] = Policy.safe_load_all(policies) + with handler.flask_app.app_context(): + handler.consume(message) + + assert len(mock_fedmsg.mock_calls) == 1 + + mock_call = mock_fedmsg.mock_calls[0][2] + assert mock_call['topic'] == 'decision.update' + + actual_msgs_sent = [mock_call['msg'] for call in mock_fedmsg.mock_calls] + assert actual_msgs_sent[0] == { + 'decision_context': 'test_context', + 'product_version': 'fedora-rawhide', + 'subject': [ + {'item': nvr, 'type': 'koji_build'}, + ], + 'subject_type': 'koji_build', + 'subject_identifier': nvr, + 'previous': None, + } + + +@mock.patch('greenwave.resources.retrieve_update_for_build', return_value=None) +@mock.patch('greenwave.resources.retrieve_decision') +@mock.patch('greenwave.resources.retrieve_scm_from_koji') +@mock.patch('greenwave.resources.retrieve_yaml_remote_rule') +@mock.patch('greenwave.consumers.resultsdb.fedmsg.publish') +def test_remote_rule_decision_change_not_matching( + mock_fedmsg, + mock_retrieve_yaml_remote_rule, + mock_retrieve_scm_from_koji, + mock_retrieve_decision, + testdatabuilder): + """ + Test publishing decision change message for test cases mentioned in + gating.yaml. + """ + # gating.yaml + gating_yaml = dedent(""" + --- !Policy + product_versions: [fedora-rawhide] + decision_context: test_context + rules: + - !PassingTestCaseRule {test_case_name: dist.rpmdeplint} + """) + mock_retrieve_yaml_remote_rule.return_value = gating_yaml + + policies = dedent(""" + --- !Policy + id: test_policy + product_versions: [fedora-rawhide] + decision_context: another_test_context + subject_type: koji_build + rules: + - !RemoteRule {} + """) + + nvr = testdatabuilder.unique_nvr(product_version='rawhide') + result = testdatabuilder.create_result( + item=nvr, testcase_name='dist.rpmdeplint', outcome='PASSED') + + def retrieve_decision(url, data): + if 'ignore_result' in data: + return None + return {} + mock_retrieve_decision.side_effect = retrieve_decision + mock_retrieve_scm_from_koji.return_value = ('rpms', nvr, + 'c3c47a08a66451cb9686c49f040776ed35a0d1bb') + + message = { + 'body': { + 'topic': 'resultsdb.result.new', + 'msg': { + 'id': result['id'], + 'outcome': 'PASSED', + 'testcase': { + 'name': 'dist.rpmdeplint', + }, + 'data': { + 'item': [nvr], + 'type': ['koji_build'], + } + } + } + } + hub = mock.MagicMock() + hub.config = { + 'environment': 'environment', + 'topic_prefix': 'topic_prefix', + } + handler = greenwave.consumers.resultsdb.ResultsDBHandler(hub) + + handler.flask_app.config['policies'] = Policy.safe_load_all(policies) + with handler.flask_app.app_context(): + handler.consume(message) + + assert len(mock_fedmsg.mock_calls) == 0