diff options
Diffstat (limited to 'config_binding_service')
-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 |
5 files changed, 124 insertions, 54 deletions
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 |