From 3a26fedd63fb74b909f6ef04f10d965d38aba0c3 Mon Sep 17 00:00:00 2001 From: Lukas Holecek Date: Aug 05 2021 10:34:38 +0000 Subject: Docs: Add detailed description for requirements JIRA: RHELWF-3628 --- diff --git a/docs/decision_requirements.rst b/docs/decision_requirements.rst index d1efbdf..d3b8450 100644 --- a/docs/decision_requirements.rst +++ b/docs/decision_requirements.rst @@ -5,9 +5,34 @@ Decision Requirements ===================== Response data for :http:post:`/api/v1.0/decision` contain -``satisfied_requirements`` and ``unsatisfied_requirements`` fields. -Value for each field is a list containing requirements of specific -type. +``satisfied_requirements`` and ``unsatisfied_requirements`` properties with +list of requirements. + +Satisfied requirements may contain: + +- passed test results +- waived unsatisfied requirements +- other satisfied requirements + +Unsatisfied requirements contain: + +- failed test results +- missing/incomplete tests results +- other unsatisfied requirements (mainly related to remote rule file) + +Each item in the list contains ``type`` property indicating type of the +requirement. + +Unsatisfied requirements containing ``testcase`` property can be waived (using +this value in a new waiver). + +See :ref:`decision_requirements_examples` to get an idea about the data of +various requirements. + +See :ref:`decision_requirements_code_examples` for Python code examples for +extracting and using the data. + +.. _decision_requirements_examples: Examples ======== @@ -207,3 +232,173 @@ found and successfully retrieved, a satisfied requirement is created. "subject_identifier": "bash-4.4.20-1.el8_4", "subject_type": "koji_build" } + +.. _decision_requirements_code_examples: + +Code Examples +============= + +Below are Python code snippets for working with specific requirement types. + +Retrieve decision from Greenwave using Requests Python library: + +.. code-block:: python + + import requests + + response = requests.post(GREENWAVE_URL, DECISION_REQUEST_DATA); + response.raise_for_status() + decision = response.json() + + satisfied = decision["satisfied_requirements"] + unsatisfied = decision["unsatisfied_requirements"] + +.. important:: + + The above code does not handle intermittent network issues. Normally, you + would want to use requests session which can retry on a failure. + +Passed test results are stored in the ``satisfied_requirements`` list and have +``test-result-passed`` type. + +.. code-block:: python + + passed = [ + req + for req in satisfied + if req["type"] == "test-result-passed" + ] + if passed: + print("Passed:") + for req in passed: + subject_id = req["subject_identifier"] + subject_type = req["subject_type"] + print(f' {req["testcase"]} ({subject_id} {subject_type})') + +Waived requirements have type ending with "-waived": + +- ``test-result-failed-waived`` +- ``test-result-errored-waived`` +- ``test-result-missing-waived`` +- ``invalid-gating-yaml-waived`` +- ``missing-gating-yaml-waived`` +- ``failed-fetch-gating-yaml-waived`` +- other types (can be extended in the future) + +.. code-block:: python + + waived = [ + req + for req in satisfied + if req["type"].endswith("-waived") + ] + if waived: + print("Waived:") + for req in waived: + print(f' {req["testcase"]} ({req["type"]})') + +Other satisfied requirements types: + +- ``fetched-gating-yaml`` +- ``blacklisted`` (from ``blacklist`` in a policy) +- ``excluded`` (from ``excluded_packages`` in a policy) +- other types (can be extended in the future) + +.. code-block:: python + + other_satisfied = [ + req + for req in satisfied + if req not in waived and req not in passed + ] + if other_satisfied: + print("Passed (not test cases):") + for req in other_satisfied: + if req["type"] == "fetched-gating-yaml": + print(f' Fetched {req["source"]}') + else: + print(f' {req["type"]}: {json.dumps(req)}') + +Missing/incomplete test results have ``test-result-missing`` type. + +.. code-block:: python + + missing = [ + req + for req in unsatisfied + if req["type"] == "test-result-missing" + ] + if missing: + print("Missing:") + for req in missing: + subject_id = req["subject_identifier"] + subject_type = req["subject_type"] + print(f' {req["testcase"]} ({subject_id} {subject_type})') + +Failed tests results have ``test-result-failed`` or ``test-result-errored`` type. + +.. code-block:: python + + failed = [ + req + for req in unsatisfied + if req["type"] in ("test-result-failed", "test-result-errored") + ] + for req in failed: + subject_id = req.get("subject_identifier") or req["item"].get("type") + subject_type = req.get("subject_type") or req["item"].get("item") + print(f'Failed: {req["testcase"]} ({subject_id} {subject_type})') + +Other unsatisfied requirement types: + +- ``invalid-gating-yaml`` +- ``missing-gating-yaml`` +- ``failed-fetch-gating-yaml`` +- other types (can be extended in the future) + +.. code-block:: python + + other_failed = [ + req + for req in unsatisfied + if req not in failed and req not in missing + ] + if other_failed: + print("Failed (not test cases):") + for req in other_failed: + print(f' {req["testcase"]} ({req["type"]})') + +Unsatisfied requirements containing ``testcase`` property can be waived. + +.. code-block:: python + + waivable = [ + req + for req in unsatisfied + if "testcase" in req + ] + +We can print a command to create waivers but user needs to provide **product +version** (same as in the decision request) and a **comment** (reason for the +waiver). + +.. code-block:: python + + waiver_data = [ + { + "subject_identifier": req.get("subject_identifier") or req["item"].get("type"), + "subject_type": req.get("subject_type") or req["item"].get("item"), + "testcase": req["testcase"], + "scenario": req.get("scenario"), + "waived": True, + "product_version": PRODUCT_VERSION, + "comment": COMMENT, + } + for req in waivable + ] + if waiver_data: + payload = json.dumps(waiver_data, indent=2) + print('Waive failed (ensure "product_version" and "comment" is correct):') + print(f"curl --negotiate -u: {WAIVERDB_URL} -d @- <