From 1ce0314856018710ea5ed72485a7d6831170ba00 Mon Sep 17 00:00:00 2001 From: Johnathan Kupferer Date: Mon, 7 Aug 2023 22:27:34 -0400 Subject: [PATCH] Add handling of null values in dictionary lists --- operator/config.py | 21 ------------------ operator/poolboy.py | 34 +++++++++++++++++++++++++++++ operator/poolboy_templating.py | 2 +- test/unittest-poolboy_templating.py | 18 +++++++++++++++ 4 files changed, 53 insertions(+), 22 deletions(-) delete mode 100644 operator/config.py create mode 100644 operator/poolboy.py diff --git a/operator/config.py b/operator/config.py deleted file mode 100644 index 792c555..0000000 --- a/operator/config.py +++ /dev/null @@ -1,21 +0,0 @@ -import asyncio -import kubernetes_asyncio -import os - -manage_claims_interval = int(os.environ.get('MANAGE_CLAIMS_INTERVAL', 60)) -manage_handles_interval = int(os.environ.get('MANAGE_HANDLES_INTERVAL', 60)) -operator_domain = os.environ.get('OPERATOR_DOMAIN', 'poolboy.gpte.redhat.com') -operator_version = os.environ.get('OPERATOR_VERSION', 'v1') -operator_api_version = f"{operator_domain}/{operator_version}" -resource_refresh_interval = int(os.environ.get('RESOURCE_REFRESH_INTERVAL', 600)) - -if os.path.exists('/run/secrets/kubernetes.io/serviceaccount'): - kubernetes_asyncio.config.load_incluster_config() - with open('/run/secrets/kubernetes.io/serviceaccount/namespace') as f: - operator_namespace = f.read() -else: - asyncio.get_event_loop().run_until_complete(kubernetes_asyncio.config.load_kube_config()) - operator_namespace = kubernetes_asyncio.config.list_kube_config_contexts()[1]['context']['namespace'] - -core_v1_api = kubernetes_asyncio.client.CoreV1Api() -custom_objects_api = kubernetes_asyncio.client.CustomObjectsApi() diff --git a/operator/poolboy.py b/operator/poolboy.py new file mode 100644 index 0000000..19dbde1 --- /dev/null +++ b/operator/poolboy.py @@ -0,0 +1,34 @@ +import kubernetes_asyncio +import os + +class Poolboy(): + manage_claims_interval = int(os.environ.get('MANAGE_CLAIMS_INTERVAL', 60)) + manage_handles_interval = int(os.environ.get('MANAGE_HANDLES_INTERVAL', 60)) + operator_domain = os.environ.get('OPERATOR_DOMAIN', 'poolboy.gpte.redhat.com') + operator_version = os.environ.get('OPERATOR_VERSION', 'v1') + operator_api_version = f"{operator_domain}/{operator_version}" + resource_refresh_interval = int(os.environ.get('RESOURCE_REFRESH_INTERVAL', 600)) + + @classmethod + async def on_cleanup(cls): + await cls.api_client.close() + + @classmethod + async def on_startup(cls): + if os.path.exists('/run/secrets/kubernetes.io/serviceaccount'): + kubernetes_asyncio.config.load_incluster_config() + with open('/run/secrets/kubernetes.io/serviceaccount/namespace') as f: + cls.namespace = f.read() + else: + await kubernetes_asyncio.config.load_kube_config() + if 'OPERATOR_NAMESPACE' in os.environ: + cls.namespace = os.environ['OPERATOR_NAMESPACE'] + else: + raise Exception( + 'Unable to determine operator namespace. ' + 'Please set OPERATOR_NAMESPACE environment variable.' + ) + + cls.api_client = kubernetes_asyncio.client.ApiClient() + cls.core_v1_api = kubernetes_asyncio.client.CoreV1Api(cls.api_client) + cls.custom_objects_api = kubernetes_asyncio.client.CustomObjectsApi(cls.api_client) diff --git a/operator/poolboy_templating.py b/operator/poolboy_templating.py index b5c3301..ce94c4b 100644 --- a/operator/poolboy_templating.py +++ b/operator/poolboy_templating.py @@ -89,7 +89,7 @@ def seconds_to_interval(seconds:int) -> str: } jinja2envs['jinja2'].filters['bool'] = lambda x: bool(strtobool(x)) if isinstance(x, str) else bool(x) jinja2envs['jinja2'].filters['json_query'] = lambda x, query: jmespath.search(query, x) -jinja2envs['jinja2'].filters['merge_list_of_dicts'] = lambda a: functools.reduce(lambda d1, d2: {**d1, **d2}, a) +jinja2envs['jinja2'].filters['merge_list_of_dicts'] = lambda a: functools.reduce(lambda d1, d2: {**(d1 or {}), **(d2 or {})}, a) jinja2envs['jinja2'].filters['object'] = lambda x: json.dumps(x) jinja2envs['jinja2'].filters['parse_time_interval'] = lambda x: timedelta(seconds=pytimeparse.parse(x)) jinja2envs['jinja2'].filters['strgen'] = lambda x: StringGenerator(x).render() diff --git a/test/unittest-poolboy_templating.py b/test/unittest-poolboy_templating.py index 0266648..66d8bb3 100755 --- a/test/unittest-poolboy_templating.py +++ b/test/unittest-poolboy_templating.py @@ -391,6 +391,15 @@ def test_26(self): ) def test_27(self): + template = "{{ l | json_query('from_items(zip([].keys(@)[], [].values(@)[]))') | object }}" + template_vars = { + "l": [{"a": "A", "b": "X"}, {"b": "B", "c": "C"}, {"d": "D"}] + } + self.assertEqual( + recursive_process_template_strings(template, 'jinja2', template_vars), {"a": "A", "b": "B", "c": "C", "d": "D"} + ) + + def test_28(self): template = "{{ l | merge_list_of_dicts | object }}" template_vars = { "l": [{"a": "A", "b": "X"}, {"b": "B", "c": "C"}, {"d": "D"}] @@ -399,5 +408,14 @@ def test_27(self): recursive_process_template_strings(template, 'jinja2', template_vars), {"a": "A", "b": "B", "c": "C", "d": "D"} ) + def test_29(self): + template = "{{ l | merge_list_of_dicts | object }}" + template_vars = { + "l": [{"a": "A", "b": "X"}, None, {"b": "B", "c": "C"}, {}, {"d": "D"}] + } + self.assertEqual( + recursive_process_template_strings(template, 'jinja2', template_vars), {"a": "A", "b": "B", "c": "C", "d": "D"} + ) + if __name__ == '__main__': unittest.main()