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 (
@@ -1390,6 +1391,38 @@ def test_user_exec_auth(self, mock):
1390
1391
active_context = "exec_cred_user" ).load_and_set (actual )
1391
1392
self .assertEqual (expected , actual )
1392
1393
1394
+ @mock .patch ('kubernetes.config.kube_config.ExecProvider.run' )
1395
+ def test_user_exec_auth_with_expiry (self , mock ):
1396
+ expired_token = "expired"
1397
+ current_token = "current"
1398
+ mock .side_effect = [
1399
+ {
1400
+ "token" : expired_token ,
1401
+ "expirationTimestamp" : format_rfc3339 (DATETIME_EXPIRY_PAST )
1402
+ },
1403
+ {
1404
+ "token" : current_token ,
1405
+ "expirationTimestamp" : format_rfc3339 (DATETIME_EXPIRY_FUTURE )
1406
+ }
1407
+ ]
1408
+
1409
+ fake_config = FakeConfig ()
1410
+ self .assertIsNone (fake_config .refresh_api_key_hook )
1411
+
1412
+ KubeConfigLoader (
1413
+ config_dict = self .TEST_KUBE_CONFIG ,
1414
+ active_context = "exec_cred_user" ).load_and_set (fake_config )
1415
+ # The kube config should use the first token returned from the
1416
+ # exec provider.
1417
+ self .assertEqual (fake_config .api_key ["authorization" ],
1418
+ BEARER_TOKEN_FORMAT % expired_token )
1419
+ # Should now be populated with a method to refresh expired tokens.
1420
+ self .assertIsNotNone (fake_config .refresh_api_key_hook )
1421
+ # Refresh the token; the kube config should be updated.
1422
+ fake_config .refresh_api_key_hook (fake_config )
1423
+ self .assertEqual (fake_config .api_key ["authorization" ],
1424
+ BEARER_TOKEN_FORMAT % current_token )
1425
+
1393
1426
@mock .patch ('kubernetes.config.kube_config.ExecProvider.run' )
1394
1427
def test_user_exec_auth_certificates (self , mock ):
1395
1428
mock .return_value = {
@@ -1419,7 +1452,6 @@ def test_user_cmd_path(self):
1419
1452
KubeConfigLoader (
1420
1453
config_dict = self .TEST_KUBE_CONFIG ,
1421
1454
active_context = "contexttestcmdpath" ).load_and_set (actual )
1422
- del actual .get_api_key_with_prefix
1423
1455
self .assertEqual (expected , actual )
1424
1456
1425
1457
def test_user_cmd_path_empty (self ):
@@ -1497,31 +1529,28 @@ def test__get_kube_config_loader_dict_no_persist(self):
1497
1529
class TestKubernetesClientConfiguration (BaseTestCase ):
1498
1530
# Verifies properties of kubernetes.client.Configuration.
1499
1531
# These tests guard against changes to the upstream configuration class,
1500
- # since GCP authorization overrides get_api_key_with_prefix to refresh its
1501
- # token regularly.
1532
+ # since GCP and Exec authorization use refresh_api_key_hook to refresh
1533
+ # their tokens regularly.
1502
1534
1503
- def test_get_api_key_with_prefix_exists (self ):
1504
- self .assertTrue (hasattr (Configuration , 'get_api_key_with_prefix ' ))
1535
+ def test_refresh_api_key_hook_exists (self ):
1536
+ self .assertTrue (hasattr (Configuration () , 'refresh_api_key_hook ' ))
1505
1537
1506
- def test_get_api_key_with_prefix_returns_token (self ):
1507
- expected_token = 'expected_token'
1508
- config = Configuration ()
1509
- config .api_key ['authorization' ] = expected_token
1510
- self .assertEqual (expected_token ,
1511
- config .get_api_key_with_prefix ('authorization' ))
1512
-
1513
- def test_auth_settings_calls_get_api_key_with_prefix (self ):
1538
+ def test_get_api_key_calls_refresh_api_key_hook (self ):
1539
+ identifier = 'authorization'
1514
1540
expected_token = 'expected_token'
1515
1541
old_token = 'old_token'
1542
+ config = Configuration (
1543
+ api_key = {identifier : old_token },
1544
+ api_key_prefix = {identifier : 'Bearer' }
1545
+ )
1546
+
1547
+ def refresh_api_key_hook (client_config ):
1548
+ self .assertEqual (client_config , config )
1549
+ client_config .api_key [identifier ] = expected_token
1550
+ config .refresh_api_key_hook = refresh_api_key_hook
1516
1551
1517
- def fake_get_api_key_with_prefix (identifier ):
1518
- self .assertEqual ('authorization' , identifier )
1519
- return expected_token
1520
- config = Configuration ()
1521
- config .api_key ['authorization' ] = old_token
1522
- config .get_api_key_with_prefix = fake_get_api_key_with_prefix
1523
- self .assertEqual (expected_token ,
1524
- config .auth_settings ()['BearerToken' ]['value' ])
1552
+ self .assertEqual ('Bearer ' + expected_token ,
1553
+ config .get_api_key_with_prefix (identifier ))
1525
1554
1526
1555
1527
1556
class TestKubeConfigMerger (BaseTestCase ):
0 commit comments