Skip to content

Commit

Permalink
[Connectedk8s] Non-existing namespace deploy check + Error experience…
Browse files Browse the repository at this point in the history
… Improvement (#3736)

* Added error fix

* Fix

* big bang

* lint fix

* Fixe

* Nit

* Up

* Fix
  • Loading branch information
alphaWizard authored Aug 7, 2021
1 parent 927c622 commit 1698603
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 4 deletions.
7 changes: 7 additions & 0 deletions src/connectedk8s/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/connectedk8s/azext_connectedk8s/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -101,6 +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.'
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"
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
Expand Down
42 changes: 39 additions & 3 deletions src/connectedk8s/azext_connectedk8s/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
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
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
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -395,3 +396,38 @@ 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":
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)))


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"
39 changes: 39 additions & 0 deletions src/connectedk8s/azext_connectedk8s/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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.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.")

# Get kubernetes cluster info
kubernetes_version = get_server_version(configuration)

Expand Down Expand Up @@ -200,6 +209,36 @@ 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
break
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
Expand Down
2 changes: 1 addition & 1 deletion src/connectedk8s/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 1698603

Please sign in to comment.