From a488f0aa1dab358d1f7437968d6610efbdc194b7 Mon Sep 17 00:00:00 2001 From: Valerij Maljulin Date: Aug 12 2020 12:10:20 +0000 Subject: Add multiple decision contexts support New policy field decision_contexts supports list, context of this field is being added to the original decision_context field JIRA: RHELWF-413 --- diff --git a/docs/policies.rst b/docs/policies.rst index f112742..0293919 100644 --- a/docs/policies.rst +++ b/docs/policies.rst @@ -22,6 +22,9 @@ Here is an example policy: --- !Policy id: taskotron_release_critical_tasks decision_context: bodhi_update_push_stable + decision_contexts: + - bodhi_update_context1 + - bodhi_update_context2 subject_type: bodhi_update product_versions: - fedora-26 @@ -61,6 +64,11 @@ The document is a map (dictionary) with the following keys: passes this value when it asks Greenwave to decide whether a Bodhi update is ready to be pushed to the stable repositories. +``decision_contexts`` + Allows to specify many decision contexts for one policy. Previous + parameter was kept for backward compatibility and its value is being + added to this list if provided. + .. _subject_type: ``subject_type`` diff --git a/greenwave/policies.py b/greenwave/policies.py index 2243b21..4e335cc 100644 --- a/greenwave/policies.py +++ b/greenwave/policies.py @@ -498,7 +498,7 @@ class RemoteRule(Rule): return [ sub_policy for sub_policy in policies - if sub_policy.decision_context == policy.decision_context + if set(sub_policy.all_decision_contexts).intersection(policy.all_decision_contexts) ] @remove_duplicates @@ -675,6 +675,7 @@ class Policy(SafeYAMLObject): 'id': SafeYAMLString(), 'product_versions': SafeYAMLList(str), 'decision_context': SafeYAMLString(), + 'decision_contexts': SafeYAMLList(str, optional=True, default=list()), 'subject_type': SafeYAMLString(), 'rules': SafeYAMLList(Rule), 'blacklist': SafeYAMLList(str, optional=True), @@ -695,7 +696,7 @@ class Policy(SafeYAMLObject): There must be at least one matching rule or no rules in the policy. """ decision_context = attributes.get('decision_context') - if decision_context and decision_context != self.decision_context: + if decision_context and (decision_context not in self.all_decision_contexts): return False product_version = attributes.get('product_version') @@ -749,6 +750,15 @@ class Policy(SafeYAMLObject): def safe_yaml_label(self): return 'Policy {!r}'.format(self.id or 'untitled') + @property + def all_decision_contexts(self): + rv = [] + if self.decision_contexts: + rv.extend(self.decision_contexts) + if self.decision_context and self.decision_context not in rv: + rv.append(self.decision_context) + return rv + class OnDemandPolicy(Policy): root_yaml_tag = None @@ -780,6 +790,7 @@ class RemotePolicy(Policy): 'product_versions': SafeYAMLList(str, default=['*'], optional=True), 'subject_type': SafeYAMLString(optional=True, default='koji_build'), 'decision_context': SafeYAMLString(), + 'decision_contexts': SafeYAMLList(str, optional=True), 'rules': SafeYAMLList(Rule), 'blacklist': SafeYAMLList(str, optional=True), 'excluded_packages': SafeYAMLList(str, optional=True), @@ -804,12 +815,14 @@ def _applicable_decision_context_product_version_pairs(policies, **attributes): product_version = attributes.get('product_version') if product_version: for policy in applicable_policies: - yield policy.decision_context, product_version + for decision_context in policy.all_decision_contexts: + yield decision_context, product_version else: for policy in applicable_policies: # FIXME: This can returns product version patterns like 'fedora-*'. for product_version in policy.product_versions: - yield policy.decision_context, product_version + for decision_context in policy.all_decision_contexts: + yield decision_context, product_version def applicable_decision_context_product_version_pairs(policies, **attributes): @@ -822,16 +835,11 @@ def applicable_decision_context_product_version_pairs(policies, **attributes): def _missing_decision_contexts_in_parent_policies(policies): - missing_decision_contexts = [] + missing_decision_contexts = set() for policy in policies: # Assume a parent policy is not present for a policy in the remote rule - parent_present = False for parent_policy in current_app.config['policies']: - if parent_policy.decision_context == policy.decision_context: - parent_present = True - break - # If there are no parent policies for a decision_context in the remote rule, - # report it as missing to warn the user. - if not parent_present: - missing_decision_contexts.append(policy.decision_context) - return missing_decision_contexts + missing_decision_contexts.update( + set(parent_policy.all_decision_contexts).difference(policy.all_decision_contexts) + ) + return list(missing_decision_contexts) diff --git a/greenwave/tests/test_policies.py b/greenwave/tests/test_policies.py index 1316615..12c9413 100644 --- a/greenwave/tests/test_policies.py +++ b/greenwave/tests/test_policies.py @@ -198,6 +198,8 @@ def test_load_policies(): for policy in app.config['policies']) assert any(policy.decision_context == 'bodhi_update_push_stable' for policy in app.config['policies']) + assert any(policy.all_decision_contexts == ['bodhi_update_push_stable'] + for policy in app.config['policies']) assert any(getattr(rule, 'test_case_name', None) == 'dist.rpmdeplint' for policy in app.config['policies'] for rule in policy.rules) @@ -912,6 +914,56 @@ def test_policy_with_arbitrary_subject_type(tmpdir): assert isinstance(decision[0], TestResultPassed) +def test_policy_all_decision_contexts(tmpdir): + p = tmpdir.join('fedora.yaml') + p.write(dedent(""" + --- !Policy + id: "some_policy1" + product_versions: + - rhel-9000 + decision_context: test1 + decision_contexts: + - test1 + - test2 + - test3 + subject_type: kind-of-magic + rules: + - !PassingTestCaseRule {test_case_name: sometest} + + --- !Policy + id: "some_policy2" + product_versions: + - rhel-9000 + decision_context: test4 + decision_contexts: + - test1 + - test2 + - test3 + subject_type: kind-of-magic + rules: + - !PassingTestCaseRule {test_case_name: sometest} + + --- !Policy + id: "some_policy2" + product_versions: + - rhel-9000 + decision_context: test4 + subject_type: kind-of-magic + rules: + - !PassingTestCaseRule {test_case_name: sometest} + """)) + policies = load_policies(tmpdir.strpath) + policy = policies[0] + assert len(policy.all_decision_contexts) == 3 + assert set(policy.all_decision_contexts) == {'test1', 'test2', 'test3'} + policy = policies[1] + assert len(policy.all_decision_contexts) == 4 + assert set(policy.all_decision_contexts) == {'test1', 'test2', 'test3', 'test4'} + policy = policies[2] + assert len(policy.all_decision_contexts) == 1 + assert policy.all_decision_contexts == ['test4'] + + @pytest.mark.parametrize(('package', 'num_decisions'), [ ('nethack', 1), ('net*', 1), @@ -1103,6 +1155,7 @@ def test_policies_to_json(): 'id': 'test', 'product_versions': ['fedora-rawhide'], 'decision_context': 'test', + 'decision_contexts': [], 'subject_type': 'compose', 'blacklist': [], 'excluded_packages': [],