From 876d17be6b1916c08fb36edcdf924859cb04e3cc Mon Sep 17 00:00:00 2001 From: "Hansen, Tony (th1395)" Date: Tue, 22 Jun 2021 21:12:32 +0000 Subject: support local config and policy files * DCAEGEN2-2733/DCAEGEN2-2753 - onap-dcae-cbs-docker-client support local config and policy files Support $CBS_CLIENT_CONFIG_PATH and $CBS_CLIENT_POLICY_PATH, Default paths are /app-config/application_config.yaml and /etc/policies/policies.json. If files are missing or malformed, drop back to the current behavior. Change-Id: Iba2484f6be7fc54e412f7de302bb79a4d7bfde17 Signed-off-by: Hansen, Tony (th1395) Issue-ID: DCAEGEN2-2733 Issue-ID: DCAEGEN2-2753 Signed-off-by: Hansen, Tony (th1395) --- onap-dcae-cbs-docker-client/Changelog.md | 14 +- .../onap_dcae_cbs_docker_client/client.py | 137 ++++++++- .../onap_dcae_cbs_docker_client/exceptions.py | 6 +- onap-dcae-cbs-docker-client/pom.xml | 2 +- onap-dcae-cbs-docker-client/setup.py | 4 +- onap-dcae-cbs-docker-client/tests/conftest.py | 7 +- onap-dcae-cbs-docker-client/tests/test_client.py | 316 ++++++++++++++++++++- onap-dcae-cbs-docker-client/tox.ini | 7 +- 8 files changed, 459 insertions(+), 34 deletions(-) (limited to 'onap-dcae-cbs-docker-client') diff --git a/onap-dcae-cbs-docker-client/Changelog.md b/onap-dcae-cbs-docker-client/Changelog.md index c16950d..c70aff4 100644 --- a/onap-dcae-cbs-docker-client/Changelog.md +++ b/onap-dcae-cbs-docker-client/Changelog.md @@ -4,16 +4,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [2.1.2] - 05/13/2021 +## [2.2.0] 2021/06/22 +* DCAEGEN2-2733/DCAEGEN2-2753 - onap-dcae-cbs-docker-client support local config and policy files + Support $CBS_CLIENT_CONFIG_PATH and $CBS_CLIENT_POLICY_PATH, + Default paths are /app-config/application_config.yaml and /etc/policies/policies.json. + If files are missing or malformed, drop back to the current behavior. + +## [2.1.2] - 2021/05/13 * Add service for envs present in configuration -## [2.1.1] - 04/27/2020 +## [2.1.1] - 2020/04/27 * Bug fix DCAEGEN2-2213 ConnectionError exception is lost when CBSUnreachable is raised -## [2.1.0] - 6/24/2019 +## [2.1.0] - 2019/6/24 * Add support for connecting to the CBS if it is running as HTTPS instead of HTTP -## [2.0.0] - 6/19/2019 +## [2.0.0] - 2019/6/19 * The env variable CONFIG_BINDING_SERVICE now has a different meaning per DCAEGEN2-1537. Specifically this variable now holds a resolvable hostname for the CBS, rather than a consul lookup key * Since the API was broken anyway, the decision not to throw an exception was revisted and overturned. This was causing problems for some users, who were getting `{}` back in their configuration, but without knowing why; either the config wasn't set up, the config was set but as `{}`, or the CBS being unreachable altogether. This client library now throws native python exceptions, rather than logging and returning `{}`. The application client code can handle the exceptions, and retry if they choose. * Add more tests, move fixtures to conftest diff --git a/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/client.py b/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/client.py index 7f36169..c42b4ae 100644 --- a/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/client.py +++ b/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/client.py @@ -1,5 +1,5 @@ # ================================================================================ -# Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2021 AT&T Intellectual Property. All rights reserved. # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,18 +14,36 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============LICENSE_END========================================================= -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. + +""" provide a means to return a configuration to CBS """ import json import os import requests +import sys +import yaml + from onap_dcae_cbs_docker_client import get_module_logger from onap_dcae_cbs_docker_client.exceptions import ENVsMissing, CantGetConfig, CBSUnreachable logger = get_module_logger(__name__) +DEFAULT_CONFIG_PATH = "/app-config/application_config.yaml" +DEFAULT_POLICY_PATH = "/etc/policies/policies.json" + + +# provide a means to import the default paths into unit tests +# For some reason, tox does not like +# from onap_dcae_cbs_docker_client.client import DEFAULT_CONFIG_PATH, DEFAULT_POLICY_PATH + +def default_config_path(): + return DEFAULT_CONFIG_PATH + + +def default_policy_path(): + return DEFAULT_POLICY_PATH + ######### # HELPERS @@ -35,7 +53,7 @@ def _recurse(config): Recurse through a configuration, or recursively a sub element of it. If it's a dict: recurse over all the values If it's a list: recurse over all the values - If it's a string: return the replacement + If it's a string: expand the string and return its replacement If none of the above, just return the item. """ if isinstance(config, list): @@ -96,11 +114,13 @@ def change_envs(value): """ Replace env reference by actual value and return it """ - if value.startswith('$'): - try: - value = os.environ[value.replace('${', '').replace('}', '')] - except KeyError as e: - raise ENVsMissing("Required ENV Variable {0} missing".format(e)) + if value.startswith('${'): + name = value[2:-1] + if name in os.environ: + return os.environ[name] + + logger.error(f"Required ENV Variable '{name}' missing. Is '{value}' properly formatted?") + raise ENVsMissing(f"Required ENV Variable {name} missing. Is '{value}' properly formatted?") return value @@ -108,18 +128,113 @@ def change_envs(value): # Public def get_all(): """ - Hit the CBS service_component_all endpoint + If not configured locally, + hit the CBS service_component_all endpoint + + Local configuration comes from $CBS_CLIENT_CONFIG_PATH and $CBS_CLIENT_POLICY_PATH, + defaulted to /app-config/application_config.yaml and /etc/policies/policies.json. """ + config_path = os.getenv("CBS_CLIENT_CONFIG_PATH", DEFAULT_CONFIG_PATH) + if config_path == "": + config_path = DEFAULT_CONFIG_PATH + policy_path = os.getenv("CBS_CLIENT_POLICY_PATH", DEFAULT_POLICY_PATH) + if policy_path == "": + policy_path = DEFAULT_POLICY_PATH + + try: + logger.debug(f"opening config_path={config_path}") + with open(config_path) as fp: + config = yaml.safe_load(fp) + + policies = None + try: + logger.debug(f"opening policy_path={policy_path}") + with open(policy_path) as fp: + policies = json.load(fp) + + except FileNotFoundError: + logger.debug("Policy File Not Found exception received") + pass + + except (json.decoder.JSONDecodeError, ValueError) as e: + logger.error(f"The policy file '{policy_path}' has invalid JSON: %s", e) + pass + + except Exception as e: + logger.error(f"An error occurred processing the policy file '{policy_path}': %s", e) + import traceback + traceback.print_exc(file=sys.stderr) + pass + + if policies is not None: + if "policies" in policies: + logger.debug(f"Returning config read from {config_path} an policy read from {policy_path}") + ret = {"config": _recurse(config), "policies": policies["policies"]} + else: + logger.error(f"The policy file '{policy_path}' does NOT have a 'policies' block in it.") + ret = {"config": _recurse(config)} + else: + logger.debug(f"Returning config read from {config_path}") + ret = {"config": _recurse(config)} + + return ret + + except yaml.scanner.ScannerError as e: + logger.error(f"The configuration file '{config_path}' has invalid YAML: {e}") + pass + + except FileNotFoundError: + logger.debug("Config File Not Found exception received") + pass + + except Exception as e: + logger.error(f"An error occurred processing the configuration file '{config_path}': {e}") + import traceback + traceback.print_exc(file=sys.stderr) + pass + + logger.debug("Fallback to using REST call") config = _get_path("service_component_all") return _recurse(config) def get_config(): """ - Hit the CBS service_component endpoint + If not configured locally, + hit the CBS service_component endpoint for the configuration. + + Local configuration comes from $CBS_CLIENT_CONFIG_PATH, + defaulted to /app-config/application_config.yaml. TODO: should we take in a "retry" boolean, and retry on behalf of the caller? Currently, we return an exception and let the application decide how it wants to proceed (Crash, try again, etc). """ + config_path = os.getenv("CBS_CLIENT_CONFIG_PATH", DEFAULT_CONFIG_PATH) + if config_path == "": + config_path = DEFAULT_CONFIG_PATH + + try: + logger.debug(f"opening config_path={config_path}") + with open(config_path) as fp: + config = yaml.safe_load(fp) + + logger.debug(f"Returning config read from {config_path}") + return _recurse({"config": config}) + + except yaml.scanner.ScannerError as e: + logger.error(f"The configuration file '{config_path}' has invalid YAML: {e}") + pass + + except FileNotFoundError: + logger.debug("Config File Not Found exception received") + pass + + except Exception as e: + logger.error(f"An error occurred processing the configuration file '{config_path}': {e}") + import traceback + traceback.print_exc(file=sys.stderr) + pass + + logger.debug("Fallback to using REST call") config = _get_path("service_component") return _recurse(config) diff --git a/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/exceptions.py b/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/exceptions.py index 6b73b91..0cd19fd 100644 --- a/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/exceptions.py +++ b/onap-dcae-cbs-docker-client/onap_dcae_cbs_docker_client/exceptions.py @@ -1,5 +1,5 @@ # ================================================================================ -# Copyright (c) 2019 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2019-2021 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============LICENSE_END========================================================= -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. + +""" exceptions used by the Config Binding Server """ class ENVsMissing(Exception): diff --git a/onap-dcae-cbs-docker-client/pom.xml b/onap-dcae-cbs-docker-client/pom.xml index b566d89..4783ada 100644 --- a/onap-dcae-cbs-docker-client/pom.xml +++ b/onap-dcae-cbs-docker-client/pom.xml @@ -28,7 +28,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. org.onap.dcaegen2.utils onap-dcae-cbs-docker-client dcaegen2-utils-python-cbs-docker-client - 2.1.2-SNAPSHOT + 2.2.0-SNAPSHOT http://maven.apache.org diff --git a/onap-dcae-cbs-docker-client/setup.py b/onap-dcae-cbs-docker-client/setup.py index 84b7436..585d628 100644 --- a/onap-dcae-cbs-docker-client/setup.py +++ b/onap-dcae-cbs-docker-client/setup.py @@ -1,5 +1,5 @@ # ================================================================================ -# Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2021 AT&T Intellectual Property. All rights reserved. # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ from setuptools import setup, find_packages setup( name="onap_dcae_cbs_docker_client", description="very lightweight client for a DCAE dockerized component to get it's config from the CBS", - version="2.1.2", + version="2.2.0", packages=find_packages(), author="Tommy Carpenter", author_email="tommy@research.att.com", diff --git a/onap-dcae-cbs-docker-client/tests/conftest.py b/onap-dcae-cbs-docker-client/tests/conftest.py index 0f34ff1..5dc877d 100644 --- a/onap-dcae-cbs-docker-client/tests/conftest.py +++ b/onap-dcae-cbs-docker-client/tests/conftest.py @@ -1,5 +1,5 @@ # ================================================================================ -# Copyright (c) 2019 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2019-2021 AT&T Intellectual Property. All rights reserved. # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,8 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============LICENSE_END========================================================= -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. + +""" helper functions/classes for unit tests for Config Binding Server """ + import pytest from requests.exceptions import HTTPError, ConnectionError diff --git a/onap-dcae-cbs-docker-client/tests/test_client.py b/onap-dcae-cbs-docker-client/tests/test_client.py index 3b10923..76249d4 100644 --- a/onap-dcae-cbs-docker-client/tests/test_client.py +++ b/onap-dcae-cbs-docker-client/tests/test_client.py @@ -1,5 +1,5 @@ # ================================================================================ -# Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2021 AT&T Intellectual Property. All rights reserved. # Copyright (C) 2021 Nokia. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,22 +14,37 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============LICENSE_END========================================================= + +""" unit tests for Config Binding Server """ + import pytest +import io +import json +import os +import yaml + +from unittest.mock import patch + from onap_dcae_cbs_docker_client.client import get_config, get_all +from onap_dcae_cbs_docker_client.client import default_config_path, default_policy_path from onap_dcae_cbs_docker_client.exceptions import CantGetConfig, CBSUnreachable, ENVsMissing +DEFAULT_REQUESTS_CONFIG = {"key_to_your_heart": 666} +DEFAULT_REQUESTS_CONFIG_ALL = { + "config": {"key_to_your_heart": 666}, + "dti": {"some amazing": "dti stuff"}, + "policies": {"event": {"foo": "bar"}, "items": [{"foo2": "bar2"}]}, + "otherkey": {"foo3": "bar3"}, +} + + def test_http(monkeypatch, monkeyed_requests_get): monkeypatch.setattr("requests.get", monkeyed_requests_get) - assert get_config() == {"key_to_your_heart": 666} + assert get_config() == DEFAULT_REQUESTS_CONFIG - assert get_all() == { - "config": {"key_to_your_heart": 666}, - "dti": {"some amazing": "dti stuff"}, - "policies": {"event": {"foo": "bar"}, "items": [{"foo2": "bar2"}]}, - "otherkey": {"foo3": "bar3"}, - } + assert get_all() == DEFAULT_REQUESTS_CONFIG_ALL def test_https_url(monkeypatch, monkeyed_requests_get_https): @@ -113,3 +128,288 @@ def test_http_with_wrong_env(monkeypatch, monkeyed_requests_get_http_with_wrong_ get_config() with pytest.raises(ENVsMissing): get_all() + + +################ +################ +# Tests for using local configuration and profile files. +################ +################ + +# Combinations to test: +# $CBS_CLIENT_CONFIG_PATH set, set to "", not set +# $CBS_CLIENT_POLICY_PATH set, set to "", not set +# config_path file exists, not exists +# policy_path file exists, not exists +# good config, bad config +# good policy, bad policy +# call get_all(), get_config() +# config has ${VAR} to be expanded +# policy does NOT expand ${VAR} + +# JSON found in https://jira.onap.org/browse/DCAEGEN2-2753 +policy_default = ''.join(['{"policies": {"items": [{"type": "onap.policies.monitoring.tcagen2", ', + '"type_version": "1.0.0", "name": "onap.vfirewall.tca", "version": "1.0.0", ', + '"metadata": {"policy-id": "onap.vfirewall.tca", "policy-version": "1.0.0"}, ', + '"policyName": "onap.vfirewall.tca.1-0-0.xml", "policyVersion": "1.0.0", ', + '"config": {"tca.policy": {"domain": "measurementsForVfScaling", ', + '"metricsPerEventName": [{"eventName": "vFirewallBroadcastPackets", ', + '"controlLoopSchemaType": "VM", "policyScope": "DCAE", "policyName": ', + '"DCAE.Config_tca-hi-lo", "policyVersion": "v0.0.1", "thresholds": ', + '[{"closedLoopControlName": ', + '"ControlLoop-vFirewall-d0a1dfc6-94f5-4fd4-a5b5-4630b438850a", ', + '"version": "1.0.2", "fieldPath": ', + '"$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].', + 'receivedTotalPacketsDelta", "thresholdValue": 300, "direction": ', + '"LESS_OR_EQUAL", "severity": "MAJOR", "closedLoopEventStatus": ', + '"ONSET"}, {"closedLoopControlName": "ControlLoop-vFirewall-d0a1dfc6-', + '94f5-4fd4-a5b5-4630b438850a", "version": "1.0.2", "fieldPath": ', + '"$.event.measurementsForVfScalingFields.vNicPerformanceArray[*].', + 'receivedTotalPacketsDelta", "thresholdValue": 700, "direction": ', + '"GREATER_OR_EQUAL", "severity": "CRITICAL", "closedLoopEventStatus": ', + '"ONSET"}]}]}}}]}, "event": {"action": "gathered", "timestamp": ', + '"2021-04-19T23:37:19.709Z", "update_id": "379fb01a-cfe2-4c06-8f6b-', + 'd51f3c8504af", "policies_count": 1}}']) +found_policy_path = '{"policies": "found policy/path"}' +found_policy_path_with_env = '{"policies": "${POLICYENV}"}' +found_policy_path_with_env_expanded = '{"policies": "pOlIcYeNv"}' +found_policy_path_missing_policy = '{"XXpoliciesXX": "found policy/path"}' +found_config_default = '["found config/default"]' +found_config_default_with_env = '["${CONFIGENV}"]' +found_config_default_with_env_expanded = '["cOnFiGeNv"]' +found_config_path = '["found config/path"]' + + +class my_mock_open(io.StringIO): + """ + This class acts like the mock_open function, + but is able to return different results dependent + on the path being opened, or even raise different + exceptions. + """ + + def __init__(self, fname, mode="r"): + """ Do the right thing for the filename + """ + ce = os.getenv("CONFIG_EXISTS", "no") + pe = os.getenv("POLICY_EXISTS", "no") + + # are we a file with data to be returned? + ret_data = { + "config/path": [ce, found_config_path], + "config/env": [ce, found_config_default_with_env], + default_config_path(): [ce, found_config_default], + "policy/path": [pe, found_policy_path], + "policy/env": [pe, found_policy_path_with_env], + default_policy_path(): [pe, policy_default], + "bad/policy3": ["yes", found_policy_path_missing_policy], + } + + if fname in ret_data: + if ret_data[fname][0] == "yes": + super().__init__(ret_data[fname][1]) + return + + # These return specifc exceptions + if fname == "bad/config": + raise yaml.scanner.ScannerError() + if fname == "bad/policy": + raise ValueError() + if fname == "bad/policy2": + raise json.decoder.JSONDecodeError(msg="bad json", doc=fname, pos=1) + if fname == "other/error": + raise ZeroDivisionError() + # raise OSError(2, "ENOENT") + + raise FileNotFoundError(fname) + + +def test_config_file_both_exist(monkeypatch, monkeyed_requests_get): + """ + These tests cover the cases where both the policy and configuration files exist. + We vary WHICH file is being pointed at using the environment variables. + """ + + monkeypatch.setattr("requests.get", monkeyed_requests_get) + + monkeypatch.setenv("CONFIG_EXISTS", "yes") + monkeypatch.setenv("POLICY_EXISTS", "yes") + monkeypatch.setenv("CONFIGENV", "cOnFiGeNv") + monkeypatch.setenv("POLICYENV", "pOlIcYeNv") + for (config_path, expected_config, set_config) in [ + ("config/path", json.loads(found_config_path), True), + ("config/env", json.loads(found_config_default_with_env_expanded), True), + ("", json.loads(found_config_default), True), + (None, json.loads(found_config_default), False)]: + + if set_config: + monkeypatch.setenv("CBS_CLIENT_CONFIG_PATH", config_path) + else: + monkeypatch.delenv("CBS_CLIENT_CONFIG_PATH") + + expected_get_config = {"config": expected_config} + + for (policy_path, expected_policy, set_policy) in [ + ("policy/path", json.loads(found_policy_path)['policies'], True), + ("policy/env", json.loads(found_policy_path_with_env)['policies'], True), + ("", json.loads(policy_default)['policies'], True), + (None, json.loads(policy_default)['policies'], False)]: + + if set_policy: + monkeypatch.setenv("CBS_CLIENT_POLICY_PATH", policy_path) + else: + monkeypatch.delenv("CBS_CLIENT_POLICY_PATH") + + expected_get_all = {"config": expected_config, "policies": expected_policy} + + with patch('builtins.open', my_mock_open): + gc = get_config() + assert gc == expected_get_config + + ga = get_all() + assert ga == expected_get_all + + +def test_config_file_config_exists_policy_does_not(monkeypatch, monkeyed_requests_get): + """ + In this test, the policy file is always missing. Its value is always defaulted to {}. + The test structure is identical to what we had in test_config_file_both_exist(), but + the expected configuration to be returned is different. + """ + + monkeypatch.setattr("requests.get", monkeyed_requests_get) + + monkeypatch.setenv("CONFIG_EXISTS", "yes") + monkeypatch.setenv("POLICY_EXISTS", "no") + + for (config_path, expected_config, set_config) in [ + ("config/path", json.loads(found_config_path), True), + ("", json.loads(found_config_default), True), + (None, json.loads(found_config_default), False)]: + + if set_config: + monkeypatch.setenv("CBS_CLIENT_CONFIG_PATH", config_path) + else: + monkeypatch.delenv("CBS_CLIENT_CONFIG_PATH") + + expected_get_config = {"config": expected_config} + expected_get_all = {"config": expected_config} + + for (policy_path, expected_policy, set_policy) in [ + ("policy/path", json.loads(found_policy_path)['policies'], True), + ("", json.loads(policy_default)['policies'], True), + (None, json.loads(policy_default)['policies'], False)]: + + if set_policy: + monkeypatch.setenv("CBS_CLIENT_POLICY_PATH", policy_path) + else: + monkeypatch.delenv("CBS_CLIENT_POLICY_PATH") + + with patch('builtins.open', my_mock_open): + gc = get_config() + assert gc == expected_get_config + + ga = get_all() + assert ga == expected_get_all + + +def test_config_file_config_does_not_policy_exists(monkeypatch, monkeyed_requests_get): + """ + In this test, the config file is always missing. Its value is always defaulted to + the CBS response. + The test structure is identical to what we had in test_config_file_both_exist(), but + the expected configuration to be returned is different. + """ + + monkeypatch.setattr("requests.get", monkeyed_requests_get) + + monkeypatch.setenv("CONFIG_EXISTS", "no") + monkeypatch.setenv("POLICY_EXISTS", "yes") + + expected_get_config = DEFAULT_REQUESTS_CONFIG + expected_get_all = DEFAULT_REQUESTS_CONFIG_ALL + + for (config_path, expected_config, set_config) in [ + ("config/path", json.loads(found_config_path), True), + ("", json.loads(found_config_default), True), + (None, json.loads(found_config_default), False)]: + + if set_config: + monkeypatch.setenv("CBS_CLIENT_CONFIG_PATH", config_path) + else: + monkeypatch.delenv("CBS_CLIENT_CONFIG_PATH") + + for (policy_path, expected_policy, set_policy) in [ + ("policy/path", json.loads(found_policy_path)['policies'], True), + ("", json.loads(policy_default)['policies'], True), + (None, json.loads(policy_default)['policies'], False)]: + + if set_policy: + monkeypatch.setenv("CBS_CLIENT_POLICY_PATH", policy_path) + else: + monkeypatch.delenv("CBS_CLIENT_POLICY_PATH") + + with patch('builtins.open', my_mock_open): + gc = get_config() + assert gc == expected_get_config + + ga = get_all() + assert ga == expected_get_all + + +def test_config_file_various_exceptions(monkeypatch, monkeyed_requests_get): + """ + In this test, various forms of exceptions are tested while accessing the + configuration and policy files. + Exceptions on the policy file act as if the policy file does not exist. + Exceptions on the configuration file act as if the configuration file does not exist. + """ + + monkeypatch.setattr("requests.get", monkeyed_requests_get) + + monkeypatch.setenv("CONFIG_EXISTS", "yes") + monkeypatch.setenv("POLICY_EXISTS", "yes") + + # first do exceptions for the policy file + for (config_path, expected_config, set_config) in [ + ("config/path", json.loads(found_config_path), True), + ("", json.loads(found_config_default), True), + (None, json.loads(found_config_default), False)]: + + if set_config: + monkeypatch.setenv("CBS_CLIENT_CONFIG_PATH", config_path) + else: + monkeypatch.delenv("CBS_CLIENT_CONFIG_PATH") + + expected_get_config = {"config": expected_config} + expected_get_all = {"config": expected_config} + + for policy_path in ["bad/policy", "bad/policy2", + "bad/policy3", "other/error"]: + + monkeypatch.setenv("CBS_CLIENT_POLICY_PATH", policy_path) + + with patch('builtins.open', my_mock_open): + gc = get_config() + assert gc == expected_get_config + + ga = get_all() + assert ga == expected_get_all + + # next do exceptions for the config file + expected_get_config = DEFAULT_REQUESTS_CONFIG + expected_get_all = DEFAULT_REQUESTS_CONFIG_ALL + + policy_path = "policy/path" + monkeypatch.setenv("CBS_CLIENT_POLICY_PATH", policy_path) + + for config_path in ["bad/config", "other/error"]: + + monkeypatch.setenv("CBS_CLIENT_CONFIG_PATH", config_path) + + with patch('builtins.open', my_mock_open): + gc = get_config() + assert gc == expected_get_config + + ga = get_all() + assert ga == expected_get_all diff --git a/onap-dcae-cbs-docker-client/tox.ini b/onap-dcae-cbs-docker-client/tox.ini index 04b292e..5f6d8f8 100644 --- a/onap-dcae-cbs-docker-client/tox.ini +++ b/onap-dcae-cbs-docker-client/tox.ini @@ -1,12 +1,15 @@ # content of: tox.ini , put in same dir as setup.py [tox] -envlist = py36, flake8, py38, py39 +# envlist = py36, flake8, py38, py39 +envlist = flake8, py38, py39 [testenv] deps= pytest coverage pytest-cov + pyyaml + setenv = HOSTNAME = testhostname CONFIG_BINDING_SERVICE = config-binding-service @@ -14,7 +17,7 @@ setenv = TEST_ENV=test_env commands= - pytest --junitxml xunit-results.xml --cov onap_dcae_cbs_docker_client --cov-report xml + pytest --junitxml xunit-results.xml --cov onap_dcae_cbs_docker_client --cov-report xml --cov-report=term --cov-report=html coverage xml [testenv:flake8] -- cgit 1.2.3-korg