Skip to content
This repository was archived by the owner on Mar 13, 2022. It is now read-only.

Commit b7bb86f

Browse files
committed
Refresh GCP tokens on retrieval by overriding client config method.
[Fix #59]
1 parent 7d1e449 commit b7bb86f

File tree

2 files changed

+85
-15
lines changed

2 files changed

+85
-15
lines changed

config/kube_config.py

+14
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,21 @@ def _load_cluster_info(self):
392392
if 'insecure-skip-tls-verify' in self._cluster:
393393
self.verify_ssl = not self._cluster['insecure-skip-tls-verify']
394394

395+
def _using_gcp_auth_provider(self):
396+
return self._user and \
397+
'auth-provider' in self._user and \
398+
'name' in self._user['auth-provider'] and \
399+
self._user['auth-provider']['name'] == 'gcp'
400+
395401
def _set_config(self, client_configuration):
402+
if self._using_gcp_auth_provider():
403+
# GCP auth tokens must be refreshed regularly, but swagger expects
404+
# a constant token. Replace the swagger-generated client config's
405+
# get_api_key_with_prefix method with our own to allow automatic
406+
# token refresh.
407+
def _gcp_get_api_key(*args):
408+
return self._load_gcp_token(self._user['auth-provider'])
409+
client_configuration.get_api_key_with_prefix = _gcp_get_api_key
396410
if 'token' in self.__dict__:
397411
client_configuration.api_key['authorization'] = self.token
398412
# copy these keys directly from self to configuration object

config/kube_config_test.py

+71-15
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434

3535
EXPIRY_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
3636
# should be less than kube_config.EXPIRY_SKEW_PREVENTION_DELAY
37-
EXPIRY_TIMEDELTA = 2
37+
PAST_EXPIRY_TIMEDELTA = 2
38+
# should be more than kube_config.EXPIRY_SKEW_PREVENTION_DELAY
39+
FUTURE_EXPIRY_TIMEDELTA = 60
3840

3941
NON_EXISTING_FILE = "zz_non_existing_file_472398324"
4042

@@ -47,9 +49,9 @@ def _format_expiry_datetime(dt):
4749
return dt.strftime(EXPIRY_DATETIME_FORMAT)
4850

4951

50-
def _get_expiry(loader):
52+
def _get_expiry(loader, active_context):
5153
expired_gcp_conf = (item for item in loader._config.value.get("users")
52-
if item.get("name") == "expired_gcp")
54+
if item.get("name") == active_context)
5355
return next(expired_gcp_conf).get("user").get("auth-provider") \
5456
.get("config").get("expiry")
5557

@@ -73,8 +75,9 @@ def _raise_exception(st):
7375
TEST_PASSWORD = "pass"
7476
# token for me:pass
7577
TEST_BASIC_TOKEN = "Basic bWU6cGFzcw=="
76-
TEST_TOKEN_EXPIRY = _format_expiry_datetime(
77-
datetime.datetime.utcnow() - datetime.timedelta(minutes=EXPIRY_TIMEDELTA))
78+
DATETIME_EXPIRY_PAST = datetime.datetime.utcnow() - datetime.timedelta(minutes=PAST_EXPIRY_TIMEDELTA)
79+
DATETIME_EXPIRY_FUTURE = datetime.datetime.utcnow() + datetime.timedelta(minutes=FUTURE_EXPIRY_TIMEDELTA)
80+
TEST_TOKEN_EXPIRY_PAST = _format_expiry_datetime(DATETIME_EXPIRY_PAST)
7881

