Skip to content

Commit a4c16a1

Browse files
Accept kubeconfig as a dict (#361)
1 parent 6dba956 commit a4c16a1

File tree

5 files changed

+50
-10
lines changed

5 files changed

+50
-10
lines changed

kr8s/_api.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import asyncio
66
import contextlib
7+
import copy
78
import json
89
import ssl
910
import threading
@@ -64,7 +65,8 @@ def __init__(self, **kwargs) -> None:
6465
thread_loop_id = f"{thread_id}.{loop_id}"
6566
if thread_loop_id not in Api._instances:
6667
Api._instances[thread_loop_id] = weakref.WeakValueDictionary()
67-
Api._instances[thread_loop_id][frozenset(kwargs.items())] = self
68+
key = hash_kwargs(kwargs)
69+
Api._instances[thread_loop_id][key] = self
6870

6971
def __await__(self):
7072
async def f():
@@ -508,3 +510,11 @@ def namespace(self) -> str:
508510
@namespace.setter
509511
def namespace(self, value):
510512
self.auth.namespace = value
513+
514+
515+
def hash_kwargs(kwargs: dict):
516+
key_kwargs = copy.copy(kwargs)
517+
for key in key_kwargs:
518+
if isinstance(key_kwargs[key], dict):
519+
key_kwargs[key] = json.dumps(key_kwargs[key])
520+
return frozenset(key_kwargs.items())

kr8s/_auth.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import binascii
55
import json
66
import os
7+
import pathlib
78
import ssl
89

910
import anyio
@@ -42,7 +43,7 @@ def __init__(
4243
if serviceaccount is not None
4344
else "/var/run/secrets/kubernetes.io/serviceaccount"
4445
)
45-
self._kubeconfig_path = kubeconfig or os.environ.get(
46+
self._kubeconfig_path_or_dict = kubeconfig or os.environ.get(
4647
"KUBECONFIG", "~/.kube/config"
4748
)
4849
self.__auth_lock = anyio.Lock()
@@ -60,7 +61,7 @@ async def reauthenticate(self) -> None:
6061
if self._url:
6162
self.server = self._url
6263
else:
63-
if self._kubeconfig_path is not False:
64+
if self._kubeconfig_path_or_dict is not False:
6465
await self._load_kubeconfig()
6566
if self._serviceaccount and not self.server:
6667
await self._load_service_account()
@@ -91,10 +92,19 @@ async def ssl_context(self):
9192

9293
async def _load_kubeconfig(self) -> None:
9394
"""Load kubernetes auth from kubeconfig."""
94-
self._kubeconfig_path = os.path.expanduser(self._kubeconfig_path)
95-
if not os.path.exists(self._kubeconfig_path):
96-
return
97-
self.kubeconfig = await KubeConfigSet(*self._kubeconfig_path.split(":"))
95+
if isinstance(self._kubeconfig_path_or_dict, str) or isinstance(
96+
self._kubeconfig_path_or_dict, pathlib.Path
97+
):
98+
self._kubeconfig_path_or_dict = os.path.expanduser(
99+
self._kubeconfig_path_or_dict
100+
)
101+
if not os.path.exists(self._kubeconfig_path_or_dict):
102+
return
103+
self.kubeconfig = await KubeConfigSet(
104+
*self._kubeconfig_path_or_dict.split(":")
105+
)
106+
else:
107+
self.kubeconfig = await KubeConfigSet(self._kubeconfig_path_or_dict)
98108
if self._use_context:
99109
try:
100110
self._context = self.kubeconfig.get_context(self._use_context)

kr8s/_config.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2024, Kr8s Developers (See LICENSE for list)
22
# SPDX-License-Identifier: BSD 3-Clause License
3+
import pathlib
34
from typing import Dict, List, Union
45

56
import anyio
@@ -19,7 +20,9 @@
1920

2021
class KubeConfigSet(object):
2122
def __init__(self, *paths_or_dicts: Union[List[str], List[Dict]]):
22-
if isinstance(paths_or_dicts[0], str):
23+
if isinstance(paths_or_dicts[0], str) or isinstance(
24+
paths_or_dicts[0], pathlib.Path
25+
):
2326
self._configs = [KubeConfig(path) for path in paths_or_dicts]
2427
else:
2528
self._configs = [KubeConfig(config) for config in paths_or_dicts]
@@ -177,7 +180,7 @@ class KubeConfig(object):
177180
def __init__(self, path_or_config: Union[str, Dict]):
178181
self.path = None
179182
self._raw = None
180-
if isinstance(path_or_config, str):
183+
if isinstance(path_or_config, str) or isinstance(path_or_config, pathlib.Path):
181184
self.path = path_or_config
182185
else:
183186
self._raw = path_or_config

kr8s/asyncio/_api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import threading
55

66
from kr8s._api import Api as _AsyncApi
7+
from kr8s._api import hash_kwargs
78

89

910
async def api(
@@ -52,7 +53,7 @@ async def api(
5253
_cls = _SyncApi
5354

5455
async def _f(**kwargs):
55-
key = frozenset(kwargs.items())
56+
key = hash_kwargs(kwargs)
5657
thread_id = threading.get_ident()
5758
try:
5859
loop_id = id(asyncio.get_running_loop())

kr8s/tests/test_auth.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ async def test_kubeconfig(k8s_cluster):
100100
assert await api.whoami() == "kubernetes-admin"
101101

102102

103+
async def test_kubeconfig_dict(k8s_cluster):
104+
config = yaml.safe_load(k8s_cluster.kubeconfig_path.read_text())
105+
assert isinstance(config, dict)
106+
api = await kr8s.asyncio.api(kubeconfig=config)
107+
assert await api.get("pods", namespace=kr8s.ALL)
108+
assert await api.whoami() == "kubernetes-admin"
109+
110+
111+
def test_kubeconfig_dict_sync(k8s_cluster):
112+
config = yaml.safe_load(k8s_cluster.kubeconfig_path.read_text())
113+
assert isinstance(config, dict)
114+
api = kr8s.api(kubeconfig=config)
115+
assert api.get("pods", namespace=kr8s.ALL)
116+
assert api.whoami() == "kubernetes-admin"
117+
118+
103119
async def test_kubeconfig_context(kubeconfig_with_second_context):
104120
kubeconfig_path, context_name = kubeconfig_with_second_context
105121
api = await kr8s.asyncio.api(kubeconfig=kubeconfig_path, context=context_name)

0 commit comments

Comments
 (0)