From 0a5fd98028aab4e4d881ab19d6f628df78079042 Mon Sep 17 00:00:00 2001 From: Valerij Maljulin Date: Nov 15 2019 13:51:11 +0000 Subject: Avoid duplicate results Signed-off-by: Valerij Maljulin --- diff --git a/greenwave/policies.py b/greenwave/policies.py index 3c6aabc..e9b6536 100644 --- a/greenwave/policies.py +++ b/greenwave/policies.py @@ -8,7 +8,7 @@ import re import greenwave.resources from werkzeug.exceptions import BadRequest from flask import current_app - +from greenwave.utils import remove_duplicates, to_hashable from greenwave.safe_yaml import ( SafeYAMLBool, SafeYAMLChoice, @@ -69,6 +69,17 @@ class Answer(object): """ raise NotImplementedError() + def __hash__(self): + return hash(to_hashable(self.to_json())) + + def __eq__(self, other): + try: + json1 = self.to_json() + json2 = other.to_json() + except NotImplementedError: + return id(self) == id(other) + return json1 == json2 + class RuleSatisfied(Answer): """ @@ -426,6 +437,7 @@ class RemoteRule(Rule): if sub_policy.decision_context == policy.decision_context ] + @remove_duplicates def check( self, policy, @@ -497,6 +509,7 @@ class PassingTestCaseRule(Rule): 'scenario': SafeYAMLString(optional=True), } + @remove_duplicates def check( self, policy, @@ -655,6 +668,7 @@ class Policy(SafeYAMLObject): return not self.rules or any(rule.matches(self, **attributes) for rule in self.rules) + @remove_duplicates def check( self, product_version, diff --git a/greenwave/tests/test_policies.py b/greenwave/tests/test_policies.py index 45b0ea6..481fb23 100644 --- a/greenwave/tests/test_policies.py +++ b/greenwave/tests/test_policies.py @@ -1069,3 +1069,58 @@ def test_on_demand_policy_match(two_rules): if two_rules: assert len(decision) == 1 assert isinstance(decision[0], RuleSatisfied) + + +def test_two_rules_no_duplicate(tmpdir): + nvr = 'nethack-1.2.3-1.el9000' + + serverside_fragment = dedent(""" + --- !Policy + id: "taskotron_release_critical_tasks_with_remoterule" + product_versions: + - fedora-31 + decision_context: bodhi_update_push_stable_with_remoterule + subject_type: koji_build + rules: + - !RemoteRule {} + - !PassingTestCaseRule {test_case_name: dist.upgradepath} + """) + + remote_fragment = dedent(""" + --- !Policy + id: "some-policy-from-a-random-packager" + product_versions: + - fedora-31 + decision_context: bodhi_update_push_stable_with_remoterule + rules: + - !PassingTestCaseRule {test_case_name: dist.upgradepath} + """) + + p = tmpdir.join('gating.yaml') + p.write(serverside_fragment) + app = create_app('greenwave.config.TestingConfig') + with app.app_context(): + with mock.patch('greenwave.resources.retrieve_scm_from_koji') as scm: + scm.return_value = ('rmps', 'nethack', 'c3c47a08a66451cb9686c49f040776ed35a0d1bb') + with mock.patch('greenwave.resources.retrieve_yaml_remote_rule') as f: + f.return_value = remote_fragment + policies = load_policies(tmpdir.strpath) + policy = policies[0] + + # Ensure that presence of a result is success. + results = DummyResultsRetriever(nvr, 'dist.upgradepath') + decision = policy.check('fedora-31', nvr, results) + assert len(decision) == 1 + assert isinstance(decision[0], RuleSatisfied) + + # Ensure that absence of a result is failure. + results = DummyResultsRetriever() + decision = policy.check('fedora-31', nvr, results) + assert len(decision) == 1 + assert isinstance(decision[0], TestResultMissing) + + # And that a result with a failure, is a failure. + results = DummyResultsRetriever(nvr, 'dist.upgradepath', 'FAILED') + decision = policy.check('fedora-31', nvr, results) + assert len(decision) == 1 + assert isinstance(decision[0], TestResultFailed) diff --git a/greenwave/utils.py b/greenwave/utils.py index 1166144..c979a5d 100644 --- a/greenwave/utils.py +++ b/greenwave/utils.py @@ -141,3 +141,22 @@ def right_before_this_time(timestamp): return datetime.datetime.strftime( datetime.datetime.strptime(timestamp, from_date_format) - datetime.timedelta(microseconds=1), date_format) + + +def remove_duplicates(func): + def wrapper(*args, **kwargs): + rv = func(*args, **kwargs) + if isinstance(rv, list) and len(rv): + rv = list(set(rv)) + return rv + return wrapper + + +def to_hashable(val): + if isinstance(val, list) or isinstance(val, tuple): + return tuple([to_hashable(v) for v in val]) + if isinstance(val, dict): + return tuple([(k, to_hashable(val[k])) for k in sorted(val.keys())]) + if isinstance(val, set): + return tuple(sorted(val)) + return val