From 1a6b498657ec5dd29ddf4f6b240c6fc0c5d88f7a Mon Sep 17 00:00:00 2001 From: Micah Hausler Date: Mon, 11 Oct 2021 15:27:24 -0400 Subject: [PATCH 1/4] Deprecate Kubernetes client API version v1alpha1 Kubernetes has deprecated v1alpha1, v1beta1 has been available since Kubernetes v1.11 (kubernetes/kubernetes#64482), and EKS currently supports Kubernetes versions v1.16 through v1.21. This is a breaking change for clients running versions v1.10 and older, which haven't been supported by EKS since September 2019. "aws eks get-token" now respects the KUBERNETES_EXEC_INFO environment variable and conservatively falls back to v1alpha1, which is supported by Kubernetes versions 1.10 through 1.22 (released upstream August 2021, to be released by EKS in Q4 2021). It also now supports "v1beta1" and "v1". "aws eks update-kubeconfig" now writes "v1beta1" in the kubeconfig which will be supported by Kubernetes until 1.29 (aproximately December 2023). At or around that date, we can change the default version written to kubeconfigs to "v1" Signed-off-by: Micah Hausler --- awscli/customizations/eks/get_token.py | 70 +++++++++++- .../customizations/eks/update_kubeconfig.py | 9 +- awscli/examples/eks/get-token.rst | 2 +- tests/functional/eks/test_get_token.py | 103 ++++++++++++++++++ tests/functional/eks/test_kubeconfig.py | 10 +- .../functional/eks/test_update_kubeconfig.py | 23 ++-- .../eks/testdata/invalid_string_cluster_entry | 2 +- .../eks/testdata/invalid_string_clusters | 2 +- .../eks/testdata/invalid_string_context_entry | 2 +- .../eks/testdata/invalid_string_contexts | 2 +- tests/functional/eks/testdata/output_combined | 4 +- .../testdata/output_combined_changed_ordering | 4 +- tests/functional/eks/testdata/output_single | 2 +- .../functional/eks/testdata/valid_bad_cluster | 2 +- .../eks/testdata/valid_bad_cluster2 | 2 +- .../functional/eks/testdata/valid_bad_context | 2 +- .../eks/testdata/valid_bad_context2 | 2 +- tests/functional/eks/testdata/valid_bad_user | 2 +- .../eks/testdata/valid_changed_ordering | 2 +- tests/functional/eks/testdata/valid_existing | 2 +- .../functional/eks/testdata/valid_no_cluster | 2 +- .../functional/eks/testdata/valid_no_context | 2 +- .../eks/testdata/valid_no_current_context | 2 +- .../eks/testdata/valid_old_api_version | 27 +++++ .../testdata/valid_old_api_version_updated | 27 +++++ tests/functional/eks/testdata/valid_old_data | 4 +- 26 files changed, 272 insertions(+), 41 deletions(-) create mode 100644 tests/functional/eks/testdata/valid_old_api_version create mode 100644 tests/functional/eks/testdata/valid_old_api_version_updated diff --git a/awscli/customizations/eks/get_token.py b/awscli/customizations/eks/get_token.py index 1208f965505d..cc0dbb79d3a4 100644 --- a/awscli/customizations/eks/get_token.py +++ b/awscli/customizations/eks/get_token.py @@ -13,6 +13,8 @@ import base64 import botocore import json +import os +import sys from datetime import datetime, timedelta from botocore.signers import RequestSigner @@ -74,7 +76,7 @@ def _run_main(self, parsed_args, parsed_globals): full_object = { "kind": "ExecCredential", - "apiVersion": "client.authentication.k8s.io/v1alpha1", + "apiVersion": self.discover_api_version(), "spec": {}, "status": { "expirationTimestamp": token_expiration, @@ -86,6 +88,72 @@ def _run_main(self, parsed_args, parsed_globals): uni_print('\n') return 0 + def discover_api_version(self): + """ + Parses the KUBERNETES_EXEC_INFO environment variable and returns the API + version. If the environment variable is empty, malformed, or invalid, + return the v1alpha1 response and print an message to stderr. + + If the v1alpha1 API is specified explicitly, a message is printed to + stderr with instructions to update. + + :return: The client authentication API version + :rtype: string + """ + alpha_api = "client.authentication.k8s.io/v1alpha1" + beta_api = "client.authentication.k8s.io/v1beta1" + v1_api = "client.authentication.k8s.io/v1" + # At the time Kubernetes v1.29 is released upstream (aprox Dec 2023), + # "v1beta1" will be removed. At or around that time, EKS will likely + # support v1.22 through v1.28, in which client API version "v1beta1" + # will be supported by all EKS versions. + fallback_api_version = alpha_api + + error_prefixes = { + "error": "Error parsing", + "empty": "Empty", + } + + error_msg_tpl = ("{0} KUBERNETES_EXEC_INFO, defaulting " + "to {1}. This is likely a bug in your Kubernetes " + "client. Please update your Kubernetes client.") + unrecognized_msg = ("Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting " + "to {0}. This is likely due to an outdated AWS CLI." + " Please update your AWS CLI.".format(fallback_api_version)) + deprecation_msg_tpl = ("Kubeconfig user entry is using deprecated API " + "version {0}. Run 'aws eks update-kubeconfig' to update") + + exec_info_raw = os.environ.get("KUBERNETES_EXEC_INFO", "") + if len(exec_info_raw) == 0: + # All kube clients should be setting this, we'll return the fallback and write an error + sys.stderr.write(error_msg_tpl.format(error_prefixes["empty"], fallback_api_version)) + sys.stderr.write("\n") + sys.stderr.flush() + return fallback_api_version + try: + exec_info = json.loads(exec_info_raw) + except json.JSONDecodeError as e: + # The environment variable was malformed + sys.stderr.write(error_msg_tpl.format(error_prefixes["error"], fallback_api_version)) + sys.stderr.write("\n") + sys.stderr.flush() + return fallback_api_version + + api_version_raw = exec_info.get("apiVersion") + if api_version_raw == v1_api: + return v1_api + if api_version_raw == beta_api: + return beta_api + if api_version_raw == alpha_api: + sys.stderr.write(deprecation_msg_tpl.format(alpha_api)) + sys.stderr.write("\n") + sys.stderr.flush() + return alpha_api + + sys.stderr.write(unrecognized_msg) + sys.stderr.write("\n") + sys.stderr.flush() + return fallback_api_version class TokenGenerator(object): def __init__(self, sts_client): diff --git a/awscli/customizations/eks/update_kubeconfig.py b/awscli/customizations/eks/update_kubeconfig.py index cf9d02c64510..436ab30ae5d3 100644 --- a/awscli/customizations/eks/update_kubeconfig.py +++ b/awscli/customizations/eks/update_kubeconfig.py @@ -31,10 +31,9 @@ DEFAULT_PATH = os.path.expanduser("~/.kube/config") -# Use the endpoint for kubernetes 1.10 -# To get the most recent endpoint we will need to -# Do a check on the cluster's version number -API_VERSION = "client.authentication.k8s.io/v1alpha1" +# At the time EKS no longer supports Kubernetes v1.21 (probably ~Dec 2023), +# this can be safely changed to default to writing "v1" +API_VERSION = "client.authentication.k8s.io/v1beta1" class UpdateKubeconfigCommand(BasicCommand): NAME = 'update-kubeconfig' @@ -306,7 +305,7 @@ def get_user_entry(self): "--cluster-name", self._cluster_name, ]), - ("command", "aws") + ("command", "aws"), ])) ])) ]) diff --git a/awscli/examples/eks/get-token.rst b/awscli/examples/eks/get-token.rst index 92a94e5be01b..6a5c7b25c2f0 100644 --- a/awscli/examples/eks/get-token.rst +++ b/awscli/examples/eks/get-token.rst @@ -10,7 +10,7 @@ Output:: { "kind": "ExecCredential", - "apiVersion": "client.authentication.k8s.io/v1alpha1", + "apiVersion": "client.authentication.k8s.io/v1beta1", "spec": {}, "status": { "expirationTimestamp": "2019-08-14T18:44:27Z", diff --git a/tests/functional/eks/test_get_token.py b/tests/functional/eks/test_get_token.py index 379c2a090214..8fd453f2b871 100644 --- a/tests/functional/eks/test_get_token.py +++ b/tests/functional/eks/test_get_token.py @@ -13,6 +13,7 @@ import base64 from datetime import datetime import json +import os from awscli.testutils import mock from awscli.testutils import BaseAWSCommandParamsTest @@ -143,3 +144,105 @@ def test_url_different_partition(self): expected_endpoint='sts.cn-north-1.amazonaws.com.cn', expected_signing_region='cn-north-1' ) + + def test_api_version_discovery_deprecated(self): + os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1alpha1","spec":{"interactive":true}}' + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + stdout, stderr, _ = self.run_cmd(cmd) + response = json.loads(stdout) + + self.assertEqual( + response["apiVersion"], + "client.authentication.k8s.io/v1alpha1", + ) + + self.assertEqual( + stderr, + ("Kubeconfig user entry is using deprecated API " + "version client.authentication.k8s.io/v1alpha1. Run 'aws eks update-kubeconfig' to update\n") + ) + + del os.environ["KUBERNETES_EXEC_INFO"] + + def test_api_version_discovery_malformed(self): + os.environ["KUBERNETES_EXEC_INFO"] = '{{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1alpha1","spec":{"interactive":true}}' + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + stdout, stderr, _ = self.run_cmd(cmd) + response = json.loads(stdout) + + self.assertEqual( + response["apiVersion"], + "client.authentication.k8s.io/v1alpha1", + ) + + self.assertEqual( + stderr, + ("Error parsing KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " + "This is likely a bug in your Kubernetes client. Please update your Kubernetes client.\n") + ) + + del os.environ["KUBERNETES_EXEC_INFO"] + + def test_api_version_discovery_empty(self): + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + stdout, stderr, _ = self.run_cmd(cmd) + response = json.loads(stdout) + + self.assertEqual( + response["apiVersion"], + "client.authentication.k8s.io/v1alpha1", + ) + + self.assertEqual( + stderr, + ("Empty KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " + "This is likely a bug in your Kubernetes client. Please update your Kubernetes client.\n") + ) + def test_api_version_discovery_v1(self): + os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1","spec":{"interactive":true}}' + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + stdout, stderr, _ = self.run_cmd(cmd) + response = json.loads(stdout) + + self.assertEqual( + response["apiVersion"], + "client.authentication.k8s.io/v1", + ) + + self.assertEqual(stderr, "") + + del os.environ["KUBERNETES_EXEC_INFO"] + + def test_api_version_discovery_v1beta1(self): + os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":true}}' + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + stdout, stderr, _ = self.run_cmd(cmd) + response = json.loads(stdout) + + self.assertEqual( + response["apiVersion"], + "client.authentication.k8s.io/v1beta1", + ) + + self.assertEqual(stderr, "") + + del os.environ["KUBERNETES_EXEC_INFO"] + + def test_api_version_discovery_unknown(self): + os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v2","spec":{"interactive":true}}' + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + stdout, stderr, _ = self.run_cmd(cmd) + response = json.loads(stdout) + + self.assertEqual( + response["apiVersion"], + "client.authentication.k8s.io/v1alpha1", + ) + + self.assertEqual( + stderr, + ("Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " + "This is likely due to an outdated AWS CLI. Please update your AWS CLI.\n") + ) + + del os.environ["KUBERNETES_EXEC_INFO"] \ No newline at end of file diff --git a/tests/functional/eks/test_kubeconfig.py b/tests/functional/eks/test_kubeconfig.py index 5d1ae12468dd..43b2221d175a 100644 --- a/tests/functional/eks/test_kubeconfig.py +++ b/tests/functional/eks/test_kubeconfig.py @@ -95,7 +95,7 @@ def _clone_config(self, config): """ old_path = os.path.abspath(get_testdata(config)) new_path = os.path.join(self._temp_directory, config) - shutil.copy2(old_path, + shutil.copy2(old_path, new_path) return new_path @@ -119,14 +119,14 @@ def test_load_simple(self): ]) loaded_config = self._loader.load_kubeconfig(simple_path) self.assertEqual(loaded_config.content, content) - self._validator.validate_config.called_with(Kubeconfig(simple_path, + self._validator.validate_config.called_with(Kubeconfig(simple_path, content)) def test_load_noexist(self): no_exist_path = os.path.join(self._temp_directory, "this_does_not_exist") loaded_config = self._loader.load_kubeconfig(no_exist_path) - self.assertEqual(loaded_config.content, + self.assertEqual(loaded_config.content, _get_new_kubeconfig_content()) self._validator.validate_config.called_with( Kubeconfig(no_exist_path, _get_new_kubeconfig_content())) @@ -134,10 +134,10 @@ def test_load_noexist(self): def test_load_empty(self): empty_path = self._clone_config("valid_empty_existing") loaded_config = self._loader.load_kubeconfig(empty_path) - self.assertEqual(loaded_config.content, + self.assertEqual(loaded_config.content, _get_new_kubeconfig_content()) self._validator.validate_config.called_with( - Kubeconfig(empty_path, + Kubeconfig(empty_path, _get_new_kubeconfig_content())) def test_load_directory(self): diff --git a/tests/functional/eks/test_update_kubeconfig.py b/tests/functional/eks/test_update_kubeconfig.py index 31c44f6fd7e8..9ceb97885c15 100644 --- a/tests/functional/eks/test_update_kubeconfig.py +++ b/tests/functional/eks/test_update_kubeconfig.py @@ -58,14 +58,14 @@ def setUp(self): self.create_client_patch = mock.patch( 'botocore.session.Session.create_client' ) - + self.mock_create_client = self.create_client_patch.start() self.session = get_session() self.client = mock.Mock() self.client.describe_cluster.return_value = describe_cluster_response() self.mock_create_client.return_value = self.client - + self.command = UpdateKubeconfigCommand(self.session) self.maxDiff = None @@ -102,7 +102,7 @@ def initialize_tempfiles(self, files): self.addCleanup(shutil.rmtree, self._temp_directory) if files is not None: for file in files: - shutil.copy2(get_testdata(file), + shutil.copy2(get_testdata(file), self._get_temp_config(file)) return self._temp_directory @@ -116,7 +116,7 @@ def build_temp_environment_variable(self, configs): to put in the environment variable :type configs: list """ - return build_environment([self._get_temp_config(config) + return build_environment([self._get_temp_config(config) for config in configs]) def assert_config_state(self, config_name, correct_output_name): @@ -125,7 +125,7 @@ def assert_config_state(self, config_name, correct_output_name): as the testdata named correct_output_name. Should be called after initialize_tempfiles. - :param config_name: The filename (not the path) of the tempfile + :param config_name: The filename (not the path) of the tempfile to compare :type config_name: str @@ -189,7 +189,7 @@ def assert_cmd(self, configs, passed_config, verbose=False): """ Run update-kubeconfig in a temp directory, - This directory will have copies of all testdata files whose names + This directory will have copies of all testdata files whose names are listed in configs. The KUBECONFIG environment variable will be set to contain the configs listed in env_variable_configs (regardless of whether they exist). @@ -384,7 +384,7 @@ def test_update_existing(self): configs = ["valid_old_data"] passed = "valid_old_data" environment = [] - + self.assert_cmd(configs, passed, environment) self.assert_config_state("valid_old_data", "output_combined") @@ -394,7 +394,7 @@ def test_update_existing_environment(self): environment = ["valid_old_data", "output_combined", "output_single"] - + self.assert_cmd(configs, passed, environment) self.assert_config_state("valid_old_data", "output_combined") @@ -415,3 +415,10 @@ def test_kubeconfig_order(self): self.assert_cmd(configs, passed, environment) self.assert_config_state("valid_changed_ordering", "output_combined_changed_ordering") + def test_update_old_api_version(self): + configs = ["valid_old_api_version"] + passed = "valid_old_api_version" + environment = [] + + self.assert_cmd(configs, passed, environment) + self.assert_config_state("valid_old_api_version", "valid_old_api_version_updated") diff --git a/tests/functional/eks/testdata/invalid_string_cluster_entry b/tests/functional/eks/testdata/invalid_string_cluster_entry index 3445428dfa90..6ddbe5038426 100644 --- a/tests/functional/eks/testdata/invalid_string_cluster_entry +++ b/tests/functional/eks/testdata/invalid_string_cluster_entry @@ -13,7 +13,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/invalid_string_clusters b/tests/functional/eks/testdata/invalid_string_clusters index f6eeb57fd9a8..664414392d20 100644 --- a/tests/functional/eks/testdata/invalid_string_clusters +++ b/tests/functional/eks/testdata/invalid_string_clusters @@ -12,7 +12,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/invalid_string_context_entry b/tests/functional/eks/testdata/invalid_string_context_entry index 3762197aaf6b..038276cb80cd 100644 --- a/tests/functional/eks/testdata/invalid_string_context_entry +++ b/tests/functional/eks/testdata/invalid_string_context_entry @@ -13,7 +13,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/invalid_string_contexts b/tests/functional/eks/testdata/invalid_string_contexts index 07c34aa406f3..be84d241734b 100644 --- a/tests/functional/eks/testdata/invalid_string_contexts +++ b/tests/functional/eks/testdata/invalid_string_contexts @@ -12,7 +12,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/output_combined b/tests/functional/eks/testdata/output_combined index 4c097a5a4e03..ed2e94a07326 100644 --- a/tests/functional/eks/testdata/output_combined +++ b/tests/functional/eks/testdata/output_combined @@ -24,7 +24,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 @@ -36,7 +36,7 @@ users: - name: arn:aws:eks:region:111222333444:cluster/ExampleCluster user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - region diff --git a/tests/functional/eks/testdata/output_combined_changed_ordering b/tests/functional/eks/testdata/output_combined_changed_ordering index fea7a86524c6..3e489d3351cb 100644 --- a/tests/functional/eks/testdata/output_combined_changed_ordering +++ b/tests/functional/eks/testdata/output_combined_changed_ordering @@ -2,7 +2,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 @@ -14,7 +14,7 @@ users: - name: arn:aws:eks:region:111222333444:cluster/ExampleCluster user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - region diff --git a/tests/functional/eks/testdata/output_single b/tests/functional/eks/testdata/output_single index 20e776b5fc39..382c1c2400f3 100644 --- a/tests/functional/eks/testdata/output_single +++ b/tests/functional/eks/testdata/output_single @@ -16,7 +16,7 @@ users: - name: arn:aws:eks:region:111222333444:cluster/ExampleCluster user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - region diff --git a/tests/functional/eks/testdata/valid_bad_cluster b/tests/functional/eks/testdata/valid_bad_cluster index 3c3ae4702c0d..1cefd5d84a3e 100644 --- a/tests/functional/eks/testdata/valid_bad_cluster +++ b/tests/functional/eks/testdata/valid_bad_cluster @@ -13,7 +13,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_bad_cluster2 b/tests/functional/eks/testdata/valid_bad_cluster2 index a99fe2ffc6ef..b9aa058f716f 100644 --- a/tests/functional/eks/testdata/valid_bad_cluster2 +++ b/tests/functional/eks/testdata/valid_bad_cluster2 @@ -15,7 +15,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_bad_context b/tests/functional/eks/testdata/valid_bad_context index cadf61557e60..25822d2ff511 100644 --- a/tests/functional/eks/testdata/valid_bad_context +++ b/tests/functional/eks/testdata/valid_bad_context @@ -13,7 +13,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_bad_context2 b/tests/functional/eks/testdata/valid_bad_context2 index b3e1e6f2c29b..b3fa13325d7c 100644 --- a/tests/functional/eks/testdata/valid_bad_context2 +++ b/tests/functional/eks/testdata/valid_bad_context2 @@ -15,7 +15,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_bad_user b/tests/functional/eks/testdata/valid_bad_user index 30287eadb34e..606958ffa944 100644 --- a/tests/functional/eks/testdata/valid_bad_user +++ b/tests/functional/eks/testdata/valid_bad_user @@ -15,7 +15,7 @@ preferences: {} users: - user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_changed_ordering b/tests/functional/eks/testdata/valid_changed_ordering index 5dc823f7a925..08752be0d3e6 100644 --- a/tests/functional/eks/testdata/valid_changed_ordering +++ b/tests/functional/eks/testdata/valid_changed_ordering @@ -2,7 +2,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_existing b/tests/functional/eks/testdata/valid_existing index 9a00b4e3ef14..34efc84a18be 100644 --- a/tests/functional/eks/testdata/valid_existing +++ b/tests/functional/eks/testdata/valid_existing @@ -16,7 +16,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_no_cluster b/tests/functional/eks/testdata/valid_no_cluster index 4da6bae2e105..01c73a713d41 100644 --- a/tests/functional/eks/testdata/valid_no_cluster +++ b/tests/functional/eks/testdata/valid_no_cluster @@ -11,7 +11,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_no_context b/tests/functional/eks/testdata/valid_no_context index d98d38ed6af9..00a36d1a0da2 100644 --- a/tests/functional/eks/testdata/valid_no_context +++ b/tests/functional/eks/testdata/valid_no_context @@ -11,7 +11,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_no_current_context b/tests/functional/eks/testdata/valid_no_current_context index 0bb78320e3bd..b8399db64ec1 100644 --- a/tests/functional/eks/testdata/valid_no_current_context +++ b/tests/functional/eks/testdata/valid_no_current_context @@ -15,7 +15,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 diff --git a/tests/functional/eks/testdata/valid_old_api_version b/tests/functional/eks/testdata/valid_old_api_version new file mode 100644 index 000000000000..6a99cc9cb365 --- /dev/null +++ b/tests/functional/eks/testdata/valid_old_api_version @@ -0,0 +1,27 @@ +apiVersion: v1 +clusters: +- cluster: + certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpWR1Z6ZEdsdVp5QkVZWFJoRFFwVVpYTjBhVzVuSUVSaGRHRU5DbFJsYzNScGJtY2dSR0YwWVEwS2EzVmlaWEp1WlhSbGN6QWVGdzBLVkdWemRHbHVaeUJFWVhSaERRcFVaWE4wYVc1bklFUmhkR0ZWQkFNVERRcHJkV0psY201bGRHVnpNQUVpTUEwS1ZHVnpkR2x1WnlCRVlYUmhEUXBVWlhOMGFXNW5JRVJoZEdFTkNsUmxjM1JwYm1jZ1JHRjBZY3UvR1FnbmFTcDNZaHBDTWhGVVpYTjBhVzVuSUVSaGRHRXl3clZqeEpWNjNwNFVHRmpZdHdGR1drUldJVkV1VkdWemRHbHVaeUJFWVhSaGJzT0MxSVJiTDhPd0lpMVhiWGg2VkdWemRHbHVaeUJFWVhSaFpXVndTTk9VVUZKNmN5QWJaaFpnWVNkTUV3MEtGMVJsYzNScGJtY2dSR0YwWVFZRFZSMFBBUUVFQkFNQ0FsUmxjM1JwYm1jZ1JHRjBZUUV3RFFvR0NTcElEUXBVWlhOMGFXNW5JRVJoZEdGcEgxc1pPRTNMa3lrMU9DWUNHUloyTEZjM3paOCtHell3WEZSbGMzUnBibWNnUkdGMFlYMUR5NjFNMVlGV1AxWVRIMVJsYzNScGJtY2dSR0YwWVd0aE5oMVphM2dWUDBGaGNSWjdKaW9oZVc4N1JsUmxjM1JwYm1jZ1JHRjBZUVpIVHd4NE9IdzZmZz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t + server: https://endpoint.amazonaws.com + name: arn:aws:eks:region:111222333444:cluster/ExampleCluster +contexts: +- context: + cluster: arn:aws:eks:region:111222333444:cluster/ExampleCluster + user: arn:aws:eks:region:111222333444:cluster/ExampleCluster + name: arn:aws:eks:region:111222333444:cluster/ExampleCluster +current-context: arn:aws:eks:region:111222333444:cluster/ExampleCluster +kind: Config +preferences: {} +users: +- name: arn:aws:eks:region:111222333444:cluster/ExampleCluster + user: + exec: + apiVersion: client.authentication.k8s.io/v1alpha1 + args: + - --region + - us-west-2 + - eks + - get-token + - --cluster-name + - ExampleCluster + command: aws diff --git a/tests/functional/eks/testdata/valid_old_api_version_updated b/tests/functional/eks/testdata/valid_old_api_version_updated new file mode 100644 index 000000000000..382c1c2400f3 --- /dev/null +++ b/tests/functional/eks/testdata/valid_old_api_version_updated @@ -0,0 +1,27 @@ +apiVersion: v1 +clusters: +- cluster: + certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpWR1Z6ZEdsdVp5QkVZWFJoRFFwVVpYTjBhVzVuSUVSaGRHRU5DbFJsYzNScGJtY2dSR0YwWVEwS2EzVmlaWEp1WlhSbGN6QWVGdzBLVkdWemRHbHVaeUJFWVhSaERRcFVaWE4wYVc1bklFUmhkR0ZWQkFNVERRcHJkV0psY201bGRHVnpNQUVpTUEwS1ZHVnpkR2x1WnlCRVlYUmhEUXBVWlhOMGFXNW5JRVJoZEdFTkNsUmxjM1JwYm1jZ1JHRjBZY3UvR1FnbmFTcDNZaHBDTWhGVVpYTjBhVzVuSUVSaGRHRXl3clZqeEpWNjNwNFVHRmpZdHdGR1drUldJVkV1VkdWemRHbHVaeUJFWVhSaGJzT0MxSVJiTDhPd0lpMVhiWGg2VkdWemRHbHVaeUJFWVhSaFpXVndTTk9VVUZKNmN5QWJaaFpnWVNkTUV3MEtGMVJsYzNScGJtY2dSR0YwWVFZRFZSMFBBUUVFQkFNQ0FsUmxjM1JwYm1jZ1JHRjBZUUV3RFFvR0NTcElEUXBVWlhOMGFXNW5JRVJoZEdGcEgxc1pPRTNMa3lrMU9DWUNHUloyTEZjM3paOCtHell3WEZSbGMzUnBibWNnUkdGMFlYMUR5NjFNMVlGV1AxWVRIMVJsYzNScGJtY2dSR0YwWVd0aE5oMVphM2dWUDBGaGNSWjdKaW9oZVc4N1JsUmxjM1JwYm1jZ1JHRjBZUVpIVHd4NE9IdzZmZz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t + server: https://endpoint.amazonaws.com + name: arn:aws:eks:region:111222333444:cluster/ExampleCluster +contexts: +- context: + cluster: arn:aws:eks:region:111222333444:cluster/ExampleCluster + user: arn:aws:eks:region:111222333444:cluster/ExampleCluster + name: arn:aws:eks:region:111222333444:cluster/ExampleCluster +current-context: arn:aws:eks:region:111222333444:cluster/ExampleCluster +kind: Config +preferences: {} +users: +- name: arn:aws:eks:region:111222333444:cluster/ExampleCluster + user: + exec: + apiVersion: client.authentication.k8s.io/v1beta1 + args: + - --region + - region + - eks + - get-token + - --cluster-name + - ExampleCluster + command: aws diff --git a/tests/functional/eks/testdata/valid_old_data b/tests/functional/eks/testdata/valid_old_data index 525c7e56da08..6850397cdb7f 100644 --- a/tests/functional/eks/testdata/valid_old_data +++ b/tests/functional/eks/testdata/valid_old_data @@ -24,7 +24,7 @@ users: - name: arn:aws:eks:us-west-2:111222333444:cluster/Existing user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - --region - us-west-2 @@ -36,7 +36,7 @@ users: - name: arn:aws:eks:region:111222333444:cluster/ExampleCluster user: exec: - apiVersion: client.authentication.k8s.io/v1alpha1 + apiVersion: client.authentication.k8s.io/v1beta1 args: - token - -i From 18fdfa30f06a4b5eb777bf4b26e111c8eaea27f5 Mon Sep 17 00:00:00 2001 From: Justin Ho Date: Thu, 5 May 2022 16:08:02 -0700 Subject: [PATCH 2/4] Address feedback in PR --- .../enhancement-eksgettoken-91863.json | 5 + ...enhancement-eksupdatekubeconfig-63017.json | 5 + awscli/customizations/eks/get_token.py | 157 +++++++++++------- tests/functional/eks/test_get_token.py | 106 +++++++----- 4 files changed, 165 insertions(+), 108 deletions(-) create mode 100644 .changes/next-release/enhancement-eksgettoken-91863.json create mode 100644 .changes/next-release/enhancement-eksupdatekubeconfig-63017.json diff --git a/.changes/next-release/enhancement-eksgettoken-91863.json b/.changes/next-release/enhancement-eksgettoken-91863.json new file mode 100644 index 000000000000..81c9102f3797 --- /dev/null +++ b/.changes/next-release/enhancement-eksgettoken-91863.json @@ -0,0 +1,5 @@ +{ + "type": "enhancement", + "category": "eks get-token", + "description": "Add support to respect env var KUBERNETES_EXEC_INFO in eks get-token" +} diff --git a/.changes/next-release/enhancement-eksupdatekubeconfig-63017.json b/.changes/next-release/enhancement-eksupdatekubeconfig-63017.json new file mode 100644 index 000000000000..33dc9461243e --- /dev/null +++ b/.changes/next-release/enhancement-eksupdatekubeconfig-63017.json @@ -0,0 +1,5 @@ +{ + "type": "enhancement", + "category": "eks update-kubeconfig", + "description": "Update default API version for eks update-kubeconfig" +} diff --git a/awscli/customizations/eks/get_token.py b/awscli/customizations/eks/get_token.py index cc0dbb79d3a4..0a60fe0372ff 100644 --- a/awscli/customizations/eks/get_token.py +++ b/awscli/customizations/eks/get_token.py @@ -28,6 +28,18 @@ AUTH_API_VERSION = "2011-06-15" AUTH_SIGNING_VERSION = "v4" +ALPHA_API = "client.authentication.k8s.io/v1alpha1" +BETA_API = "client.authentication.k8s.io/v1beta1" +V1_API = "client.authentication.k8s.io/v1" + +FULLY_SUPPORTED_API_VERSIONS = [ + V1_API, + BETA_API, +] +DEPRECATED_API_VERSIONS = [ + ALPHA_API, +] + # Presigned url timeout in seconds URL_TIMEOUT = 60 @@ -41,32 +53,40 @@ class GetTokenCommand(BasicCommand): NAME = 'get-token' - DESCRIPTION = ("Get a token for authentication with an Amazon EKS cluster. " - "This can be used as an alternative to the " - "aws-iam-authenticator.") + DESCRIPTION = ( + "Get a token for authentication with an Amazon EKS cluster. " + "This can be used as an alternative to the " + "aws-iam-authenticator." + ) ARG_TABLE = [ { 'name': 'cluster-name', - 'help_text': ("Specify the name of the Amazon EKS cluster to create a token for."), - 'required': True + 'help_text': ( + "Specify the name of the Amazon EKS cluster to create a token for." + ), + 'required': True, }, { 'name': 'role-arn', - 'help_text': ("Assume this role for credentials when signing the token."), - 'required': False - } + 'help_text': ( + "Assume this role for credentials when signing the token." + ), + 'required': False, + }, ] def get_expiration_time(self): - token_expiration = datetime.utcnow() + timedelta(minutes=TOKEN_EXPIRATION_MINS) + token_expiration = datetime.utcnow() + timedelta( + minutes=TOKEN_EXPIRATION_MINS + ) return token_expiration.strftime('%Y-%m-%dT%H:%M:%SZ') def _run_main(self, parsed_args, parsed_globals): client_factory = STSClientFactory(self._session) sts_client = client_factory.get_sts_client( - region_name=parsed_globals.region, - role_arn=parsed_args.role_arn) + region_name=parsed_globals.region, role_arn=parsed_args.role_arn + ) token = TokenGenerator(sts_client).get_token(parsed_args.cluster_name) # By default STS signs the url for 15 minutes so we are creating a @@ -80,8 +100,8 @@ def _run_main(self, parsed_args, parsed_globals): "spec": {}, "status": { "expirationTimestamp": token_expiration, - "token": token - } + "token": token, + }, } uni_print(json.dumps(full_object)) @@ -90,9 +110,9 @@ def _run_main(self, parsed_args, parsed_globals): def discover_api_version(self): """ - Parses the KUBERNETES_EXEC_INFO environment variable and returns the API - version. If the environment variable is empty, malformed, or invalid, - return the v1alpha1 response and print an message to stderr. + Parses the KUBERNETES_EXEC_INFO environment variable and returns the + API version. If the environment variable is empty, malformed, or + invalid, return the v1alpha1 response and print a message to stderr. If the v1alpha1 API is specified explicitly, a message is printed to stderr with instructions to update. @@ -100,70 +120,83 @@ def discover_api_version(self): :return: The client authentication API version :rtype: string """ - alpha_api = "client.authentication.k8s.io/v1alpha1" - beta_api = "client.authentication.k8s.io/v1beta1" - v1_api = "client.authentication.k8s.io/v1" - # At the time Kubernetes v1.29 is released upstream (aprox Dec 2023), + # At the time Kubernetes v1.29 is released upstream (approx Dec 2023), # "v1beta1" will be removed. At or around that time, EKS will likely # support v1.22 through v1.28, in which client API version "v1beta1" # will be supported by all EKS versions. - fallback_api_version = alpha_api + fallback_api_version = ALPHA_API error_prefixes = { "error": "Error parsing", "empty": "Empty", } - error_msg_tpl = ("{0} KUBERNETES_EXEC_INFO, defaulting " - "to {1}. This is likely a bug in your Kubernetes " - "client. Please update your Kubernetes client.") - unrecognized_msg = ("Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting " - "to {0}. This is likely due to an outdated AWS CLI." - " Please update your AWS CLI.".format(fallback_api_version)) - deprecation_msg_tpl = ("Kubeconfig user entry is using deprecated API " - "version {0}. Run 'aws eks update-kubeconfig' to update") + error_msg_tpl = ( + "{0} KUBERNETES_EXEC_INFO, defaulting to {1}. This is likely a " + "bug in your Kubernetes client. Please update your Kubernetes " + "client." + ) + unrecognized_msg = ( + "Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting to " + f"{fallback_api_version}. This is likely due to an outdated AWS " + "CLI. Please update your AWS CLI." + ) + deprecation_msg_tpl = ( + "Kubeconfig user entry is using deprecated API version {0}. Run " + "'aws eks update-kubeconfig' to update." + ) exec_info_raw = os.environ.get("KUBERNETES_EXEC_INFO", "") - if len(exec_info_raw) == 0: - # All kube clients should be setting this, we'll return the fallback and write an error - sys.stderr.write(error_msg_tpl.format(error_prefixes["empty"], fallback_api_version)) - sys.stderr.write("\n") - sys.stderr.flush() + if not exec_info_raw: + # All kube clients should be setting this. Otherewise, we'll return + # the fallback and write an error. + uni_print( + error_msg_tpl.format( + error_prefixes["empty"], + fallback_api_version, + ), + sys.stderr, + ) + uni_print("\n", sys.stderr) return fallback_api_version try: exec_info = json.loads(exec_info_raw) - except json.JSONDecodeError as e: + except json.JSONDecodeError: # The environment variable was malformed - sys.stderr.write(error_msg_tpl.format(error_prefixes["error"], fallback_api_version)) - sys.stderr.write("\n") - sys.stderr.flush() + uni_print( + error_msg_tpl.format( + error_prefixes["error"], + fallback_api_version, + ), + sys.stderr, + ) + uni_print("\n", sys.stderr) return fallback_api_version api_version_raw = exec_info.get("apiVersion") - if api_version_raw == v1_api: - return v1_api - if api_version_raw == beta_api: - return beta_api - if api_version_raw == alpha_api: - sys.stderr.write(deprecation_msg_tpl.format(alpha_api)) - sys.stderr.write("\n") - sys.stderr.flush() - return alpha_api - - sys.stderr.write(unrecognized_msg) - sys.stderr.write("\n") - sys.stderr.flush() - return fallback_api_version + if api_version_raw in FULLY_SUPPORTED_API_VERSIONS: + return api_version_raw + elif api_version_raw in DEPRECATED_API_VERSIONS: + uni_print(deprecation_msg_tpl.format(ALPHA_API), sys.stderr) + uni_print("\n", sys.stderr) + return api_version_raw + else: + # write unrecognized api version message + uni_print(unrecognized_msg, sys.stderr) + uni_print("\n", sys.stderr) + return fallback_api_version + class TokenGenerator(object): def __init__(self, sts_client): self._sts_client = sts_client def get_token(self, cluster_name): - """ Generate a presigned url token to pass to kubectl. """ + """Generate a presigned url token to pass to kubectl.""" url = self._get_presigned_url(cluster_name) token = TOKEN_PREFIX + base64.urlsafe_b64encode( - url.encode('utf-8')).decode('utf-8').rstrip('=') + url.encode('utf-8') + ).decode('utf-8').rstrip('=') return token def _get_presigned_url(self, cluster_name): @@ -180,9 +213,7 @@ def __init__(self, session): self._session = session def get_sts_client(self, region_name=None, role_arn=None): - client_kwargs = { - 'region_name': region_name - } + client_kwargs = {'region_name': region_name} if role_arn is not None: creds = self._get_role_credentials(region_name, role_arn) client_kwargs['aws_access_key_id'] = creds['AccessKeyId'] @@ -195,18 +226,17 @@ def get_sts_client(self, region_name=None, role_arn=None): def _get_role_credentials(self, region_name, role_arn): sts = self._session.create_client('sts', region_name) return sts.assume_role( - RoleArn=role_arn, - RoleSessionName='EKSGetTokenAuth' + RoleArn=role_arn, RoleSessionName='EKSGetTokenAuth' )['Credentials'] def _register_cluster_name_handlers(self, sts_client): sts_client.meta.events.register( 'provide-client-params.sts.GetCallerIdentity', - self._retrieve_cluster_name + self._retrieve_cluster_name, ) sts_client.meta.events.register( 'before-sign.sts.GetCallerIdentity', - self._inject_cluster_name_header + self._inject_cluster_name_header, ) def _retrieve_cluster_name(self, params, context, **kwargs): @@ -215,5 +245,6 @@ def _retrieve_cluster_name(self, params, context, **kwargs): def _inject_cluster_name_header(self, request, **kwargs): if 'eks_cluster' in request.context: - request.headers[ - CLUSTER_NAME_HEADER] = request.context['eks_cluster'] + request.headers[CLUSTER_NAME_HEADER] = request.context[ + 'eks_cluster' + ] diff --git a/tests/functional/eks/test_get_token.py b/tests/functional/eks/test_get_token.py index 8fd453f2b871..6f5cba5a0ed9 100644 --- a/tests/functional/eks/test_get_token.py +++ b/tests/functional/eks/test_get_token.py @@ -36,19 +36,24 @@ def run_get_token(self, cmd): response, _, _ = self.run_cmd(cmd) return json.loads(response) - def assert_url_correct(self, response, - expected_endpoint='sts.amazonaws.com', - expected_signing_region='us-east-1', - has_session_token=False): + def assert_url_correct( + self, + response, + expected_endpoint='sts.amazonaws.com', + expected_signing_region='us-east-1', + has_session_token=False, + ): url = self._get_url(response) url_components = urlparse.urlparse(url) self.assertEqual(url_components.netloc, expected_endpoint) parsed_qs = urlparse.parse_qs(url_components.query) self.assertIn( - expected_signing_region, parsed_qs['X-Amz-Credential'][0]) + expected_signing_region, parsed_qs['X-Amz-Credential'][0] + ) if has_session_token: self.assertEqual( - [self.session_token], parsed_qs['X-Amz-Security-Token']) + [self.session_token], parsed_qs['X-Amz-Security-Token'] + ) else: self.assertNotIn('X-Amz-Security-Token', parsed_qs) @@ -57,12 +62,24 @@ def assert_url_correct(self, response, def _get_url(self, response): token = response['status']['token'] - b64_token = token[len(self.expected_token_prefix):].encode() + b64_token = token[len(self.expected_token_prefix) :].encode() missing_padding = len(b64_token) % 4 if missing_padding: b64_token += b'=' * (4 - missing_padding) return base64.urlsafe_b64decode(b64_token).decode() + def set_kubernetes_exec_info(self, api_version): + # Set KUBERNETES_EXEC_INFO env var with default payload and provided + # api version + kubernetes_exec_info_tpl = ( + '{{"kind":"ExecCredential",' + '"apiVersion":"client.authentication.k8s.io/{0}",' + '"spec":{{"interactive":true}}}}' + ) + # kubernetes_exec_info_tpl = '{{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/{}","spec":{{"interactive":true}}}}' + self.environ['KUBERNETES_EXEC_INFO'] = kubernetes_exec_info_tpl.format( + api_version, + ) @mock.patch('awscli.customizations.eks.get_token.datetime') def test_get_token(self, mock_datetime): @@ -79,7 +96,7 @@ def test_get_token(self, mock_datetime): "expirationTimestamp": "2019-10-23T23:14:00Z", "token": mock.ANY, # This is asserted in later cases }, - } + }, ) def test_url(self): @@ -96,7 +113,7 @@ def test_url_with_region(self): self.assert_url_correct( response, expected_endpoint='sts.amazonaws.com', - expected_signing_region='us-east-1' + expected_signing_region='us-east-1', ) def test_url_with_arn(self): @@ -116,13 +133,9 @@ def test_url_with_arn(self): self.assertEqual(assume_role_call[0].name, 'AssumeRole') self.assertEqual( assume_role_call[1], - { - 'RoleArn': self.role_arn, - 'RoleSessionName': 'EKSGetTokenAuth' - } + {'RoleArn': self.role_arn, 'RoleSessionName': 'EKSGetTokenAuth'}, ) - self.assert_url_correct( - response, has_session_token=True) + self.assert_url_correct(response, has_session_token=True) def test_token_has_no_padding(self): cmd = 'eks get-token --cluster-name %s' % self.cluster_name @@ -142,47 +155,51 @@ def test_url_different_partition(self): self.assert_url_correct( response, expected_endpoint='sts.cn-north-1.amazonaws.com.cn', - expected_signing_region='cn-north-1' + expected_signing_region='cn-north-1', ) def test_api_version_discovery_deprecated(self): - os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1alpha1","spec":{"interactive":true}}' + self.set_kubernetes_exec_info('v1alpha1') cmd = 'eks get-token --cluster-name %s' % self.cluster_name stdout, stderr, _ = self.run_cmd(cmd) response = json.loads(stdout) self.assertEqual( response["apiVersion"], - "client.authentication.k8s.io/v1alpha1", + "client.authentication.k8s.io/v1alpha1", ) self.assertEqual( stderr, - ("Kubeconfig user entry is using deprecated API " - "version client.authentication.k8s.io/v1alpha1. Run 'aws eks update-kubeconfig' to update\n") + ( + "Kubeconfig user entry is using deprecated API " + "version client.authentication.k8s.io/v1alpha1. Run 'aws eks update-kubeconfig' to update.\n" + ), ) - del os.environ["KUBERNETES_EXEC_INFO"] - def test_api_version_discovery_malformed(self): - os.environ["KUBERNETES_EXEC_INFO"] = '{{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1alpha1","spec":{"interactive":true}}' + self.environ['KUBERNETES_EXEC_INFO'] = ( + '{{"kind":"ExecCredential",' + '"apiVersion":"client.authentication.k8s.io/v1alpha1",' + '"spec":{"interactive":true}}' + ) cmd = 'eks get-token --cluster-name %s' % self.cluster_name stdout, stderr, _ = self.run_cmd(cmd) response = json.loads(stdout) self.assertEqual( response["apiVersion"], - "client.authentication.k8s.io/v1alpha1", + "client.authentication.k8s.io/v1alpha1", ) self.assertEqual( stderr, - ("Error parsing KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " - "This is likely a bug in your Kubernetes client. Please update your Kubernetes client.\n") + ( + "Error parsing KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " + "This is likely a bug in your Kubernetes client. Please update your Kubernetes client.\n" + ), ) - del os.environ["KUBERNETES_EXEC_INFO"] - def test_api_version_discovery_empty(self): cmd = 'eks get-token --cluster-name %s' % self.cluster_name stdout, stderr, _ = self.run_cmd(cmd) @@ -190,59 +207,58 @@ def test_api_version_discovery_empty(self): self.assertEqual( response["apiVersion"], - "client.authentication.k8s.io/v1alpha1", + "client.authentication.k8s.io/v1alpha1", ) self.assertEqual( stderr, - ("Empty KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " - "This is likely a bug in your Kubernetes client. Please update your Kubernetes client.\n") + ( + "Empty KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " + "This is likely a bug in your Kubernetes client. Please update your Kubernetes client.\n" + ), ) + def test_api_version_discovery_v1(self): - os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1","spec":{"interactive":true}}' + self.set_kubernetes_exec_info('v1') cmd = 'eks get-token --cluster-name %s' % self.cluster_name stdout, stderr, _ = self.run_cmd(cmd) response = json.loads(stdout) self.assertEqual( response["apiVersion"], - "client.authentication.k8s.io/v1", + "client.authentication.k8s.io/v1", ) self.assertEqual(stderr, "") - del os.environ["KUBERNETES_EXEC_INFO"] - def test_api_version_discovery_v1beta1(self): - os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":true}}' + self.set_kubernetes_exec_info('v1beta1') cmd = 'eks get-token --cluster-name %s' % self.cluster_name stdout, stderr, _ = self.run_cmd(cmd) response = json.loads(stdout) self.assertEqual( response["apiVersion"], - "client.authentication.k8s.io/v1beta1", + "client.authentication.k8s.io/v1beta1", ) self.assertEqual(stderr, "") - del os.environ["KUBERNETES_EXEC_INFO"] - def test_api_version_discovery_unknown(self): - os.environ["KUBERNETES_EXEC_INFO"] = '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v2","spec":{"interactive":true}}' + self.set_kubernetes_exec_info('v2') cmd = 'eks get-token --cluster-name %s' % self.cluster_name stdout, stderr, _ = self.run_cmd(cmd) response = json.loads(stdout) self.assertEqual( response["apiVersion"], - "client.authentication.k8s.io/v1alpha1", + "client.authentication.k8s.io/v1alpha1", ) self.assertEqual( stderr, - ("Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " - "This is likely due to an outdated AWS CLI. Please update your AWS CLI.\n") + ( + "Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting to client.authentication.k8s.io/v1alpha1. " + "This is likely due to an outdated AWS CLI. Please update your AWS CLI.\n" + ), ) - - del os.environ["KUBERNETES_EXEC_INFO"] \ No newline at end of file From 3124795897979a6532cc2913b7400c3ae15335ef Mon Sep 17 00:00:00 2001 From: Justin Ho Date: Thu, 5 May 2022 17:18:25 -0700 Subject: [PATCH 3/4] Address PR feedback --- .../enhancement-eksgettoken-91863.json | 2 +- ...enhancement-eksupdatekubeconfig-63017.json | 2 +- awscli/customizations/eks/get_token.py | 42 ++++++++++--------- tests/functional/eks/test_get_token.py | 3 +- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.changes/next-release/enhancement-eksgettoken-91863.json b/.changes/next-release/enhancement-eksgettoken-91863.json index 81c9102f3797..2215ce64194f 100644 --- a/.changes/next-release/enhancement-eksgettoken-91863.json +++ b/.changes/next-release/enhancement-eksgettoken-91863.json @@ -1,5 +1,5 @@ { "type": "enhancement", "category": "eks get-token", - "description": "Add support to respect env var KUBERNETES_EXEC_INFO in eks get-token" + "description": "Add support for respecting API version found in KUBERNETES_EXEC_INFO environment variable" } diff --git a/.changes/next-release/enhancement-eksupdatekubeconfig-63017.json b/.changes/next-release/enhancement-eksupdatekubeconfig-63017.json index 33dc9461243e..713b37daed95 100644 --- a/.changes/next-release/enhancement-eksupdatekubeconfig-63017.json +++ b/.changes/next-release/enhancement-eksupdatekubeconfig-63017.json @@ -1,5 +1,5 @@ { "type": "enhancement", "category": "eks update-kubeconfig", - "description": "Update default API version for eks update-kubeconfig" + "description": "Update default API version to v1beta1" } diff --git a/awscli/customizations/eks/get_token.py b/awscli/customizations/eks/get_token.py index 0a60fe0372ff..bc2b0ac00d35 100644 --- a/awscli/customizations/eks/get_token.py +++ b/awscli/customizations/eks/get_token.py @@ -40,6 +40,21 @@ ALPHA_API, ] +ERROR_MSG_TPL = ( + "{0} KUBERNETES_EXEC_INFO, defaulting to {1}. This is likely a " + "bug in your Kubernetes client. Please update your Kubernetes " + "client." +) +UNRECOGNIZED_MSG_TPL = ( + "Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting to " + "{0}. This is likely due to an outdated AWS " + "CLI. Please update your AWS CLI." +) +DEPRECATION_MSG_TPL = ( + "Kubeconfig user entry is using deprecated API version {0}. Run " + "'aws eks update-kubeconfig' to update." +) + # Presigned url timeout in seconds URL_TIMEOUT = 60 @@ -131,27 +146,12 @@ def discover_api_version(self): "empty": "Empty", } - error_msg_tpl = ( - "{0} KUBERNETES_EXEC_INFO, defaulting to {1}. This is likely a " - "bug in your Kubernetes client. Please update your Kubernetes " - "client." - ) - unrecognized_msg = ( - "Unrecognized API version in KUBERNETES_EXEC_INFO, defaulting to " - f"{fallback_api_version}. This is likely due to an outdated AWS " - "CLI. Please update your AWS CLI." - ) - deprecation_msg_tpl = ( - "Kubeconfig user entry is using deprecated API version {0}. Run " - "'aws eks update-kubeconfig' to update." - ) - exec_info_raw = os.environ.get("KUBERNETES_EXEC_INFO", "") if not exec_info_raw: # All kube clients should be setting this. Otherewise, we'll return # the fallback and write an error. uni_print( - error_msg_tpl.format( + ERROR_MSG_TPL.format( error_prefixes["empty"], fallback_api_version, ), @@ -164,7 +164,7 @@ def discover_api_version(self): except json.JSONDecodeError: # The environment variable was malformed uni_print( - error_msg_tpl.format( + ERROR_MSG_TPL.format( error_prefixes["error"], fallback_api_version, ), @@ -177,12 +177,14 @@ def discover_api_version(self): if api_version_raw in FULLY_SUPPORTED_API_VERSIONS: return api_version_raw elif api_version_raw in DEPRECATED_API_VERSIONS: - uni_print(deprecation_msg_tpl.format(ALPHA_API), sys.stderr) + uni_print(DEPRECATION_MSG_TPL.format(ALPHA_API), sys.stderr) uni_print("\n", sys.stderr) return api_version_raw else: - # write unrecognized api version message - uni_print(unrecognized_msg, sys.stderr) + uni_print( + UNRECOGNIZED_MSG_TPL.format(fallback_api_version), + sys.stderr, + ) uni_print("\n", sys.stderr) return fallback_api_version diff --git a/tests/functional/eks/test_get_token.py b/tests/functional/eks/test_get_token.py index 6f5cba5a0ed9..e73554f03e1a 100644 --- a/tests/functional/eks/test_get_token.py +++ b/tests/functional/eks/test_get_token.py @@ -73,10 +73,9 @@ def set_kubernetes_exec_info(self, api_version): # api version kubernetes_exec_info_tpl = ( '{{"kind":"ExecCredential",' - '"apiVersion":"client.authentication.k8s.io/{0}",' + f'"apiVersion":"client.authentication.k8s.io/{api_version}",' '"spec":{{"interactive":true}}}}' ) - # kubernetes_exec_info_tpl = '{{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/{}","spec":{{"interactive":true}}}}' self.environ['KUBERNETES_EXEC_INFO'] = kubernetes_exec_info_tpl.format( api_version, ) From 320f898424b499b2b3e2f2fa7c848669a0719c73 Mon Sep 17 00:00:00 2001 From: Justin Ho Date: Thu, 5 May 2022 17:35:42 -0700 Subject: [PATCH 4/4] Fix string formatting --- tests/functional/eks/test_get_token.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/functional/eks/test_get_token.py b/tests/functional/eks/test_get_token.py index e73554f03e1a..ecf64bed5e5c 100644 --- a/tests/functional/eks/test_get_token.py +++ b/tests/functional/eks/test_get_token.py @@ -71,13 +71,10 @@ def _get_url(self, response): def set_kubernetes_exec_info(self, api_version): # Set KUBERNETES_EXEC_INFO env var with default payload and provided # api version - kubernetes_exec_info_tpl = ( - '{{"kind":"ExecCredential",' + self.environ['KUBERNETES_EXEC_INFO'] = ( + '{"kind":"ExecCredential",' f'"apiVersion":"client.authentication.k8s.io/{api_version}",' - '"spec":{{"interactive":true}}}}' - ) - self.environ['KUBERNETES_EXEC_INFO'] = kubernetes_exec_info_tpl.format( - api_version, + '"spec":{"interactive":true}}' ) @mock.patch('awscli.customizations.eks.get_token.datetime')