diff --git a/clusterman/kubernetes/util.py b/clusterman/kubernetes/util.py index ab3ff183f..0e9253a5f 100644 --- a/clusterman/kubernetes/util.py +++ b/clusterman/kubernetes/util.py @@ -60,7 +60,18 @@ def __init__(self, kubeconfig_path: str, client_class: Type) -> None: :param Type client_class: k8s client class to initialize """ try: - kubernetes.config.load_kube_config(kubeconfig_path, context=os.getenv("KUBECONTEXT")) + """ + https://kubernetes.io/docs/concepts/containers/container-environment/#container-environment + Every pod in k8s gets some default environment variable injected, including KUBERNETES_SERVICE_HOST + which points to default kuberbetes service. We are using this variable to distinguise between + when cluterman is started in a pod vs when it's started on host. For clusterman instances running inside + a k8s cluster, we prioritise using K8s Service account since that let us avoid creating any kubeconfig + in advance. For clusterman CLI invocation we continue using provided KUBECONFIG file + """ + if os.getenv("KUBERNETES_SERVICE_HOST"): + kubernetes.config.load_incluster_config() + else: + kubernetes.config.load_kube_config(kubeconfig_path, context=os.getenv("KUBECONTEXT")) except TypeError: error_msg = "Could not load KUBECONFIG; is this running on Kubernetes master?" if "yelpcorp" in socket.getfqdn(): diff --git a/tests/kubernetes/util_test.py b/tests/kubernetes/util_test.py index ef4da0799..3316a4660 100644 --- a/tests/kubernetes/util_test.py +++ b/tests/kubernetes/util_test.py @@ -26,6 +26,21 @@ def test_cached_corev1_api_no_kubeconfig(caplog): assert "Could not load KUBECONFIG" in caplog.text +def test_cached_corev1_api_use_load_incluster_config_when_running_in_pod(): + with mock.patch.dict(os.environ, {"KUBERNETES_SERVICE_HOST": "ABC"}): + with mock.patch( + "clusterman.kubernetes.util.kubernetes.config.load_incluster_config" + ) as mock_load_incluster_config: + _ = CachedCoreV1Api("/foo/bar/admin.conf") + assert mock_load_incluster_config.called + + +def test_cached_corev1_api_use_load_kubeconfig_config_when_running_as_cli(): + with mock.patch("clusterman.kubernetes.util.kubernetes.config.load_kube_config") as mock_load_kube_config: + _ = CachedCoreV1Api("/foo/bar/admin.conf") + assert mock_load_kube_config.called + + def test_cached_corev1_api_caches_non_cached_function(mock_cached_core_v1_api): mock_cached_core_v1_api.list_namespace() assert mock_cached_core_v1_api._client.list_namespace.call_count == 1