7982
TEST_SSL_HOST = "https://test-host"
8083
TEST_CERTIFICATE_AUTH = "cert-auth"
@@ -371,6 +374,13 @@ class TestKubeConfigLoader(BaseTestCase):
371374
"user": "expired_gcp"
372375
}
373376
},
377+
{
378+
"name": "expired_gcp_refresh",
379+
"context": {
380+
"cluster": "default",
381+
"user": "expired_gcp_refresh"
382+
}
383+
},
374384
{
375385
"name": "oidc",
376386
"context": {
@@ -509,7 +519,22 @@ class TestKubeConfigLoader(BaseTestCase):
509519
"name": "gcp",
510520
"config": {
511521
"access-token": TEST_DATA_BASE64,
512-
"expiry": TEST_TOKEN_EXPIRY, # always in past
522+
"expiry": TEST_TOKEN_EXPIRY_PAST, # always in past
523+
}
524+
},
525+
"token": TEST_DATA_BASE64, # should be ignored
526+
"username": TEST_USERNAME, # should be ignored
527+
"password": TEST_PASSWORD, # should be ignored
528+
}
529+
},
530+
{
531+
"name": "expired_gcp_refresh",
532+
"user": {
533+
"auth-provider": {
534+
"name": "gcp",
535+
"config": {
536+
"access-token": TEST_DATA_BASE64,
537+
"expiry": TEST_TOKEN_EXPIRY_PAST, # always in past
513538
}
514539
},
515540
"token": TEST_DATA_BASE64, # should be ignored
@@ -630,16 +655,20 @@ def test_load_user_token(self):
630655
self.assertEqual(BEARER_TOKEN_FORMAT % TEST_DATA_BASE64, loader.token)
631656

632657
def test_gcp_no_refresh(self):
633-
expected = FakeConfig(
634-
host=TEST_HOST,
635-
token=BEARER_TOKEN_FORMAT % TEST_DATA_BASE64)
636-
actual = FakeConfig()
658+
fake_config = FakeConfig()
659+
# swagger-generated config has this, but FakeConfig does not.
660+
self.assertFalse(hasattr(fake_config, 'get_api_key_with_prefix'))
637661
KubeConfigLoader(
638662
config_dict=self.TEST_KUBE_CONFIG,
639663
active_context="gcp",
640664
get_google_credentials=lambda: _raise_exception(
641-
"SHOULD NOT BE CALLED")).load_and_set(actual)
642-
self.assertEqual(expected, actual)
665+
"SHOULD NOT BE CALLED")).load_and_set(fake_config)
666+
# Should now be populated with a gcp token fetcher.
667+
self.assertIsNotNone(fake_config.get_api_key_with_prefix)
668+
self.assertEqual(TEST_HOST, fake_config.host)
669+
# For backwards compatibility, authorization field should still be set.
670+
self.assertEqual(BEARER_TOKEN_FORMAT % TEST_DATA_BASE64,
671+
fake_config.api_key['authorization'])
643672

644673
def test_load_gcp_token_no_refresh(self):
645674
loader = KubeConfigLoader(
@@ -654,20 +683,47 @@ def test_load_gcp_token_no_refresh(self):
654683
def test_load_gcp_token_with_refresh(self):
655684
def cred(): return None
656685
cred.token = TEST_ANOTHER_DATA_BASE64
657-
cred.expiry = datetime.datetime.now()
686+
cred.expiry = datetime.datetime.utcnow()
658687

659688
loader = KubeConfigLoader(
660689
config_dict=self.TEST_KUBE_CONFIG,
661690
active_context="expired_gcp",
662691
get_google_credentials=lambda: cred)
663-
original_expiry = _get_expiry(loader)
692+
original_expiry = _get_expiry(loader, "expired_gcp")
664693
self.assertTrue(loader._load_auth_provider_token())
665-
new_expiry = _get_expiry(loader)
694+
new_expiry = _get_expiry(loader, "expired_gcp")
666695
# assert that the configs expiry actually updates
667696
self.assertTrue(new_expiry > original_expiry)
668697
self.assertEqual(BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64,
669698
loader.token)
670699

700+
def test_gcp_get_api_key_with_prefix(self):
701+
class cred_old:
702+
token = TEST_DATA_BASE64
703+
expiry = DATETIME_EXPIRY_PAST
704+
class cred_new:
705+
token = TEST_ANOTHER_DATA_BASE64
706+
expiry = DATETIME_EXPIRY_FUTURE
707+
fake_config = FakeConfig()
708+
_get_google_credentials = mock.Mock()
709+
_get_google_credentials.side_effect = [cred_old, cred_new]
710+
711+
loader = KubeConfigLoader(
712+
config_dict=self.TEST_KUBE_CONFIG,
713+
active_context="expired_gcp_refresh",
714+
get_google_credentials=_get_google_credentials)
715+
loader.load_and_set(fake_config)
716+
original_expiry = _get_expiry(loader, "expired_gcp_refresh")
717+
# Call GCP token fetcher.
718+
token = fake_config.get_api_key_with_prefix()
719+
new_expiry = _get_expiry(loader, "expired_gcp_refresh")
720+
721+
self.assertTrue(new_expiry > original_expiry)
722+
self.assertEqual(BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64,
723+
loader.token)
724+
self.assertEqual(BEARER_TOKEN_FORMAT % TEST_ANOTHER_DATA_BASE64,
725+
token)
726+
671727
def test_oidc_no_refresh(self):
672728
loader = KubeConfigLoader(
673729
config_dict=self.TEST_KUBE_CONFIG,

0 commit comments

Comments
 (0)