diff options
author | Tommy Carpenter <tommy@research.att.com> | 2018-02-15 13:14:33 -0500 |
---|---|---|
committer | Tommy Carpenter <tommy@research.att.com> | 2018-02-15 14:50:29 -0500 |
commit | 857c41af07fd88b50c2e960071611f425268e486 (patch) | |
tree | 6c70be46b3f7e04e6df5b56369a068814a6f5d51 | |
parent | e2f8b1019b0c64747d2dca8801f1c0c9b2fbb12a (diff) |
Add DTI and Policy to ONAP CBS
Issue-ID: DCAEGEN2-341
Change-Id: Iead3b6568ec379988b840b5a01c7744c29b5fcf4
Signed-off-by: Tommy Carpenter <tommy@research.att.com>
-rw-r--r-- | Changelog.md | 17 | ||||
-rw-r--r-- | Dockerfile | 1 | ||||
-rw-r--r-- | README.md | 31 | ||||
-rwxr-xr-x | bin/run.py | 7 | ||||
-rw-r--r-- | config_binding_service/__init__.py | 12 | ||||
-rw-r--r-- | config_binding_service/client.py | 65 | ||||
-rw-r--r-- | config_binding_service/controller.py | 40 | ||||
-rw-r--r-- | config_binding_service/swagger/swagger.yaml | 44 | ||||
-rw-r--r-- | pom.xml | 4 | ||||
-rw-r--r-- | setup.py | 18 | ||||
-rw-r--r-- | tests/test_binding.py | 72 |
11 files changed, 241 insertions, 70 deletions
diff --git a/Changelog.md b/Changelog.md index 9985855..4a8ea05 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,13 +1,20 @@ # Change Log All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) +The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.3.0] +* Sync ONAP with Internal CBS +* Add tests (Currently 62%) +* Update docker python version to 3.6 +* Move installation of reqs into Docker container + ## [1.2.0] -* Remove waterfalled CONSUL_HOST +* Remove waterfalled CONSUL_HOST * Add ONAP liscenses * Remove references to specific telco and it's IPs in tests +* [Internal version conflict]: Add dti and policies endpoints ## [1.1.0] * Add a healthcheck endpoint @@ -15,11 +22,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [1.0.1] * Fix {{}} to resolve to [] instead of whatever is in rels key -* Remove all impure tests. All tests are now unit tests. +* Remove all impure tests. All tests are now unit tests. ## [1.0.0] -* GLORIOUS CHANGE! At some point, CASK fixed a bug where if you sent a configuration JSON to CDAP that contained a value that was not a string, it would blow up. This allows me to remove the endpoint specific to CDAP components so the same endpoint is now used for Docker and CDAP. -* Props to Terry Troutman for helping me discover this. +* GLORIOUS CHANGE! At some point, CASK fixed a bug where if you sent a configuration JSON to CDAP that contained a value that was not a string, it would blow up. This allows me to remove the endpoint specific to CDAP components so the same endpoint is now used for Docker and CDAP. +* Props to Terry Troutman for helping me discover this. * Removes some impure tests. Still some impurity there ## [0.9.0] @@ -7,6 +7,7 @@ ADD . /tmp RUN pip install --upgrade pip #do the install WORKDIR /tmp +RUN pip install -r requirements.txt RUN pip install -e . EXPOSE 10000 @@ -1,29 +1,35 @@ # config_binding_service -# Interface Diagram +# Interface Diagram This repo is the thing in red: ![Alt text](doc/cbs_diagram.png?raw=true) -# Overview +# Overview DCAE has a "templating language" built into components' configurations, as explained further below. The orchestrator populates one/two keys (depending on the blueprint) into Consul that are used to *bind* component configurations config, a "rels key" and a "dmaap key". -If component A wants to connect to a component of type B, then A's rels key holds what specific service component name of B that A should connect to over direct HTTP. -Service component name here means the full name that the component of type B is registered under in Consul (there can be multiple components of type B registered in Consul). -The CBS (config binding service) then pulls down that rels key, fetches the connection information about that B (IP:Port), and replaces it into A's config. +If component A wants to connect to a component of type B, then A's rels key holds what specific service component name of B that A should connect to over direct HTTP. +Service component name here means the full name that the component of type B is registered under in Consul (there can be multiple components of type B registered in Consul). +The CBS (config binding service) then pulls down that rels key, fetches the connection information about that B (IP:Port), and replaces it into A's config. There is also a "dmaap key", which is the same concept, except what gets injected is a JSON of DMaaP connection information instead of an IP:Port. +In addition, this service provides the capability to retrieve either the DTI events (not history) or the policies for a given service_component. + # Usage hit `url_of_this/service_component/service_component_name` and you are returned your bound config. +hit `url_of_this/dtievents/service_component_name` and you are returned the dti events for your service_component. + +hit `url_of_this/policies/service_component_name` and you are returned the policies for your service_component. + (Note: there is also a backdoor in the `client` module that allows you to pass in a direct JSON and a direct rels, but this isn't exposed via the HTTP API as of now) # Assumptions 1. `CONSUL_HOST` is set as an environmental variable where this binding service is run. If it is not, it defaults to the Rework Consul which is probably not what you want. 2. `service_component_name` is in consul as a key and holds the config -3. `service_component_name:rel` is in consul as a key *if* you are expecting a direct HTTP resolution, and holds the service component names of connections. -4. `service_component_name:dmaap` is in consul *if* you are expecting a DMaaP resolution, and holds the components DMaaP information. +3. `service_component_name:rel` is in consul as a key *if* you are expecting a direct HTTP resolution, and holds the service component names of connections. +4. `service_component_name:dmaap` is in consul *if* you are expecting a DMaaP resolution, and holds the components DMaaP information. # Templating Language The CBS tries to resolve a component's configuration with a templating language. We have two templating languages embedded in our component's configuration (`{{...}}` and `<<...>>`). There are two because the CBS has to be able to distinguish between a rels-key-resolve and a dmaap-key-resolve. That is, if component X is trying to bind their component, and they want to talk to Y, someone has to tell the CBS whether they are trying to talk via IP:port or a feed. @@ -39,7 +45,14 @@ X's configuration: } ``` -# Tests And Test Coverage +# Testing +You need tox: ``` -tox -c tox-local.ini +pip install tox ``` +Then from the root dir, *not in a virtual env*, just run: +``` +tox +``` +You may have to alter the tox.ini for the python envs you wish to test with. + @@ -1,7 +1,9 @@ #!/usr/bin/env python3 +# ============LICENSE_START======================================================= +# org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 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. @@ -18,7 +20,6 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. - import connexion import sys from config_binding_service import get_logger @@ -29,6 +30,6 @@ if __name__ == '__main__': try: app = connexion.App(__name__, specification_dir='../config_binding_service/swagger/') app.add_api('swagger.yaml', arguments={'title': 'Config Binding Service'}) - app.run(host='0.0.0.0', port=10000, debug=False) + app.run(host='0.0.0.0', port=10000, debug=False) except Exception as e: _logger.error("Fatal error. Could not start webserver due to: {0}".format(e)) diff --git a/config_binding_service/__init__.py b/config_binding_service/__init__.py index 51d3246..c50f47f 100644 --- a/config_binding_service/__init__.py +++ b/config_binding_service/__init__.py @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 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 -# +# +# 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. @@ -17,6 +17,7 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + import os import logging @@ -45,8 +46,9 @@ def get_consul_uri(): if "CONSUL_HOST" in os.environ: # WARNING! TODO! Currently the env file does not include the port. # But some other people think that the port should be a part of that. - # For now, I'm hardcoding 8500 until this gets resolved. + # For now, I'm hardcoding 8500 until this gets resolved. return "http://{0}:{1}".format(os.environ["CONSUL_HOST"], 8500) else: raise BadEnviornmentENVNotFound("CONSUL_HOST") + diff --git a/config_binding_service/client.py b/config_binding_service/client.py index 02354ee..6b53996 100644 --- a/config_binding_service/client.py +++ b/config_binding_service/client.py @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 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 -# +# +# 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. @@ -17,6 +17,7 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + import re import requests import copy @@ -44,7 +45,7 @@ class CantGetConfig(Exception): ### def _consul_get_key(key): """ - Try to fetch a key from Consul. + Try to fetch a key from Consul. No error checking here, let caller deal with it """ _logger.info("Fetching {0}".format(key)) @@ -78,7 +79,7 @@ def _get_connection_info_from_consul(service_component_name): TODO: currently assumes there is only one service TODO: WARNING: FIXTHIS: CALLINTHENATIONALARMY: - This tries to determine that a service_component_name is a cdap application by inspecting service_component_name and name munging. However, this would force all CDAP applications to have cdap_app in their name. A much better way to do this is to do some kind of catalog_lookup here, OR MAYBE change this API so that the component_type is passed in somehow. THis is a gaping TODO. + This tries to determine that a service_component_name is a cdap application by inspecting service_component_name and name munging. However, this would force all CDAP applications to have cdap_app in their name. A much better way to do this is to do some kind of catalog_lookup here, OR MAYBE change this API so that the component_type is passed in somehow. THis is a gaping TODO. """ _logger.info("Retrieving connection information for {0}".format(service_component_name)) res = requests.get("{0}/v1/catalog/service/{1}".format(CONSUL, service_component_name)) @@ -87,7 +88,7 @@ def _get_connection_info_from_consul(service_component_name): if services == []: _logger.info("Warning: config and rels keys were both valid, but there is no component named {0} registered in Consul!".format(service_component_name)) return None #later will get filtered out - else: + else: ip = services[0]["ServiceAddress"] port = services[0]["ServicePort"] if "cdap_app" in service_component_name: @@ -100,11 +101,11 @@ def _get_connection_info_from_consul(service_component_name): return { key: details[key] for key in ["connectionurl", "serviceendpoints"] } else: return "{0}:{1}".format(ip, port) - + def _replace_rels_template(rels, template_identifier): """ The magic. Replaces a template identifier {{...}} with the entrie(s) from the rels keys - NOTE: There was a discussion over whether the CBS should treat {{}} as invalid. Mike asked that + NOTE: There was a discussion over whether the CBS should treat {{}} as invalid. Mike asked that it resolve to the empty list. So, it does resolve it to empty list. """ returnl = [] @@ -125,7 +126,7 @@ def _replace_value(v, rels, dmaap): """ Takes a value v that was some value in the templatized configuration, determines whether it needs replacement (either {{}} or <<>>), and if so, replaces it. Otherwise just returns v - + implementation notes: - the split below sees if we have v = x,y,z... so we can support {{x,y,z,....}} - the lambda is because we can't fold operators in Python, wanted fold(+, L) where + when applied to lists in python is list concatenation @@ -150,16 +151,24 @@ def _replace_value(v, rels, dmaap): return v #was not a match or was not a string, return value as is def _recurse(config, rels, dmaap): - for key in config: - v = config[key] - if isinstance(v, list): - replacement = [_recurse(item, rels, dmaap) for item in v] - elif isinstance(v,dict): - replacement = _recurse(v, rels, dmaap) - else: - replacement = _replace_value(config[key], rels, dmaap) - config[key] = replacement - return config + """ + Recurse throug a configuration, or recursively a sub elemebt 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 none of the above, just return the item. + """ + if isinstance(config, list): + return [_recurse(item, rels, dmaap) for item in config] + elif isinstance(config,dict): + for key in config: + config[key] = _recurse(config[key], rels, dmaap) + return config + elif isinstance(config, six.string_types): + return _replace_value(config, rels, dmaap) + else: + #not a dict, not a list, not a string, nothing to do. + return config ######### # PUBLIC API @@ -179,3 +188,19 @@ def resolve_override(config, rels=[], dmaap={}): """ #use deepcopy to make sure that config is not touched return _recurse(copy.deepcopy(config), rels, dmaap) + +def resolve_DTI(service_component_name): + try: + config = _consul_get_key("{}:dti".format(service_component_name)) + except requests.exceptions.HTTPError as e: + #might be a 404, or could be not even able to reach consul (503?), bubble up the requests error + raise CantGetConfig(e.response.status_code, e.response.text) + return config + +def resolve_policies(service_component_name): + try: + config = _consul_get_key("{}:policies".format(service_component_name)) + except requests.exceptions.HTTPError as e: + #might be a 404, or could be not even able to reach consul (503?), bubble up the requests error + raise CantGetConfig(e.response.status_code, e.response.text) + return config diff --git a/config_binding_service/controller.py b/config_binding_service/controller.py index a74d60f..ec6f05e 100644 --- a/config_binding_service/controller.py +++ b/config_binding_service/controller.py @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 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 -# +# +# 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. @@ -17,16 +17,46 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + from config_binding_service import client, get_consul_uri, get_logger import requests from flask import request, Response import json +_logger = get_logger(__name__) + +def dtievents(service_component_name): + try: + dti = client.resolve_DTI(service_component_name) + return Response(response=json.dumps(dti), + status=200, + mimetype="application/json") + except client.CantGetConfig as e: + return Response(status=e.code, + response=e.response) + except Exception as e: + _logger.error(e) + return Response(response="Unknown error: please report", + status=500) + +def policies(service_component_name): + try: + dti = client.resolve_policies(service_component_name) + return Response(response=json.dumps(dti), + status=200, + mimetype="application/json") + except client.CantGetConfig as e: + return Response(status=e.code, + response=e.response) + except Exception as e: + _logger.error(e) + return Response(response="Unknown error: please report", + status=500) def bind_config_for_scn(service_component_name): try: bound = client.resolve(service_component_name) return Response(response=json.dumps(bound), - status=200, + status=200, mimetype="application/json") except client.CantGetConfig as e: return Response(status=e.code, diff --git a/config_binding_service/swagger/swagger.yaml b/config_binding_service/swagger/swagger.yaml index ecedb75..31fc42a 100644 --- a/config_binding_service/swagger/swagger.yaml +++ b/config_binding_service/swagger/swagger.yaml @@ -1,5 +1,7 @@ +# ============LICENSE_START======================================================= +# org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 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. @@ -15,10 +17,12 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + + --- swagger: "2.0" info: - version: "1.0.0" + version: "1.3.0" title: "Config Binding Service" paths: /service_component/{service_component_name}: @@ -34,10 +38,44 @@ paths: responses: 200: description: OK; the bound config is returned as an object - schema: + schema: type: object 404: description: there is no configuration in Consul for this component + /dti/{service_component_name}: + parameters: + - name: "service_component_name" + in: "path" + description: "Service Component Name. service_component_name:dti must be a key in consul." + required: true + type: "string" + get: + description: "Returns as JSON the value for service_component_name:dti" + operationId: "config_binding_service.controller.dtievents" + responses: + 200: + description: OK; the KV value is returned as an object + schema: + type: object + 404: + description: there is no configuration in Consul for this component's DTI events + /policies/{service_component_name}: + parameters: + - name: "service_component_name" + in: "path" + description: "Service Component Name. service_component_name:policies must be a key in consul." + required: true + type: "string" + get: + description: "Returns as JSON the value for service_component_name:policies" + operationId: "config_binding_service.controller.policies" + responses: + 200: + description: OK; the KV value is returned as an object + schema: + type: object + 404: + description: there is no configuration in Consul for this component's policies /healthcheck: get: description: "This is the health check endpoint. If this returns a 200, the server is alive and consul can be reached. If not a 200, either dead, or no connection to consul" @@ -37,7 +37,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. <sonar.skip>false</sonar.skip> <sonar.sources>.</sonar.sources> <sonar.junit.reportspath>xunit-reports/xunit-result-configbinding.xml</sonar.junit.reportspath> - <!-- + <!-- <sonar.python.coverage.reportpath>coverage.xml</sonar.python.coverage.reportpath> see https://docs.sonarqube.org/display/plug/python+coverage+results+import ant pattern describing the path to coverage reports, relative to projects root. leave unset to use the default ("coverage-reports/coverage-*.xml"). @@ -111,7 +111,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. </execution> </executions> </plugin> - <!-- maven-install-plugin is called during "install" phase by default behavior. it tries to copy stuff under + <!-- maven-install-plugin is called during "install" phase by default behavior. it tries to copy stuff under target dir to ~/.m2. we do not need it --> <plugin> <groupId>org.apache.maven.plugins</groupId> @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 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 -# +# +# 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. @@ -17,24 +17,22 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. + import os from setuptools import setup, find_packages from pip.req import parse_requirements from pip.download import PipSession -import pip -pip.main(['install','-r','requirements.txt']) - setup( name='config_binding_service', - version='1.2.0', + version='1.3.0', packages=find_packages(), author = "Tommy Carpenter", - author_email = "tommy at research dot a t t dot com", + author_email = "tommy@research.att.com", description='Service to fetch and bind configurations', license = "", keywords = "", - url = "ONAP URL TBD", + url = "https://gerrit.onap.org/r/#/admin/projects/dcaegen2/platform/configbinding", zip_safe=False, scripts = ["bin/run.py"] ) diff --git a/tests/test_binding.py b/tests/test_binding.py index 3bbe5d9..60c0809 100644 --- a/tests/test_binding.py +++ b/tests/test_binding.py @@ -1,14 +1,14 @@ # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 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 -# +# +# 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. @@ -17,9 +17,12 @@ # ============LICENSE_END========================================================= # # ECOMP is a trademark and service mark of AT&T Intellectual Property. -from config_binding_service import client + +from config_binding_service import client, controller import pytest import json +from requests.exceptions import HTTPError, RequestException +from requests import Response def monkeyed_get_connection_info_from_consul(service_component_name): #shared monkeypatch. probably somewhat lazy because the function htis patches can be broken up. @@ -38,6 +41,46 @@ def monkeyed_get_connection_info_from_consul(service_component_name): broker_port = 444 return "http://{0}:{1}/application/{2}".format(broker_ip, broker_port, service_component_name) +class FakeResponse(): + def __init__(self, status_code, text): + self.text = text + self.status_code = status_code + +def monkeyed_consul_get_key(k): + if k == "dti_exists_test:dti": + return {"foo" : "bar"} + elif k == "dti_NOTexists_test:dti": + raise HTTPError(response = FakeResponse(text= "", status_code = 404)) + elif k == "policies_exists_test:policies": + return {"foo2" : "bar2"} + elif k == "policies_NOTexists_test:policies": + raise HTTPError(response = FakeResponse(text= "", status_code = 404)) + +def test_dti_policies(monkeypatch): + monkeypatch.setattr('config_binding_service.client._consul_get_key', monkeyed_consul_get_key) + + assert client.resolve_DTI("dti_exists_test") == {"foo" : "bar"} + with pytest.raises(client.CantGetConfig): + client.resolve_DTI("dti_NOTexists_test") + + R = controller.dtievents("dti_exists_test") + assert(json.loads(R.data) == {"foo" : "bar"}) + assert(R.status_code == 200) + + R = controller.dtievents("dti_NOTexists_test") + assert(R.status_code == 404) + + assert client.resolve_policies("policies_exists_test") == {"foo2" : "bar2"} + with pytest.raises(client.CantGetConfig): + client.resolve_policies("policies_NOTexists_test") + + R = controller.policies("policies_exists_test") + assert(json.loads(R.data) == {"foo2" : "bar2"}) + assert(R.status_code == 200) + + R = controller.policies("policies_NOTexists_test") + assert(R.status_code == 404) + def test_bad_config_http(): test_config = {'yeahhhhh' : "{{}}"} test_rels = ["testing_bravo.somedomain.com"] @@ -56,6 +99,19 @@ def test_config(monkeypatch): 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'} +def test_config_with_list(monkeypatch): + 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'}) + + 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"]}) + def test_non_existent(monkeypatch): #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) @@ -68,7 +124,7 @@ def test_cdap(monkeypatch): #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}}", + test_config = { "streams_publishes" : "{{alpha}}", "services_calls" : [{"somekey" : "{{charlie}}"}], #should be dumped "cdap_to_manage": {'some_nested_thing' : "{{cdap}}"} #no dumps } @@ -98,7 +154,7 @@ def test_multiple_service_types(monkeypatch): 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']}) - + #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"] @@ -114,7 +170,7 @@ def test_dmaap(monkeypatch): #matches test_bind_2 = client.resolve_override(config, dmaap={"XXX" : "ABSOLVEME"}) assert(test_bind_2 == {"TODAY IS YOUR LUCKY DAY" : "ABSOLVEME"}) - + def test_both(monkeypatch): #test rels and http |