From e0cfa4f4b96faf244cad846ddf28069e40012976 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Thu, 29 Jul 2021 12:36:35 +0530 Subject: [PATCH 1/8] Added error fix --- .../azext_connectedk8s/_constants.py | 1 + src/connectedk8s/azext_connectedk8s/custom.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/connectedk8s/azext_connectedk8s/_constants.py b/src/connectedk8s/azext_connectedk8s/_constants.py index 56ae91c6de8..5ca2a6cd37c 100644 --- a/src/connectedk8s/azext_connectedk8s/_constants.py +++ b/src/connectedk8s/azext_connectedk8s/_constants.py @@ -101,6 +101,7 @@ Error_enabling_Features = 'Error while updating agents for enabling features. Please run \"kubectl get pods -n azure-arc\" to check the pods in case of timeout error. Error: {}' Error_disabling_Features = 'Error while updating agents for disabling features. Please run \"kubectl get pods -n azure-arc\" to check the pods in case of timeout error. Error: {}' Proxy_Kubeconfig_During_Deletion_Fault_Type = 'Encountered proxy kubeconfig during deletion.' +Namespace_Does_Not_Exist_Fault_Type = "The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster." CLIENT_PROXY_VERSION = '1.1.0' API_SERVER_PORT = 47011 CLIENT_PROXY_PORT = 47010 diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 80b3f18c80e..ef52f857e3d 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -255,6 +255,23 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr # Checking if custom locations rp is registered and fetching oid if it is registered enable_custom_locations, custom_locations_oid = check_cl_registration_and_get_oid(cmd, cl_oid) + try: + current_k8s_context = config.list_kube_config_contexts()[1]['context'] + current_k8s_namespace = current_k8s_context['namespace'] + namespace_exists = False + k8s_v1 = kube_client.CoreV1Api() + k8s_ns = k8s_v1.list_namespace() + for ns in k8s_ns.items: + if ns.metadata.name == current_k8s_namespace: + namespace_exists = True + break + if namespace_exists is False: + telemetry.set_exception(exception="Namespace doesn't exist", fault_type=consts.Namespace_Does_Not_Exist_Fault_Type, + summary="The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster.") + raise ValidationError("The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster.") + except Exception as e: + logger.warning("Failed to validate if the active namespace exists on the kubernetes cluster. Exception: {}".format(str(e))) + # Install azure-arc agents utils.helm_install_release(chart_path, subscription_id, kubernetes_distro, kubernetes_infra, resource_group_name, cluster_name, location, onboarding_tenant_id, http_proxy, https_proxy, no_proxy, proxy_cert, private_key_pem, kube_config, From d6abcc1d30facfdb5e2c736af796ff2c5ca75fca Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Mon, 2 Aug 2021 14:40:36 +0530 Subject: [PATCH 2/8] Fix --- src/connectedk8s/azext_connectedk8s/_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index 05988a57ae2..2a7a7702b9d 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -20,7 +20,8 @@ from azure.cli.core.util import send_raw_request from azure.cli.core import telemetry from azure.core.exceptions import ResourceNotFoundError -from msrest.exceptions import AuthenticationError, HttpOperationError, TokenExpiredError, ValidationError +from msrest.exceptions import AuthenticationError, HttpOperationError, TokenExpiredError +from msrest.exceptions import ValidationError as MSRestValidationError from msrestazure.azure_exceptions import CloudError from kubernetes.client.rest import ApiException from azext_connectedk8s._client_factory import _resource_client_factory @@ -183,7 +184,7 @@ def arm_exception_handler(ex, fault_type, summary, return_if_not_found=False): raise AzureInternalError("Http operation error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) raise AzureResponseError("Http operation error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) - if isinstance(ex, ValidationError): + if isinstance(ex, MSRestValidationError): telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise AzureResponseError("Validation error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) From 97f7792757561d404b629d3482aa4eccd5e413fc Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 3 Aug 2021 15:37:37 +0530 Subject: [PATCH 3/8] big bang --- .../azext_connectedk8s/_constants.py | 5 +++ src/connectedk8s/azext_connectedk8s/_utils.py | 34 ++++++++++++++++++- src/connectedk8s/azext_connectedk8s/custom.py | 9 +++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/connectedk8s/azext_connectedk8s/_constants.py b/src/connectedk8s/azext_connectedk8s/_constants.py index 5ca2a6cd37c..91f261e612d 100644 --- a/src/connectedk8s/azext_connectedk8s/_constants.py +++ b/src/connectedk8s/azext_connectedk8s/_constants.py @@ -10,6 +10,9 @@ Infrastructure_Enum_Values = ["auto", "generic", "azure", "aws", "gcp", "azure_stack_hci", "azure_stack_hub", "azure_stack_edge", "vsphere", "windows_server"] Feature_Values = ["cluster-connect", "azure-rbac", "custom-locations"] Custom_Locations_Provider_Namespace = 'Microsoft.ExtendedLocation' +Connected_Cluster_Provider_Namespace = 'Microsoft.Kubernetes' +Kubernetes_Configuration_Provider_Namespace = 'Microsoft.KubernetesConfiguration' +Arc_Namespace = 'azure-arc' Azure_PublicCloudName = 'AZUREPUBLICCLOUD' Azure_USGovCloudName = 'AZUREUSGOVERNMENTCLOUD' @@ -101,6 +104,8 @@ Error_enabling_Features = 'Error while updating agents for enabling features. Please run \"kubectl get pods -n azure-arc\" to check the pods in case of timeout error. Error: {}' Error_disabling_Features = 'Error while updating agents for disabling features. Please run \"kubectl get pods -n azure-arc\" to check the pods in case of timeout error. Error: {}' Proxy_Kubeconfig_During_Deletion_Fault_Type = 'Encountered proxy kubeconfig during deletion.' +Cant_Create_ClusterRoleBindings_Fault_Type = 'Cannot create cluster role bindings on this Kubernets cluster' +CC_Provider_Namespace_Not_Registered_Fault_Type = "Connected Cluster Provider MS.K8 namespace not registered" Namespace_Does_Not_Exist_Fault_Type = "The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster." CLIENT_PROXY_VERSION = '1.1.0' API_SERVER_PORT = 47011 diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index 2a7a7702b9d..b3ed0f178ac 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -24,7 +24,7 @@ from msrest.exceptions import ValidationError as MSRestValidationError from msrestazure.azure_exceptions import CloudError from kubernetes.client.rest import ApiException -from azext_connectedk8s._client_factory import _resource_client_factory +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.azclierror import CLIInternalError, ClientRequestError, ArgumentUsageError, ManualInterrupt, AzureResponseError, AzureInternalError, ValidationError @@ -396,3 +396,35 @@ def names(self, names): V1ContainerImage.names = V1ContainerImage.names.setter(names) except Exception as ex: logger.debug("Error while trying to monkey patch the fix for list_node(): {}".format(str(ex))) + + +def check_provider_registrations(cli_ctx): + try: + rp_client = _resource_providers_client(cli_ctx) + cc_registration_state = rp_client.get(consts.Connected_Cluster_Provider_Namespace).registration_state + if cc_registration_state != "Registered": + telemetry.set_exception(exception="{} provider is not registered".format(consts.Connected_Cluster_Provider_Namespace), fault_type=consts.CC_Provider_Namespace_Not_Registered_Fault_Type, + summary="{} provider is not registered".format(consts.Connected_Cluster_Provider_Namespace)) + raise ValidationError("{} provider is not registered. Please register it using 'az provider register -n 'Microsoft.Kubernetes' before running the connect command.".format(consts.Connected_Cluster_Provider_Namespace)) + kc_registration_state = rp_client.get(consts.Kubernetes_Configuration_Provider_Namespace).registration_state + if kc_registration_state != "Registered": + logger.warning("{} provider is not registered".format(consts.Kubernetes_Configuration_Provider_Namespace)) + except Exception as ex: + logger.warning("Couldn't check the required provider's registration status. Error: {}".format(str(ex))) + + +def can_create_clusterrolebindings(configuration): + try: + api_instance = kube_client.AuthorizationV1Api(kube_client.ApiClient(configuration)) + access_review = kube_client.V1SelfSubjectAccessReview(spec={ + "resourceAttributes": { + "verb": "create", + "resource": "clusterrolebindings", + "group": "rbac.authorization.k8s.io" + } + }) + response = api_instance.create_self_subject_access_review(access_review) + return response.status.allowed + except Exception as ex: + logger.warning("Couldn't check for the permission to create clusterrolebindings on this k8s cluster. Error: {}".format(str(ex))) + return "Unknown" \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index ea04c6a66f7..5b9b63d0a5e 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -72,6 +72,9 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr graph_client = _graph_client_factory(cmd.cli_ctx) onboarding_tenant_id = graph_client.config.tenant_id + # Checking provider registration status + utils.check_provider_registrations(cmd.cli_ctx) + # Setting kubeconfig kube_config = set_kube_config(kube_config) @@ -119,6 +122,12 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr summary="Couldn't find any node on the kubernetes cluster with the architecture type 'amd64' and OS 'linux'") logger.warning("Please ensure that this Kubernetes cluster have any nodes with OS 'linux' and architecture 'amd64', for scheduling the Arc-Agents onto and connecting to Azure. Learn more at {}".format("https://aka.ms/ArcK8sSupportedOSArchitecture")) + crb_permission = utils.can_create_clusterrolebindings(configuration) + if not crb_permission: + telemetry.set_exception(exception="Your credentials doesn't have permission to create clusterrolebindings on this kubernetes cluster.", fault_type=consts.Cant_Create_ClusterRoleBindings_Fault_Type, + summary="Your credentials doesn't have permission to create clusterrolebindings on this kubernetes cluster.") + raise ValidationError("Your credentials doesn't have permission to create clusterrolebindings on this kubernetes cluster. Please check your permissions.") + # Get kubernetes cluster info kubernetes_version = get_server_version(configuration) From 9e41a7a07c04dc357be913bb13a717d8a4506fec Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Tue, 3 Aug 2021 15:46:28 +0530 Subject: [PATCH 4/8] lint fix --- src/connectedk8s/azext_connectedk8s/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index b3ed0f178ac..465be78445b 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -427,4 +427,4 @@ def can_create_clusterrolebindings(configuration): return response.status.allowed except Exception as ex: logger.warning("Couldn't check for the permission to create clusterrolebindings on this k8s cluster. Error: {}".format(str(ex))) - return "Unknown" \ No newline at end of file + return "Unknown" From d131e5ae29fef84fd4c3befc7eba6144f5d7517f Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Thu, 5 Aug 2021 12:59:32 +0530 Subject: [PATCH 5/8] Fixe --- .../azext_connectedk8s/_constants.py | 4 +- src/connectedk8s/azext_connectedk8s/_utils.py | 1 + src/connectedk8s/azext_connectedk8s/custom.py | 48 ++++++++++++------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/_constants.py b/src/connectedk8s/azext_connectedk8s/_constants.py index 91f261e612d..338bf88e159 100644 --- a/src/connectedk8s/azext_connectedk8s/_constants.py +++ b/src/connectedk8s/azext_connectedk8s/_constants.py @@ -104,9 +104,9 @@ Error_enabling_Features = 'Error while updating agents for enabling features. Please run \"kubectl get pods -n azure-arc\" to check the pods in case of timeout error. Error: {}' Error_disabling_Features = 'Error while updating agents for disabling features. Please run \"kubectl get pods -n azure-arc\" to check the pods in case of timeout error. Error: {}' Proxy_Kubeconfig_During_Deletion_Fault_Type = 'Encountered proxy kubeconfig during deletion.' -Cant_Create_ClusterRoleBindings_Fault_Type = 'Cannot create cluster role bindings on this Kubernets cluster' +Cannot_Create_ClusterRoleBindings_Fault_Type = 'Cannot create cluster role bindings on this Kubernets cluster' CC_Provider_Namespace_Not_Registered_Fault_Type = "Connected Cluster Provider MS.K8 namespace not registered" -Namespace_Does_Not_Exist_Fault_Type = "The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster." +Default_Namespace_Does_Not_Exist_Fault_Type = "The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster." CLIENT_PROXY_VERSION = '1.1.0' API_SERVER_PORT = 47011 CLIENT_PROXY_PORT = 47010 diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index 465be78445b..f4580e99411 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -408,6 +408,7 @@ def check_provider_registrations(cli_ctx): raise ValidationError("{} provider is not registered. Please register it using 'az provider register -n 'Microsoft.Kubernetes' before running the connect command.".format(consts.Connected_Cluster_Provider_Namespace)) kc_registration_state = rp_client.get(consts.Kubernetes_Configuration_Provider_Namespace).registration_state if kc_registration_state != "Registered": + telemetry.set_user_fault() logger.warning("{} provider is not registered".format(consts.Kubernetes_Configuration_Provider_Namespace)) except Exception as ex: logger.warning("Couldn't check the required provider's registration status. Error: {}".format(str(ex))) diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 5b9b63d0a5e..ebae268a0b0 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -124,7 +124,7 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr crb_permission = utils.can_create_clusterrolebindings(configuration) if not crb_permission: - telemetry.set_exception(exception="Your credentials doesn't have permission to create clusterrolebindings on this kubernetes cluster.", fault_type=consts.Cant_Create_ClusterRoleBindings_Fault_Type, + telemetry.set_exception(exception="Your credentials doesn't have permission to create clusterrolebindings on this kubernetes cluster.", fault_type=consts.Cannot_Create_ClusterRoleBindings_Fault_Type, summary="Your credentials doesn't have permission to create clusterrolebindings on this kubernetes cluster.") raise ValidationError("Your credentials doesn't have permission to create clusterrolebindings on this kubernetes cluster. Please check your permissions.") @@ -209,6 +209,35 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr "and corresponds to a different Kubernetes cluster.", recommendation="To onboard this Kubernetes cluster " + "to Azure, specify different resource name or resource group name.") + try: + k8s_contexts = config.list_kube_config_contexts() # returns tuple of (all_contexts, current_context) + if kube_context: # if custom kube-context is specified + if k8s_contexts[1].get('name') == kube_context: + current_k8s_context = k8s_contexts[1] + else: + for context in k8s_contexts[0]: + if context.get('name') == kube_context: + current_k8s_context = context + else: + current_k8s_context = k8s_contexts[1] + + current_k8s_namespace = current_k8s_context.get('context').get('namespace', "default") # Take "default" namespace, if not specified in the kube-config + namespace_exists = False + k8s_v1 = kube_client.CoreV1Api() + k8s_ns = k8s_v1.list_namespace() + for ns in k8s_ns.items: + if ns.metadata.name == current_k8s_namespace: + namespace_exists = True + break + if namespace_exists is False: + telemetry.set_exception(exception="Namespace doesn't exist", fault_type=consts.Default_Namespace_Does_Not_Exist_Fault_Type, + summary="The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster.") + raise ValidationError("The default namespace '{}' defined in the kubeconfig doesn't exist on the kubernetes cluster.".format(current_k8s_namespace)) + except ValidationError as e: + raise e + except Exception as e: + logger.warning("Failed to validate if the active namespace exists on the kubernetes cluster. Exception: {}".format(str(e))) + # Resource group Creation if resource_group_exists(cmd.cli_ctx, resource_group_name, subscription_id) is False: from azure.cli.core.profiles import ResourceType @@ -265,23 +294,6 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr # Checking if custom locations rp is registered and fetching oid if it is registered enable_custom_locations, custom_locations_oid = check_cl_registration_and_get_oid(cmd, cl_oid) - try: - current_k8s_context = config.list_kube_config_contexts()[1]['context'] - current_k8s_namespace = current_k8s_context['namespace'] - namespace_exists = False - k8s_v1 = kube_client.CoreV1Api() - k8s_ns = k8s_v1.list_namespace() - for ns in k8s_ns.items: - if ns.metadata.name == current_k8s_namespace: - namespace_exists = True - break - if namespace_exists is False: - telemetry.set_exception(exception="Namespace doesn't exist", fault_type=consts.Namespace_Does_Not_Exist_Fault_Type, - summary="The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster.") - raise ValidationError("The default namespace defined in the kubeconfig doesn't exist on the kubernetes cluster.") - except Exception as e: - logger.warning("Failed to validate if the active namespace exists on the kubernetes cluster. Exception: {}".format(str(e))) - # Install azure-arc agents utils.helm_install_release(chart_path, subscription_id, kubernetes_distro, kubernetes_infra, resource_group_name, cluster_name, location, onboarding_tenant_id, http_proxy, https_proxy, no_proxy, proxy_cert, private_key_pem, kube_config, From dfffd8b5b5bbf91fca9f2b485334a7756ef2e850 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Thu, 5 Aug 2021 13:03:17 +0530 Subject: [PATCH 6/8] Nit --- src/connectedk8s/azext_connectedk8s/_utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index f4580e99411..a02c9f923ec 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -410,6 +410,8 @@ def check_provider_registrations(cli_ctx): if kc_registration_state != "Registered": telemetry.set_user_fault() logger.warning("{} provider is not registered".format(consts.Kubernetes_Configuration_Provider_Namespace)) + except ValidationError as e: + raise e except Exception as ex: logger.warning("Couldn't check the required provider's registration status. Error: {}".format(str(ex))) From 7fd701e7aef4cd3c9f9396c33578b7c4ac24021a Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Thu, 5 Aug 2021 16:43:53 +0530 Subject: [PATCH 7/8] Up --- src/connectedk8s/azext_connectedk8s/custom.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index ebae268a0b0..4068ed3428c 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -218,6 +218,7 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_pr for context in k8s_contexts[0]: if context.get('name') == kube_context: current_k8s_context = context + break else: current_k8s_context = k8s_contexts[1] From 73acc481b5ca8d8c9879ddf3220dffaacaf76108 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Fri, 6 Aug 2021 18:56:41 +0530 Subject: [PATCH 8/8] Fix --- src/connectedk8s/HISTORY.rst | 7 +++++++ src/connectedk8s/setup.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/connectedk8s/HISTORY.rst b/src/connectedk8s/HISTORY.rst index 6e4cabf841d..764fe7e8450 100644 --- a/src/connectedk8s/HISTORY.rst +++ b/src/connectedk8s/HISTORY.rst @@ -3,6 +3,13 @@ Release History =============== + +1.1.7 +++++++ +* Add non-existing namespace deploy check +* Improve some error and warning experiences + + 1.1.6 ++++++ * Moved to track2 SDK diff --git a/src/connectedk8s/setup.py b/src/connectedk8s/setup.py index 0e5f3d59160..d7598a7f7e4 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.1.6' +VERSION = '1.1.7' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers