Skip to content

Commit b5916ce

Browse files
harryharpelHarry Harpel
andauthored
[fix] support refreshing exec api credentials (#258)
Co-authored-by: Harry Harpel <[email protected]>
1 parent 0a0f840 commit b5916ce

File tree

3 files changed

+47
-15
lines changed

3 files changed

+47
-15
lines changed

examples/tail.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import asyncio
1515

1616
from kubernetes_asyncio import client, config
17-
from kubernetes_asyncio.client.api_client import ApiClient
17+
from kubernetes_asyncio.client.api_client import ApiClient, Configuration
1818

1919

2020
def parse_args():
@@ -51,8 +51,9 @@ async def print_pod_log(v1_api, pod, namespace, container, lines, follow):
5151
async def main():
5252
args = parse_args()
5353

54-
loader = await config.load_kube_config()
55-
api = ApiClient()
54+
client_configuration = Configuration()
55+
loader = await config.load_kube_config(client_configuration=client_configuration)
56+
api = ApiClient(configuration=client_configuration)
5657
v1_api = client.CoreV1Api(api)
5758
ret = await v1_api.list_namespaced_pod(args.namespace)
5859
cmd = []
@@ -72,7 +73,9 @@ async def main():
7273

7374
if args.follow:
7475
# autorefresh gcp token
75-
cmd.append(config.refresh_token(loader))
76+
cmd.append(config.refresh_token(
77+
loader=loader,
78+
client_configuration=client_configuration))
7679

7780
await asyncio.wait(cmd)
7881
await api.close()

kubernetes_asyncio/config/kube_config.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ async def _load_authentication(self):
208208

209209
if 'exec' in self._user:
210210
logging.debug('Try to use exec provider')
211-
res_exec_plugin = await self._load_from_exec_plugin()
211+
res_exec_plugin = await self.load_from_exec_plugin()
212212
if res_exec_plugin:
213213
return
214214

@@ -309,13 +309,17 @@ def _retrieve_oidc_cacert(self, provider):
309309

310310
return None
311311

312-
async def _load_from_exec_plugin(self):
312+
async def load_from_exec_plugin(self):
313313
try:
314+
if hasattr(self, 'exec_plugin_expiry') and not _is_expired(self.exec_plugin_expiry):
315+
return True
314316
status = await ExecProvider(self._user['exec']).run()
315317
if 'token' not in status:
316318
logging.error('exec: missing token field in plugin output')
317319
return None
318320
self.token = "Bearer %s" % status['token']
321+
if 'expirationTimestamp' in status:
322+
self.exec_plugin_expiry = parse_rfc3339(status['expirationTimestamp'])
319323
return True
320324
except Exception as e:
321325
logging.error(str(e))
@@ -617,16 +621,20 @@ async def refresh_token(loader, client_configuration=None, interval=60):
617621
:param interval: how often check if token is up-to-date
618622
619623
"""
620-
if loader.provider != 'gcp':
621-
return
622624

623625
if client_configuration is None:
624-
client_configuration = Configuration()
625-
626-
while 1:
627-
await asyncio.sleep(interval)
628-
await loader.load_gcp_token()
629-
client_configuration.api_key['BearerToken'] = loader.token
626+
raise NotImplementedError
627+
628+
if loader.provider == 'gcp':
629+
while 1:
630+
await asyncio.sleep(interval)
631+
await loader.load_gcp_token()
632+
client_configuration.api_key['BearerToken'] = loader.token
633+
elif 'exec' in loader._user:
634+
while 1:
635+
await asyncio.sleep(interval)
636+
await loader.load_from_exec_plugin()
637+
client_configuration.api_key['BearerToken'] = loader.token
630638

631639

632640
async def new_client_from_config(config_file=None, context=None, persist_config=True,

kubernetes_asyncio/config/kube_config_test.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ async def test_non_existing_user(self):
961961
active_context="non_existing_user").load_and_set(actual)
962962
self.assertEqual(expected, actual)
963963

964-
async def test_refresh_token(self):
964+
async def test_refresh_gcp_token(self):
965965
loader = KubeConfigLoader(
966966
config_dict=self.TEST_KUBE_CONFIG,
967967
active_context="gcp",
@@ -979,6 +979,27 @@ async def test_refresh_token(self):
979979
self.assertEqual(BEARER_TOKEN_FORMAT % TEST_DATA_BASE64,
980980
loader.token)
981981

982+
async def test_refresh_exec_token(self):
983+
class MockKubeConfigLoader(KubeConfigLoader):
984+
async def load_from_exec_plugin(self):
985+
self.token = TEST_ANOTHER_DATA_BASE64
986+
987+
loader = MockKubeConfigLoader(
988+
config_dict=self.TEST_KUBE_CONFIG,
989+
active_context="exec_cred_user")
990+
mock_sleep = patch('asyncio.sleep').start()
991+
mock_sleep.side_effect = [0, AssertionError]
992+
993+
mock_config = Mock()
994+
mock_config.api_key = {}
995+
996+
loader.exec_plugin_expiry = datetime.datetime.now()
997+
998+
with self.assertRaises(AssertionError):
999+
await refresh_token(loader, mock_config)
1000+
1001+
self.assertEqual(TEST_ANOTHER_DATA_BASE64, mock_config.api_key["BearerToken"])
1002+
9821003

9831004
class TestKubeConfigMerger(BaseTestCase):
9841005
TEST_KUBE_CONFIG_PART1 = {

0 commit comments

Comments
 (0)