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