From 78d2001d885ce3f4b832e531dfadfe19f68e8944 Mon Sep 17 00:00:00 2001 From: Henning Jacobs Date: Fri, 2 Oct 2020 20:57:11 +0200 Subject: [PATCH] Limit access of namespaces (#81) * do not iterate over all namespaces if not necessary * ignore resources ouside of included namespaces * fix linting * Add additional test * remove duplicate code Co-authored-by: Fabian Hinz --- kube_janitor/janitor.py | 24 +++++++++++++++++++--- tests/test_clean_up.py | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/kube_janitor/janitor.py b/kube_janitor/janitor.py index e19fb6a..d93fb32 100644 --- a/kube_janitor/janitor.py +++ b/kube_janitor/janitor.py @@ -296,7 +296,16 @@ def clean_up( counter: Counter = Counter() cache: Dict[str, Any] = {} - for namespace in Namespace.objects(api): + namespaces = [] + + if "all" in include_namespaces: + namespaces.extend(list(Namespace.objects(api))) + else: + for namespace_name in include_namespaces: + namespace = Namespace.objects(api).get(name=namespace_name) + namespaces.append(namespace) + + for namespace in namespaces: if matches_resource_filter( namespace, include_resources, @@ -318,7 +327,7 @@ def clean_up( ) counter.update( handle_resource_on_expiry( - namespace, rules, delete_notification, wait_after_delete, dry_run + namespace, rules, delete_notification, wait_after_delete, dry_run, ) ) else: @@ -332,7 +341,16 @@ def clean_up( for _type in resource_types: if _type.endpoint not in exclude_resources: try: - for resource in _type.objects(api, namespace=pykube.all): + resources = [] + if "all" in include_namespaces: + resources.extend(list(_type.objects(api, namespace=pykube.all))) + else: + for namespace_name in include_namespaces: + resources.extend( + list(_type.objects(api, namespace=namespace_name)) + ) + + for resource in resources: # objects might be available via multiple API versions (e.g. deployments appear as extensions/v1beta1 and apps/v1) # => process them only once object_id = (resource.kind, resource.namespace, resource.name) diff --git a/tests/test_clean_up.py b/tests/test_clean_up.py index a12b6e6..f4988fb 100644 --- a/tests/test_clean_up.py +++ b/tests/test_clean_up.py @@ -311,6 +311,50 @@ def get(**kwargs): assert counter["resources-processed"] == 1 +def test_clean_up_only_included_namespaces(): + api_mock = MagicMock(spec=NamespacedAPIObject, name="APIMock") + + def get(**kwargs): + if kwargs.get("url") == "namespaces/foo": + data = {"metadata": {"name": "foo"}} + elif kwargs.get("url") == "namespaces": + # kube-system is skipped + data = { + "items": [ + {"metadata": {"name": "default"}}, + {"metadata": {"name": "foo"}}, + {"metadata": {"name": "kube-system"}}, + ] + } + elif kwargs.get("url") == "namespaces/foo": + # kube-system is skipped + data = {"items": [{"metadata": {"name": "foo"}}]} + elif kwargs["version"] == "v1": + data = {"resources": []} + elif kwargs["version"] == "/apis": + data = {"groups": []} + else: + data = {} + response = MagicMock() + response.json.return_value = data + return response + + api_mock.get = get + counter = clean_up( + api_mock, + ALL, + [], + ["foo"], + ["kube-system"], + [], + delete_notification=0, + deployment_time_annotation=None, + dry_run=False, + ) + + assert counter["resources-processed"] == 1 + + @mock_now def test_ignore_nonlistable_api_group(): api_mock = MagicMock(spec=NamespacedAPIObject, name="APIMock")