From f13c4952c40bc1796a8e2c4b024d373140a6cfbf Mon Sep 17 00:00:00 2001 From: siriteja <32883500+sirireddy12@users.noreply.github.com> Date: Mon, 14 Mar 2022 14:28:31 +0530 Subject: [PATCH] ADAL to MSAL move for cli>=2.30.0 (#4468) * ADAL to MSAL move for cli>=2.30.0 * nit * nit * nit * add comment * addressing review comments * linting fixes * linter fixes * linter nit * add license for new file * update release date * add token condition * Update src/connectedk8s/azext_connectedk8s/_utils.py Co-authored-by: Xing Zhou Co-authored-by: Siri Teja Reddy Kasireddy Co-authored-by: Xing Zhou --- src/connectedk8s/HISTORY.rst | 5 + .../azext_connectedk8s/_clientproxyutils.py | 135 ++++++++++ .../azext_connectedk8s/_constants.py | 12 +- src/connectedk8s/azext_connectedk8s/_utils.py | 25 ++ .../azext_connectedk8s/azext_metadata.json | 3 +- src/connectedk8s/azext_connectedk8s/custom.py | 231 +++++++----------- src/connectedk8s/setup.py | 2 +- 7 files changed, 270 insertions(+), 143 deletions(-) create mode 100644 src/connectedk8s/azext_connectedk8s/_clientproxyutils.py diff --git a/src/connectedk8s/HISTORY.rst b/src/connectedk8s/HISTORY.rst index 69a6c7c3e6b..0e89f8b3c67 100644 --- a/src/connectedk8s/HISTORY.rst +++ b/src/connectedk8s/HISTORY.rst @@ -2,6 +2,11 @@ Release History =============== +1.2.5 +++++++ + +* Using MSAL based auth for CLI version >= 2.30.0 + 1.2.4 ++++++ diff --git a/src/connectedk8s/azext_connectedk8s/_clientproxyutils.py b/src/connectedk8s/azext_connectedk8s/_clientproxyutils.py new file mode 100644 index 00000000000..69b04a9b154 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/_clientproxyutils.py @@ -0,0 +1,135 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import sys +import os +import platform +import base64 +import json +import requests +import yaml +import time +import azext_connectedk8s._constants as consts +from base64 import b64encode, b64decode +from azure.cli.core._profile import Profile +from azure.cli.core import telemetry +from azure.cli.core.azclierror import CLIInternalError +from psutil import process_iter, NoSuchProcess, AccessDenied, ZombieProcess, net_connections +from knack.log import get_logger +logger = get_logger(__name__) + + +def check_if_port_is_open(port): + try: + connections = net_connections(kind='inet') + for tup in connections: + if int(tup[3][1]) == int(port): + return True + except Exception as e: + telemetry.set_exception(exception=e, fault_type=consts.Port_Check_Fault_Type, + summary='Failed to check if port is in use.') + if platform.system() != 'Darwin': + logger.info("Failed to check if port is in use. " + str(e)) + return False + return False + + +def close_subprocess_and_raise_cli_error(proc_subprocess, msg): + proc_subprocess.terminate() + raise CLIInternalError(msg) + + +def check_if_csp_is_running(clientproxy_process): + if clientproxy_process.poll() is None: + return True + else: + return False + + +def make_api_call_with_retries(uri, data, method, tls_verify, fault_type, summary, cli_error, clientproxy_process): + for i in range(consts.API_CALL_RETRIES): + try: + response = requests.request(method, uri, json=data, verify=tls_verify) + return response + except Exception as e: + time.sleep(5) + if i != consts.API_CALL_RETRIES - 1: + pass + else: + telemetry.set_exception(exception=e, fault_type=fault_type, summary=summary) + close_subprocess_and_raise_cli_error(clientproxy_process, cli_error + str(e)) + + +def fetch_pop_publickey_kid(api_server_port, clientproxy_process): + requestbody = {} + poppublickey_uri = f'https://localhost:{api_server_port}/identity/poppublickey' + # Needed to prevent skip tls warning from printing to the console + original_stderr = sys.stderr + f = open(os.devnull, 'w') + sys.stderr = f + + get_publickey_response = make_api_call_with_retries(poppublickey_uri, requestbody, "get", False, consts.Get_PublicKey_Info_Fault_Type, + 'Failed to fetch public key info from clientproxy', + "Failed to fetch public key info from client proxy", clientproxy_process) + + sys.stderr = original_stderr + publickey_info = json.loads(get_publickey_response.text) + kid = publickey_info['publicKey']['kid'] + + return kid + + +def fetch_and_post_at_to_csp(cmd, api_server_port, tenantId, kid, clientproxy_process): + req_cnfJSON = {"kid": kid, "xms_ksl": "sw"} + req_cnf = base64.urlsafe_b64encode(json.dumps(req_cnfJSON).encode('utf-8')).decode('utf-8') + + # remove padding '=' character + if req_cnf[len(req_cnf) - 1] == '=': + req_cnf = req_cnf[:-1] + + token_data = {"token_type": "pop", "key_id": kid, "req_cnf": req_cnf} + profile = Profile(cli_ctx=cmd.cli_ctx) + try: + credential, _, _ = profile.get_login_credentials(subscription_id=profile.get_subscription()["id"], resource=consts.KAP_1P_Server_App_Scope) + accessToken = credential.get_token(consts.KAP_1P_Server_App_Scope, data=token_data) + jwtToken = accessToken.token + except Exception as e: + telemetry.set_exception(exception=e, fault_type=consts.Post_AT_To_ClientProxy_Failed_Fault_Type, + summary='Failed to fetch access token using the PoP public key sent by client proxy') + close_subprocess_and_raise_cli_error(clientproxy_process, 'Failed to post access token to client proxy' + str(e)) + + jwtTokenData = {"accessToken": jwtToken, "serverId": consts.KAP_1P_Server_AppId, "tenantID": tenantId, "kid": kid} + post_at_uri = f'https://localhost:{api_server_port}/identity/at' + # Needed to prevent skip tls warning from printing to the console + original_stderr = sys.stderr + f = open(os.devnull, 'w') + sys.stderr = f + post_at_response = make_api_call_with_retries(post_at_uri, jwtTokenData, "post", False, consts.PublicKey_Export_Fault_Type, 'Failed to post access token to client proxy', "Failed to post access token to client proxy", clientproxy_process) + + sys.stderr = original_stderr + return post_at_response + + +def insert_token_in_kubeconfig(data, token): + b64kubeconfig = data['kubeconfigs'][0]['value'] + decoded_kubeconfig_str = b64decode(b64kubeconfig).decode("utf-8") + dict_yaml = yaml.safe_load(decoded_kubeconfig_str) + dict_yaml['users'][0]['user']['token'] = token + kubeconfig = yaml.dump(dict_yaml).encode("utf-8") + b64kubeconfig = b64encode(kubeconfig).decode("utf-8") + return b64kubeconfig + + +def check_process(processName): + ''' + Check if there is any running process that contains the given name processName. + ''' + for proc in process_iter(): + try: + if proc.name().startswith(processName): + return True + except (NoSuchProcess, AccessDenied, ZombieProcess): + pass + return False diff --git a/src/connectedk8s/azext_connectedk8s/_constants.py b/src/connectedk8s/azext_connectedk8s/_constants.py index be63dbf5d73..31494f4df6b 100644 --- a/src/connectedk8s/azext_connectedk8s/_constants.py +++ b/src/connectedk8s/azext_connectedk8s/_constants.py @@ -113,14 +113,20 @@ CC_Provider_Namespace_Not_Registered_Fault_Type = "Connected Cluster Provider MS.K8 namespace not registered" Default_Namespace_Does_Not_Exist_Fault_Type = "The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster." ClusterConnect_Not_Present_Fault_Type = "cluster-connect-feature-unavailable" -CLIENT_PROXY_VERSION = '1.3.018101' +KAP_1P_Server_App_Scope = "6256c85f-0aad-4d50-b960-e6e9b21efe35/.default" +KAP_1P_Server_AppId = "6256c85f-0aad-4d50-b960-e6e9b21efe35" +Get_PublicKey_Info_Fault_Type = 'Error while fetching the PoP publickey information from client proxy' +PoP_Public_Key_Expried_Fault_Type = 'The PoP public key used to generate the at has expired' +Post_AT_To_ClientProxy_Failed_Fault_Type = 'Failed to post access token to client proxy' +AZ_CLI_ADAL_TO_MSAL_MIGRATE_VERSION = '2.30.0' +CLIENT_PROXY_VERSION = '1.3.018802' API_SERVER_PORT = 47011 CLIENT_PROXY_PORT = 47010 CLIENTPROXY_CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' API_CALL_RETRIES = 12 DEFAULT_REQUEST_TIMEOUT = 10 # seconds -RELEASE_DATE_WINDOWS = 'release16-12-21' -RELEASE_DATE_LINUX = 'release16-12-21' +RELEASE_DATE_WINDOWS = 'release04-03-22' +RELEASE_DATE_LINUX = 'release04-03-22' CSP_REFRESH_TIME = 300 # URL constants CSP_Storage_Url = "https://k8sconnectcsp.azureedge.net" diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index 4c87a356f03..d29f03d6a00 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -26,6 +26,8 @@ from azext_connectedk8s._client_factory import _resource_client_factory, _resource_providers_client import azext_connectedk8s._constants as consts from kubernetes import client as kube_client +from azure.cli.core import get_default_cli +from packaging import version from azure.cli.core.azclierror import CLIInternalError, ClientRequestError, ArgumentUsageError, ManualInterrupt, AzureResponseError, AzureInternalError, ValidationError logger = get_logger(__name__) @@ -444,3 +446,26 @@ def validate_node_api_response(api_instance, node_api_response): return None else: return node_api_response + + +def az_cli(args_str): + args = args_str.split() + cli = get_default_cli() + cli.invoke(args, out_file=open(os.devnull, 'w')) + if cli.result.result: + return cli.result.result + elif cli.result.error: + raise Exception(cli.result.error) + return True + + +def is_cli_using_msal_auth(): + response_cli_version = az_cli("version --output json") + try: + cli_version = response_cli_version['azure-cli'] + except Exception as ex: + raise CLIInternalError("Unable to decode the az cli version installed: {}".format(str(ex))) + if version.parse(cli_version) >= version.parse(consts.AZ_CLI_ADAL_TO_MSAL_MIGRATE_VERSION): + return True + else: + return False diff --git a/src/connectedk8s/azext_connectedk8s/azext_metadata.json b/src/connectedk8s/azext_connectedk8s/azext_metadata.json index 09bc07d3377..688e7b852b6 100644 --- a/src/connectedk8s/azext_connectedk8s/azext_metadata.json +++ b/src/connectedk8s/azext_connectedk8s/azext_metadata.json @@ -1,6 +1,5 @@ { "name": "connectedk8s", "version": "1.2.1", - "azext.minCliCoreVersion": "2.16.0", - "azext.maxCliCoreVersion": "2.29.0" + "azext.minCliCoreVersion": "2.16.0" } \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 4fde18967f2..e1a03ef7d3c 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -9,6 +9,7 @@ import json import tempfile import time +import base64 from subprocess import Popen, PIPE, run, STDOUT, call, DEVNULL from base64 import b64encode, b64decode import stat @@ -40,6 +41,7 @@ from azext_connectedk8s._client_factory import get_graph_client_service_principals import azext_connectedk8s._constants as consts import azext_connectedk8s._utils as utils +import azext_connectedk8s._clientproxyutils as clientproxyutils from glob import glob from .vendored_sdks.models import ConnectedCluster, ConnectedClusterIdentity, ListClusterUserCredentialProperties from threading import Timer, Thread @@ -1629,6 +1631,7 @@ def client_side_proxy_wrapper(cmd, summary=f'User tried proxy command in fairfax.') raise ClientRequestError(f'Cluster Connect feature is not yet available in {consts.Azure_USGovCloudName}') + tenantId = _graph_client_factory(cmd.cli_ctx).config.tenant_id client_proxy_port = consts.CLIENT_PROXY_PORT if int(client_proxy_port) == int(api_server_port): raise ClientRequestError('Proxy uses port 47010 internally.', recommendation='Please pass some other unused port through --port option.') @@ -1640,13 +1643,13 @@ def client_side_proxy_wrapper(cmd, telemetry.set_debug_info('CSP Version is ', consts.CLIENT_PROXY_VERSION) telemetry.set_debug_info('OS is ', operating_system) - if(check_process(proc_name)): + if(clientproxyutils.check_process(proc_name)): raise ClientRequestError('Another instance of proxy already running') port_error_string = "" - if check_if_port_is_open(api_server_port): + if clientproxyutils.check_if_port_is_open(api_server_port): port_error_string += f'Port {api_server_port} is already in use. Please select a different port with --port option.\n' - if check_if_port_is_open(client_proxy_port): + if clientproxyutils.check_if_port_is_open(client_proxy_port): telemetry.set_exception(exception='Client proxy port was in use.', fault_type=consts.Client_Proxy_Port_Fault_Type, summary=f'Client proxy port was in use.') port_error_string += f"Port {client_proxy_port} is already in use. This is an internal port that proxy uses. Please ensure that this port is open before running 'az connectedk8s proxy'.\n" @@ -1745,7 +1748,6 @@ def client_side_proxy_wrapper(cmd, subscription_id = get_subscription_id(cmd.cli_ctx) account = Profile().get_subscription(subscription_id) user_type = account['user']['type'] - tenantId = _graph_client_factory(cmd.cli_ctx).config.tenant_id if user_type == 'user': dict_file = {'server': {'httpPort': int(client_proxy_port), 'httpsPort': int(api_server_port)}, 'identity': {'tenantID': tenantId, 'clientID': consts.CLIENTPROXY_CLIENT_ID}} @@ -1758,39 +1760,40 @@ def client_side_proxy_wrapper(cmd, if cloud == consts.Azure_ChinaCloudName: dict_file['cloud'] = 'AzureChinaCloud' - # Fetching creds - creds_location = os.path.expanduser(os.path.join('~', creds_string)) - try: - with open(creds_location) as f: - creds_list = json.load(f) - except Exception as e: - telemetry.set_exception(exception=e, fault_type=consts.Load_Creds_Fault_Type, - summary='Unable to load accessToken.json') - raise FileOperationError("Failed to load credentials." + str(e)) + if not utils.is_cli_using_msal_auth(): + # Fetching creds + creds_location = os.path.expanduser(os.path.join('~', creds_string)) + try: + with open(creds_location) as f: + creds_list = json.load(f) + except Exception as e: + telemetry.set_exception(exception=e, fault_type=consts.Load_Creds_Fault_Type, + summary='Unable to load accessToken.json') + raise FileOperationError("Failed to load credentials." + str(e)) - user_name = account['user']['name'] + user_name = account['user']['name'] - if user_type == 'user': - key = 'userId' - key2 = 'refreshToken' - else: - key = 'servicePrincipalId' - key2 = 'accessToken' + if user_type == 'user': + key = 'userId' + key2 = 'refreshToken' + else: + key = 'servicePrincipalId' + key2 = 'accessToken' - for i in range(len(creds_list)): - creds_obj = creds_list[i] + for i in range(len(creds_list)): + creds_obj = creds_list[i] - if key in creds_obj and creds_obj[key] == user_name: - creds = creds_obj[key2] - break + if key in creds_obj and creds_obj[key] == user_name: + creds = creds_obj[key2] + break - if creds == '': - telemetry.set_exception(exception='Credentials of user not found.', fault_type=consts.Creds_NotFound_Fault_Type, - summary='Unable to find creds of user') - raise UnclassifiedUserFault("Credentials of user not found.") + if creds == '': + telemetry.set_exception(exception='Credentials of user not found.', fault_type=consts.Creds_NotFound_Fault_Type, + summary='Unable to find creds of user') + raise UnclassifiedUserFault("Credentials of user not found.") - if user_type != 'user': - dict_file['identity']['clientSecret'] = creds + if user_type != 'user': + dict_file['identity']['clientSecret'] = creds else: dict_file = {'server': {'httpPort': int(client_proxy_port), 'httpsPort': int(api_server_port)}} if cloud == consts.Azure_ChinaCloudName: @@ -1814,7 +1817,7 @@ def client_side_proxy_wrapper(cmd, args.append("-d") debug_mode = True - client_side_proxy_main(cmd, client, resource_group_name, cluster_name, 0, args, client_proxy_port, api_server_port, operating_system, creds, user_type, debug_mode, token=token, path=path, context_name=context_name, clientproxy_process=None) + client_side_proxy_main(cmd, tenantId, client, resource_group_name, cluster_name, 0, args, client_proxy_port, api_server_port, operating_system, creds, user_type, debug_mode, token=token, path=path, context_name=context_name, clientproxy_process=None) # Prepare data as needed by client proxy executable @@ -1834,6 +1837,7 @@ def prepare_clientproxy_data(response): def client_side_proxy_main(cmd, + tenantId, client, resource_group_name, cluster_name, @@ -1849,14 +1853,14 @@ def client_side_proxy_main(cmd, path=os.path.join(os.path.expanduser('~'), '.kube', 'config'), context_name=None, clientproxy_process=None): - expiry, clientproxy_process = client_side_proxy(cmd, client, resource_group_name, cluster_name, 0, args, client_proxy_port, api_server_port, operating_system, creds, user_type, debug_mode, token=token, path=path, context_name=context_name, clientproxy_process=None) + expiry, clientproxy_process = client_side_proxy(cmd, tenantId, client, resource_group_name, cluster_name, 0, args, client_proxy_port, api_server_port, operating_system, creds, user_type, debug_mode, token=token, path=path, context_name=context_name, clientproxy_process=None) next_refresh_time = expiry - consts.CSP_REFRESH_TIME while(True): time.sleep(60) - if(check_if_csp_is_running(clientproxy_process)): + if(clientproxyutils.check_if_csp_is_running(clientproxy_process)): if time.time() >= next_refresh_time: - expiry, clientproxy_process = client_side_proxy(cmd, client, resource_group_name, cluster_name, 1, args, client_proxy_port, api_server_port, operating_system, creds, user_type, debug_mode, token=token, path=path, context_name=context_name, clientproxy_process=clientproxy_process) + expiry, clientproxy_process = client_side_proxy(cmd, tenantId, client, resource_group_name, cluster_name, 1, args, client_proxy_port, api_server_port, operating_system, creds, user_type, debug_mode, token=token, path=path, context_name=context_name, clientproxy_process=clientproxy_process) next_refresh_time = expiry - consts.CSP_REFRESH_TIME else: telemetry.set_exception(exception='Process closed externally.', fault_type=consts.Proxy_Closed_Externally_Fault_Type, @@ -1865,6 +1869,7 @@ def client_side_proxy_main(cmd, def client_side_proxy(cmd, + tenantId, client, resource_group_name, cluster_name, @@ -1882,7 +1887,6 @@ def client_side_proxy(cmd, clientproxy_process=None): subscription_id = get_subscription_id(cmd.cli_ctx) - if token is not None: auth_method = 'Token' else: @@ -1915,33 +1919,50 @@ def client_side_proxy(cmd, summary='Unable to run client proxy executable') raise CLIInternalError("Failed to start proxy process." + str(e)) - if user_type == 'user': - identity_data = {} - identity_data['refreshToken'] = creds - identity_uri = f'https://localhost:{api_server_port}/identity/rt' + if not utils.is_cli_using_msal_auth(): # refresh token approach if cli is using ADAL auth. This is for cli < 2.30.0 + if user_type == 'user': + identity_data = {} + identity_data['refreshToken'] = creds + identity_uri = f'https://localhost:{api_server_port}/identity/rt' - # Needed to prevent skip tls warning from printing to the console - original_stderr = sys.stderr - f = open(os.devnull, 'w') - sys.stderr = f + # Needed to prevent skip tls warning from printing to the console + original_stderr = sys.stderr + f = open(os.devnull, 'w') + sys.stderr = f - make_api_call_with_retries(identity_uri, identity_data, False, consts.Post_RefreshToken_Fault_Type, - 'Unable to post refresh token details to clientproxy', - "Failed to pass refresh token details to proxy.", clientproxy_process) - sys.stderr = original_stderr + clientproxyutils.make_api_call_with_retries(identity_uri, identity_data, "post", False, consts.Post_RefreshToken_Fault_Type, + 'Unable to post refresh token details to clientproxy', + "Failed to pass refresh token details to proxy.", clientproxy_process) + sys.stderr = original_stderr + + if token is None: + if utils.is_cli_using_msal_auth(): # jwt token approach if cli is using MSAL. This is for cli >= 2.30.0 + kid = clientproxyutils.fetch_pop_publickey_kid(api_server_port, clientproxy_process) + post_at_response = clientproxyutils.fetch_and_post_at_to_csp(cmd, api_server_port, tenantId, "gTYVsmkQfNwajR0w-v6A3ekPkiI7Wcz2T5ZCb7hwHTU", clientproxy_process) + + if post_at_response.status_code != 200: + if post_at_response.status_code == 500 and "public key expired" in post_at_response.text: # pop public key must have been rotated + telemetry.set_exception(exception=post_at_response.text, fault_type=consts.PoP_Public_Key_Expried_Fault_Type, + summary='PoP public key has expired') + kid = clientproxyutils.fetch_pop_publickey_kid(api_server_port, clientproxy_process) # fetch the rotated PoP public key + clientproxyutils.fetch_and_post_at_to_csp(cmd, api_server_port, tenantId, kid, clientproxy_process) # fetch and post the at corresponding to the new public key + else: + telemetry.set_exception(exception=post_at_response.text, fault_type=consts.Post_AT_To_ClientProxy_Failed_Fault_Type, + summary='Failed to post access token to client proxy') + clientproxyutils.close_subprocess_and_raise_cli_error(clientproxy_process, 'Failed to post access token to client proxy' + post_at_response.text) data = prepare_clientproxy_data(response) expiry = data['hybridConnectionConfig']['expirationTime'] if token is not None: - data['kubeconfigs'][0]['value'] = insert_token_in_kubeconfig(data, token) + data['kubeconfigs'][0]['value'] = clientproxyutils.insert_token_in_kubeconfig(data, token) uri = f'http://localhost:{client_proxy_port}/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/Microsoft.Kubernetes/connectedClusters/{cluster_name}/register?api-version=2020-10-01' # Posting hybrid connection details to proxy in order to get kubeconfig - response = make_api_call_with_retries(uri, data, False, consts.Post_Hybridconn_Fault_Type, - 'Unable to post hybrid connection details to clientproxy', - "Failed to pass hybrid connection details to proxy.", clientproxy_process) + response = clientproxyutils.make_api_call_with_retries(uri, data, "post", False, consts.Post_Hybridconn_Fault_Type, + 'Unable to post hybrid connection details to clientproxy', + "Failed to pass hybrid connection details to proxy.", clientproxy_process) if flag == 0: # Decoding kubeconfig into a string @@ -1950,7 +1971,7 @@ def client_side_proxy(cmd, except Exception as e: telemetry.set_exception(exception=e, fault_type=consts.Load_Kubeconfig_Fault_Type, summary='Unable to load Kubeconfig') - close_subprocess_and_raise_cli_error(clientproxy_process, "Failed to load kubeconfig." + str(e)) + clientproxyutils.close_subprocess_and_raise_cli_error(clientproxy_process, "Failed to load kubeconfig." + str(e)) kubeconfig = kubeconfig['kubeconfigs'][0]['value'] kubeconfig = b64decode(kubeconfig).decode("utf-8") @@ -1970,46 +1991,30 @@ def client_side_proxy(cmd, except Exception as e: telemetry.set_exception(exception=e, fault_type=consts.Merge_Kubeconfig_Fault_Type, summary='Unable to merge kubeconfig.') - close_subprocess_and_raise_cli_error(clientproxy_process, "Failed to merge kubeconfig." + str(e)) + clientproxyutils.close_subprocess_and_raise_cli_error(clientproxy_process, "Failed to merge kubeconfig." + str(e)) return expiry, clientproxy_process -def make_api_call_with_retries(uri, data, tls_verify, fault_type, summary, cli_error, clientproxy_process): - for i in range(consts.API_CALL_RETRIES): - try: - response = requests.post(uri, json=data, verify=tls_verify) - return response - except Exception as e: - time.sleep(5) - if i != consts.API_CALL_RETRIES - 1: - pass - else: - telemetry.set_exception(exception=e, fault_type=fault_type, summary=summary) - close_subprocess_and_raise_cli_error(clientproxy_process, cli_error + str(e)) - - -def insert_token_in_kubeconfig(data, token): - b64kubeconfig = data['kubeconfigs'][0]['value'] - decoded_kubeconfig_str = b64decode(b64kubeconfig).decode("utf-8") - dict_yaml = yaml.safe_load(decoded_kubeconfig_str) - dict_yaml['users'][0]['user']['token'] = token - kubeconfig = yaml.dump(dict_yaml).encode("utf-8") - b64kubeconfig = b64encode(kubeconfig).decode("utf-8") - return b64kubeconfig - - -def check_process(processName): - ''' - Check if there is any running process that contains the given name processName. - ''' - for proc in process_iter(): - try: - if proc.name().startswith(processName): - return True - except (NoSuchProcess, AccessDenied, ZombieProcess): - pass - return False +def check_cl_registration_and_get_oid(cmd, cl_oid): + enable_custom_locations = True + custom_locations_oid = "" + try: + rp_client = _resource_providers_client(cmd.cli_ctx) + cl_registration_state = rp_client.get(consts.Custom_Locations_Provider_Namespace).registration_state + if cl_registration_state != "Registered": + enable_custom_locations = False + logger.warning("'Custom-locations' feature couldn't be enabled on this cluster as the pre-requisite registration of 'Microsoft.ExtendedLocation' was not met. More details for enabling this feature later on this cluster can be found here - https://aka.ms/EnableCustomLocations") + else: + custom_locations_oid = get_custom_locations_oid(cmd, cl_oid) + if custom_locations_oid == "": + enable_custom_locations = False + except Exception as e: + enable_custom_locations = False + logger.warning("Unable to fetch registration state of 'Microsoft.ExtendedLocation'. Failed to enable 'custom-locations' feature...") + telemetry.set_exception(exception=e, fault_type=consts.Custom_Locations_Registration_Check_Fault_Type, + summary='Unable to fetch status of Custom Locations RP registration.') + return enable_custom_locations, custom_locations_oid def get_custom_locations_oid(cmd, cl_oid): @@ -2041,51 +2046,3 @@ def get_custom_locations_oid(cmd, cl_oid): log_string += "Unable to enable the 'custom-locations' feature. " + str(e) logger.warning(log_string) return "" - - -def check_cl_registration_and_get_oid(cmd, cl_oid): - enable_custom_locations = True - custom_locations_oid = "" - try: - rp_client = _resource_providers_client(cmd.cli_ctx) - cl_registration_state = rp_client.get(consts.Custom_Locations_Provider_Namespace).registration_state - if cl_registration_state != "Registered": - enable_custom_locations = False - logger.warning("'Custom-locations' feature couldn't be enabled on this cluster as the pre-requisite registration of 'Microsoft.ExtendedLocation' was not met. More details for enabling this feature later on this cluster can be found here - https://aka.ms/EnableCustomLocations") - else: - custom_locations_oid = get_custom_locations_oid(cmd, cl_oid) - if custom_locations_oid == "": - enable_custom_locations = False - except Exception as e: - enable_custom_locations = False - logger.warning("Unable to fetch registration state of 'Microsoft.ExtendedLocation'. Failed to enable 'custom-locations' feature...") - telemetry.set_exception(exception=e, fault_type=consts.Custom_Locations_Registration_Check_Fault_Type, - summary='Unable to fetch status of Custom Locations RP registration.') - return enable_custom_locations, custom_locations_oid - - -def check_if_port_is_open(port): - try: - connections = net_connections(kind='inet') - for tup in connections: - if int(tup[3][1]) == int(port): - return True - except Exception as e: - telemetry.set_exception(exception=e, fault_type=consts.Port_Check_Fault_Type, - summary='Failed to check if port is in use.') - if platform.system() != 'Darwin': - logger.info("Failed to check if port is in use. " + str(e)) - return False - return False - - -def close_subprocess_and_raise_cli_error(proc_subprocess, msg): - proc_subprocess.terminate() - raise CLIInternalError(msg) - - -def check_if_csp_is_running(clientproxy_process): - if clientproxy_process.poll() is None: - return True - else: - return False diff --git a/src/connectedk8s/setup.py b/src/connectedk8s/setup.py index df92c4d6167..ccf8bce462e 100644 --- a/src/connectedk8s/setup.py +++ b/src/connectedk8s/setup.py @@ -17,7 +17,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '1.2.4' +VERSION = '1.2.5' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers