29
29
from kubernetes .client import Configuration
30
30
31
31
from .config_exception import ConfigException
32
- from .dateutil import parse_rfc3339
32
+ from .dateutil import format_rfc3339 , parse_rfc3339
33
33
from .kube_config import (ENV_KUBECONFIG_PATH_SEPARATOR , CommandTokenSource ,
34
34
ConfigNode , FileOrData , KubeConfigLoader ,
35
35
KubeConfigMerger , _cleanup_temp_files ,
@@ -346,9 +346,12 @@ def test_get_with_name_on_duplicate_name(self):
346
346
class FakeConfig :
347
347
348
348
FILE_KEYS = ["ssl_ca_cert" , "key_file" , "cert_file" ]
349
+ IGNORE_KEYS = ["refresh_api_key_hook" ]
349
350
350
351
def __init__ (self , token = None , ** kwargs ):
351
352
self .api_key = {}
353
+ # Provided by the OpenAPI-generated Configuration class
354
+ self .refresh_api_key_hook = None
352
355
if token :
353
356
self .api_key ['authorization' ] = token
354
357
@@ -358,6 +361,8 @@ def __eq__(self, other):
358
361
if len (self .__dict__ ) != len (other .__dict__ ):
359
362
return
360
363
for k , v in self .__dict__ .items ():
364
+ if k in self .IGNORE_KEYS :
365
+ continue
361
366
if k not in other .__dict__ :
362
367
return
363
368
if k in self .FILE_KEYS :
@@ -956,17 +961,15 @@ def test_load_user_token(self):
956
961
957
962
def test_gcp_no_refresh (self ):
958
963
fake_config = FakeConfig ()
959
- # swagger-generated config has this, but FakeConfig does not.
960
- self .assertFalse (hasattr (fake_config , 'get_api_key_with_prefix' ))
964
+ self .assertIsNone (fake_config .refresh_api_key_hook )
961
965
KubeConfigLoader (
962
966
config_dict = self .TEST_KUBE_CONFIG ,
963
967
active_context = "gcp" ,
964
968
get_google_credentials = lambda : _raise_exception (
965
969
"SHOULD NOT BE CALLED" )).load_and_set (fake_config )
966
970
# Should now be populated with a gcp token fetcher.
967
- self .assertIsNotNone (fake_config .get_api_key_with_prefix )
971
+ self .assertIsNotNone (fake_config .refresh_api_key_hook )
968
972
self .assertEqual (TEST_HOST , fake_config .host )
969
- # For backwards compatibility, authorization field should still be set.
970
973
self .assertEqual (BEARER_TOKEN_FORMAT % TEST_DATA_BASE64 ,
971
974
fake_config .api_key ['authorization' ])
972
975
@@ -997,7 +1000,7 @@ def cred(): return None
997
1000
self .assertEqual (BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64 ,
998
1001
loader .token )
999
1002
1000
- def test_gcp_get_api_key_with_prefix (self ):
1003
+ def test_gcp_refresh_api_key_hook (self ):
1001
1004
class cred_old :
1002
1005
token = TEST_DATA_BASE64
1003
1006
expiry = DATETIME_EXPIRY_PAST
@@ -1015,15 +1018,13 @@ class cred_new:
1015
1018
get_google_credentials = _get_google_credentials )
1016
1019
loader .load_and_set (fake_config )
1017
1020
original_expiry = _get_expiry (loader , "expired_gcp_refresh" )
1018
- # Call GCP token fetcher .
1019
- token = fake_config .get_api_key_with_prefix ( )
1021
+ # Refresh the GCP token.
1022
+ fake_config .refresh_api_key_hook ( fake_config )
1020
1023
new_expiry = _get_expiry (loader , "expired_gcp_refresh" )
1021
1024
1022
1025
self .assertTrue (new_expiry > original_expiry )
1023
1026
self .assertEqual (BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64 ,
1024
1027
loader .token )
1025
- self .assertEqual (BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64 ,
1026
- token )
1027
1028
1028
1029
def test_oidc_no_refresh (self ):
1029
1030
loader = KubeConfigLoader (
@@ -1383,6 +1384,38 @@ def test_user_exec_auth(self, mock):
1383
1384
active_context = "exec_cred_user" ).load_and_set (actual )
1384
1385
self .assertEqual (expected , actual )
1385
1386
1387
+ @mock .patch ('kubernetes.config.kube_config.ExecProvider.run' )
1388
+ def test_user_exec_auth_with_expiry (self , mock ):
1389
+ expired_token = "expired"
1390
+ current_token = "current"
1391
+ mock .side_effect = [
1392
+ {
1393
+ "token" : expired_token ,
1394
+ "expirationTimestamp" : format_rfc3339 (DATETIME_EXPIRY_PAST )
1395
+ },
1396
+ {
1397
+ "token" : current_token ,
1398
+ "expirationTimestamp" : format_rfc3339 (DATETIME_EXPIRY_FUTURE )
1399
+ }
1400
+ ]
1401
+
1402
+ fake_config = FakeConfig ()
1403
+ self .assertIsNone (fake_config .refresh_api_key_hook )
1404
+
1405
+ KubeConfigLoader (
1406
+ config_dict = self .TEST_KUBE_CONFIG ,
1407
+ active_context = "exec_cred_user" ).load_and_set (fake_config )
1408
+ # The kube config should use the first token returned from the
1409
+ # exec provider.
1410
+ self .assertEqual (fake_config .api_key ["authorization" ],
1411
+ BEARER_TOKEN_FORMAT % expired_token )
1412
+ # Should now be populated with a method to refresh expired tokens.
1413
+ self .assertIsNotNone (fake_config .refresh_api_key_hook )
1414
+ # Refresh the token; the kube config should be updated.
1415
+ fake_config .refresh_api_key_hook (fake_config )
1416
+ self .assertEqual (fake_config .api_key ["authorization" ],
1417
+ BEARER_TOKEN_FORMAT % current_token )
1418
+
1386
1419
@mock .patch ('kubernetes.config.kube_config.ExecProvider.run' )
1387
1420
def test_user_exec_auth_certificates (self , mock ):
1388
1421
mock .return_value = {
@@ -1412,7 +1445,6 @@ def test_user_cmd_path(self):
1412
1445
KubeConfigLoader (
1413
1446
config_dict = self .TEST_KUBE_CONFIG ,
1414
1447
active_context = "contexttestcmdpath" ).load_and_set (actual )
1415
- del actual .get_api_key_with_prefix
1416
1448
self .assertEqual (expected , actual )
1417
1449
1418
1450
def test_user_cmd_path_empty (self ):
@@ -1490,31 +1522,28 @@ def test__get_kube_config_loader_dict_no_persist(self):
1490
1522
class TestKubernetesClientConfiguration (BaseTestCase ):
1491
1523
# Verifies properties of kubernetes.client.Configuration.
1492
1524
# These tests guard against changes to the upstream configuration class,
1493
- # since GCP authorization overrides get_api_key_with_prefix to refresh its
1494
- # token regularly.
1525
+ # since GCP and Exec authorization use refresh_api_key_hook to refresh
1526
+ # their tokens regularly.
1495
1527
1496
- def test_get_api_key_with_prefix_exists (self ):
1497
- self .assertTrue (hasattr (Configuration , 'get_api_key_with_prefix ' ))
1528
+ def test_refresh_api_key_hook_exists (self ):
1529
+ self .assertTrue (hasattr (Configuration () , 'refresh_api_key_hook ' ))
1498
1530
1499
- def test_get_api_key_with_prefix_returns_token (self ):
1500
- expected_token = 'expected_token'
1501
- config = Configuration ()
1502
- config .api_key ['authorization' ] = expected_token
1503
- self .assertEqual (expected_token ,
1504
- config .get_api_key_with_prefix ('authorization' ))
1505
-
1506
- def test_auth_settings_calls_get_api_key_with_prefix (self ):
1531
+ def test_get_api_key_calls_refresh_api_key_hook (self ):
1532
+ identifier = 'authorization'
1507
1533
expected_token = 'expected_token'
1508
1534
old_token = 'old_token'
1535
+ config = Configuration (
1536
+ api_key = {identifier : old_token },
1537
+ api_key_prefix = {identifier : 'Bearer' }
1538
+ )
1539
+
1540
+ def refresh_api_key_hook (client_config ):
1541
+ self .assertEqual (client_config , config )
1542
+ client_config .api_key [identifier ] = expected_token
1543
+ config .refresh_api_key_hook = refresh_api_key_hook
1509
1544
1510
- def fake_get_api_key_with_prefix (identifier ):
1511
- self .assertEqual ('authorization' , identifier )
1512
- return expected_token
1513
- config = Configuration ()
1514
- config .api_key ['authorization' ] = old_token
1515
- config .get_api_key_with_prefix = fake_get_api_key_with_prefix
1516
- self .assertEqual (expected_token ,
1517
- config .auth_settings ()['BearerToken' ]['value' ])
1545
+ self .assertEqual ('Bearer ' + expected_token ,
1546
+ config .get_api_key_with_prefix (identifier ))
1518
1547
1519
1548
1520
1549
class TestKubeConfigMerger (BaseTestCase ):
0 commit comments