25
25
from six import PY3 , next
26
26
27
27
from .config_exception import ConfigException
28
+ from kubernetes .client import Configuration
28
29
from .kube_config import (ConfigNode , FileOrData , KubeConfigLoader ,
29
30
_cleanup_temp_files , _create_temp_file_with_content ,
30
31
list_kube_config_contexts , load_kube_config ,
34
35
35
36
EXPIRY_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
36
37
# should be less than kube_config.EXPIRY_SKEW_PREVENTION_DELAY
37
- EXPIRY_TIMEDELTA = 2
38
+ PAST_EXPIRY_TIMEDELTA = 2
39
+ # should be more than kube_config.EXPIRY_SKEW_PREVENTION_DELAY
40
+ FUTURE_EXPIRY_TIMEDELTA = 60
38
41
39
42
NON_EXISTING_FILE = "zz_non_existing_file_472398324"
40
43
@@ -47,9 +50,9 @@ def _format_expiry_datetime(dt):
47
50
return dt .strftime (EXPIRY_DATETIME_FORMAT )
48
51
49
52
50
- def _get_expiry (loader ):
53
+ def _get_expiry (loader , active_context ):
51
54
expired_gcp_conf = (item for item in loader ._config .value .get ("users" )
52
- if item .get ("name" ) == "expired_gcp" )
55
+ if item .get ("name" ) == active_context )
53
56
return next (expired_gcp_conf ).get ("user" ).get ("auth-provider" ) \
54
57
.get ("config" ).get ("expiry" )
55
58
@@ -73,8 +76,11 @@ def _raise_exception(st):
73
76
TEST_PASSWORD = "pass"
74
77
# token for me:pass
75
78
TEST_BASIC_TOKEN = "Basic bWU6cGFzcw=="
76
- TEST_TOKEN_EXPIRY = _format_expiry_datetime (
77
- datetime .datetime .utcnow () - datetime .timedelta (minutes = EXPIRY_TIMEDELTA ))
79
+ DATETIME_EXPIRY_PAST = datetime .datetime .utcnow (
80
+ ) - datetime .timedelta (minutes = PAST_EXPIRY_TIMEDELTA )
81
+ DATETIME_EXPIRY_FUTURE = datetime .datetime .utcnow (
82
+ ) + datetime .timedelta (minutes = FUTURE_EXPIRY_TIMEDELTA )
83
+ TEST_TOKEN_EXPIRY_PAST = _format_expiry_datetime (DATETIME_EXPIRY_PAST )
78
84
79
85
TEST_SSL_HOST = "https://test-host"
80
86
TEST_CERTIFICATE_AUTH = "cert-auth"
@@ -371,6 +377,13 @@ class TestKubeConfigLoader(BaseTestCase):
371
377
"user" : "expired_gcp"
372
378
}
373
379
},
380
+ {
381
+ "name" : "expired_gcp_refresh" ,
382
+ "context" : {
383
+ "cluster" : "default" ,
384
+ "user" : "expired_gcp_refresh"
385
+ }
386
+ },
374
387
{
375
388
"name" : "oidc" ,
376
389
"context" : {
@@ -509,7 +522,24 @@ class TestKubeConfigLoader(BaseTestCase):
509
522
"name" : "gcp" ,
510
523
"config" : {
511
524
"access-token" : TEST_DATA_BASE64 ,
512
- "expiry" : TEST_TOKEN_EXPIRY , # always in past
525
+ "expiry" : TEST_TOKEN_EXPIRY_PAST , # always in past
526
+ }
527
+ },
528
+ "token" : TEST_DATA_BASE64 , # should be ignored
529
+ "username" : TEST_USERNAME , # should be ignored
530
+ "password" : TEST_PASSWORD , # should be ignored
531
+ }
532
+ },
533
+ # Duplicated from "expired_gcp" so test_load_gcp_token_with_refresh
534
+ # is isolated from test_gcp_get_api_key_with_prefix.
535
+ {
536
+ "name" : "expired_gcp_refresh" ,
537
+ "user" : {
538
+ "auth-provider" : {
539
+ "name" : "gcp" ,
540
+ "config" : {
541
+ "access-token" : TEST_DATA_BASE64 ,
542
+ "expiry" : TEST_TOKEN_EXPIRY_PAST , # always in past
513
543
}
514
544
},
515
545
"token" : TEST_DATA_BASE64 , # should be ignored
@@ -630,16 +660,20 @@ def test_load_user_token(self):
630
660
self .assertEqual (BEARER_TOKEN_FORMAT % TEST_DATA_BASE64 , loader .token )
631
661
632
662
def test_gcp_no_refresh (self ):
633
- expected = FakeConfig (
634
- host = TEST_HOST ,
635
- token = BEARER_TOKEN_FORMAT % TEST_DATA_BASE64 )
636
- actual = FakeConfig ()
663
+ fake_config = FakeConfig ()
664
+ # swagger-generated config has this, but FakeConfig does not.
665
+ self .assertFalse (hasattr (fake_config , 'get_api_key_with_prefix' ))
637
666
KubeConfigLoader (
638
667
config_dict = self .TEST_KUBE_CONFIG ,
639
668
active_context = "gcp" ,
640
669
get_google_credentials = lambda : _raise_exception (
641
- "SHOULD NOT BE CALLED" )).load_and_set (actual )
642
- self .assertEqual (expected , actual )
670
+ "SHOULD NOT BE CALLED" )).load_and_set (fake_config )
671
+ # Should now be populated with a gcp token fetcher.
672
+ self .assertIsNotNone (fake_config .get_api_key_with_prefix )
673
+ self .assertEqual (TEST_HOST , fake_config .host )
674
+ # For backwards compatibility, authorization field should still be set.
675
+ self .assertEqual (BEARER_TOKEN_FORMAT % TEST_DATA_BASE64 ,
676
+ fake_config .api_key ['authorization' ])
643
677
644
678
def test_load_gcp_token_no_refresh (self ):
645
679
loader = KubeConfigLoader (
@@ -654,20 +688,48 @@ def test_load_gcp_token_no_refresh(self):
654
688
def test_load_gcp_token_with_refresh (self ):
655
689
def cred (): return None
656
690
cred .token = TEST_ANOTHER_DATA_BASE64
657
- cred .expiry = datetime .datetime .now ()
691
+ cred .expiry = datetime .datetime .utcnow ()
658
692
659
693
loader = KubeConfigLoader (
660
694
config_dict = self .TEST_KUBE_CONFIG ,
661
695
active_context = "expired_gcp" ,
662
696
get_google_credentials = lambda : cred )
663
- original_expiry = _get_expiry (loader )
697
+ original_expiry = _get_expiry (loader , "expired_gcp" )
664
698
self .assertTrue (loader ._load_auth_provider_token ())
665
- new_expiry = _get_expiry (loader )
699
+ new_expiry = _get_expiry (loader , "expired_gcp" )
666
700
# assert that the configs expiry actually updates
667
701
self .assertTrue (new_expiry > original_expiry )
668
702
self .assertEqual (BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64 ,
669
703
loader .token )
670
704
705
+ def test_gcp_get_api_key_with_prefix (self ):
706
+ class cred_old :
707
+ token = TEST_DATA_BASE64
708
+ expiry = DATETIME_EXPIRY_PAST
709
+
710
+ class cred_new :
711
+ token = TEST_ANOTHER_DATA_BASE64
712
+ expiry = DATETIME_EXPIRY_FUTURE
713
+ fake_config = FakeConfig ()
714
+ _get_google_credentials = mock .Mock ()
715
+ _get_google_credentials .side_effect = [cred_old , cred_new ]
716
+
717
+ loader = KubeConfigLoader (
718
+ config_dict = self .TEST_KUBE_CONFIG ,
719
+ active_context = "expired_gcp_refresh" ,
720
+ get_google_credentials = _get_google_credentials )
721
+ loader .load_and_set (fake_config )
722
+ original_expiry = _get_expiry (loader , "expired_gcp_refresh" )
723
+ # Call GCP token fetcher.
724
+ token = fake_config .get_api_key_with_prefix ()
725
+ new_expiry = _get_expiry (loader , "expired_gcp_refresh" )
726
+
727
+ self .assertTrue (new_expiry > original_expiry )
728
+ self .assertEqual (BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64 ,
729
+ loader .token )
730
+ self .assertEqual (BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64 ,
731
+ token )
732
+
671
733
def test_oidc_no_refresh (self ):
672
734
loader = KubeConfigLoader (
673
735
config_dict = self .TEST_KUBE_CONFIG ,
@@ -893,5 +955,33 @@ def test_user_exec_auth(self, mock):
893
955
self .assertEqual (expected , actual )
894
956
895
957
958
+ class TestKubernetesClientConfiguration (BaseTestCase ):
959
+ # Verifies properties of kubernetes.client.Configuration.
960
+ # These tests guard against changes to the upstream configuration class,
961
+ # since GCP authorization overrides get_api_key_with_prefix to refresh its
962
+ # token regularly.
963
+
964
+ def test_get_api_key_with_prefix_exists (self ):
965
+ self .assertTrue (hasattr (Configuration , 'get_api_key_with_prefix' ))
966
+
967
+ def test_get_api_key_with_prefix_returns_token (self ):
968
+ expected_token = 'expected_token'
969
+ config = Configuration ()
970
+ config .api_key ['authorization' ] = expected_token
971
+ self .assertEqual (expected_token ,
972
+ config .get_api_key_with_prefix ('authorization' ))
973
+
974
+ def test_auth_settings_calls_get_api_key_with_prefix (self ):
975
+ expected_token = 'expected_token'
976
+
977
+ def fake_get_api_key_with_prefix (identifier ):
978
+ self .assertEqual ('authorization' , identifier )
979
+ return expected_token
980
+ config = Configuration ()
981
+ config .get_api_key_with_prefix = fake_get_api_key_with_prefix
982
+ self .assertEqual (expected_token ,
983
+ config .auth_settings ()['BearerToken' ]['value' ])
984
+
985
+
896
986
if __name__ == '__main__' :
897
987
unittest .main ()
0 commit comments