def check_helm_version(kube_config, kube_context): cmd_helm_version = ["helm", "version", "--short", "--client"] if kube_config: cmd_helm_version.extend(["--kubeconfig", kube_config]) if kube_context: cmd_helm_version.extend(["--kube-context", kube_context]) response_helm_version = Popen(cmd_helm_version, stdout=PIPE, stderr=PIPE) output_helm_version, error_helm_version = response_helm_version.communicate( ) if response_helm_version.returncode != 0: telemetry.set_exception(exception=error_helm_version.decode('ascii'), fault_type=consts.Check_HelmVersion_Fault_Type, summary='Unable to determine helm version') raise CLIError("Unable to determine helm version: " + error_helm_version.decode("ascii")) if "v2" in output_helm_version.decode("ascii"): telemetry.set_user_fault() telemetry.set_exception(exception='Helm 3 not found', fault_type=consts.Helm_Version_Fault_Type, summary='Helm3 not found on the machine') raise CLIError( "Helm version 3+ is required. " + "Ensure that you have installed the latest version of Helm. " + "Learn more at https://aka.ms/arc/k8s/onboarding-helm-install") return output_helm_version.decode('ascii')
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 _check_value(self, action, value): # Override to customize the error message when a argument is not among the available choices # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: if not self.command_source: # parser has no `command_source`, value is part of command itself error_msg = "{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'.".format( prog=self.prog, value=value) else: # `command_source` indicates command values have been parsed, value is an argument parameter = action.option_strings[ 0] if action.option_strings else action.dest error_msg = "{prog}: '{value}' is not a valid value for '{param}'. See '{prog} --help'.".format( prog=self.prog, value=value, param=parameter) telemetry.set_user_fault(error_msg) with CommandLoggerContext(logger): logger.error(error_msg) candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) if candidates: print_args = { 's': 's' if len(candidates) > 1 else '', 'verb': 'are' if len(candidates) > 1 else 'is', 'value': value } suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format( **print_args) suggestion_msg += '\n'.join( ['\t' + candidate for candidate in candidates]) print(suggestion_msg, file=sys.stderr) self.exit(2)
def error(self, message): telemetry.set_user_fault('parse error: {}'.format(message)) self._handle_command_package_error(message) args = {'prog': self.prog, 'message': message} logger.error('%(prog)s: error: %(message)s', args) self.print_usage(sys.stderr) self.exit(2)
def error(self, message): telemetry.set_user_fault('parse error: {}'.format(message)) args = {'prog': self.prog, 'message': message} with CommandLoggerContext(logger): logger.error('%(prog)s: error: %(message)s', args) self.print_usage(sys.stderr) self.exit(2)
def kubernetes_exception_handler( ex, fault_type, summary, error_message='Error occured while connecting to the kubernetes cluster: ', message_for_unauthorized_request='The user does not have required privileges on the kubernetes cluster to deploy Azure Arc enabled Kubernetes agents. Please ensure you have cluster admin privileges on the cluster to onboard.', message_for_not_found='The requested kubernetes resource was not found.', raise_error=True): telemetry.set_user_fault() if isinstance(ex, ApiException): status_code = ex.status if status_code == 403: logger.warning(message_for_unauthorized_request) elif status_code == 404: logger.warning(message_for_not_found) else: logger.debug("Kubernetes Exception: " + str(ex)) if raise_error: telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ValidationError(error_message + "\nError Response: " + str(ex.body)) else: if raise_error: telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ValidationError(error_message + "\nError: " + str(ex)) else: logger.debug("Kubernetes Exception: " + str(ex))
def delete_arc_agents(release_namespace, kube_config, kube_context, configuration): cmd_helm_delete = [ "helm", "delete", "azure-arc", "--namespace", release_namespace ] if kube_config: cmd_helm_delete.extend(["--kubeconfig", kube_config]) if kube_context: cmd_helm_delete.extend(["--kube-context", kube_context]) response_helm_delete = Popen(cmd_helm_delete, stdout=PIPE, stderr=PIPE) _, error_helm_delete = response_helm_delete.communicate() if response_helm_delete.returncode != 0: if 'forbidden' in error_helm_delete.decode( "ascii" ) or 'Error: warning: Hook pre-delete' in error_helm_delete.decode( "ascii" ) or 'Error: timed out waiting for the condition' in error_helm_delete.decode( "ascii"): telemetry.set_user_fault() telemetry.set_exception( exception=error_helm_delete.decode("ascii"), fault_type=consts.Delete_HelmRelease_Fault_Type, summary='Unable to delete helm release') raise CLIInternalError( "Error occured while cleaning up arc agents. " + "Helm release deletion failed: " + error_helm_delete.decode("ascii") + " Please run 'helm delete azure-arc' to ensure that the release is deleted." ) ensure_namespace_cleanup(configuration)
def validate_location(cmd, location): subscription_id = get_subscription_id(cmd.cli_ctx) rp_locations = [] resourceClient = _resource_client_factory(cmd.cli_ctx, subscription_id=subscription_id) providerDetails = resourceClient.providers.get('Microsoft.Kubernetes') for resourceTypes in providerDetails.resource_types: if resourceTypes.resource_type == 'connectedClusters': rp_locations = [ location.replace(" ", "").lower() for location in resourceTypes.locations ] if location.lower() not in rp_locations: telemetry.set_user_fault() telemetry.set_exception( exception='Location not supported', fault_type=consts.Invalid_Location_Fault_Type, summary= 'Provided location is not supported for creating connected clusters' ) raise CLIError( "Connected cluster resource creation is supported only in the following locations: " + ', '.join(map(str, rp_locations)) + ". Use the --location flag to specify one of these locations." ) break
def _check_value(self, action, value): # Override to customize the error message when a argument is not among the available choices # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: if not self.command_source: # parser has no `command_source`, value is part of command itself extensions_link = 'https://docs.microsoft.com/en-us/cli/azure/azure-cli-extensions-overview' error_msg = ("{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'. " "If the command is from an extension, " "please make sure the corresponding extension is installed. " "To learn more about extensions, please visit " "{extensions_link}").format(prog=self.prog, value=value, extensions_link=extensions_link) else: # `command_source` indicates command values have been parsed, value is an argument parameter = action.option_strings[0] if action.option_strings else action.dest error_msg = "{prog}: '{value}' is not a valid value for '{param}'. See '{prog} --help'.".format( prog=self.prog, value=value, param=parameter) telemetry.set_user_fault(error_msg) with CommandLoggerContext(logger): logger.error(error_msg) candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) if candidates: print_args = { 's': 's' if len(candidates) > 1 else '', 'verb': 'are' if len(candidates) > 1 else 'is', 'value': value } suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format(**print_args) suggestion_msg += '\n'.join(['\t' + candidate for candidate in candidates]) print(suggestion_msg, file=sys.stderr) self.exit(2)
def get_release_namespace(kube_config, kube_context): cmd_helm_release = [ "helm", "list", "-a", "--all-namespaces", "--output", "json" ] if kube_config: cmd_helm_release.extend(["--kubeconfig", kube_config]) if kube_context: cmd_helm_release.extend(["--kube-context", kube_context]) response_helm_release = Popen(cmd_helm_release, stdout=PIPE, stderr=PIPE) output_helm_release, error_helm_release = response_helm_release.communicate( ) if response_helm_release.returncode != 0: if 'forbidden' in error_helm_release.decode("ascii"): telemetry.set_user_fault() telemetry.set_exception(exception=error_helm_release.decode("ascii"), fault_type=consts.List_HelmRelease_Fault_Type, summary='Unable to list helm release') raise CLIError("Helm list release failed: " + error_helm_release.decode("ascii")) output_helm_release = output_helm_release.decode("ascii") try: output_helm_release = json.loads(output_helm_release) except json.decoder.JSONDecodeError: return None for release in output_helm_release: if release['name'] == 'azure-arc': return release['namespace'] return None
def _check_value(self, action, value): # Override to customize the error message when a argument is not among the available choices # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: if not self.command_source: # parser has no `command_source`, value is part of command itself error_msg = "{prog}: '{value}' is not in the '{prog}' command group. See '{prog} --help'.".format( prog=self.prog, value=value) else: # `command_source` indicates command values have been parsed, value is an argument parameter = action.option_strings[0] if action.option_strings else action.dest error_msg = "{prog}: '{value}' is not a valid value for '{param}'. See '{prog} --help'.".format( prog=self.prog, value=value, param=parameter) telemetry.set_user_fault(error_msg) logger.error(error_msg) candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) if candidates: print_args = { 's': 's' if len(candidates) > 1 else '', 'verb': 'are' if len(candidates) > 1 else 'is', 'value': value } suggestion_msg = "\nThe most similar choice{s} to '{value}' {verb}:\n".format(**print_args) suggestion_msg += '\n'.join(['\t' + candidate for candidate in candidates]) print(suggestion_msg, file=sys.stderr) self.exit(2)
def arm_exception_handler(ex, fault_type, summary, return_if_not_found=False): if isinstance(ex, AuthenticationError): telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ClientRequestError("Authentication error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) if isinstance(ex, TokenExpiredError): telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ClientRequestError("Token expiration error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) if isinstance(ex, HttpOperationError): status_code = ex.response.status_code if status_code == 404 and return_if_not_found: return if status_code // 100 == 4: telemetry.set_user_fault() telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ClientRequestError("Http operation error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) if isinstance(ex, ValidationError): telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ClientRequestError("Validation error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) if isinstance(ex, CloudError): status_code = ex.status_code if status_code == 404 and return_if_not_found: return if status_code // 100 == 4: telemetry.set_user_fault() telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ClientRequestError("Cloud error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary)) telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary) raise ClientRequestError("Error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary))
def check_helm_install(kube_config, kube_context): cmd_helm_installed = ["helm", "--kubeconfig", kube_config, "--debug"] if kube_context: cmd_helm_installed.extend(["--kube-context", kube_context]) try: response_helm_installed = Popen(cmd_helm_installed, stdout=PIPE, stderr=PIPE) _, error_helm_installed = response_helm_installed.communicate() if response_helm_installed.returncode != 0: if "unknown flag" in error_helm_installed.decode("ascii"): telemetry.set_user_fault() telemetry.set_exception(exception='Helm 3 not found', fault_type=Helm_Version_Fault_Type, summary='Helm3 not found on the machine') raise CLIError("Please install the latest version of Helm. " + "Learn more at https://aka.ms/arc/k8s/onboarding-helm-install") telemetry.set_user_fault() telemetry.set_exception(exception=error_helm_installed.decode("ascii"), fault_type=Helm_Installation_Fault_Type, summary='Helm3 not installed on the machine') raise CLIError(error_helm_installed.decode("ascii")) except FileNotFoundError as e: telemetry.set_exception(exception=e, fault_type=Check_HelmInstallation_Fault_Type, summary='Unable to verify helm installation') raise CLIError("Helm is not installed or requires elevated permissions. " + "Ensure that you have the latest version of Helm installed on your machine. " + "Learn more at https://aka.ms/arc/k8s/onboarding-helm-install") except subprocess.CalledProcessError as e2: e2.output = e2.output.decode("ascii") print(e2.output)
def helm_install_release(chart_path, subscription_id, kubernetes_distro, resource_group_name, cluster_name, location, onboarding_tenant_id, http_proxy, https_proxy, no_proxy, proxy_cert, private_key_pem, kube_config, kube_context, no_wait, values_file_provided, values_file, cloud_name): cmd_helm_install = [ "helm", "upgrade", "--install", "azure-arc", chart_path, "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.kubernetesDistro={}".format(kubernetes_distro), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.onboardingPrivateKey={}".format(private_key_pem), "--set", "systemDefaultValues.spnOnboarding=false", "--set", "global.azureEnvironment={}".format(cloud_name), "--output", "json" ] # To set some other helm parameters through file if values_file_provided: cmd_helm_install.extend(["-f", values_file]) if https_proxy: cmd_helm_install.extend( ["--set", "global.httpsProxy={}".format(https_proxy)]) if http_proxy: cmd_helm_install.extend( ["--set", "global.httpProxy={}".format(http_proxy)]) if no_proxy: cmd_helm_install.extend( ["--set", "global.noProxy={}".format(no_proxy)]) if proxy_cert: cmd_helm_install.extend( ["--set-file", "global.proxyCert={}".format(proxy_cert)]) if https_proxy or http_proxy or no_proxy: cmd_helm_install.extend( ["--set", "global.isProxyEnabled={}".format(True)]) if kube_config: cmd_helm_install.extend(["--kubeconfig", kube_config]) if kube_context: cmd_helm_install.extend(["--kube-context", kube_context]) if not no_wait: cmd_helm_install.extend(["--wait"]) response_helm_install = Popen(cmd_helm_install, stdout=PIPE, stderr=PIPE) _, error_helm_install = response_helm_install.communicate() if response_helm_install.returncode != 0: if ('forbidden' in error_helm_install.decode("ascii") or 'timed out waiting for the condition' in error_helm_install.decode("ascii")): telemetry.set_user_fault() telemetry.set_exception( exception=error_helm_install.decode("ascii"), fault_type=consts.Install_HelmRelease_Fault_Type, summary='Unable to install helm release') logger.warning( "Please check if the azure-arc namespace was deployed and run 'kubectl get pods -n azure-arc' to check if all the pods are in running state. A possible cause for pods stuck in pending state could be insufficient resources on the kubernetes cluster to onboard to arc." ) raise CLIError("Unable to install helm release: " + error_helm_install.decode("ascii"))
def get_kubeconfig_node_dict(kube_config=None): if kube_config is None: kube_config = os.getenv('KUBECONFIG') if os.getenv('KUBECONFIG') else os.path.join(os.path.expanduser('~'), '.kube', 'config') try: kubeconfig_data = config.kube_config._get_kube_config_loader_for_yaml_file(kube_config)._config except Exception as ex: telemetry.set_user_fault() telemetry.set_exception(exception=ex, fault_type=consts.Load_Kubeconfig_Fault_Type, summary='Error while fetching details from kubeconfig') raise CLIError("Error while fetching details from kubeconfig." + str(ex)) return kubeconfig_data
def error(self, message): telemetry.set_user_fault('parse error: {}'.format(message)) args = {'prog': self.prog, 'message': message} with CommandLoggerContext(logger): logger.error('%(prog)s: error: %(message)s', args) self.print_usage(sys.stderr) failure_recovery_recommendations = self._get_failure_recovery_recommendations( ) self._suggestion_msg.extend(failure_recovery_recommendations) self._print_suggestion_msg(sys.stderr) self.exit(2)
def check_kube_connection(configuration): api_instance = kube_client.NetworkingV1Api(kube_client.ApiClient(configuration)) try: api_instance.get_api_resources() except Exception as e: telemetry.set_user_fault() telemetry.set_exception(exception=e, fault_type=Kubernetes_Connectivity_FaultType, summary='Unable to verify connectivity to the Kubernetes cluster') logger.warning("Unable to verify connectivity to the Kubernetes cluster: %s\n", e) raise CLIError("If you are using AAD Enabled cluster, " + "verify that you are able to access the cluster. Learn more at " + "https://aka.ms/arc/k8s/onboarding-aad-enabled-clusters")
def check_helm_install(kube_config, kube_context): cmd_helm_installed = ["helm", "--debug"] if kube_config: cmd_helm_installed.extend(["--kubeconfig", kube_config]) if kube_context: cmd_helm_installed.extend(["--kube-context", kube_context]) try: response_helm_installed = Popen(cmd_helm_installed, stdout=PIPE, stderr=PIPE) _, error_helm_installed = response_helm_installed.communicate() if response_helm_installed.returncode != 0: if "unknown flag" in error_helm_installed.decode("ascii"): telemetry.set_user_fault() telemetry.set_exception(exception='Helm 3 not found', fault_type=consts.Helm_Version_Fault_Type, summary='Helm3 not found on the machine') raise CLIError("Please install the latest version of Helm. " + "Learn more at https://aka.ms/arc/k8s/onboarding-helm-install") telemetry.set_user_fault() telemetry.set_exception(exception=error_helm_installed.decode("ascii"), fault_type=consts.Helm_Installation_Fault_Type, summary='Helm3 not installed on the machine') raise CLIError(error_helm_installed.decode("ascii")) except FileNotFoundError as e: telemetry.set_user_fault() telemetry.set_exception(exception=e, fault_type=consts.Check_HelmInstallation_Fault_Type, summary='Unable to verify helm installation') raise CLIError("Helm is not installed or the helm binary is not accessible to the connectedk8s cli. Could be a permission issue." + "Ensure that you have the latest version of Helm installed on your machine and run using admin privilege. " + "Learn more at https://aka.ms/arc/k8s/onboarding-helm-install") except Exception as e2: telemetry.set_user_fault() telemetry.set_exception(exception=e2, fault_type=consts.Check_HelmInstallation_Fault_Type, summary='Error while verifying helm installation') raise CLIError("Error occured while verifying helm installation: " + str(e2))
def ensure_namespace_cleanup(configuration): api_instance = kube_client.CoreV1Api(kube_client.ApiClient(configuration)) timeout = time.time() + 180 while True: if time.time() > timeout: telemetry.set_user_fault() logger.warning("Namespace 'azure-arc' still in terminating state. Please ensure that you delete the 'azure-arc' namespace before onboarding the cluster again.") return try: api_response = api_instance.list_namespace(field_selector='metadata.name=azure-arc') if not api_response.items: return time.sleep(5) except Exception as e: # pylint: disable=broad-except logger.warning("Error while retrieving namespace information.") kubernetes_exception_handler(e, consts.Get_Kubernetes_Namespace_Fault_Type, 'Unable to fetch kubernetes namespace', raise_error=False)
def send_telemetry(self): import azure.cli.core.telemetry as telemetry telemetry.set_error_type(self.error_type.value) # For userfaults if self.error_type in [ AzCLIErrorType.CommandNotFoundError, AzCLIErrorType.ArgumentParseError, AzCLIErrorType.ValidationError, AzCLIErrorType.ManualInterrupt ]: telemetry.set_user_fault(self.error_msg) # For failures: service side error, client side error, unexpected error else: telemetry.set_failure(self.error_msg) # For unexpected error if self.raw_exception: telemetry.set_exception(self.raw_exception, '')
def _check_value(self, action, value): # Override to customize the error message when a argument is not among the available choices # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: error_msg = "{prog}: '{value}' is not an {prog} command. See '{prog} --help'.".format(prog=self.prog, value=value) telemetry.set_user_fault(error_msg) logger.error(error_msg) candidates = difflib.get_close_matches(value, action.choices, cutoff=0.8) if candidates: print_args = { 's': 's' if len(candidates) > 1 else '', 'verb': 'are' if len(candidates) > 1 else 'is', 'value': value } suggestion_msg = "\nThe most similar command{s} to '{value}' {verb}:\n".format(**print_args) suggestion_msg += '\n'.join(['\t' + candidate for candidate in candidates]) print(suggestion_msg, file=sys.stderr) self.exit(2)
def validate_env_file_dogfood(values_file, values_file_provided): if not values_file_provided: telemetry.set_user_fault() telemetry.set_exception(exception='Helm environment file not provided', fault_type=consts.Helm_Environment_File_Fault_Type, summary='Helm environment file missing') raise CLIError("Helm environment file is required when using Dogfood environment for onboarding the cluster. Please set the environment variable 'HELMVALUESPATH' to point to the file.") with open(values_file, 'r') as f: try: env_dict = yaml.safe_load(f) except Exception as e: telemetry.set_user_fault() telemetry.set_exception(exception=e, fault_type=consts.Helm_Environment_File_Fault_Type, summary='Problem loading the helm environment file') raise CLIError("Problem loading the helm environment file: " + str(e)) try: assert env_dict.get('global').get('azureEnvironment') == 'AZUREDOGFOOD' assert env_dict.get('systemDefaultValues').get('azureArcAgents').get('config_dp_endpoint_override') except Exception as e: telemetry.set_user_fault() telemetry.set_exception(exception=e, fault_type=consts.Helm_Environment_File_Fault_Type, summary='Problem loading the helm environment variables') raise CLIError("The required helm environment variables for dogfood onboarding are either not present in the file or incorrectly set. Please check the values 'global.azureEnvironment' and 'systemDefaultValues.azureArcAgents.config_dp_endpoint_override' in the file.") # Return the dp endpoint and release train dp_endpoint = env_dict.get('systemDefaultValues').get('azureArcAgents').get('config_dp_endpoint_override') release_train = env_dict.get('systemDefaultValues').get('azureArcAgents').get('releaseTrain') return dp_endpoint, release_train
def _check_value(self, action, value): # Override to customize the error message when a argument is not among the available choices # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: error_msg = "{prog}: '{value}' is not an {prog} command. See '{prog} --help'.".format( prog=self.prog, value=value) telemetry.set_user_fault(error_msg) logger.error(error_msg) candidates = difflib.get_close_matches(value, action.choices, cutoff=0.8) if candidates: print_args = { 's': 's' if len(candidates) > 1 else '', 'verb': 'are' if len(candidates) > 1 else 'is', 'value': value } suggestion_msg = "\nThe most similar command{s} to '{value}' {verb}:\n".format( **print_args) suggestion_msg += '\n'.join( ['\t' + candidate for candidate in candidates]) print(suggestion_msg, file=sys.stderr) self.exit(2)
def validate_jar(namespace): if namespace.disable_validation: telemetry.set_user_fault("jar validation is disabled") return file_type = _get_file_type(namespace.runtime_version, namespace.artifact_path) if file_type != "Jar": return values = _parse_jar_file(namespace.artifact_path) if values is None: # ignore jar_file check return file_size, spring_boot_version, spring_cloud_version, has_actuator, has_manifest, has_jar, has_class, ms_sdk_version = values tips = ", if you choose to ignore these errors, turn validation off with --disable-validation" if not has_jar and not has_class: telemetry.set_user_fault("invalid_jar_no_class_jar") raise InvalidArgumentValueError( "Do not find any class or jar file, please check if your artifact is a valid fat jar" + tips) if not has_manifest: telemetry.set_user_fault("invalid_jar_no_manifest") raise InvalidArgumentValueError( "Do not find MANIFEST.MF, please check if your artifact is a valid fat jar" + tips) if file_size / 1024 / 1024 < 10: telemetry.set_user_fault("invalid_jar_thin_jar") raise InvalidArgumentValueError( "Thin jar detected, please check if your artifact is a valid fat jar" + tips) # validate spring boot version if spring_boot_version and spring_boot_version.startswith('1'): telemetry.set_user_fault("old_spring_boot_version") raise InvalidArgumentValueError( "The spring boot {} you are using is not supported. To get the latest supported " "versions please refer to: https://aka.ms/ascspringversion".format( spring_boot_version) + tips) # old spring cloud version, need to import ms sdk <= 2.2.1 if spring_cloud_version: if spring_cloud_version < "2.2.5": if not ms_sdk_version or ms_sdk_version > "2.2.1": telemetry.set_user_fault("old_spring_cloud_version") raise InvalidArgumentValueError( "The spring cloud {} you are using is not supported. To get the latest supported " "versions please refer to: https://aka.ms/ascspringversion" .format(spring_cloud_version) + tips) else: if ms_sdk_version and ms_sdk_version <= "2.2.1": telemetry.set_user_fault("old_ms_sdk_version") raise InvalidArgumentValueError( "The spring-cloud-starter-azure-spring-cloud-client version {} is no longer " "supported, please remove it or upgrade to a higher version, to get the latest " "supported versions please refer to: " "https://mvnrepository.com/artifact/com.microsoft.azure/spring-cloud-starter-azure" "-spring-cloud-client".format(ms_sdk_version) + tips) if not has_actuator: telemetry.set_user_fault("no_spring_actuator") logger.warning( "Seems you do not import spring actuator, thus metrics are not enabled, please refer to " "https://aka.ms/ascdependencies for more details")
def create_connectedk8s(cmd, client, resource_group_name, cluster_name, https_proxy="", http_proxy="", no_proxy="", location=None, kube_config=None, kube_context=None, no_wait=False, tags=None): logger.warning( "Ensure that you have the latest helm version installed before proceeding." ) logger.warning("This operation might take a while...\n") # Setting subscription id subscription_id = get_subscription_id(cmd.cli_ctx) # Send cloud information to telemetry send_cloud_telemetry(cmd) # Fetching Tenant Id graph_client = _graph_client_factory(cmd.cli_ctx) onboarding_tenant_id = graph_client.config.tenant_id # Setting kubeconfig kube_config = set_kube_config(kube_config) # Escaping comma, forward slash present in https proxy urls, needed for helm params. https_proxy = escape_proxy_settings(https_proxy) # Escaping comma, forward slash present in http proxy urls, needed for helm params. http_proxy = escape_proxy_settings(http_proxy) # Escaping comma, forward slash present in no proxy urls, needed for helm params. no_proxy = escape_proxy_settings(no_proxy) # Checking whether optional extra values file has been provided. values_file_provided = False values_file = os.getenv('HELMVALUESPATH') if (values_file is not None) and (os.path.isfile(values_file)): values_file_provided = True logger.warning( "Values files detected. Reading additional helm parameters from same." ) # trimming required for windows os if (values_file.startswith("'") or values_file.startswith('"')): values_file = values_file[1:] if (values_file.endswith("'") or values_file.endswith('"')): values_file = values_file[:-1] # Validate the helm environment file for Dogfood. dp_endpoint_dogfood = None release_train_dogfood = None if cmd.cli_ctx.cloud.endpoints.resource_manager == consts.Dogfood_RMEndpoint: dp_endpoint_dogfood, release_train_dogfood = validate_env_file_dogfood( values_file, values_file_provided) # Loading the kubeconfig file in kubernetes client configuration try: config.load_kube_config(config_file=kube_config, context=kube_context) except Exception as e: telemetry.set_user_fault() telemetry.set_exception(exception=e, fault_type=consts.Load_Kubeconfig_Fault_Type, summary='Problem loading the kubeconfig file') raise CLIError("Problem loading the kubeconfig file." + str(e)) 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) # Get kubernetes cluster info for telemetry kubernetes_version = get_server_version(configuration) kubernetes_distro = get_kubernetes_distro(configuration) kubernetes_properties = { 'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version, 'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro } telemetry.add_extension_event('connectedk8s', kubernetes_properties) # Checking helm installation check_helm_install(kube_config, kube_context) # Check helm version helm_version = check_helm_version(kube_config, kube_context) telemetry.add_extension_event( 'connectedk8s', {'Context.Default.AzureCLI.HelmVersion': helm_version}) # Validate location utils.validate_location(cmd, location) resourceClient = _resource_client_factory(cmd.cli_ctx, subscription_id=subscription_id) # Check Release Existance release_namespace = get_release_namespace(kube_config, kube_context) if release_namespace: # Loading config map api_instance = kube_client.CoreV1Api( kube_client.ApiClient(configuration)) try: configmap = api_instance.read_namespaced_config_map( 'azure-clusterconfig', 'azure-arc') except Exception as e: # pylint: disable=broad-except utils.kubernetes_exception_handler( e, consts.Read_ConfigMap_Fault_Type, 'Unable to read ConfigMap', error_message= "Unable to read ConfigMap 'azure-clusterconfig' in 'azure-arc' namespace: ", message_for_not_found= "The helm release 'azure-arc' is present but the azure-arc namespace/configmap is missing. Please run 'helm delete azure-arc --no-hooks' to cleanup the release before onboarding the cluster again." ) configmap_rg_name = configmap.data["AZURE_RESOURCE_GROUP"] configmap_cluster_name = configmap.data["AZURE_RESOURCE_NAME"] if connected_cluster_exists(client, configmap_rg_name, configmap_cluster_name): if (configmap_rg_name.lower() == resource_group_name.lower() and configmap_cluster_name.lower() == cluster_name.lower()): # Re-put connected cluster try: public_key = client.get( configmap_rg_name, configmap_cluster_name).agent_public_key_certificate except Exception as e: # pylint: disable=broad-except utils.arm_exception_handler( e, consts.Get_ConnectedCluster_Fault_Type, 'Failed to check if connected cluster resource already exists.' ) cc = generate_request_payload(configuration, location, public_key, tags) create_cc_resource(client, resource_group_name, cluster_name, cc, no_wait) else: telemetry.set_user_fault() telemetry.set_exception( exception='The kubernetes cluster is already onboarded', fault_type=consts.Cluster_Already_Onboarded_Fault_Type, summary='Kubernetes cluster already onboarded') raise CLIError( "The kubernetes cluster you are trying to onboard " + "is already onboarded to the resource group" + " '{}' with resource name '{}'.".format( configmap_rg_name, configmap_cluster_name)) else: # Cleanup agents and continue with put delete_arc_agents(release_namespace, kube_config, kube_context, configuration) else: if connected_cluster_exists(client, resource_group_name, cluster_name): telemetry.set_user_fault() telemetry.set_exception( exception='The connected cluster resource already exists', fault_type=consts.Resource_Already_Exists_Fault_Type, summary='Connected cluster resource already exists') raise CLIError( "The connected cluster resource {} already exists ".format( cluster_name) + "in the resource group {} ".format(resource_group_name) + "and corresponds to a different Kubernetes cluster. To onboard this Kubernetes cluster" + "to Azure, specify different resource name or resource group name." ) # Resource group Creation if resource_group_exists(cmd.cli_ctx, resource_group_name, subscription_id) is False: resource_group_params = {'location': location} try: resourceClient.resource_groups.create_or_update( resource_group_name, resource_group_params) except Exception as e: # pylint: disable=broad-except utils.arm_exception_handler(e, consts.Create_ResourceGroup_Fault_Type, 'Failed to create the resource group') # Adding helm repo if os.getenv('HELMREPONAME') and os.getenv('HELMREPOURL'): utils.add_helm_repo(kube_config, kube_context) # Retrieving Helm chart OCI Artifact location registry_path = os.getenv('HELMREGISTRY') if os.getenv( 'HELMREGISTRY') else utils.get_helm_registry( cmd, location, dp_endpoint_dogfood, release_train_dogfood) # Get azure-arc agent version for telemetry azure_arc_agent_version = registry_path.split(':')[1] telemetry.add_extension_event( 'connectedk8s', {'Context.Default.AzureCLI.AgentVersion': azure_arc_agent_version}) # Get helm chart path chart_path = utils.get_chart_path(registry_path, kube_config, kube_context) # Generate public-private key pair try: key_pair = RSA.generate(4096) except Exception as e: telemetry.set_exception( exception=e, fault_type=consts.KeyPair_Generate_Fault_Type, summary='Failed to generate public-private key pair') raise CLIError("Failed to generate public-private key pair. " + str(e)) try: public_key = get_public_key(key_pair) except Exception as e: telemetry.set_exception(exception=e, fault_type=consts.PublicKey_Export_Fault_Type, summary='Failed to export public key') raise CLIError("Failed to export public key." + str(e)) try: private_key_pem = get_private_key(key_pair) except Exception as e: telemetry.set_exception(exception=e, fault_type=consts.PrivateKey_Export_Fault_Type, summary='Failed to export private key') raise CLIError("Failed to export private key." + str(e)) # Generate request payload cc = generate_request_payload(configuration, location, public_key, tags) # Create connected cluster resource put_cc_response = create_cc_resource(client, resource_group_name, cluster_name, cc, no_wait) # Install azure-arc agents helm_install_release(chart_path, subscription_id, kubernetes_distro, resource_group_name, cluster_name, location, onboarding_tenant_id, http_proxy, https_proxy, no_proxy, private_key_pem, kube_config, kube_context, no_wait, values_file_provided, values_file) return put_cc_response
# Log the init finish time init_finish_time = timeit.default_timer() try: telemetry.start() exit_code = cli_main(az_cli, sys.argv[1:]) if exit_code == 0: telemetry.set_success() sys.exit(exit_code) except KeyboardInterrupt: telemetry.set_user_fault('Keyboard interrupt is captured.') sys.exit(1) except SystemExit as ex: # some code directly call sys.exit, this is to make sure command metadata is logged exit_code = ex.code if ex.code is not None else 1 raise ex finally: try: # Log the invoke finish time invoke_finish_time = timeit.default_timer() logger.info("Command ran in %.3f seconds (init: %.3f, invoke: %.3f)", invoke_finish_time - start_time, init_finish_time - start_time, invoke_finish_time - init_finish_time) except NameError: pass
def validation_error(self, message): telemetry.set_user_fault('validation error') return super(AzCliCommandParser, self).error(message)
def _check_value(self, action, value): # pylint: disable=too-many-statements, too-many-locals # Override to customize the error message when a argument is not among the available choices # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: # pylint: disable=too-many-nested-blocks # self.cli_ctx is None when self.prog is beyond 'az', such as 'az iot'. # use cli_ctx from cli_help which is not lost. cli_ctx = self.cli_ctx or (self.cli_help.cli_ctx if self.cli_help else None) caused_by_extension_not_installed = False command_name_inferred = self.prog error_msg = None if not self.command_source: candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) if candidates: # use the most likely candidate to replace the misspelled command args = self.prog.split() + self._raw_arguments args_inferred = [item if item != value else candidates[0] for item in args] command_name_inferred = ' '.join(args_inferred).split('-')[0] use_dynamic_install = self._get_extension_use_dynamic_install_config() if use_dynamic_install != 'no' and not candidates: # Check if the command is from an extension from azure.cli.core.util import roughly_parse_command cmd_list = self.prog.split() + self._raw_arguments command_str = roughly_parse_command(cmd_list[1:]) ext_name = self._search_in_extension_commands(command_str) if ext_name: caused_by_extension_not_installed = True telemetry.set_command_details(command_str, parameters=AzCliCommandInvoker._extract_parameter_names(cmd_list), # pylint: disable=protected-access extension_name=ext_name) run_after_extension_installed = self._get_extension_run_after_dynamic_install_config() if use_dynamic_install == 'yes_without_prompt': logger.warning('The command requires the extension %s. ' 'It will be installed first.', ext_name) go_on = True else: from knack.prompting import prompt_y_n, NoTTYException prompt_msg = 'The command requires the extension {}. ' \ 'Do you want to install it now?'.format(ext_name) if run_after_extension_installed: prompt_msg = '{} The command will continue to run after the extension is installed.' \ .format(prompt_msg) NO_PROMPT_CONFIG_MSG = "Run 'az config set extension.use_dynamic_install=" \ "yes_without_prompt' to allow installing extensions without prompt." try: go_on = prompt_y_n(prompt_msg, default='y') if go_on: logger.warning(NO_PROMPT_CONFIG_MSG) except NoTTYException: logger.warning("The command requires the extension %s.\n " "Unable to prompt for extension install confirmation as no tty " "available. %s", ext_name, NO_PROMPT_CONFIG_MSG) go_on = False if go_on: from azure.cli.core.extension.operations import add_extension add_extension(cli_ctx=cli_ctx, extension_name=ext_name, upgrade=True) if run_after_extension_installed: import subprocess import platform exit_code = subprocess.call(cmd_list, shell=platform.system() == 'Windows') error_msg = ("Extension {} dynamically installed and commands will be " "rerun automatically.").format(ext_name) telemetry.set_user_fault(error_msg) self.exit(exit_code) else: with CommandLoggerContext(logger): error_msg = 'Extension {} installed. Please rerun your command.'.format(ext_name) logger.error(error_msg) telemetry.set_user_fault(error_msg) self.exit(2) else: error_msg = "The command requires the latest version of extension {ext_name}. " \ "To install, run 'az extension add --upgrade -n {ext_name}'.".format(ext_name=ext_name) if not error_msg: # parser has no `command_source`, value is part of command itself error_msg = "'{value}' is misspelled or not recognized by the system.".format(value=value) az_error = CommandNotFoundError(error_msg) else: # `command_source` indicates command values have been parsed, value is an argument parameter = action.option_strings[0] if action.option_strings else action.dest error_msg = "{prog}: '{value}' is not a valid value for '{param}'.".format( prog=self.prog, value=value, param=parameter) candidates = difflib.get_close_matches(value, action.choices, cutoff=0.7) az_error = InvalidArgumentValueError(error_msg) command_arguments = self._get_failure_recovery_arguments(action) if candidates: az_error.set_recommendation("Did you mean '{}' ?".format(candidates[0])) # recommend a command for user recommender = CommandRecommender(*command_arguments, error_msg, cli_ctx) recommender.set_help_examples(self.get_examples(command_name_inferred)) recommended_command = recommender.recommend_a_command() if recommended_command: az_error.set_recommendation("Try this: '{}'".format(recommended_command)) # remind user to check extensions if we can not find a command to recommend if isinstance(az_error, CommandNotFoundError) \ and not az_error.recommendations and self.prog == 'az' \ and use_dynamic_install == 'no': az_error.set_recommendation(EXTENSION_REFERENCE) az_error.set_recommendation(OVERVIEW_REFERENCE.format(command=self.prog)) if not caused_by_extension_not_installed: az_error.print_error() az_error.send_telemetry() self.exit(2)
def update_agents(cmd, client, resource_group_name, cluster_name, https_proxy="", http_proxy="", no_proxy="", kube_config=None, kube_context=None, no_wait=False): logger.warning( "Ensure that you have the latest helm version installed before proceeding." ) logger.warning("This operation might take a while...\n") # Send cloud information to telemetry send_cloud_telemetry(cmd) # Setting kubeconfig kube_config = set_kube_config(kube_config) # Escaping comma, forward slash present in https proxy urls, needed for helm params. https_proxy = escape_proxy_settings(https_proxy) # Escaping comma, forward slash present in http proxy urls, needed for helm params. http_proxy = escape_proxy_settings(http_proxy) # Escaping comma, forward slash present in no proxy urls, needed for helm params. no_proxy = escape_proxy_settings(no_proxy) # Checking whether optional extra values file has been provided. values_file_provided = False values_file = os.getenv('HELMVALUESPATH') if (values_file is not None) and (os.path.isfile(values_file)): values_file_provided = True logger.warning( "Values files detected. Reading additional helm parameters from same." ) # trimming required for windows os if (values_file.startswith("'") or values_file.startswith('"')): values_file = values_file[1:] if (values_file.endswith("'") or values_file.endswith('"')): values_file = values_file[:-1] # Validate the helm environment file for Dogfood. dp_endpoint_dogfood = None release_train_dogfood = None if cmd.cli_ctx.cloud.endpoints.resource_manager == consts.Dogfood_RMEndpoint: dp_endpoint_dogfood, release_train_dogfood = validate_env_file_dogfood( values_file, values_file_provided) # Loading the kubeconfig file in kubernetes client configuration try: config.load_kube_config(config_file=kube_config, context=kube_context) except Exception as e: telemetry.set_user_fault() telemetry.set_exception(exception=e, fault_type=consts.Load_Kubeconfig_Fault_Type, summary='Problem loading the kubeconfig file') raise CLIError("Problem loading the kubeconfig file." + str(e)) 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) # Get kubernetes cluster info for telemetry kubernetes_version = get_server_version(configuration) kubernetes_distro = get_kubernetes_distro(configuration) kubernetes_properties = { 'Context.Default.AzureCLI.KubernetesVersion': kubernetes_version, 'Context.Default.AzureCLI.KubernetesDistro': kubernetes_distro } telemetry.add_extension_event('connectedk8s', kubernetes_properties) # Checking helm installation check_helm_install(kube_config, kube_context) # Check helm version helm_version = check_helm_version(kube_config, kube_context) telemetry.add_extension_event( 'connectedk8s', {'Context.Default.AzureCLI.HelmVersion': helm_version}) # Check whether Connected Cluster is present if not connected_cluster_exists(client, resource_group_name, cluster_name): telemetry.set_user_fault() telemetry.set_exception( exception='The connected cluster resource does not exist', fault_type=consts.Resource_Does_Not_Exist_Fault_Type, summary='Connected cluster resource does not exist') raise CLIError( "The connected cluster resource {} does not exist ".format( cluster_name) + "in the resource group {} ".format(resource_group_name) + "Please onboard the connected cluster using: az connectedk8s connect -n <connected-cluster-name> -g <resource-group-name>" ) # Fetch Connected Cluster for agent version connected_cluster = get_connectedk8s(cmd, client, resource_group_name, cluster_name) # Adding helm repo if os.getenv('HELMREPONAME') and os.getenv('HELMREPOURL'): utils.add_helm_repo(kube_config, kube_context) # Retrieving Helm chart OCI Artifact location registry_path = os.getenv('HELMREGISTRY') if os.getenv( 'HELMREGISTRY') else utils.get_helm_registry( cmd, connected_cluster.location, dp_endpoint_dogfood, release_train_dogfood) reg_path_array = registry_path.split(':') agent_version = reg_path_array[1] # Set agent version in registry path if connected_cluster.agent_version is not None: agent_version = connected_cluster.agent_version registry_path = reg_path_array[ 0] + ":" + connected_cluster.agent_version telemetry.add_extension_event( 'connectedk8s', {'Context.Default.AzureCLI.AgentVersion': agent_version}) # Get Helm chart path chart_path = utils.get_chart_path(registry_path, kube_config, kube_context) cmd_helm_upgrade = [ "helm", "upgrade", "azure-arc", chart_path, "--reuse-values", "--set", "global.httpsProxy={}".format(https_proxy), "--set", "global.httpProxy={}".format(http_proxy), "--set", "global.noProxy={}".format(no_proxy), "--wait", "--output", "json" ] if values_file_provided: cmd_helm_upgrade.extend(["-f", values_file]) if kube_config: cmd_helm_upgrade.extend(["--kubeconfig", kube_config]) if kube_context: cmd_helm_upgrade.extend(["--kube-context", kube_context]) response_helm_upgrade = Popen(cmd_helm_upgrade, stdout=PIPE, stderr=PIPE) _, error_helm_upgrade = response_helm_upgrade.communicate() if response_helm_upgrade.returncode != 0: if ('forbidden' in error_helm_upgrade.decode("ascii") or 'timed out waiting for the condition' in error_helm_upgrade.decode("ascii")): telemetry.set_user_fault() telemetry.set_exception( exception=error_helm_upgrade.decode("ascii"), fault_type=consts.Install_HelmRelease_Fault_Type, summary='Unable to install helm release') raise CLIError( str.format(consts.Update_Agent_Failure, error_helm_upgrade.decode("ascii"))) return str.format(consts.Update_Agent_Success, connected_cluster.name)
def send_telemetry(self): super().send_telemetry() telemetry.set_user_fault(self.error_msg)
def error(self, message): telemetry.set_user_fault('parse error: {}'.format(message)) self._handle_command_package_error(message) return super(AzCliCommandParser, self).error(message)
uuid.uuid1 = uuid.uuid4 logger = get_logger(__name__) def cli_main(cli, args): return cli.invoke(args) az_cli = get_default_cli() telemetry.set_application(az_cli, ARGCOMPLETE_ENV_NAME) try: telemetry.start() exit_code = cli_main(az_cli, sys.argv[1:]) if exit_code and exit_code != 0: telemetry.set_failure() else: telemetry.set_success() sys.exit(exit_code) except KeyboardInterrupt: telemetry.set_user_fault('keyboard interrupt') sys.exit(1) finally: telemetry.conclude()
def delete_connectedk8s(cmd, client, resource_group_name, cluster_name, kube_config=None, kube_context=None, no_wait=False): logger.warning( "Ensure that you have the latest helm version installed before proceeding to avoid unexpected errors." ) logger.warning("This operation might take a while ...\n") # Send cloud information to telemetry send_cloud_telemetry(cmd) # Setting kubeconfig kube_config = set_kube_config(kube_config) # Loading the kubeconfig file in kubernetes client configuration try: config.load_kube_config(config_file=kube_config, context=kube_context) except Exception as e: telemetry.set_user_fault() telemetry.set_exception(exception=e, fault_type=consts.Load_Kubeconfig_Fault_Type, summary='Problem loading the kubeconfig file') raise CLIError("Problem loading the kubeconfig file." + str(e)) 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 check_helm_install(kube_config, kube_context) # Check helm version check_helm_version(kube_config, kube_context) # Check Release Existance release_namespace = get_release_namespace(kube_config, kube_context) if not release_namespace: delete_cc_resource(client, resource_group_name, cluster_name, no_wait) return # Loading config map api_instance = kube_client.CoreV1Api(kube_client.ApiClient(configuration)) try: configmap = api_instance.read_namespaced_config_map( 'azure-clusterconfig', 'azure-arc') except Exception as e: # pylint: disable=broad-except utils.kubernetes_exception_handler( e, consts.Read_ConfigMap_Fault_Type, 'Unable to read ConfigMap', error_message= "Unable to read ConfigMap 'azure-clusterconfig' in 'azure-arc' namespace: ", message_for_not_found= "The helm release 'azure-arc' is present but the azure-arc namespace/configmap is missing. Please run 'helm delete azure-arc --no-hooks' to cleanup the release before onboarding the cluster again." ) if (configmap.data["AZURE_RESOURCE_GROUP"].lower() == resource_group_name.lower() and configmap.data["AZURE_RESOURCE_NAME"].lower() == cluster_name.lower()): delete_cc_resource(client, resource_group_name, cluster_name, no_wait) else: telemetry.set_user_fault() telemetry.set_exception( exception='Unable to delete connected cluster', fault_type=consts.Bad_DeleteRequest_Fault_Type, summary= 'The resource cannot be deleted as kubernetes cluster is onboarded with some other resource id' ) raise CLIError( "The current context in the kubeconfig file does not correspond " + "to the connected cluster resource specified. Agents installed on this cluster correspond " + "to the resource group name '{}' ".format( configmap.data["AZURE_RESOURCE_GROUP"]) + "and resource name '{}'.".format( configmap.data["AZURE_RESOURCE_NAME"])) # Deleting the azure-arc agents delete_arc_agents(release_namespace, kube_config, kube_context, configuration)