From 09474c0b83fa79cab2523ea0b11bf09059cc4079 Mon Sep 17 00:00:00 2001 From: Lukas Holecek Date: Jul 12 2019 14:09:58 +0000 Subject: Add missing-gating-yaml answer Added test to check "no tests are required" summary (e.g. checked by Bodhi). Signed-off-by: Lukas Holecek --- diff --git a/docs/package-specific-policies.rst b/docs/package-specific-policies.rst index e7ecefc..ee0d796 100644 --- a/docs/package-specific-policies.rst +++ b/docs/package-specific-policies.rst @@ -69,6 +69,33 @@ a result in Resultsdb for this testcase. The side effect is that all the policies defined in the gating.yaml file will be completely ignored by Greenwave. +.. _missing-gating-yaml: + + +Missing gating.yaml file +------------------------ + +If gating.yaml file is missing (i.e. not present in dist-git repo of the tested +package in the required revision), "missing-gating-yaml" will appear in +satisfied requirements. + +.. code-block:: json + + { + "applicable_policies": ["some_policy"], + "policies_satisfied": true, + "satisfied_requirements": [{ + "subject_identifier": "nethack-1.2.3-1.f31", + "subject_type": "koji_build", + "type": "missing-gating-yaml" + }], + "summary": "no tests are required", + "unsatisfied_requirements": [] + } + +This still results in the decision summary "no tests are required" if no other +test are required. + .. _tutorial-configure-remoterule: diff --git a/greenwave/api_v1.py b/greenwave/api_v1.py index 421f7a1..6c1dcd0 100644 --- a/greenwave/api_v1.py +++ b/greenwave/api_v1.py @@ -471,7 +471,8 @@ def make_decision(): answers = waive_answers(answers, waivers) response = { - 'policies_satisfied': all(answer.is_satisfied for answer in answers), + 'policies_satisfied': + all(answer.is_satisfied or not answer.is_required for answer in answers), 'summary': summarize_answers(answers), 'satisfied_requirements': [answer.to_json() for answer in answers if answer.is_satisfied], diff --git a/greenwave/policies.py b/greenwave/policies.py index 5baba0c..af43bf7 100644 --- a/greenwave/policies.py +++ b/greenwave/policies.py @@ -62,6 +62,8 @@ class Answer(object): a subclass, depending on what the answer was. """ + is_required = True + def to_json(self): """ Returns a machine-readable description of the problem for API responses. @@ -204,6 +206,25 @@ class InvalidGatingYaml(RuleNotSatisfied): return None +class MissingGatingYaml(RuleSatisfied): + """ + Remote policy not found in remote repository. + """ + + is_required = False + + def __init__(self, subject_type, subject_identifier): + self.subject_type = subject_type + self.subject_identifier = subject_identifier + + def to_json(self): + return { + 'type': 'missing-gating-yaml', + 'subject_type': self.subject_type, + 'subject_identifier': self.subject_identifier, + } + + class TestResultPassed(RuleSatisfied): """ A required test case passed (that is, its outcome in ResultsDB was @@ -259,7 +280,7 @@ def summarize_answers(answers): Returns: str: Human-readable summary. """ - if not answers: + if all(not answer.is_required for answer in answers): return 'no tests are required' failure_count = len([answer for answer in answers if isinstance(answer, RuleNotSatisfied)]) @@ -380,7 +401,7 @@ class RemoteRule(Rule): if response is None: # greenwave extension file not found - return [] + return None policies = RemotePolicy.safe_load_all(response) if isinstance(policy, OnDemandPolicy): @@ -408,6 +429,11 @@ class RemoteRule(Rule): policy.subject_type, subject_identifier, 'invalid-gating-yaml', str(e)) ] + if policies is None: + return [ + MissingGatingYaml(policy.subject_type, subject_identifier) + ] + answers = [] for remote_policy in policies: if remote_policy.matches_product_version(product_version): @@ -437,6 +463,9 @@ class RemoteRule(Rule): logging.exception( 'Failed to retrieve policies for %r', subject_identifier) + if sub_policies is None: + return True + return any(sub_policy.matches(**attributes) for sub_policy in sub_policies) def to_json(self): diff --git a/greenwave/tests/test_api_v1.py b/greenwave/tests/test_api_v1.py index 72c9f94..dc54ae8 100644 --- a/greenwave/tests/test_api_v1.py +++ b/greenwave/tests/test_api_v1.py @@ -53,9 +53,9 @@ def mock_waivers(): yield mocked -def make_decision(**kwargs): +def make_decision(policies=DEFAULT_DECISION_POLICIES, **kwargs): app = create_app('greenwave.config.TestingConfig') - app.config['policies'] = Policy.safe_load_all(dedent(DEFAULT_DECISION_POLICIES)) + app.config['policies'] = Policy.safe_load_all(dedent(policies)) client = app.test_client() data = DEFAULT_DECISION_DATA.copy() data.update(kwargs) @@ -98,6 +98,47 @@ def test_make_decision_retrieves_waivers_once_on_verbose_and_missing(mock_result mock_waivers.assert_called_once() +def test_make_decision_with_no_tests_required(mock_results, mock_waivers): + mock_results.return_value = [] + mock_waivers.return_value = [] + policies = """ + --- !Policy + id: "test_policy" + product_versions: + - fedora-rawhide + decision_context: test_policies + subject_type: koji_build + rules: [] + """ + response = make_decision(policies=policies) + assert 200 == response.status_code + assert 'no tests are required' == response.json['summary'] + mock_waivers.assert_not_called() + + +def test_make_decision_missing_gating_yaml(mock_results, mock_waivers): + mock_results.return_value = [] + mock_waivers.return_value = [] + policies = """ + --- !Policy + id: "test_policy" + product_versions: + - fedora-rawhide + decision_context: test_policies + subject_type: koji_build + rules: + - !RemoteRule {} + """ + with mock.patch('greenwave.resources.retrieve_scm_from_koji') as scm: + scm.return_value = ('rpms', 'nethack', 'c3c47a08a66451cb9686c49f040776ed35a0d1bb') + with mock.patch('greenwave.resources.retrieve_yaml_remote_rule') as f: + f.return_value = None + response = make_decision(policies=policies) + assert 200 == response.status_code + assert 'no tests are required' == response.json['summary'] + mock_waivers.assert_not_called() + + def test_life_decision(): app = create_app('greenwave.config.TestingConfig') client = app.test_client() diff --git a/greenwave/tests/test_policies.py b/greenwave/tests/test_policies.py index bd0c628..2825b67 100644 --- a/greenwave/tests/test_policies.py +++ b/greenwave/tests/test_policies.py @@ -10,6 +10,7 @@ from greenwave.app_factory import create_app from greenwave.policies import ( load_policies, summarize_answers, + MissingGatingYaml, Policy, RemotePolicy, RuleSatisfied, @@ -552,6 +553,33 @@ def test_remote_rule_malformed_yaml_with_waiver(tmpdir): assert len(decision) == 0 +def test_remote_rule_missing_yaml(): + """ Testing the RemoteRule with a missing gating.yaml file """ + nvr = 'nethack-1.2.3-1.el9000' + app = create_app('greenwave.config.TestingConfig') + with app.app_context(): + with mock.patch('greenwave.resources.retrieve_scm_from_koji') as scm: + scm.return_value = ('rpms', 'nethack', 'c3c47a08a66451cb9686c49f040776ed35a0d1bb') + with mock.patch('greenwave.resources.retrieve_yaml_remote_rule') as f: + f.return_value = None + policies = Policy.safe_load_all(dedent(""" + --- !Policy + id: test + product_versions: [fedora-rawhide] + decision_context: test + subject_type: koji_build + rules: + - !RemoteRule {} + """)) + policy = policies[0] + results = DummyResultsRetriever() + decision = policy.check('fedora-rawhide', nvr, results) + assert len(decision) == 1 + assert isinstance(decision[0], MissingGatingYaml) + assert decision[0].is_satisfied + assert decision[0].subject_identifier == nvr + + def test_parse_policies_missing_tag(): expected_error = "Missing !Policy tag" with pytest.raises(SafeYAMLError, match=expected_error):