From 172bf9005a578851daf0ab1d38682bee0d160190 Mon Sep 17 00:00:00 2001 From: Sophie Zhao Date: Thu, 25 Mar 2021 14:58:00 -0400 Subject: [PATCH 1/6] add troubleshoot command under connectedk8s --- src/connectedk8s/README.md | 8 +++++++- src/connectedk8s/azext_connectedk8s/_help.py | 8 ++++++++ src/connectedk8s/azext_connectedk8s/_params.py | 4 ++++ src/connectedk8s/azext_connectedk8s/commands.py | 2 +- src/connectedk8s/azext_connectedk8s/custom.py | 4 ++++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/connectedk8s/README.md b/src/connectedk8s/README.md index a2a3690f524..9ffb65a86fe 100644 --- a/src/connectedk8s/README.md +++ b/src/connectedk8s/README.md @@ -51,4 +51,10 @@ or az connectedk8s delete \ --ids "/subscriptions/subscription_id/resourceGroups/my-rg/providers/Microsoft.Kubernetes/connectedClusters/my-cluster" \ -y -``` \ No newline at end of file +``` + +#### Troubleshoot and get logs for a broken cluster +``` +az connectedk8s troubleshoot \ + --resource-group my-rg \ + --name my-cluster \ \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/_help.py b/src/connectedk8s/azext_connectedk8s/_help.py index 8f41bb82ed6..032cd7407fb 100644 --- a/src/connectedk8s/azext_connectedk8s/_help.py +++ b/src/connectedk8s/azext_connectedk8s/_help.py @@ -117,3 +117,11 @@ - name: Disable multiple features. text: az connectedk8s disable-features -n clusterName -g resourceGroupName --features custom-locations azure-rbac """ + +helps['connectedk8s troubleshoot'] = """ +type: command + short-summary: Troubleshoots and gets logs for a broken cluster. + examples: + - name: Troubleshoots a broken cluster . + text: az connectedk8s troubleshoot -n clusterName -g resourceGroupName +""" diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py index 12e4fc53c84..e1b48cb4cf9 100644 --- a/src/connectedk8s/azext_connectedk8s/_params.py +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -84,3 +84,7 @@ def load_arguments(self, _): c.argument('context_name', options_list=['--kube-context'], help='If specified, overwrite the default context name.') c.argument('path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(), default=os.path.join(os.path.expanduser('~'), '.kube', 'config'), help="Kubernetes configuration file to update. If not provided, updates the file '~/.kube/config'. Use '-' to print YAML to stdout instead.") c.argument('api_server_port', options_list=['--port'], help='Port used for accessing connected cluster.') + + with self.argument_context('connectedk8s troubleshoot') as c: + c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.') + c.argument('location', arg_type=get_location_type(self.cli_ctx), validator=get_default_location_from_resource_group) diff --git a/src/connectedk8s/azext_connectedk8s/commands.py b/src/connectedk8s/azext_connectedk8s/commands.py index 6c79422d655..2db739bcc64 100644 --- a/src/connectedk8s/azext_connectedk8s/commands.py +++ b/src/connectedk8s/azext_connectedk8s/commands.py @@ -28,7 +28,7 @@ def load_command_table(self, _): g.custom_command('enable-features', 'enable_features', is_preview=True) g.custom_command('disable-features', 'disable_features', is_preview=True) g.custom_command('list', 'list_connectedk8s', table_transformer=connectedk8s_list_table_format) + g.custom_command('troubleshoot', 'troubleshoot', supports_no_wait=True) g.custom_show_command('show', 'get_connectedk8s', table_transformer=connectedk8s_show_table_format) - with self.command_group('connectedk8s', connectedk8s_sdk_prev, client_factory=cf_connected_cluster_prev_2021_04_01) as g: g.custom_command('proxy', 'client_side_proxy_wrapper', is_preview=True) diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 594c2feaad4..4f62552a224 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -2022,3 +2022,7 @@ def check_if_csp_is_running(clientproxy_process): return True else: return False + +def troubleshoot(resource_group_name, cluster_name): + print("Hello world") + return "Needs implementation" \ No newline at end of file From 29a8518c1dba4e3c54c71628be4bfea145b70f1a Mon Sep 17 00:00:00 2001 From: Sophie Zhao Date: Thu, 25 Mar 2021 17:08:11 -0400 Subject: [PATCH 2/6] resolve check issues --- src/connectedk8s/azext_connectedk8s/_help.py | 2 +- src/connectedk8s/azext_connectedk8s/_params.py | 10 +++++----- src/connectedk8s/azext_connectedk8s/custom.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/_help.py b/src/connectedk8s/azext_connectedk8s/_help.py index 032cd7407fb..7014871841c 100644 --- a/src/connectedk8s/azext_connectedk8s/_help.py +++ b/src/connectedk8s/azext_connectedk8s/_help.py @@ -119,7 +119,7 @@ """ helps['connectedk8s troubleshoot'] = """ -type: command + type: command short-summary: Troubleshoots and gets logs for a broken cluster. examples: - name: Troubleshoots a broken cluster . diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py index e1b48cb4cf9..89e5b9dc0f6 100644 --- a/src/connectedk8s/azext_connectedk8s/_params.py +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -78,13 +78,13 @@ def load_arguments(self, _): c.argument('kube_config', options_list=['--kube-config'], help='Path to the kube config file.') c.argument('kube_context', options_list=['--kube-context'], help='Kubconfig context from current machine.') + with self.argument_context('connectedk8s troubleshoot') as c: + c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.') + with self.argument_context('connectedk8s proxy') as c: c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.') c.argument('token', options_list=['--token'], help='Service account token to use to authenticate to the kubernetes cluster.') c.argument('context_name', options_list=['--kube-context'], help='If specified, overwrite the default context name.') c.argument('path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(), default=os.path.join(os.path.expanduser('~'), '.kube', 'config'), help="Kubernetes configuration file to update. If not provided, updates the file '~/.kube/config'. Use '-' to print YAML to stdout instead.") - c.argument('api_server_port', options_list=['--port'], help='Port used for accessing connected cluster.') - - with self.argument_context('connectedk8s troubleshoot') as c: - c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.') - c.argument('location', arg_type=get_location_type(self.cli_ctx), validator=get_default_location_from_resource_group) + c.argument('api_server_port', options_list=['--port'], help='Port used for accessing connected cluster.') + \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 4f62552a224..0537d92dac2 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -2023,6 +2023,6 @@ def check_if_csp_is_running(clientproxy_process): else: return False + def troubleshoot(resource_group_name, cluster_name): - print("Hello world") return "Needs implementation" \ No newline at end of file From fe3c1e8566cd2831908be38078c68880f3454dd2 Mon Sep 17 00:00:00 2001 From: Sophie Zhao Date: Thu, 25 Mar 2021 17:33:25 -0400 Subject: [PATCH 3/6] fix final new line --- src/connectedk8s/azext_connectedk8s/_params.py | 6 +----- src/connectedk8s/azext_connectedk8s/custom.py | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py index 89e5b9dc0f6..72ad36bba89 100644 --- a/src/connectedk8s/azext_connectedk8s/_params.py +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -78,13 +78,9 @@ def load_arguments(self, _): c.argument('kube_config', options_list=['--kube-config'], help='Path to the kube config file.') c.argument('kube_context', options_list=['--kube-context'], help='Kubconfig context from current machine.') - with self.argument_context('connectedk8s troubleshoot') as c: - c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.') - with self.argument_context('connectedk8s proxy') as c: c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.') c.argument('token', options_list=['--token'], help='Service account token to use to authenticate to the kubernetes cluster.') c.argument('context_name', options_list=['--kube-context'], help='If specified, overwrite the default context name.') c.argument('path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(), default=os.path.join(os.path.expanduser('~'), '.kube', 'config'), help="Kubernetes configuration file to update. If not provided, updates the file '~/.kube/config'. Use '-' to print YAML to stdout instead.") - c.argument('api_server_port', options_list=['--port'], help='Port used for accessing connected cluster.') - \ No newline at end of file + c.argument('api_server_port', options_list=['--port'], help='Port used for accessing connected cluster.') diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 0537d92dac2..184f4881958 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -2025,4 +2025,4 @@ def check_if_csp_is_running(clientproxy_process): def troubleshoot(resource_group_name, cluster_name): - return "Needs implementation" \ No newline at end of file + return "Needs implementation" From be0e8cdf571cab543a75948dc58aeef6d033bd54 Mon Sep 17 00:00:00 2001 From: Sophie Zhao Date: Thu, 25 Mar 2021 17:53:38 -0400 Subject: [PATCH 4/6] add back troubleshoot params --- src/connectedk8s/azext_connectedk8s/_params.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py index 72ad36bba89..ddca87212b8 100644 --- a/src/connectedk8s/azext_connectedk8s/_params.py +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -83,4 +83,7 @@ def load_arguments(self, _): c.argument('token', options_list=['--token'], help='Service account token to use to authenticate to the kubernetes cluster.') c.argument('context_name', options_list=['--kube-context'], help='If specified, overwrite the default context name.') c.argument('path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(), default=os.path.join(os.path.expanduser('~'), '.kube', 'config'), help="Kubernetes configuration file to update. If not provided, updates the file '~/.kube/config'. Use '-' to print YAML to stdout instead.") - c.argument('api_server_port', options_list=['--port'], help='Port used for accessing connected cluster.') + c.argument('api_server_port', options_list=['--port'], help='Port used for accessing connected cluster.') + + with self.argument_context('connectedk8s troubleshoot') as c: + c.argument('cluster_name', options_list=['--name', '-n'], id_part='name', help='The name of the connected cluster.') From 18059a8783aed7ea0d9a3c97d734ebd049ff3e4d Mon Sep 17 00:00:00 2001 From: Sophie Zhao Date: Mon, 5 Apr 2021 11:21:26 -0400 Subject: [PATCH 5/6] implement helm version and permission checks --- src/connectedk8s/MANIFEST.in | 1 + .../azext_connectedk8s/_constants.py | 1 + src/connectedk8s/azext_connectedk8s/_help.py | 6 +- src/connectedk8s/azext_connectedk8s/custom.py | 70 ++++++++++++++++++- .../azext_connectedk8s/troubleshoot.json | 26 +++++++ src/connectedk8s/setup.py | 4 +- 6 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 src/connectedk8s/MANIFEST.in create mode 100644 src/connectedk8s/azext_connectedk8s/troubleshoot.json diff --git a/src/connectedk8s/MANIFEST.in b/src/connectedk8s/MANIFEST.in new file mode 100644 index 00000000000..3eafd0662e5 --- /dev/null +++ b/src/connectedk8s/MANIFEST.in @@ -0,0 +1 @@ +include azext_connectedk8s/troubleshoot.json \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/_constants.py b/src/connectedk8s/azext_connectedk8s/_constants.py index 7d90c72732a..c7b015b2e2c 100644 --- a/src/connectedk8s/azext_connectedk8s/_constants.py +++ b/src/connectedk8s/azext_connectedk8s/_constants.py @@ -41,6 +41,7 @@ Kubernetes_Connectivity_FaultType = 'kubernetes-cluster-connection-error' Helm_Version_Fault_Type = 'helm-not-updated-error' Check_HelmVersion_Fault_Type = 'helm-version-check-error' +Check_AzureCliVersion_Fault_Type = 'azure-version-check-error' Helm_Installation_Fault_Type = 'helm-not-installed-error' Check_HelmInstallation_Fault_Type = 'check-helm-installed-error' Get_HelmRegistery_Path_Fault_Type = 'helm-registry-path-fetch-error' diff --git a/src/connectedk8s/azext_connectedk8s/_help.py b/src/connectedk8s/azext_connectedk8s/_help.py index 7014871841c..d4e74b7c976 100644 --- a/src/connectedk8s/azext_connectedk8s/_help.py +++ b/src/connectedk8s/azext_connectedk8s/_help.py @@ -120,8 +120,8 @@ helps['connectedk8s troubleshoot'] = """ type: command - short-summary: Troubleshoots and gets logs for a broken cluster. + short-summary: Collects diagnose infomation and gets logs on the connected cluster. examples: - - name: Troubleshoots a broken cluster . - text: az connectedk8s troubleshoot -n clusterName -g resourceGroupName + - name: Troubleshoots a connected cluster . + text: az connectedk8s troubleshoot -g resourceGroupName -n connectedClusterName """ diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 184f4881958..162f392aad8 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -44,6 +44,7 @@ from .vendored_sdks.models import ConnectedCluster, ConnectedClusterIdentity from threading import Timer, Thread import sys +from packaging import version logger = get_logger(__name__) # pylint:disable=unused-argument # pylint: disable=too-many-locals @@ -2023,6 +2024,73 @@ def check_if_csp_is_running(clientproxy_process): else: return False +def check_helm_installed(condition, failMsg, passMsg, username): + kube_config = set_kube_config(None) + + # Loading the kubeconfig file in kubernetes client configuration + load_kube_config(kube_config, None) + configuration = kube_client.Configuration() + + # Checking the connection to kubernetes cluster. + # This check was added to avoid large timeouts when connecting to AAD Enabled + # AKS clusters if the user had not logged in. + check_kube_connection(configuration) + + # Checking helm installation + try: + check_helm_install(kube_config, None) + print(passMsg) + except Exception as e: + print(e) + print(failMsg) + return + + +def check_helm_3(condition, failMsg, passMsg, username): + try: + ver = check_helm_version(None, None)[1:] + if eval("version.parse(ver)"+condition): + print(passMsg) + else: + print(failMsg) + return + except Exception as e: + print(e) + print(failMsg) + return + + + +def check_azure_folder_permissions(condition, failMsg, passMsg, username): + #username = input("Checking to see if you have access to ~/.azure. Please enter your username: ") + try: + path = 'C:\\Users\\'+username+'\\.azure' + for dirpath, dirnames, filenames in os.walk(path): + dirs = [os.path.join(dirpath, x) for x in dirnames] + for dirname in dirs: + if os.access(path, os.R_OK | os.X_OK | os.W_OK): + print(path + " "+passMsg) + else: + cmd = "(Get-Acl "+path+").Access | ?{$_.IdentityReference -match \""+username+"\"} | Select IdentityReference,FileSystemRights" + #permissions = run(["powershell", "-Command", cmd], capture_output=True) + print(failMsg + cmd) + except Exception as e: + print(e) + print("An exception occured: Please try again.") + return def troubleshoot(resource_group_name, cluster_name): - return "Needs implementation" + username = input("Please enter your username.This is used to locate the ~/.azure folder on windows: ") + troubleshoot_file= 'C:\\Users\\'+username+'\\.azure\\cliextensions\\connectedk8s\\azext_connectedk8s\\troubleshoot.json' + platform = '' + try: + with open(troubleshoot_file, 'r') as f: + checks = json.load(f) + for check in checks: + if check['Enabled']: + method = eval(check["Method"]+'(check["Condition"], check["FailMessage"], check["PassMessage"], username)') + method + except OSError as e: + print(e) + print("Please ensure troubleshoot.json is in local directory.") + diff --git a/src/connectedk8s/azext_connectedk8s/troubleshoot.json b/src/connectedk8s/azext_connectedk8s/troubleshoot.json new file mode 100644 index 00000000000..3d84155a15f --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/troubleshoot.json @@ -0,0 +1,26 @@ +[ + { + "Check": "HelmInstallation", + "Condition": "true", + "Method": "check_helm_installed", + "PassMessage": "Helm installation check passed!", + "FailMessage": "Helm not found. Please install the latest version of Helm. Learn more at https://aka.ms/arc/k8s/onboarding-helm-install", + "Enabled": "true" + }, + { + "Check": "HelmVersion", + "Condition": ">=version.parse(\"3.0\")", + "Method": "check_helm_3", + "PassMessage": "Your Helm version is up to date!", + "FailMessage": "Helm 3+ required. Please visit https://aka.ms/arc/k8s/onboarding-helm-install to download the latest installation of Helm.", + "Enabled": "true" + }, + { + "Check": "AzurePermissionCheck", + "Condition": "true", + "Method": "check_azure_folder_permissions", + "PassMessage": "You have the correct permissions!", + "FailMessage": "Please use the following command to get the correct permissions: ", + "Enabled": "true" + } +] \ No newline at end of file diff --git a/src/connectedk8s/setup.py b/src/connectedk8s/setup.py index 70ab8aa0d54..4bf4cab22a5 100644 --- a/src/connectedk8s/setup.py +++ b/src/connectedk8s/setup.py @@ -58,5 +58,7 @@ classifiers=CLASSIFIERS, packages=find_packages(), install_requires=DEPENDENCIES, - package_data={'azext_connectedk8s': ['azext_metadata.json']}, + package_data={'azext_connectedk8s': ['azext_metadata.json', 'troubleshoot.json']}, + #data_files=[('azext_connectedk8s', ['troubleshoot.json'])], + include_package_data=True ) From 049b1e0ec0d9b5be1fef88eb2f7dd1d7b9837887 Mon Sep 17 00:00:00 2001 From: sophie zhao Date: Sun, 27 Apr 2025 15:22:57 -0700 Subject: [PATCH 6/6] add helper.py --- src/aks-preview/azext_aks_preview/_helpers.py | 26 ++++++++++++++++++- src/connectedk8s/README.md | 8 +----- .../azext_connectedk8s/troubleshoot.json | 26 ------------------- 3 files changed, 26 insertions(+), 34 deletions(-) delete mode 100644 src/connectedk8s/azext_connectedk8s/troubleshoot.json diff --git a/src/aks-preview/azext_aks_preview/_helpers.py b/src/aks-preview/azext_aks_preview/_helpers.py index e952f238318..b0522b91c32 100644 --- a/src/aks-preview/azext_aks_preview/_helpers.py +++ b/src/aks-preview/azext_aks_preview/_helpers.py @@ -19,7 +19,8 @@ from azext_aks_preview._consts import ( ADDONS, - CONST_MONITORING_ADDON_NAME + CONST_MONITORING_ADDON_NAME, + CONST_K8S_EXTENSION_NAME, ) from azure.cli.command_modules.acs._helpers import map_azure_error_to_cli_error @@ -28,6 +29,7 @@ FileOperationError, InvalidArgumentValueError, ResourceNotFoundError, + UnknownError, ) from azure.core.exceptions import AzureError from knack.log import get_logger @@ -376,6 +378,28 @@ def check_is_monitoring_addon_enabled(addons, instance): return is_monitoring_addon_enabled +def get_k8s_extension_module(module_name): + try: + # adding the installed extension in the path + from azure.cli.core.extension.operations import add_extension_to_path + add_extension_to_path(CONST_K8S_EXTENSION_NAME) + # import the extension module + from importlib import import_module + azext_custom = import_module(module_name) + return azext_custom + except ImportError: + raise UnknownError( # pylint: disable=raise-missing-from + "Please add CLI extension `k8s-extension` for performing Azure Extension operations.\n" + "Run command `az extension add --name k8s-extension`" + ) + + +# TODO: Need to should source the set of allowed extensions from the extensions API at some point +def check_if_extension_type_is_in_allow_list(extension_type_name): + allowedListOfExtensions = ["microsoft.dataprotection.kubernetes", "microsoft.flux"] + return extension_type_name.lower() in allowedListOfExtensions + + def filter_hard_taints(node_initialization_taints: List[str]) -> List[str]: filtered_taints = [] for taint in node_initialization_taints: diff --git a/src/connectedk8s/README.md b/src/connectedk8s/README.md index 9ffb65a86fe..a2a3690f524 100644 --- a/src/connectedk8s/README.md +++ b/src/connectedk8s/README.md @@ -51,10 +51,4 @@ or az connectedk8s delete \ --ids "/subscriptions/subscription_id/resourceGroups/my-rg/providers/Microsoft.Kubernetes/connectedClusters/my-cluster" \ -y -``` - -#### Troubleshoot and get logs for a broken cluster -``` -az connectedk8s troubleshoot \ - --resource-group my-rg \ - --name my-cluster \ \ No newline at end of file +``` \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/troubleshoot.json b/src/connectedk8s/azext_connectedk8s/troubleshoot.json deleted file mode 100644 index 3d84155a15f..00000000000 --- a/src/connectedk8s/azext_connectedk8s/troubleshoot.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "Check": "HelmInstallation", - "Condition": "true", - "Method": "check_helm_installed", - "PassMessage": "Helm installation check passed!", - "FailMessage": "Helm not found. Please install the latest version of Helm. Learn more at https://aka.ms/arc/k8s/onboarding-helm-install", - "Enabled": "true" - }, - { - "Check": "HelmVersion", - "Condition": ">=version.parse(\"3.0\")", - "Method": "check_helm_3", - "PassMessage": "Your Helm version is up to date!", - "FailMessage": "Helm 3+ required. Please visit https://aka.ms/arc/k8s/onboarding-helm-install to download the latest installation of Helm.", - "Enabled": "true" - }, - { - "Check": "AzurePermissionCheck", - "Condition": "true", - "Method": "check_azure_folder_permissions", - "PassMessage": "You have the correct permissions!", - "FailMessage": "Please use the following command to get the correct permissions: ", - "Enabled": "true" - } -] \ No newline at end of file