From 1f9236735ee2c3e6691472068136bae2ebaf48ff Mon Sep 17 00:00:00 2001 From: Matt Jia Date: Jul 05 2017 01:41:37 +0000 Subject: allow configuring the policies in yaml files --- diff --git a/conf/policies/fedora.yaml b/conf/policies/fedora.yaml new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/conf/policies/fedora.yaml diff --git a/conf/policies/redhat.yaml b/conf/policies/redhat.yaml new file mode 100644 index 0000000..d3234ad --- /dev/null +++ b/conf/policies/redhat.yaml @@ -0,0 +1,89 @@ +# Mimic the default Errata rule used for RHEL-7 https://errata.devel.redhat.com/workflow_rules/1 +# In Errata, in order to transition to QE state, an advisory must complete rpmdiff test. +# A completed rpmdiff test could be some dist.rpmdiff.* testcases in ResultsDB and all the +# tests need to be passed. +--- !Policy +id: "1" +product_versions: + - rhel-7 +decision_context: errata_newfile_to_qe +rules: + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.abi_symbols} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.binary_stripping} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.build_log} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.changes_in_rpms} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.desktop_file_sanity} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.elflint} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.empty_payload} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.execshield} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.file_list} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.file_permissions} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.file_sizes} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.ipv_} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.java_byte_code} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.kernel_module_parameters} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.manpage_integrity} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.metadata} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.multilib_regressions} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.ownership} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.patches} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.pathnames} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.politics} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.rpath} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.rpm_changelog} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.rpm_config_doc_files} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.rpm_requires_provides} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.rpm_scripts} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.rpm_triggers} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.shell_syntax} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.specfile_checks} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.symlinks} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.upstream_source} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.virus_scan} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.analysis.xml_validity} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.abi_symbols} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.binary_stripping} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.build_log} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.changed_files} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.changes_in_rpms} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.desktop_file_sanity} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.dt_needed} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.elflint} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.empty_payload} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.execshield} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.file_list} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.file_permissions} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.file_sizes} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.files_moving_rpm} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.file_types} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.ipv_} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.java_byte_code} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.kernel_module_parameters} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.kernel_module_pci_ids} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.manpage_integrity} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.metadata} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.multilib_regressions} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.ownership} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.patches} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.pathnames} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.politics} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.rpath} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.rpm_changelog} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.rpm_config_doc_files} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.rpm_requires_provides} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.rpm_scripts} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.rpm_triggers} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.shell_syntax} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.specfile_checks} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.symlinks} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.upstream_source} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.virus_scan} + - !PassingTestCaseRule {test_case_name: dist.rpmdiff.comparison.xml_validity} +# Errata Tool "Unrestricted" rule set +--- !Policy +id: "errata-unrestricted" +product_versions: + - cdk-2 + - devstudio-2 +decision_context: errata_newfile_to_qe +rules: [] \ No newline at end of file diff --git a/greenwave.spec b/greenwave.spec index 825531e..0a49685 100644 --- a/greenwave.spec +++ b/greenwave.spec @@ -23,6 +23,7 @@ BuildRequires: python-flask BuildRequires: pytest BuildRequires: python-requests %endif +BuildRequires: PyYAML BuildArch: noarch %if 0%{?fedora} || 0%{?rhel} > 7 Requires: python2-flask @@ -31,6 +32,7 @@ Requires: python2-requests Requires: python-flask Requires: python-requests %endif +Requires: PyYAML %description Greenwave is a service for gating on automated tests by querying ResultsDB and diff --git a/greenwave/api_v1.py b/greenwave/api_v1.py index 9ea8d69..f04d342 100644 --- a/greenwave/api_v1.py +++ b/greenwave/api_v1.py @@ -3,7 +3,7 @@ import requests from flask import Blueprint, request, current_app, jsonify from werkzeug.exceptions import BadRequest, NotFound, UnsupportedMediaType -from greenwave.policies import policies, summarize_answers +from greenwave.policies import summarize_answers api = (Blueprint('api_v1', __name__)) @@ -40,7 +40,7 @@ def make_decision(): raise BadRequest('Invalid subject, must be a list of items') product_version = request.get_json()['product_version'] decision_context = request.get_json()['decision_context'] - applicable_policies = [policy for policy in policies + applicable_policies = [policy for policy in current_app.config['policies'] if policy.applies_to(decision_context, product_version)] if not applicable_policies: raise NotFound('Cannot find any applicable policies for %s' % product_version) diff --git a/greenwave/app_factory.py b/greenwave/app_factory.py index 1c59187..778ed7f 100644 --- a/greenwave/app_factory.py +++ b/greenwave/app_factory.py @@ -1,6 +1,8 @@ # SPDX-License-Identifier: GPL-2.0+ import os +import glob +import yaml from flask import Flask from greenwave.logger import init_logging @@ -33,6 +35,11 @@ def create_app(config_obj=None): load_config(app) if app.config['PRODUCTION'] and app.secret_key == 'replace-me-with-something-random': raise Warning("You need to change the app.secret_key value for production") + #load policies + policy_pathnames = glob.glob(os.path.join(app.config['POLICIES_DIR'], '*.yaml')) + app.config['policies'] = [] + for policy_pathname in policy_pathnames: + app.config['policies'].extend(yaml.load_all(open(policy_pathname, 'r'))) # register error handlers app.register_error_handler(ConnectionError, lambda e: (str(e), 503)) app.register_error_handler(Timeout, lambda e: (str(e), 503)) diff --git a/greenwave/config.py b/greenwave/config.py index abcb1f7..ee38235 100644 --- a/greenwave/config.py +++ b/greenwave/config.py @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ +import os class Config(object): @@ -13,6 +14,7 @@ class Config(object): RESULTSDB_API_URL = 'https://taskotron.fedoraproject.org/resultsdb_api/api/v2.0' WAIVERDB_API_URL = 'https://waiverdb.fedoraproject.org/api/v1.0' REQUESTS_TIMEOUT = (6.1, 15) + POLICIES_DIR = '/etc/greenwave/policies' class ProductionConfig(Config): @@ -25,8 +27,16 @@ class DevelopmentConfig(Config): RESULTSDB_API_URL = 'http://localhost:5001/api/v2.0' #WAIVERDB_API_URL = 'http://waiverdb-dev.fedorainfracloud.org/api/v1.0' WAIVERDB_API_URL = 'http://localhost:5004/api/v1.0' + POLICIES_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'conf', + 'policies' + ) class TestingConfig(Config): RESULTSDB_API_URL = 'http://localhost:5001/api/v2.0' WAIVERDB_API_URL = 'http://localhost:5004/api/v1.0' + POLICIES_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'conf', + 'policies' + ) diff --git a/greenwave/policies.py b/greenwave/policies.py index 290fe69..129ee12 100644 --- a/greenwave/policies.py +++ b/greenwave/policies.py @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ +import yaml + class Answer(object): """ @@ -102,7 +104,7 @@ def summarize_answers(answers): return 'inexplicable result' -class Rule(object): +class Rule(yaml.YAMLObject): """ An individual rule within a policy. A policy consists of multiple rules. When the policy is evaluated, each rule returns an answer @@ -110,7 +112,6 @@ class Rule(object): This base class is not used directly. """ - def check(self, item, results, waivers): """ Evaluate this policy rule for the given item. @@ -132,6 +133,7 @@ class PassingTestCaseRule(Rule): This rule requires either a passing result for the given test case, or a non-passing result with a waiver. """ + yaml_tag = u'!PassingTestCaseRule' def __init__(self, test_case_name): self.test_case_name = test_case_name @@ -149,8 +151,12 @@ class PassingTestCaseRule(Rule): return RuleSatisfied() return TestResultFailed(item, self.test_case_name, matching_result['id']) + def __repr__(self): + return "%s(test_case_name=%r)" % (self.__class__.__name__, self.test_case_name) -class Policy(object): + +class Policy(yaml.YAMLObject): + yaml_tag = u'!Policy' def __init__(self, id, product_versions, decision_context, rules): self.id = id @@ -165,100 +171,7 @@ class Policy(object): def check(self, item, results, waivers): return [rule.check(item, results, waivers) for rule in self.rules] - -policies = [ - # Mimic the default Errata rule used for RHEL-7 https://errata.devel.redhat.com/workflow_rules/1 - # In Errata, in order to transition to QE state, an advisory must complete rpmdiff test. - # A completed rpmdiff test could be some dist.rpmdiff.* testcases in ResultsDB and all the - # tests need to be passed. - Policy( - id='1', - product_versions=[ - 'rhel-7', - ], - decision_context='errata_newfile_to_qe', - rules=[ - PassingTestCaseRule('dist.rpmdiff.analysis.abi_symbols'), - PassingTestCaseRule('dist.rpmdiff.analysis.binary_stripping'), - PassingTestCaseRule('dist.rpmdiff.analysis.build_log'), - PassingTestCaseRule('dist.rpmdiff.analysis.changes_in_rpms'), - PassingTestCaseRule('dist.rpmdiff.analysis.desktop_file_sanity'), - PassingTestCaseRule('dist.rpmdiff.analysis.elflint'), - PassingTestCaseRule('dist.rpmdiff.analysis.empty_payload'), - PassingTestCaseRule('dist.rpmdiff.analysis.execshield'), - PassingTestCaseRule('dist.rpmdiff.analysis.file_list'), - PassingTestCaseRule('dist.rpmdiff.analysis.file_permissions'), - PassingTestCaseRule('dist.rpmdiff.analysis.file_sizes'), - PassingTestCaseRule('dist.rpmdiff.analysis.ipv_'), - PassingTestCaseRule('dist.rpmdiff.analysis.java_byte_code'), - PassingTestCaseRule('dist.rpmdiff.analysis.kernel_module_parameters'), - PassingTestCaseRule('dist.rpmdiff.analysis.manpage_integrity'), - PassingTestCaseRule('dist.rpmdiff.analysis.metadata'), - PassingTestCaseRule('dist.rpmdiff.analysis.multilib_regressions'), - PassingTestCaseRule('dist.rpmdiff.analysis.ownership'), - PassingTestCaseRule('dist.rpmdiff.analysis.patches'), - PassingTestCaseRule('dist.rpmdiff.analysis.pathnames'), - PassingTestCaseRule('dist.rpmdiff.analysis.politics'), - PassingTestCaseRule('dist.rpmdiff.analysis.rpath'), - PassingTestCaseRule('dist.rpmdiff.analysis.rpm_changelog'), - PassingTestCaseRule('dist.rpmdiff.analysis.rpm_config_doc_files'), - PassingTestCaseRule('dist.rpmdiff.analysis.rpm_requires_provides'), - PassingTestCaseRule('dist.rpmdiff.analysis.rpm_scripts'), - PassingTestCaseRule('dist.rpmdiff.analysis.rpm_triggers'), - PassingTestCaseRule('dist.rpmdiff.analysis.shell_syntax'), - PassingTestCaseRule('dist.rpmdiff.analysis.specfile_checks'), - PassingTestCaseRule('dist.rpmdiff.analysis.symlinks'), - PassingTestCaseRule('dist.rpmdiff.analysis.upstream_source'), - PassingTestCaseRule('dist.rpmdiff.analysis.virus_scan'), - PassingTestCaseRule('dist.rpmdiff.analysis.xml_validity'), - PassingTestCaseRule('dist.rpmdiff.comparison.abi_symbols'), - PassingTestCaseRule('dist.rpmdiff.comparison.binary_stripping'), - PassingTestCaseRule('dist.rpmdiff.comparison.build_log'), - PassingTestCaseRule('dist.rpmdiff.comparison.changed_files'), - PassingTestCaseRule('dist.rpmdiff.comparison.changes_in_rpms'), - PassingTestCaseRule('dist.rpmdiff.comparison.desktop_file_sanity'), - PassingTestCaseRule('dist.rpmdiff.comparison.dt_needed'), - PassingTestCaseRule('dist.rpmdiff.comparison.elflint'), - PassingTestCaseRule('dist.rpmdiff.comparison.empty_payload'), - PassingTestCaseRule('dist.rpmdiff.comparison.execshield'), - PassingTestCaseRule('dist.rpmdiff.comparison.file_list'), - PassingTestCaseRule('dist.rpmdiff.comparison.file_permissions'), - PassingTestCaseRule('dist.rpmdiff.comparison.file_sizes'), - PassingTestCaseRule('dist.rpmdiff.comparison.files_moving_rpm'), - PassingTestCaseRule('dist.rpmdiff.comparison.file_types'), - PassingTestCaseRule('dist.rpmdiff.comparison.ipv_'), - PassingTestCaseRule('dist.rpmdiff.comparison.java_byte_code'), - PassingTestCaseRule('dist.rpmdiff.comparison.kernel_module_parameters'), - PassingTestCaseRule('dist.rpmdiff.comparison.kernel_module_pci_ids'), - PassingTestCaseRule('dist.rpmdiff.comparison.manpage_integrity'), - PassingTestCaseRule('dist.rpmdiff.comparison.metadata'), - PassingTestCaseRule('dist.rpmdiff.comparison.multilib_regressions'), - PassingTestCaseRule('dist.rpmdiff.comparison.ownership'), - PassingTestCaseRule('dist.rpmdiff.comparison.patches'), - PassingTestCaseRule('dist.rpmdiff.comparison.pathnames'), - PassingTestCaseRule('dist.rpmdiff.comparison.politics'), - PassingTestCaseRule('dist.rpmdiff.comparison.rpath'), - PassingTestCaseRule('dist.rpmdiff.comparison.rpm_changelog'), - PassingTestCaseRule('dist.rpmdiff.comparison.rpm_config_doc_files'), - PassingTestCaseRule('dist.rpmdiff.comparison.rpm_requires_provides'), - PassingTestCaseRule('dist.rpmdiff.comparison.rpm_scripts'), - PassingTestCaseRule('dist.rpmdiff.comparison.rpm_triggers'), - PassingTestCaseRule('dist.rpmdiff.comparison.shell_syntax'), - PassingTestCaseRule('dist.rpmdiff.comparison.specfile_checks'), - PassingTestCaseRule('dist.rpmdiff.comparison.symlinks'), - PassingTestCaseRule('dist.rpmdiff.comparison.upstream_source'), - PassingTestCaseRule('dist.rpmdiff.comparison.virus_scan'), - PassingTestCaseRule('dist.rpmdiff.comparison.xml_validity'), - ], - ), - # Errata Tool "Unrestricted" rule set - Policy( - id='errata-unrestricted', - decision_context='errata_newfile_to_qe', - product_versions=[ - 'cdk-2', - 'devstudio-2', - ], - rules=[], - ), -] + def __repr__(self): + return "%s(id=%r, product_versions=%r, decision_context=%r, rules=%r)" % ( + self.__class__.__name__, self.id, self.product_versions, self.decision_context, + self.rules) diff --git a/greenwave/tests/test_policies.py b/greenwave/tests/test_policies.py index 0948126..afafbbd 100644 --- a/greenwave/tests/test_policies.py +++ b/greenwave/tests/test_policies.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ +from greenwave.app_factory import create_app from greenwave.policies import summarize_answers, RuleSatisfied, TestResultMissing, TestResultFailed @@ -16,3 +17,13 @@ def test_summarize_answers(): '1 of 2 required tests failed' assert summarize_answers([TestResultMissing('item', 'test'), RuleSatisfied()]) == \ '1 of 2 required tests not found' + + +def test_load_policies(): + app = create_app('greenwave.config.TestingConfig') + assert len(app.config['policies']) > 0 + assert app.config['policies'][0].id == '1' + assert app.config['policies'][0].product_versions == ['rhel-7'] + assert app.config['policies'][0].decision_context == 'errata_newfile_to_qe' + rule = app.config['policies'][0].rules[0] + assert rule.test_case_name == 'dist.rpmdiff.analysis.abi_symbols' diff --git a/requirements.txt b/requirements.txt index 30692b7..6f96835 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ flask requests +PyYAML