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')
Exemple #2
0
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)))
Exemple #3
0
    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)
Exemple #4
0
 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)
Exemple #5
0
 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)
Exemple #6
0
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))
Exemple #7
0
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)
Exemple #8
0
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
Exemple #9
0
    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
Exemple #11
0
    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)
Exemple #12
0
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))
Exemple #13
0
 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 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)
Exemple #15
0
 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)
Exemple #16
0
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"))
Exemple #17
0
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
Exemple #18
0
 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")
Exemple #20
0
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))
Exemple #21
0
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)
Exemple #22
0
    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, '')
Exemple #23
0
    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)
Exemple #24
0
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
Exemple #25
0
    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)
Exemple #26
0
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
Exemple #28
0
# 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
Exemple #29
0
 def validation_error(self, message):
     telemetry.set_user_fault('validation error')
     return super(AzCliCommandParser, self).error(message)
Exemple #30
0
    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)
Exemple #32
0
 def send_telemetry(self):
     super().send_telemetry()
     telemetry.set_user_fault(self.error_msg)
Exemple #33
0
 def validation_error(self, message):
     telemetry.set_user_fault('validation error')
     return super(AzCliCommandParser, self).error(message)
Exemple #34
0
 def error(self, message):
     telemetry.set_user_fault('parse error: {}'.format(message))
     self._handle_command_package_error(message)
     return super(AzCliCommandParser, self).error(message)
Exemple #35
0
    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)