diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Changelog.md | 5 | ||||
-rw-r--r-- | README.md | 29 | ||||
-rw-r--r-- | config_binding_service/client.py | 62 | ||||
-rw-r--r-- | config_binding_service/controller.py | 27 | ||||
-rw-r--r-- | config_binding_service/exceptions.py | 45 | ||||
-rwxr-xr-x | config_binding_service/run.py | 11 | ||||
-rw-r--r-- | config_binding_service/utils.py | 33 | ||||
-rw-r--r-- | pom.xml | 2 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | tests/fixtures/test_c.crt | 22 | ||||
-rw-r--r-- | tests/fixtures/test_k.key | 28 | ||||
-rw-r--r-- | tests/test_api.py | 135 | ||||
-rw-r--r-- | tests/test_client.py | 262 | ||||
-rw-r--r-- | tests/test_utils.py | 41 | ||||
-rw-r--r-- | version.properties | 2 |
16 files changed, 531 insertions, 176 deletions
@@ -1,3 +1,4 @@ +tox-local.ini .pytest_cache/ run_tests.sh run_tests_https.sh diff --git a/Changelog.md b/Changelog.md index 415c15b..7576d1e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,11 @@ 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.5.0] - 6/5/2019 +* Add back HTTPS support and a flag to enable it +* Move exceptions into a common file +* Run python BLACK formatter + ## [2.4.0] - 5/29/2019 * Switch from NGINX to Gevent. The CBS is not CPU bound, and doesn't make any non-network blocking calls, so we don't really need a threaded server; an asyncronous event loop is fine. Gevent handles the patching of requests. The benefits of this are twofold; it will be easier to add https/http switching support, and it will be much easier to run as non-root in the Dockerfile. Moreover, it's "as fast" because again the CBS is not at all CPU bound so threading really doesn't buy anything over an async loop. This also has the practical benefit of 1 pom.xml instead of 3! @@ -74,16 +74,39 @@ You need `tox`; then just run: # Deployment ## HTTPS -Details coming soon + +The default non HTTPS port is 10000. The default HTTPS port is 10443. + +To deploy with HTTPS, you must then set three ENVs: +1. `USE_HTTPS=1` (any other value, HTTPS is not used) +2. `HTTPS_KEY_PATH`: path to the `.key` file (including the filename) +3. `HTTPS_CERT_PATH`: path to the .crt file (including the filename) + +If `USE_HTTPS` is set, but the other two are not supplied or do not exist, the CBS will crash immediately. + +For testing, I created a self signed cert with + + openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/k.key -out /tmp/c.crt -subj "/C=US/ST=NJ/L=foo/O=ONAP/OU=ONAP/CN=configbinding" ## Docker +### Without HTTPS sudo docker run -dt -p 10000:10000 -e CONSUL_HOST=<YOUR_HOST>cbs:X.Y.Z -If you wish to turn ON HTTP healthchecks and turn OFF HTTPS healthchecks, swith 10000 and 443 above. That will work even with `v7` of registrator (that is, `SERVICE_x_CHECK_HTTP` was already supported) +###With HTTPS +Mount the key and crt into `/opt/`; in theory other paths should work if the ENV is set correctly, but this one has been tested and `/opt` is gauranteed to exist in the container because that's where the logs go. + + + docker run -dt -p 10443:10443 -e CONSUL_HOST=<YOUR_HOST> -e USE_HTTPS=1 -e HTTPS_KEY_PATH=/opt/k.key -e HTTPS_CERT_PATH=/opt/c.crt -v /tmp/k.key:/opt/k.key -v /tmp/c.crt:/opt/c.crt cbs:X.Y. + ## Locally for development (no docker) +###Without HTTPS It is recommended that you do this step in a virtualenv. (set -x is Fish notaion, change for Bash etc. accordingly) - pip install --ignore-installed .; set -x CONSUL_HOST <YOUR_HOST>; ./run.py + pip install --ignore-installed .; set -x CONSUL_HOST <YOUR_HOST>; run.py + +###With HTTPS + + pip install --ignore-installed .; set -x CONSUL_HOST <YOUR_HOST>; set -x USE_HTTPS 1; set -x USE_HTTPS 1; set -x HTTPS_KEY_PATH /tmp/k.key; set -x HTTPS_CERT_PATH /tmp/c.crt; run.py diff --git a/config_binding_service/client.py b/config_binding_service/client.py index c6a6753..63f3003 100644 --- a/config_binding_service/client.py +++ b/config_binding_service/client.py @@ -25,6 +25,7 @@ import requests import six from config_binding_service import get_consul_uri from config_binding_service.logging import utc, metrics +from config_binding_service import exceptions CONSUL = get_consul_uri() @@ -32,30 +33,6 @@ CONSUL = get_consul_uri() template_match_rels = re.compile("\{{2}([^\}\{]*)\}{2}") template_match_dmaap = re.compile("<{2}([^><]*)>{2}") -### -# Cusom Exception -### - - -class CantGetConfig(Exception): - """ - Represents an exception where a required key in consul isn't there - """ - - def __init__(self, code, response): - self.code = code - self.response = response - - -class BadRequest(Exception): - """ - Exception to be raised when the user tried to do something they shouldn't - """ - - def __init__(self, response): - self.code = 400 - self.response = response - ### # Private Functions @@ -67,24 +44,19 @@ def _consul_get_all_as_transaction(service_component_name, raw_request, xer): Use Consul's transaction API to get all keys of the form service_component_name:* Return a dict with all the values decoded """ - payload = [ - { - "KV": { - "Verb": "get-tree", - "Key": service_component_name, - } - }] + payload = [{"KV": {"Verb": "get-tree", "Key": service_component_name}}] bts = utc() response = requests.put("{0}/v1/txn".format(CONSUL), json=payload) - metrics(raw_request, bts, xer, "Consul", "/v1/txn".format(service_component_name), response.status_code, __name__, msg="Retrieving Consul transaction for all keys for {0}".format(service_component_name)) + msg = "Retrieving Consul transaction for all keys for {0}".format(service_component_name) + metrics(raw_request, bts, xer, "Consul", "/v1/txn", response.status_code, __name__, msg=msg) try: response.raise_for_status() except requests.exceptions.HTTPError as exc: - raise CantGetConfig(exc.response.status_code, exc.response.text) + raise exceptions.CantGetConfig(exc.response.status_code, exc.response.text) - result = json.loads(response.text)['Results'] + result = json.loads(response.text)["Results"] new_res = {} for res in result: @@ -96,7 +68,7 @@ def _consul_get_all_as_transaction(service_component_name, raw_request, xer): new_res[key] = "INVALID JSON" # TODO, should we just include the original value somehow? if service_component_name not in new_res: - raise CantGetConfig(404, "") + raise exceptions.CantGetConfig(404, "") return new_res @@ -249,9 +221,11 @@ def resolve_all(service_component_name, raw_request, xer): returnk = {} # replace the config with the resolved config - returnk["config"] = resolve_override(allk[service_component_name], - allk.get("{0}:rels".format(service_component_name), []), - allk.get("{0}:dmaap".format(service_component_name), {})) + returnk["config"] = resolve_override( + allk[service_component_name], + allk.get("{0}:rels".format(service_component_name), []), + allk.get("{0}:dmaap".format(service_component_name), {}), + ) # concatenate the items for k in allk: @@ -266,7 +240,7 @@ def resolve_all(service_component_name, raw_request, xer): elif ":policies/items" in k: returnk["policies"]["items"].append(allk[k]) else: - if not(k == service_component_name or k.endswith(":rels") or k.endswith(":dmaap")): + if not (k == service_component_name or k.endswith(":rels") or k.endswith(":dmaap")): # this would blow up if you had a key in consul without a : but this shouldnt happen suffix = k.split(":")[1] returnk[suffix] = allk[k] @@ -281,17 +255,19 @@ def get_key(key, service_component_name, raw_request, xer): raw_request and xer are needed to form the correct metrics log """ if key == "policies": - raise BadRequest( - ":policies is a complex folder and should be retrieved using the service_component_all API") + raise exceptions.BadRequest( + ":policies is a complex folder and should be retrieved using the service_component_all API" + ) bts = utc() path = "v1/kv/{0}:{1}".format(service_component_name, key) response = requests.get("{0}/{1}".format(CONSUL, path)) - metrics(raw_request, bts, xer, "Consul", path, response.status_code, __name__, msg="Retrieving single Consul key {0} for {1}".format(key, service_component_name)) + msg = "Retrieving single Consul key {0} for {1}".format(key, service_component_name) + metrics(raw_request, bts, xer, "Consul", path, response.status_code, __name__, msg=msg) try: response.raise_for_status() except requests.exceptions.HTTPError as exc: - raise CantGetConfig(exc.response.status_code, exc.response.text) + raise exceptions.CantGetConfig(exc.response.status_code, exc.response.text) rest = json.loads(response.text)[0] return json.loads(base64.b64decode(rest["Value"]).decode("utf-8")) diff --git a/config_binding_service/controller.py b/config_binding_service/controller.py index c2eb21c..80a932f 100644 --- a/config_binding_service/controller.py +++ b/config_binding_service/controller.py @@ -21,7 +21,8 @@ import requests import connexion import uuid from flask import Response -from config_binding_service import client, get_consul_uri +from config_binding_service import client, exceptions +from config_binding_service import get_consul_uri from config_binding_service.logging import audit, utc, error, metrics @@ -32,9 +33,9 @@ def _get_helper(json_expecting_func, **kwargs): try: payload = json_expecting_func(**kwargs) response, status_code, mimetype = json.dumps(payload), 200, "application/json" - except client.BadRequest as exc: + except exceptions.BadRequest as exc: response, status_code, mimetype = exc.response, exc.code, "text/plain" - except client.CantGetConfig as exc: + except exceptions.CantGetConfig as exc: response, status_code, mimetype = exc.response, exc.code, "text/plain" except Exception: response, status_code, mimetype = "Unknown error", 500, "text/plain" @@ -59,7 +60,9 @@ def bind_all(service_component_name): """ xer = _get_or_generate_xer(connexion.request) bts = utc() - response, status_code, mimetype = _get_helper(client.resolve_all, service_component_name=service_component_name, raw_request=connexion.request, xer=xer) + response, status_code, mimetype = _get_helper( + client.resolve_all, service_component_name=service_component_name, raw_request=connexion.request, xer=xer + ) audit(connexion.request, bts, xer, status_code, __name__, "called for component {0}".format(service_component_name)) # Even though some older components might be using the ecomp name, we return the proper one return Response(response=response, status=status_code, mimetype=mimetype, headers={"x-onap-requestid": xer}) @@ -71,7 +74,9 @@ def bind_config_for_scn(service_component_name): """ xer = _get_or_generate_xer(connexion.request) bts = utc() - response, status_code, mimetype = _get_helper(client.resolve, service_component_name=service_component_name, raw_request=connexion.request, xer=xer) + response, status_code, mimetype = _get_helper( + client.resolve, service_component_name=service_component_name, raw_request=connexion.request, xer=xer + ) audit(connexion.request, bts, xer, status_code, __name__, "called for component {0}".format(service_component_name)) return Response(response=response, status=status_code, mimetype=mimetype, headers={"x-onap-requestid": xer}) @@ -83,7 +88,9 @@ def get_key(key, service_component_name): """ xer = _get_or_generate_xer(connexion.request) bts = utc() - response, status_code, mimetype = _get_helper(client.get_key, key=key, service_component_name=service_component_name, raw_request=connexion.request, xer=xer) + response, status_code, mimetype = _get_helper( + client.get_key, key=key, service_component_name=service_component_name, raw_request=connexion.request, xer=xer + ) audit(connexion.request, bts, xer, status_code, __name__, "called for component {0}".format(service_component_name)) return Response(response=response, status=status_code, mimetype=mimetype, headers={"x-onap-requestid": xer}) @@ -92,7 +99,8 @@ def healthcheck(): """ CBS Healthcheck """ - xer = _get_or_generate_xer(connexion.request) + req = connexion.request + xer = _get_or_generate_xer(req) path = "v1/catalog/service/config_binding_service" bts = utc() res = requests.get("{0}/{1}".format(get_consul_uri(), path)) @@ -102,7 +110,8 @@ def healthcheck(): else: msg = "CBS is alive but cannot reach Consul" # treating this as a WARN because this could be a temporary network glitch. Also per EELF guidelines this is a 200 ecode (availability) - error(connexion.request, xer, "WARN", 200, tgt_entity="Consul", tgt_path="/v1/catalog/service/config_binding_service", msg=msg) - metrics(connexion.request, bts, xer, "Consul", path, res.status_code, __name__, msg="Checking Consul connectivity during CBS healthcheck, {0}".format(msg)) + error(req, xer, "WARN", 200, tgt_entity="Consul", tgt_path="/v1/catalog/service/config_binding_service", msg=msg) + msg = ("Checking Consul connectivity during CBS healthcheck, {0}".format(msg),) + metrics(connexion.request, bts, xer, "Consul", path, res.status_code, __name__, msg=msg) audit(connexion.request, bts, xer, status, __name__, msg=msg) return Response(response=msg, status=status) diff --git a/config_binding_service/exceptions.py b/config_binding_service/exceptions.py new file mode 100644 index 0000000..5400295 --- /dev/null +++ b/config_binding_service/exceptions.py @@ -0,0 +1,45 @@ +# ============LICENSE_START======================================================= +# Copyright (c) 2017-2019 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# 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. + + +class BadHTTPSEnvs(BaseException): + """ + used for bad http setup + """ + + pass + + +class CantGetConfig(Exception): + """ + Represents an exception where a required key in consul isn't there + """ + + def __init__(self, code, response): + self.code = code + self.response = response + + +class BadRequest(Exception): + """ + Exception to be raised when the user tried to do something they shouldn't + """ + + def __init__(self, response): + self.code = 400 + self.response = response diff --git a/config_binding_service/run.py b/config_binding_service/run.py index 175c0cf..a161c53 100755 --- a/config_binding_service/run.py +++ b/config_binding_service/run.py @@ -21,12 +21,19 @@ import os from gevent.pywsgi import WSGIServer from config_binding_service.logging import create_loggers, DEBUG_LOGGER from config_binding_service import app +from config_binding_service import utils def main(): """Entrypoint""" if "PROD_LOGGING" in os.environ: create_loggers() - DEBUG_LOGGER.debug("Starting gevent server") - http_server = WSGIServer(("", 10000), app) + key_loc, cert_loc = utils.get_https_envs() + if key_loc: + DEBUG_LOGGER.debug("Starting gevent server as HTTPS") + http_server = WSGIServer(("", 10443), app, keyfile=key_loc, certfile=cert_loc) + else: + DEBUG_LOGGER.debug("Starting gevent server as HTTP") + http_server = WSGIServer(("", 10000), app) + http_server.serve_forever() diff --git a/config_binding_service/utils.py b/config_binding_service/utils.py new file mode 100644 index 0000000..55afe7f --- /dev/null +++ b/config_binding_service/utils.py @@ -0,0 +1,33 @@ +# ============LICENSE_START======================================================= +# Copyright (c) 2017-2019 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# 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. +import os +from config_binding_service import exceptions + + +def get_https_envs(): + if "USE_HTTPS" in os.environ and os.environ["USE_HTTPS"] == "1": + try: + key_loc = os.environ["HTTPS_KEY_PATH"] + cert_loc = os.environ["HTTPS_CERT_PATH"] + # We check whether both these files exist. Future fail fast optimization: check that they're valid too + if not (os.path.isfile(key_loc) and os.path.isfile(cert_loc)): + raise exceptions.BadHTTPSEnvs() + return key_loc, cert_loc + except KeyError: + raise exceptions.BadHTTPSEnvs() + return None, None @@ -30,7 +30,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. <groupId>org.onap.dcaegen2.platform</groupId> <artifactId>configbinding</artifactId> <name>dcaegen2-platform-configbinding</name> - <version>2.4.0-SNAPSHOT</version> + <version>2.5.0-SNAPSHOT</version> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> @@ -20,7 +20,7 @@ from setuptools import setup, find_packages setup( name="config_binding_service", - version="2.4.0", + version="2.5.0", packages=find_packages(exclude=["tests.*", "tests"]), author="Tommy Carpenter", author_email="tommy@research.att.com", diff --git a/tests/fixtures/test_c.crt b/tests/fixtures/test_c.crt new file mode 100644 index 0000000..ebe25fa --- /dev/null +++ b/tests/fixtures/test_c.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDnTCCAoWgAwIBAgIUZ6JZNlD9m6/JiXwHOvDGyflH4UgwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5KMQwwCgYDVQQHDANmb28xDTAL +BgNVBAoMBE9OQVAxDTALBgNVBAsMBE9OQVAxFjAUBgNVBAMMDWNvbmZpZ2JpbmRp +bmcwHhcNMTkwNjA1MDIwNDM2WhcNMjAwNjA0MDIwNDM2WjBeMQswCQYDVQQGEwJV +UzELMAkGA1UECAwCTkoxDDAKBgNVBAcMA2ZvbzENMAsGA1UECgwET05BUDENMAsG +A1UECwwET05BUDEWMBQGA1UEAwwNY29uZmlnYmluZGluZzCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAPSPu2V8PvEudWa6u9Zdfmh98/s3HFTHSufeRLW+ +FfXX/8IljV2HOyvKU+FEqJ2NghXFqcvJEVxflitbyvg+vlVwgVsK5CHwSng8CE5E +UVQrdqNsypOwLMAT3VqL4isM3vvtLvwza0aVHIrqjHh6AcRWYBvqdSMSgdQ99syT +D+iPvs7uBijPdl13IoXLPE8jz7PbJTqRqDbkzHTSBMZQRI2QovfKAsDIncaJk04r +FGnX9pEdJtnZtfdEALPjapb4vuI4WXa2KaAMMSK8UQn2UKpg6W7vAJio5I23NoeG +0TKAS9a7icSlNy/Ahf0O+af7NVWbUXUkSdbQM12C5+XwARUCAwEAAaNTMFEwHQYD +VR0OBBYEFMNaZ6UckYHriuwyLbtvkCB/cuiXMB8GA1UdIwQYMBaAFMNaZ6UckYHr +iuwyLbtvkCB/cuiXMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +ALg3RDTyEhjK5rPnK/b2ryOleOg+bQwuKGpTBGb1KQ5q/25mFbNV10YZb8ikZ8TU +VJ38CbffzmWuNuIuD5R6a1KHPhDWbBGX3Wap1kKEMlLcWioYtynEj4Pe/dmpYRlk +GkgKlhWZZXlyXaxlR2hpClt20Ts3Jfksq0tpjCacDhlqrjNXyh6Kt5RmNwhhdiEw +sYWP7e858scubOG1jYIQmPa4W1cLp0aUF+Ge2b/3d8HMe1dECFJwMRQFhE6FjBQ/ +qzgxv+A84aNQn8chpSjKC6o7GSDaZAusJP9Z+oAXYXmTDlIGkbPt6g8DoK6WqGUY +eOvS5OKGh9x20GSLw8YFXxI= +-----END CERTIFICATE----- diff --git a/tests/fixtures/test_k.key b/tests/fixtures/test_k.key new file mode 100644 index 0000000..df3da7b --- /dev/null +++ b/tests/fixtures/test_k.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0j7tlfD7xLnVm +urvWXX5offP7NxxUx0rn3kS1vhX11//CJY1dhzsrylPhRKidjYIVxanLyRFcX5Yr +W8r4Pr5VcIFbCuQh8Ep4PAhORFFUK3ajbMqTsCzAE91ai+IrDN777S78M2tGlRyK +6ox4egHEVmAb6nUjEoHUPfbMkw/oj77O7gYoz3ZddyKFyzxPI8+z2yU6kag25Mx0 +0gTGUESNkKL3ygLAyJ3GiZNOKxRp1/aRHSbZ2bX3RACz42qW+L7iOFl2timgDDEi +vFEJ9lCqYOlu7wCYqOSNtzaHhtEygEvWu4nEpTcvwIX9Dvmn+zVVm1F1JEnW0DNd +gufl8AEVAgMBAAECggEBANEQ4llieohBeBmG/rbFnB+FtQIH3c9Xw2AucbyQMEPH +EBwPcCcJSPspbsV/pUlyxEcKgBn23EGEcWLFiODo+lsd32dOCzI8Md1RLKSD4Ka8 +tSIGCbNxgv/8u1kBhinZSfJBk84DwuxG4mybD9SaMFKAqI6UwuhllNk003uVKJ9c +PxjSzjqxxpSrDpblBUao0Jptmd6YRvltQ8YIdA+QyhoHkN8XzZiOcQMdgG0VepAX +ztDzudRPglHxs9IKr9otuVx9PcEV4i9lWNo0dDyoOKo1fMCEAn0Twry3lEvoT+dh +enH6fM3g68zaRxIU7YtynK0JGmx/zWskuvvHDKT+z40CgYEA/sQW2nH9FudAcTg7 +6gUVOSGLLl1PZGSkMl6lXg9tcYP52xLAokJiz25wBDa/Ptzs25HeyS+iITnnDDR1 +V4wijwrIPS+uF4byM0PizkR8Dwy+x1ccojAm9qDKVBj3FRo1XG/iz1BWhP+qY1Qd +jXUU85rXeJtuw1DFCR5g6iIyR5sCgYEA9b79Ne7/cIRr6TyxAsGHHkksfaRftRyL +gckGGU9c1nOdMTBLwy6Hd1OkcEAAnXaShzzh12EqLRXq8gEJ0o4fKFVEWlZgj2vg +zf1sCjr8hzJJ3vyr6ax1MNeNh8qQNfiVf85ZHrkrWnFh9vmVaRuzLyNo2/t2TcWk +7c8/H67X3Q8CgYEA/VTfpLhUgsHRQsQ4T6xePkEstR30JFAvNicHKVEN8yLZAQ7U +noSE3N9kY8xMv6XPoUab0lKbVY7OQUSoqjWe575A/xa6doqprHCEx7cx5YU27+Gf +h2d6VyPsDVQF3eYBybk4QA4S+o2AUOUDvfX+r335ZOSqb22RgE/E7wyyEqECgYA2 +g3mUH0JW6WkuwiHaJKgelcjizjXU7VIdJkTOFIbZo6f5I2eLEqVGWS2fIPOB+q2i +hmTq7Ej422XU7VoKdTCAN/oDuCmPZzTlPRRcaJ1gOE2ew5o3KeSNQyPIPa0j69Lu +GQ43/Iebdo9uxAra2CQ1jrtSIUQrGG7vugucsaJ9DQKBgQCnHlozZSypZwjLopbk +BWM+EKrNIWDKi5CCMhyO9EJ3eyh5jIjYDZ4m9jykoEs0Apikv0uYL1Lo5S25svbY +d/xxpHcM6fj4ilqFp0jzLn5Eagqto1SLWsjB0nF7bNL2jYCFvcWZQBSnpvVMH1Ng +zHW7trNSk4275srIOmx6OBxF8A== +-----END PRIVATE KEY----- diff --git a/tests/test_api.py b/tests/test_api.py index 118a2a0..4356629 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -28,76 +28,139 @@ TEST_NAME = "test_service_component_name.unknown.unknown.unknown.dcae.onap.org" # http://flask.pocoo.org/docs/1.0/testing/ @pytest.fixture def cbsclient(): - db_fd, app.app.config['DATABASE'] = tempfile.mkstemp() - app.app.config['TESTING'] = True + db_fd, app.app.config["DATABASE"] = tempfile.mkstemp() + app.app.config["TESTING"] = True testclient = app.app.test_client() yield testclient os.close(db_fd) - os.unlink(app.app.config['DATABASE']) + os.unlink(app.app.config["DATABASE"]) def test_get(monkeypatch, cbsclient, monkeyed_requests_put): - monkeypatch.setattr('requests.put', monkeyed_requests_put) + monkeypatch.setattr("requests.put", monkeyed_requests_put) - res = cbsclient.get('/service_component/scn_exists', - headers={"x-onap-requestid": 123456789}) - assert(json.loads(res.data) == {"foo3": "bar3"}) - assert(res.status_code == 200) - assert(res.headers["x-onap-requestid"] == "123456789") + res = cbsclient.get("/service_component/scn_exists", headers={"x-onap-requestid": 123456789}) + assert json.loads(res.data) == {"foo3": "bar3"} + assert res.status_code == 200 + assert res.headers["x-onap-requestid"] == "123456789" - res = cbsclient.get('/service_component/scn_NOTexists', - headers={"x-onap-requestid": 123456789}) - assert(res.status_code == 404) - assert(res.headers["x-onap-requestid"] == "123456789") + res = cbsclient.get("/service_component/scn_NOTexists", headers={"x-onap-requestid": 123456789}) + assert res.status_code == 404 + assert res.headers["x-onap-requestid"] == "123456789" - res = cbsclient.get('/service_component/asdfasdf', - headers={"x-onap-requestid": 123456789}) - assert(res.status_code == 500) - assert(res.headers["x-onap-requestid"] == "123456789") + res = cbsclient.get("/service_component/asdfasdf", headers={"x-onap-requestid": 123456789}) + assert res.status_code == 500 + assert res.headers["x-onap-requestid"] == "123456789" def test_generic(monkeypatch, cbsclient, monkeyed_requests_get, monkeyed_requests_put): - monkeypatch.setattr('requests.put', monkeyed_requests_put) - monkeypatch.setattr('requests.get', monkeyed_requests_get) + monkeypatch.setattr("requests.put", monkeyed_requests_put) + monkeypatch.setattr("requests.get", monkeyed_requests_get) - res = cbsclient.get('/dti/{0}'.format(TEST_NAME)) + res = cbsclient.get("/dti/{0}".format(TEST_NAME)) assert json.loads(res.data) == {"my": "dti"} assert res.json == {"my": "dti"} assert res.status_code == 200 assert "x-onap-requestid" in res.headers - res = cbsclient.get('/nokeyforyou/{0}'.format(TEST_NAME)) + res = cbsclient.get("/nokeyforyou/{0}".format(TEST_NAME)) assert res.status_code == 404 assert "x-onap-requestid" in res.headers - res = cbsclient.get('/policies/{0}'.format(TEST_NAME)) + res = cbsclient.get("/policies/{0}".format(TEST_NAME)) assert res.status_code == 400 assert "x-onap-requestid" in res.headers -def test_resolve_all(monkeypatch, cbsclient, monkeyed_requests_put, monkeyed_get_connection_info_from_consul, expected_config): - monkeypatch.setattr('requests.put', monkeyed_requests_put) - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', monkeyed_get_connection_info_from_consul) - withstuff = {'config': {'my': 'amazing config'}, - 'dti': {'my': 'dti'}, - 'policies': {'items': [{'policyName': 'DCAE_alex.Config_MS_alex_microservice.132.xml', 'policyConfigMessage': 'Config Retrieved! ', 'responseAttributes': {}, 'policyConfigStatus': 'CONFIG_RETRIEVED', 'matchingConditions': {'ONAPName': 'DCAE', 'Name': 'DCAE', 'ConfigName': 'alex_config_name'}, 'config': {'policyScope': 'alex_policy_scope', 'configName': 'alex_config_name', 'description': 'test DCAE policy-handler', 'service': 'alex_service', 'policyName': 'alex_policy_name', 'riskLevel': '3', 'key1': 'value1', 'policy_hello': 'world!', 'content': {'foo': 'microservice3', 'foo_updated': '2018-01-30T13:25:33.222Z'}, 'riskType': '1712_ETE', 'guard': 'False', 'version': '0.0.1', 'location': 'Central', 'policy_updated_ts': '2018-02-19T15:09:55.217Z', 'updated_policy_id': 'DCAE_alex.Config_MS_alex_microservice', 'policy_updated_to_ver': '132', 'priority': '4', 'policy_updated_from_ver': '131', 'templateVersion': '2', 'uuid': '5e87d7c5-0daf-4b6b-ab92-5365cf5db1ef'}, 'property': None, 'type': 'JSON', 'policyVersion': '132'}, {'policyName': 'DCAE_alex.Config_db_client_policy_id_value.133.xml', 'policyConfigMessage': 'Config Retrieved! ', 'responseAttributes': {}, 'policyConfigStatus': 'CONFIG_RETRIEVED', 'matchingConditions': {'ONAPName': 'DCAE', 'Name': 'DCAE', 'ConfigName': 'alex_config_name'}, 'config': {'db_client_ts': '2017-11-21T12:12:13.696Z', 'db_client': 'ipsum', 'policy_hello': 'world!', 'policy_updated_from_ver': '132', 'updated_policy_id': 'DCAE_alex.Config_db_client_policy_id_value', 'policy_updated_ts': '2018-02-19T15:09:55.812Z', 'policy_updated_to_ver': '133'}, 'property': None, 'type': 'JSON', 'policyVersion': '133'}], 'event': {'action': 'gathered', 'timestamp': '2018-02-19 15:36:44.877380', 'update_id': 'bb73c20a-5ff8-450f-8223-da6720ade267', 'policies_count': 2}}} - - assert cbsclient.get('service_component_all/{0}'.format(TEST_NAME)).json == withstuff - - assert cbsclient.get('service_component_all/test_resolve_scn').json == {"config": expected_config} - - res = cbsclient.get('/service_component_all/{0}'.format(TEST_NAME)) +def test_resolve_all( + monkeypatch, cbsclient, monkeyed_requests_put, monkeyed_get_connection_info_from_consul, expected_config +): + monkeypatch.setattr("requests.put", monkeyed_requests_put) + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) + withstuff = { + "config": {"my": "amazing config"}, + "dti": {"my": "dti"}, + "policies": { + "items": [ + { + "policyName": "DCAE_alex.Config_MS_alex_microservice.132.xml", + "policyConfigMessage": "Config Retrieved! ", + "responseAttributes": {}, + "policyConfigStatus": "CONFIG_RETRIEVED", + "matchingConditions": {"ONAPName": "DCAE", "Name": "DCAE", "ConfigName": "alex_config_name"}, + "config": { + "policyScope": "alex_policy_scope", + "configName": "alex_config_name", + "description": "test DCAE policy-handler", + "service": "alex_service", + "policyName": "alex_policy_name", + "riskLevel": "3", + "key1": "value1", + "policy_hello": "world!", + "content": {"foo": "microservice3", "foo_updated": "2018-01-30T13:25:33.222Z"}, + "riskType": "1712_ETE", + "guard": "False", + "version": "0.0.1", + "location": "Central", + "policy_updated_ts": "2018-02-19T15:09:55.217Z", + "updated_policy_id": "DCAE_alex.Config_MS_alex_microservice", + "policy_updated_to_ver": "132", + "priority": "4", + "policy_updated_from_ver": "131", + "templateVersion": "2", + "uuid": "5e87d7c5-0daf-4b6b-ab92-5365cf5db1ef", + }, + "property": None, + "type": "JSON", + "policyVersion": "132", + }, + { + "policyName": "DCAE_alex.Config_db_client_policy_id_value.133.xml", + "policyConfigMessage": "Config Retrieved! ", + "responseAttributes": {}, + "policyConfigStatus": "CONFIG_RETRIEVED", + "matchingConditions": {"ONAPName": "DCAE", "Name": "DCAE", "ConfigName": "alex_config_name"}, + "config": { + "db_client_ts": "2017-11-21T12:12:13.696Z", + "db_client": "ipsum", + "policy_hello": "world!", + "policy_updated_from_ver": "132", + "updated_policy_id": "DCAE_alex.Config_db_client_policy_id_value", + "policy_updated_ts": "2018-02-19T15:09:55.812Z", + "policy_updated_to_ver": "133", + }, + "property": None, + "type": "JSON", + "policyVersion": "133", + }, + ], + "event": { + "action": "gathered", + "timestamp": "2018-02-19 15:36:44.877380", + "update_id": "bb73c20a-5ff8-450f-8223-da6720ade267", + "policies_count": 2, + }, + }, + } + + assert cbsclient.get("service_component_all/{0}".format(TEST_NAME)).json == withstuff + + assert cbsclient.get("service_component_all/test_resolve_scn").json == {"config": expected_config} + + res = cbsclient.get("/service_component_all/{0}".format(TEST_NAME)) assert json.loads(res.data) == withstuff assert res.json == withstuff assert res.status_code == 200 assert "x-onap-requestid" in res.headers - res = cbsclient.get('/service_component_all/test_resolve_scn') + res = cbsclient.get("/service_component_all/test_resolve_scn") assert res.status_code == 200 assert res.json == {"config": expected_config} - res = cbsclient.get('/service_component_all/scn_NOTexists') + res = cbsclient.get("/service_component_all/scn_NOTexists") assert res.status_code == 404 assert "x-onap-requestid" in res.headers diff --git a/tests/test_client.py b/tests/test_client.py index 96c3467..1e07566 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -15,13 +15,14 @@ # ============LICENSE_END========================================================= # -from config_binding_service import client +from config_binding_service import client, exceptions import pytest # pytest doesnt support objects in conftest class FakeReq(object): """used to fake the logging params""" + def __init__(self): self.path = "/unittest in {0}".format(__name__) self.host = "localhost" @@ -39,168 +40,269 @@ class FakeConnexion(object): def test_consul_get_all_as_transaction(monkeypatch, monkeyed_requests_put): """tests _consul_get_all_as_transaction""" - monkeypatch.setattr('requests.put', monkeyed_requests_put) + monkeypatch.setattr("requests.put", monkeyed_requests_put) allk = client._consul_get_all_as_transaction( - "test_service_component_name.unknown.unknown.unknown.dcae.onap.org", FakeReq(), "unit test xer") + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org", FakeReq(), "unit test xer" + ) assert allk == { - 'test_service_component_name.unknown.unknown.unknown.dcae.onap.org': {'my': 'amazing config'}, - 'test_service_component_name.unknown.unknown.unknown.dcae.onap.org:dti': {'my': 'dti'}, - 'test_service_component_name.unknown.unknown.unknown.dcae.onap.org:dmaap': {'foo': 'bar'}, - 'test_service_component_name.unknown.unknown.unknown.dcae.onap.org:policies/event': {'action': 'gathered', 'timestamp': '2018-02-19 15:36:44.877380', 'update_id': 'bb73c20a-5ff8-450f-8223-da6720ade267', 'policies_count': 2}, - 'test_service_component_name.unknown.unknown.unknown.dcae.onap.org:policies/items/DCAE_alex.Config_MS_alex_microservice': {'policyName': 'DCAE_alex.Config_MS_alex_microservice.132.xml', 'policyConfigMessage': 'Config Retrieved! ', 'responseAttributes': {}, 'policyConfigStatus': 'CONFIG_RETRIEVED', 'matchingConditions': {'ONAPName': 'DCAE', 'Name': 'DCAE', 'ConfigName': 'alex_config_name'}, 'config': {'policyScope': 'alex_policy_scope', 'configName': 'alex_config_name', 'description': 'test DCAE policy-handler', 'service': 'alex_service', 'policyName': 'alex_policy_name', 'riskLevel': '3', 'key1': 'value1', 'policy_hello': 'world!', 'content': {'foo': 'microservice3', 'foo_updated': '2018-01-30T13:25:33.222Z'}, 'riskType': '1712_ETE', 'guard': 'False', 'version': '0.0.1', 'location': 'Central', 'policy_updated_ts': '2018-02-19T15:09:55.217Z', 'updated_policy_id': 'DCAE_alex.Config_MS_alex_microservice', 'policy_updated_to_ver': '132', 'priority': '4', 'policy_updated_from_ver': '131', 'templateVersion': '2', 'uuid': '5e87d7c5-0daf-4b6b-ab92-5365cf5db1ef'}, 'property': None, 'type': 'JSON', 'policyVersion': '132'}, - 'test_service_component_name.unknown.unknown.unknown.dcae.onap.org:policies/items/DCAE_alex.Config_db_client_policy_id_value': {'policyName': 'DCAE_alex.Config_db_client_policy_id_value.133.xml', 'policyConfigMessage': 'Config Retrieved! ', 'responseAttributes': {}, 'policyConfigStatus': 'CONFIG_RETRIEVED', 'matchingConditions': {'ONAPName': 'DCAE', 'Name': 'DCAE', 'ConfigName': 'alex_config_name'}, 'config': {'db_client_ts': '2017-11-21T12:12:13.696Z', 'db_client': 'ipsum', 'policy_hello': 'world!', 'policy_updated_from_ver': '132', 'updated_policy_id': 'DCAE_alex.Config_db_client_policy_id_value', 'policy_updated_ts': '2018-02-19T15:09:55.812Z', 'policy_updated_to_ver': '133'}, 'property': None, 'type': 'JSON', 'policyVersion': '133'}, - 'test_service_component_name.unknown.unknown.unknown.dcae.onap.org:rels': ['my.amazing.relationship'] + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org": {"my": "amazing config"}, + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org:dti": {"my": "dti"}, + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org:dmaap": {"foo": "bar"}, + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org:policies/event": { + "action": "gathered", + "timestamp": "2018-02-19 15:36:44.877380", + "update_id": "bb73c20a-5ff8-450f-8223-da6720ade267", + "policies_count": 2, + }, + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org:policies/items/DCAE_alex.Config_MS_alex_microservice": { + "policyName": "DCAE_alex.Config_MS_alex_microservice.132.xml", + "policyConfigMessage": "Config Retrieved! ", + "responseAttributes": {}, + "policyConfigStatus": "CONFIG_RETRIEVED", + "matchingConditions": {"ONAPName": "DCAE", "Name": "DCAE", "ConfigName": "alex_config_name"}, + "config": { + "policyScope": "alex_policy_scope", + "configName": "alex_config_name", + "description": "test DCAE policy-handler", + "service": "alex_service", + "policyName": "alex_policy_name", + "riskLevel": "3", + "key1": "value1", + "policy_hello": "world!", + "content": {"foo": "microservice3", "foo_updated": "2018-01-30T13:25:33.222Z"}, + "riskType": "1712_ETE", + "guard": "False", + "version": "0.0.1", + "location": "Central", + "policy_updated_ts": "2018-02-19T15:09:55.217Z", + "updated_policy_id": "DCAE_alex.Config_MS_alex_microservice", + "policy_updated_to_ver": "132", + "priority": "4", + "policy_updated_from_ver": "131", + "templateVersion": "2", + "uuid": "5e87d7c5-0daf-4b6b-ab92-5365cf5db1ef", + }, + "property": None, + "type": "JSON", + "policyVersion": "132", + }, + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org:policies/items/DCAE_alex.Config_db_client_policy_id_value": { + "policyName": "DCAE_alex.Config_db_client_policy_id_value.133.xml", + "policyConfigMessage": "Config Retrieved! ", + "responseAttributes": {}, + "policyConfigStatus": "CONFIG_RETRIEVED", + "matchingConditions": {"ONAPName": "DCAE", "Name": "DCAE", "ConfigName": "alex_config_name"}, + "config": { + "db_client_ts": "2017-11-21T12:12:13.696Z", + "db_client": "ipsum", + "policy_hello": "world!", + "policy_updated_from_ver": "132", + "updated_policy_id": "DCAE_alex.Config_db_client_policy_id_value", + "policy_updated_ts": "2018-02-19T15:09:55.812Z", + "policy_updated_to_ver": "133", + }, + "property": None, + "type": "JSON", + "policyVersion": "133", + }, + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org:rels": ["my.amazing.relationship"], } allk = client._consul_get_all_as_transaction("cbs_test_messed_up", FakeReq(), "unit test xer") - assert allk == {'cbs_test_messed_up': {'foo': 'bar'}, - 'cbs_test_messed_up:badkey': 'INVALID JSON'} + assert allk == {"cbs_test_messed_up": {"foo": "bar"}, "cbs_test_messed_up:badkey": "INVALID JSON"} def test_get_config_rels_dmaap(monkeypatch, monkeyed_requests_put): - monkeypatch.setattr('requests.put', monkeyed_requests_put) - assert ({"foo3": "bar3"}, ["foo"], {"foo4": "bar4"}) == client._get_config_rels_dmaap("scn_exists", FakeReq(), "unit test xer") + monkeypatch.setattr("requests.put", monkeyed_requests_put) + assert ({"foo3": "bar3"}, ["foo"], {"foo4": "bar4"}) == client._get_config_rels_dmaap( + "scn_exists", FakeReq(), "unit test xer" + ) assert ({"foo5": "bar5"}, [], {}) == client._get_config_rels_dmaap("scn_exists_nord", FakeReq(), "unit test xer") def test_bad_config_http(): - test_config = {'yeahhhhh': "{{}}"} + test_config = {"yeahhhhh": "{{}}"} test_rels = ["testing_bravo.somedomain.com"] - assert {'yeahhhhh': []} == client.resolve_override(test_config, test_rels) + assert {"yeahhhhh": []} == client.resolve_override(test_config, test_rels) def test_bad_config_dmaap(): - test_config = {'darkness': "<<>>"} + test_config = {"darkness": "<<>>"} test_dmaap = {"WHO?": "darkness"} - assert {'darkness': {}} == client.resolve_override(test_config, test_dmaap) + assert {"darkness": {}} == client.resolve_override(test_config, test_dmaap) def test_config_with_list(monkeypatch, monkeyed_get_connection_info_from_consul): - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', - monkeyed_get_connection_info_from_consul) - test_config_1 = {"dcae_target_type": [ - "vhss-ems", "pcrf-oam"], "downstream-laika": "{{ laika }}", "some-param": "Lorem ipsum dolor sit amet"} + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) + test_config_1 = { + "dcae_target_type": ["vhss-ems", "pcrf-oam"], + "downstream-laika": "{{ laika }}", + "some-param": "Lorem ipsum dolor sit amet", + } test_rels_1 = ["3df5292249ae4a949f173063617cea8d_docker-snmp-polling-firstnet-m"] test_bind_1 = client.resolve_override(test_config_1, test_rels_1, {}) - assert(test_bind_1 == {'dcae_target_type': [ - 'vhss-ems', 'pcrf-oam'], 'downstream-laika': [], 'some-param': 'Lorem ipsum dolor sit amet'}) + assert test_bind_1 == { + "dcae_target_type": ["vhss-ems", "pcrf-oam"], + "downstream-laika": [], + "some-param": "Lorem ipsum dolor sit amet", + } test_config_2 = {"foo": ["{{cdap}}", "notouching", "<<yo>>"]} test_rels_2 = ["cdap"] test_dmaap_2 = {"yo": "im here"} test_bind_2 = client.resolve_override(test_config_2, test_rels_2, test_dmaap_2) - assert(test_bind_2 == {"foo": [['666.666.666.666:666'], "notouching", "im here"]}) + assert test_bind_2 == {"foo": [["666.666.666.666:666"], "notouching", "im here"]} def test_cdap(monkeypatch, monkeyed_get_connection_info_from_consul): # user override to test CDAP functionality - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', - monkeyed_get_connection_info_from_consul) - test_rels = ["testing_alpha.somedomain.com", "testing_bravo.somedomain.com", - "testing_charlie.somedomain.com", "testing_charlie.somedomain.com", "cdap"] - test_config = {"streams_publishes": "{{alpha}}", - # should be dumped - "services_calls": [{"somekey": "{{charlie}}"}], - "cdap_to_manage": {'some_nested_thing': "{{cdap}}"}} # no dumps + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) + test_rels = [ + "testing_alpha.somedomain.com", + "testing_bravo.somedomain.com", + "testing_charlie.somedomain.com", + "testing_charlie.somedomain.com", + "cdap", + ] + test_config = { + "streams_publishes": "{{alpha}}", + # should be dumped + "services_calls": [{"somekey": "{{charlie}}"}], + "cdap_to_manage": {"some_nested_thing": "{{cdap}}"}, + } # no dumps test_bind_1 = client.resolve_override(test_config, test_rels) - assert test_bind_1 == {'services_calls': [{"somekey": ["5.5.5.5:555", "5.5.5.5:555"]}], 'streams_publishes': [ - "6.6.6.6:666"], 'cdap_to_manage': {'some_nested_thing': ['666.666.666.666:666']}} - assert test_bind_1['services_calls'] == [{"somekey": ["5.5.5.5:555", "5.5.5.5:555"]}] - assert test_bind_1['streams_publishes'] == ["6.6.6.6:666"] + assert test_bind_1 == { + "services_calls": [{"somekey": ["5.5.5.5:555", "5.5.5.5:555"]}], + "streams_publishes": ["6.6.6.6:666"], + "cdap_to_manage": {"some_nested_thing": ["666.666.666.666:666"]}, + } + assert test_bind_1["services_calls"] == [{"somekey": ["5.5.5.5:555", "5.5.5.5:555"]}] + assert test_bind_1["streams_publishes"] == ["6.6.6.6:666"] def test_multiple_service_types(monkeypatch, monkeyed_get_connection_info_from_consul): # test {{x,y,z}} - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', - monkeyed_get_connection_info_from_consul) + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) # test 1: they all resovle - test_rels = ["testing_alpha.somedomain.com", "testing_bravo.somedomain.com", - "testing_charlie.somedomain.com", "testing_charlie.somedomain.com"] + test_rels = [ + "testing_alpha.somedomain.com", + "testing_bravo.somedomain.com", + "testing_charlie.somedomain.com", + "testing_charlie.somedomain.com", + ] config = {"ALL YOUR SERVICE BELONG TO US": "{{alpha,bravo,charlie}}"} test_bind_1 = client.resolve_override(config, test_rels) - assert(test_bind_1 == {"ALL YOUR SERVICE BELONG TO US": [ - '6.6.6.6:666', '7.7.7.7:777', '5.5.5.5:555', '5.5.5.5:555']}) + assert test_bind_1 == {"ALL YOUR SERVICE BELONG TO US": ["6.6.6.6:666", "7.7.7.7:777", "5.5.5.5:555", "5.5.5.5:555"]} # test 2: two resolve, one is missing from rels key config2 = {"two there one not exist": "{{alpha,bravo,notexist}}"} test_bind_2 = client.resolve_override(config2, test_rels) - assert(test_bind_2 == {"two there one not exist": [ - '6.6.6.6:666', '7.7.7.7:777']}) + assert test_bind_2 == {"two there one not exist": ["6.6.6.6:666", "7.7.7.7:777"]} # test 3: two resolve, one is in rels key but not registered config3 = {"two there one unregistered": "{{alpha,bravo,unregistered}}"} - test_rels3 = ["testing_alpha.somedomain.com", - "testing_bravo.somedomain.com", "unregistered.somedomain.com"] + test_rels3 = ["testing_alpha.somedomain.com", "testing_bravo.somedomain.com", "unregistered.somedomain.com"] test_bind_3 = client.resolve_override(config3, test_rels3) - assert(test_bind_3 == {"two there one unregistered": [ - '6.6.6.6:666', '7.7.7.7:777']}) + assert test_bind_3 == {"two there one unregistered": ["6.6.6.6:666", "7.7.7.7:777"]} def test_dmaap(monkeypatch): # test resolving dmaap key config = {"TODAY IS YOUR LUCKY DAY": "<<XXX>>"} # does not match - test_bind = client.resolve_override( - config, dmaap={"XX": "ABSOLVEME"}) # XX != XXX - assert(test_bind == {"TODAY IS YOUR LUCKY DAY": {}}) + test_bind = client.resolve_override(config, dmaap={"XX": "ABSOLVEME"}) # XX != XXX + assert test_bind == {"TODAY IS YOUR LUCKY DAY": {}} # matches test_bind_2 = client.resolve_override(config, dmaap={"XXX": "ABSOLVEME"}) - assert(test_bind_2 == {"TODAY IS YOUR LUCKY DAY": "ABSOLVEME"}) + assert test_bind_2 == {"TODAY IS YOUR LUCKY DAY": "ABSOLVEME"} def test_config(monkeypatch, monkeyed_get_connection_info_from_consul): # test config override - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', - monkeyed_get_connection_info_from_consul) - test_config = {"autoderegisterafter": "10m", "cdap_to_manage": { - 'some_nested_thing': "{{cdap}}"}, "bindingttw": 5, "hcinterval": "5s"} + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) + test_config = { + "autoderegisterafter": "10m", + "cdap_to_manage": {"some_nested_thing": "{{cdap}}"}, + "bindingttw": 5, + "hcinterval": "5s", + } test_rels = ["cdap"] test_bind_1 = client.resolve_override(test_config, test_rels) - assert test_bind_1 == {'autoderegisterafter': '10m', 'cdap_to_manage': { - 'some_nested_thing': ['666.666.666.666:666']}, 'bindingttw': 5, 'hcinterval': '5s'} + assert test_bind_1 == { + "autoderegisterafter": "10m", + "cdap_to_manage": {"some_nested_thing": ["666.666.666.666:666"]}, + "bindingttw": 5, + "hcinterval": "5s", + } def test_non_existent(monkeypatch, monkeyed_get_connection_info_from_consul): # test a valid config-rels but the key is not in Consul - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', - monkeyed_get_connection_info_from_consul) + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) test_config = {"you shall not be fufilled": "{{nonexistent_hope}}"} # hopefully not registered in Consul.. test_rels = ["nonexistent_hope.rework-central.ecomp.somedomain.com"] test_bind_1 = client.resolve_override(test_config, test_rels, {}) - assert(test_bind_1 == {"you shall not be fufilled": []}) + assert test_bind_1 == {"you shall not be fufilled": []} def test_broker_redirect(monkeypatch, monkeyed_get_connection_info_from_consul): # test the broker redirect - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', - monkeyed_get_connection_info_from_consul) + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) test_config = {"gimmie_dat_cdap": "{{cdap_serv}}"} test_rels = ["cdap_serv.dcae.ecomp.somedomain.com"] - assert {"gimmie_dat_cdap": ['http://1.1.1.1:444/application/cdap_serv.dcae.ecomp.somedomain.com'] - } == client.resolve_override(test_config, test_rels) + assert { + "gimmie_dat_cdap": ["http://1.1.1.1:444/application/cdap_serv.dcae.ecomp.somedomain.com"] + } == client.resolve_override(test_config, test_rels) def test_both(monkeypatch, monkeyed_get_connection_info_from_consul, expected_config): # test rels and http - monkeypatch.setattr('config_binding_service.client._get_connection_info_from_consul', - monkeyed_get_connection_info_from_consul) - test_rels = ["testing_alpha.somedomain.com", "testing_bravo.somedomain.com", - "testing_charlie.somedomain.com", "testing_charlie.somedomain.com"] + monkeypatch.setattr( + "config_binding_service.client._get_connection_info_from_consul", monkeyed_get_connection_info_from_consul + ) + test_rels = [ + "testing_alpha.somedomain.com", + "testing_bravo.somedomain.com", + "testing_charlie.somedomain.com", + "testing_charlie.somedomain.com", + ] test_dmaap = {"WHO?": "darkness"} - config = {"deep": {"ALL YOUR SERVICE BELONG TO US": "{{alpha,bravo,charlie}}"}, - "doubledeep": {"sodeep": {"hello": "<<WHO?>>"}}} + config = { + "deep": {"ALL YOUR SERVICE BELONG TO US": "{{alpha,bravo,charlie}}"}, + "doubledeep": {"sodeep": {"hello": "<<WHO?>>"}}, + } test_bind_1 = client.resolve_override(config, test_rels, test_dmaap) - assert(test_bind_1 == expected_config) + assert test_bind_1 == expected_config def test_failures(monkeypatch, monkeyed_requests_put, monkeyed_requests_get): - monkeypatch.setattr('requests.put', monkeyed_requests_put) - monkeypatch.setattr('requests.get', monkeyed_requests_get) - monkeypatch.setattr('connexion.request', FakeConnexion({"x-onap-requestid": 123456789}, "/service_component", "mytestingmachine", "myremoteclient")) - assert(client.resolve("scn_exists", FakeReq(), "unit test xer") == {"foo3": "bar3"}) - with pytest.raises(client.CantGetConfig): + monkeypatch.setattr("requests.put", monkeyed_requests_put) + monkeypatch.setattr("requests.get", monkeyed_requests_get) + monkeypatch.setattr( + "connexion.request", + FakeConnexion({"x-onap-requestid": 123456789}, "/service_component", "mytestingmachine", "myremoteclient"), + ) + assert client.resolve("scn_exists", FakeReq(), "unit test xer") == {"foo3": "bar3"} + with pytest.raises(exceptions.CantGetConfig): client.resolve("scn_NOTexists", FakeReq(), "unit test xer") - with pytest.raises(client.CantGetConfig): - client.get_key("nokeyforyou", "test_service_component_name.unknown.unknown.unknown.dcae.onap.org", FakeReq(), "unit test xer") + with pytest.raises(exceptions.CantGetConfig): + client.get_key( + "nokeyforyou", + "test_service_component_name.unknown.unknown.unknown.dcae.onap.org", + FakeReq(), + "unit test xer", + ) diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..f131f6e --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,41 @@ +# ============LICENSE_START======================================================= +# Copyright (c) 2017-2019 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# 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. +import pytest +from config_binding_service import utils, exceptions +import os + + +def test_https_flags(monkeypatch): + """test getting https flags""" + key_loc, cert_loc = utils.get_https_envs() + assert key_loc is None and cert_loc is None + + monkeypatch.setenv("USE_HTTPS", "1") + with pytest.raises(exceptions.BadHTTPSEnvs): + utils.get_https_envs() + + cur_dir = os.path.dirname(os.path.realpath(__file__)) + monkeypatch.setenv("HTTPS_KEY_PATH", "{0}/fixtures/test_k.key".format(cur_dir)) + monkeypatch.setenv("HTTPS_CERT_PATH", "{0}/fixtures/NONEXISTENT".format(cur_dir)) + with pytest.raises(exceptions.BadHTTPSEnvs): + utils.get_https_envs() + + monkeypatch.setenv("HTTPS_CERT_PATH", "{0}/fixtures/test_c.crt".format(cur_dir)) + key_loc, cert_loc = utils.get_https_envs() + assert key_loc is not None + assert cert_loc is not None diff --git a/version.properties b/version.properties index c0f75b6..6c69733 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ major=2 -minor=4 +minor=5 patch=0 base_version=${major}.${minor}.${patch} release_version=${base_version} |