def create_certificate(client, vault_base_url, certificate_name, certificate_policy,
                       disabled=False, expires=None, not_before=None, tags=None):
    cert_attrs = CertificateAttributes(not disabled, not_before, expires)
    logger.info("Starting long running operation 'keyvault certificate create'")
    client.create_certificate(
        vault_base_url, certificate_name, certificate_policy, cert_attrs, tags)

    if certificate_policy['issuer_parameters']['name'].lower() == 'unknown':
        # return immediately for a pending certificate
        return client.get_certificate_operation(vault_base_url, certificate_name)

    # otherwise loop until the certificate creation is complete
    while True:
        check = client.get_certificate_operation(vault_base_url, certificate_name)
        if check.status != 'inProgress':
            logger.info("Long running operation 'keyvault certificate create' finished with result %s.", check) # pylint: disable=line-too-long
            return check
        try:
            time.sleep(10)
        except KeyboardInterrupt:
            logger.info("Long running operation wait cancelled.")
            raise
        except Exception as client_exception:
            telemetry.set_exception(exception=client_exception, fault_type='cert-create-error',
                                    summary='Unexpected client exception during cert creation')
            message = getattr(client_exception, 'message', client_exception)

            try:
                message = str(message) + ' ' + json.loads(client_exception.response.text) \
                    ['error']['details'][0]['message']
            except: #pylint: disable=bare-except
                pass

            raise CLIError('{}'.format(message))
Esempio n. 2
0
def handle_long_running_operation_exception(ex):
    import azure.cli.core.telemetry as telemetry

    telemetry.set_exception(
        ex,
        fault_type='failed-long-running-operation',
        summary='Unexpected client exception in {}.'.format(LongRunningOperation.__name__))

    message = getattr(ex, 'message', ex)
    error_message = 'Deployment failed.'

    try:
        correlation_id = ex.response.headers['x-ms-correlation-request-id']
        error_message = '{} Correlation ID: {}.'.format(error_message, correlation_id)
    except:  # pylint: disable=bare-except
        pass

    try:
        inner_message = json.loads(ex.response.text)['error']['details'][0]['message']
        error_message = '{} {}'.format(error_message, inner_message)
    except:  # pylint: disable=bare-except
        error_message = '{} {}'.format(error_message, message)

    cli_error = CLIError(error_message)
    # capture response for downstream commands (webapp) to dig out more details
    setattr(cli_error, 'response', getattr(ex, 'response', None))
    raise cli_error
Esempio n. 3
0
def main(args, file=sys.stdout):  # pylint: disable=redefined-builtin
    azlogging.configure_logging(args)
    logger.debug('Command arguments %s', args)

    if len(args) > 0 and args[0] == '--version':
        show_version_info_exit(file)

    azure_folder = get_config_dir()
    if not os.path.exists(azure_folder):
        os.makedirs(azure_folder)
    ACCOUNT.load(os.path.join(azure_folder, 'azureProfile.json'))
    CONFIG.load(os.path.join(azure_folder, 'az.json'))
    SESSION.load(os.path.join(azure_folder, 'az.sess'), max_age=3600)

    APPLICATION.initialize(Configuration())

    try:
        cmd_result = APPLICATION.execute(args)

        # Commands can return a dictionary/list of results
        # If they do, we print the results.
        if cmd_result and cmd_result.result is not None:
            from azure.cli.core._output import OutputProducer
            formatter = OutputProducer.get_formatter(APPLICATION.configuration.output_format)
            OutputProducer(formatter=formatter, file=file).out(cmd_result)

    except Exception as ex:  # pylint: disable=broad-except

        # TODO: include additional details of the exception in telemetry
        telemetry.set_exception(ex, 'outer-exception',
                                'Unexpected exception caught during application execution.')
        telemetry.set_failure()

        error_code = handle_exception(ex)
        return error_code
Esempio n. 4
0
def get_command_table():
    '''Loads command table(s)
    '''
    installed_command_modules = []
    try:
        mods_ns_pkg = import_module('azure.cli.command_modules')
        installed_command_modules = [modname for _, modname, _ in
                                     pkgutil.iter_modules(mods_ns_pkg.__path__)]
    except ImportError:
        pass
    logger.info('Installed command modules %s', installed_command_modules)
    cumulative_elapsed_time = 0
    for mod in installed_command_modules:
        try:
            start_time = timeit.default_timer()
            import_module('azure.cli.command_modules.' + mod).load_commands()
            elapsed_time = timeit.default_timer() - start_time
            logger.debug("Loaded module '%s' in %.3f seconds.", mod, elapsed_time)
            cumulative_elapsed_time += elapsed_time
        except Exception as ex:  # pylint: disable=broad-except
            # Changing this error message requires updating CI script that checks for failed
            # module loading.
            logger.error("Error loading command module '%s'", mod)
            telemetry.set_exception(exception=ex, fault_type='module-load-error',
                                    summary='Error loading module: {}'.format(mod))
            logger.debug(traceback.format_exc())
    logger.debug("Loaded all modules in %.3f seconds. "
                 "(note: there's always an overhead with the first module loaded)",
                 cumulative_elapsed_time)
    _update_command_definitions(command_table)
    ordered_commands = OrderedDict(command_table)
    return ordered_commands
Esempio n. 5
0
def handle_long_running_operation_exception(ex):
    import json
    import azure.cli.core.telemetry as telemetry
    from azure.cli.core.commands import LongRunningOperation

    telemetry.set_exception(
        ex,
        fault_type='failed-long-running-operation',
        summary='Unexpected client exception in {}.'.format(LongRunningOperation.__name__))
    message = getattr(ex, 'message', ex)
    error_message = 'Deployment failed.'

    try:
        correlation_id = json.loads(ex.response.content.decode())['properties']['correlationId']
        error_message = '{} Correlation ID: {}.'.format(error_message, correlation_id)
    except:  # pylint: disable=bare-except
        pass

    try:
        tracking_id = re.match(r".*(\w{8}-\w{4}-\w{4}-\w{4}-\w{12})", str(message)).group(1)
        error_message = '{} Tracking ID: {}.'.format(error_message, tracking_id)
    except:  # pylint: disable=bare-except
        pass

    try:
        inner_message = json.loads(ex.response.text)['error']['details'][0]['message']
        error_message = '{} {}'.format(error_message, inner_message)
    except:  # pylint: disable=bare-except
        error_message = '{} {}'.format(error_message, message)

    cli_error = CLIError(error_message)
    # capture response for downstream commands (webapp) to dig out more details
    setattr(cli_error, 'response', getattr(ex, 'response', None))
    raise cli_error
Esempio n. 6
0
def get_command_table(module_name=None):
    '''Loads command table(s)
    When `module_name` is specified, only commands from that module will be loaded.
    If the module is not found, all commands are loaded.
    '''
    loaded = False
    # TODO remove module_name != 'sf' once old sf module is deprecated from the repo
    if module_name and module_name not in BLACKLISTED_MODS and module_name != 'sf':
        try:
            import_module('azure.cli.command_modules.' + module_name).load_commands()
            logger.debug("Successfully loaded command table from module '%s'.", module_name)
            loaded = True
        except ImportError:
            logger.debug("Loading all installed modules as module with name '%s' not found.", module_name)
        except Exception:  # pylint: disable=broad-except
            pass
    if not loaded:
        installed_command_modules = []
        try:
            mods_ns_pkg = import_module('azure.cli.command_modules')
            installed_command_modules = [modname for _, modname, _ in
                                         pkgutil.iter_modules(mods_ns_pkg.__path__)
                                         if modname not in BLACKLISTED_MODS]
        except ImportError:
            pass
        logger.debug('Installed command modules %s', installed_command_modules)
        cumulative_elapsed_time = 0
        for mod in installed_command_modules:
            try:
                start_time = timeit.default_timer()
                import_module('azure.cli.command_modules.' + mod).load_commands()
                elapsed_time = timeit.default_timer() - start_time
                logger.debug("Loaded module '%s' in %.3f seconds.", mod, elapsed_time)
                cumulative_elapsed_time += elapsed_time
            except Exception as ex:  # pylint: disable=broad-except
                # Changing this error message requires updating CI script that checks for failed
                # module loading.
                logger.error("Error loading command module '%s'", mod)
                telemetry.set_exception(exception=ex, fault_type='module-load-error-' + mod,
                                        summary='Error loading module: {}'.format(mod))
                logger.debug(traceback.format_exc())
        logger.debug("Loaded all modules in %.3f seconds. "
                     "(note: there's always an overhead with the first module loaded)",
                     cumulative_elapsed_time)
    try:
        # We always load extensions even if the appropriate module has been loaded
        # as an extension could override the commands already loaded.
        _get_command_table_from_extensions()
    except Exception:  # pylint: disable=broad-except
        logger.warning("Unable to load extensions. Use --debug for more information.")
        logger.debug(traceback.format_exc())
    _update_command_definitions(command_table)
    ordered_commands = OrderedDict(command_table)
    return ordered_commands
Esempio n. 7
0
    def _execute_command(kwargs):
        if confirmation \
            and not kwargs.get(CONFIRM_PARAM_NAME) \
            and not az_config.getboolean('core', 'disable_confirm_prompt', fallback=False) \
                and not _user_confirmed(confirmation, kwargs):
            raise CLIError('Operation cancelled.')

        client = client_factory(kwargs) if client_factory else None
        try:
            op = get_op_handler(operation)
            for _ in range(2):  # for possible retry, we do maximum 2 times.
                try:
                    result = op(client, **kwargs) if client else op(**kwargs)
                    if no_wait_param and kwargs.get(no_wait_param, None):
                        return None  # return None for 'no-wait'

                    # apply results transform if specified
                    if transform_result:
                        return transform_result(result)

                    # otherwise handle based on return type of results
                    if _is_poller(result):
                        return LongRunningOperation('Starting {}'.format(name))(result)
                    elif _is_paged(result):
                        return list(result)
                    return result
                except Exception as ex:  # pylint: disable=broad-except
                    rp = _check_rp_not_registered_err(ex)
                    if rp:
                        _register_rp(rp)
                        continue  # retry
                    if exception_handler:
                        exception_handler(ex)
                        return
                    else:
                        reraise(*sys.exc_info())
        except _load_validation_error_class() as validation_error:
            fault_type = name.replace(' ', '-') + '-validation-error'
            telemetry.set_exception(validation_error, fault_type=fault_type,
                                    summary='SDK validation error')
            raise CLIError(validation_error)
        except _load_client_exception_class() as client_exception:
            fault_type = name.replace(' ', '-') + '-client-error'
            telemetry.set_exception(client_exception, fault_type=fault_type,
                                    summary='Unexpected client exception during command creation')
            raise client_exception
        except _load_azure_exception_class() as azure_exception:
            fault_type = name.replace(' ', '-') + '-service-error'
            telemetry.set_exception(azure_exception, fault_type=fault_type,
                                    summary='Unexpected azure exception during command creation')
            message = re.search(r"([A-Za-z\t .])+", str(azure_exception))
            raise CLIError('\n{}'.format(message.group(0) if message else str(azure_exception)))
        except ValueError as value_error:
            fault_type = name.replace(' ', '-') + '-value-error'
            telemetry.set_exception(value_error, fault_type=fault_type,
                                    summary='Unexpected value exception during command creation')
            raise CLIError(value_error)
Esempio n. 8
0
    def __call__(self, poller):
        from msrest.exceptions import ClientException
        logger.info("Starting long running operation '%s'", self.start_msg)
        correlation_message = ''
        while not poller.done():
            try:
                # pylint: disable=protected-access
                correlation_id = json.loads(
                    poller._response.__dict__['_content'])['properties']['correlationId']

                correlation_message = 'Correlation ID: {}'.format(correlation_id)
            except:  # pylint: disable=bare-except
                pass

            try:
                self._delay()
            except KeyboardInterrupt:
                logger.error('Long running operation wait cancelled.  %s', correlation_message)
                raise
        try:
            result = poller.result()
        except ClientException as client_exception:
            telemetry.set_exception(
                client_exception,
                fault_type='client-exception',
                summary='Unexpected client exception in {}.'.format(LongRunningOperation.__name__))
            message = getattr(client_exception, 'message', client_exception)

            try:
                message = '{} {}'.format(
                    str(message),
                    json.loads(client_exception.response.text)['error']['details'][0]['message'])
            except:  # pylint: disable=bare-except
                pass

            cli_error = CLIError('{}  {}'.format(message, correlation_message))
            # capture response for downstream commands (webapp) to dig out more details
            setattr(cli_error, 'response', getattr(client_exception, 'response', None))
            raise cli_error

        logger.info("Long running operation '%s' completed with result %s",
                    self.start_msg, result)
        return result
Esempio n. 9
0
def create_certificate(cmd, client, vault_base_url, certificate_name, certificate_policy,
                       disabled=False, tags=None, validity=None):
    CertificateAttributes = cmd.get_models('CertificateAttributes', resource_type=ResourceType.DATA_KEYVAULT)
    cert_attrs = CertificateAttributes(enabled=not disabled)
    logger.info("Starting long-running operation 'keyvault certificate create'")

    if validity is not None:
        certificate_policy['x509_certificate_properties']['validity_in_months'] = validity

    client.create_certificate(
        vault_base_url, certificate_name, certificate_policy, cert_attrs, tags)

    if certificate_policy['issuer_parameters']['name'].lower() == 'unknown':
        # return immediately for a pending certificate
        return client.get_certificate_operation(vault_base_url, certificate_name)

    # otherwise loop until the certificate creation is complete
    while True:
        check = client.get_certificate_operation(vault_base_url, certificate_name)
        if check.status != 'inProgress':
            logger.info(
                "Long-running operation 'keyvault certificate create' finished with result %s.",
                check)
            return check
        try:
            time.sleep(10)
        except KeyboardInterrupt:
            logger.info("Long-running operation wait cancelled.")
            raise
        except Exception as client_exception:
            telemetry.set_exception(exception=client_exception, fault_type='cert-create-error',
                                    summary='Unexpected client exception during cert creation')
            message = getattr(client_exception, 'message', client_exception)

            try:
                ex_message = json.loads(client_exception.response.text)  # pylint: disable=no-member
                message = str(message) + ' ' + ex_message['error']['details'][0]['message']
            except:  # pylint: disable=bare-except
                pass

            raise CLIError('{}'.format(message))
Esempio n. 10
0
    def _execute_command(kwargs):

        if confirmation \
            and not kwargs.get(CONFIRM_PARAM_NAME) \
            and not az_config.getboolean('core', 'disable_confirm_prompt', fallback=False) \
                and not _user_confirmed(confirmation, kwargs):
            raise CLIError('Operation cancelled.')

        client = client_factory(kwargs) if client_factory else None
        try:
            op = get_op_handler(operation)
            try:
                result = op(client, **kwargs) if client else op(**kwargs)
                if no_wait_param and kwargs.get(no_wait_param, None):
                    return None  # return None for 'no-wait'

                # apply results transform if specified
                if transform_result:
                    return transform_result(result)

                # otherwise handle based on return type of results
                if _is_poller(result):
                    return LongRunningOperation(
                        'Starting {}'.format(name))(result)
                elif _is_paged(result):
                    return list(result)
                return result
            except Exception as ex:  # pylint: disable=broad-except
                if exception_handler:
                    exception_handler(ex)
                else:
                    reraise(*sys.exc_info())
        except _load_client_exception_class() as client_exception:
            fault_type = name.replace(' ', '-') + '-client-error'
            telemetry.set_exception(
                client_exception,
                fault_type=fault_type,
                summary='Unexpected client exception during command creation')
            message = getattr(client_exception, 'message', client_exception)
            raise _polish_rp_not_registerd_error(CLIError(message))
        except _load_azure_exception_class() as azure_exception:
            fault_type = name.replace(' ', '-') + '-service-error'
            telemetry.set_exception(
                azure_exception,
                fault_type=fault_type,
                summary='Unexpected azure exception during command creation')
            message = re.search(r"([A-Za-z\t .])+", str(azure_exception))
            raise CLIError('\n{}'.format(
                message.group(0) if message else str(azure_exception)))
        except ValueError as value_error:
            fault_type = name.replace(' ', '-') + '-value-error'
            telemetry.set_exception(
                value_error,
                fault_type=fault_type,
                summary='Unexpected value exception during command creation')
            raise CLIError(value_error)
        except CLIError as cli_error:
            raise _polish_rp_not_registerd_error(cli_error)
Esempio n. 11
0
        def _update_command_table_from_modules(args):
            '''Loads command table(s)
            When `module_name` is specified, only commands from that module will be loaded.
            If the module is not found, all commands are loaded.
            '''
            installed_command_modules = []
            try:
                mods_ns_pkg = import_module('azure.cli.command_modules')
                installed_command_modules = [modname for _, modname, _ in
                                             pkgutil.iter_modules(mods_ns_pkg.__path__)
                                             if modname not in BLACKLISTED_MODS]
            except ImportError as e:
                logger.warning(e)

            logger.debug('Installed command modules %s', installed_command_modules)
            cumulative_elapsed_time = 0
            for mod in [m for m in installed_command_modules if m not in BLACKLISTED_MODS]:
                try:
                    start_time = timeit.default_timer()
                    module_command_table, module_group_table = _load_module_command_loader(self, args, mod)
                    for cmd in module_command_table.values():
                        cmd.command_source = mod
                    self.command_table.update(module_command_table)
                    self.command_group_table.update(module_group_table)
                    elapsed_time = timeit.default_timer() - start_time
                    logger.debug("Loaded module '%s' in %.3f seconds.", mod, elapsed_time)
                    cumulative_elapsed_time += elapsed_time
                except Exception as ex:  # pylint: disable=broad-except
                    # Changing this error message requires updating CI script that checks for failed
                    # module loading.
                    import azure.cli.core.telemetry as telemetry
                    logger.error("Error loading command module '%s'", mod)
                    telemetry.set_exception(exception=ex, fault_type='module-load-error-' + mod,
                                            summary='Error loading module: {}'.format(mod))
                    logger.debug(traceback.format_exc())
            logger.debug("Loaded all modules in %.3f seconds. "
                         "(note: there's always an overhead with the first module loaded)",
                         cumulative_elapsed_time)
Esempio n. 12
0
        def _update_command_table_from_modules(args):
            '''Loads command table(s)
            When `module_name` is specified, only commands from that module will be loaded.
            If the module is not found, all commands are loaded.
            '''
            installed_command_modules = []
            try:
                mods_ns_pkg = import_module('azure.cli.command_modules')
                installed_command_modules = [modname for _, modname, _ in
                                             pkgutil.iter_modules(mods_ns_pkg.__path__)
                                             if modname not in BLACKLISTED_MODS]
            except ImportError as e:
                logger.warning(e)

            logger.debug('Installed command modules %s', installed_command_modules)
            cumulative_elapsed_time = 0
            for mod in [m for m in installed_command_modules if m not in BLACKLISTED_MODS]:
                try:
                    start_time = timeit.default_timer()
                    module_command_table, module_group_table = _load_module_command_loader(self, args, mod)
                    for cmd in module_command_table.values():
                        cmd.command_source = mod
                    self.command_table.update(module_command_table)
                    self.command_group_table.update(module_group_table)
                    elapsed_time = timeit.default_timer() - start_time
                    logger.debug("Loaded module '%s' in %.3f seconds.", mod, elapsed_time)
                    cumulative_elapsed_time += elapsed_time
                except Exception as ex:  # pylint: disable=broad-except
                    # Changing this error message requires updating CI script that checks for failed
                    # module loading.
                    import azure.cli.core.telemetry as telemetry
                    logger.error("Error loading command module '%s'", mod)
                    telemetry.set_exception(exception=ex, fault_type='module-load-error-' + mod,
                                            summary='Error loading module: {}'.format(mod))
                    logger.debug(traceback.format_exc())
            logger.debug("Loaded all modules in %.3f seconds. "
                         "(note: there's always an overhead with the first module loaded)",
                         cumulative_elapsed_time)
Esempio n. 13
0
def main(args, file=sys.stdout):  # pylint: disable=redefined-builtin
    azlogging.configure_logging(args)
    logger.debug('Command arguments %s', args)

    if len(args) > 0 and args[0] == '--version':
        show_version_info_exit(file)

    azure_folder = get_config_dir()
    if not os.path.exists(azure_folder):
        os.makedirs(azure_folder)
    ACCOUNT.load(os.path.join(azure_folder, 'azureProfile.json'))
    CONFIG.load(os.path.join(azure_folder, 'az.json'))
    SESSION.load(os.path.join(azure_folder, 'az.sess'), max_age=3600)

    config = Configuration(args)
    APPLICATION.initialize(config)

    try:
        cmd_result = APPLICATION.execute(args)

        # Commands can return a dictionary/list of results
        # If they do, we print the results.
        if cmd_result and cmd_result.result is not None:
            from azure.cli.core._output import OutputProducer
            formatter = OutputProducer.get_formatter(
                APPLICATION.configuration.output_format)
            OutputProducer(formatter=formatter, file=file).out(cmd_result)

    except Exception as ex:  # pylint: disable=broad-except

        # TODO: include additional details of the exception in telemetry
        telemetry.set_exception(
            ex, 'outer-exception',
            'Unexpected exception caught during application execution.')
        telemetry.set_failure()

        error_code = handle_exception(ex)
        return error_code
Esempio n. 14
0
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
Esempio n. 15
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))
Esempio n. 16
0
    def _execute_command(kwargs):
        from msrest.paging import Paged
        from msrest.exceptions import ClientException
        from msrestazure.azure_operation import AzureOperationPoller
        from azure.common import AzureException

        if confirmation \
            and not kwargs.get(FORCE_PARAM_NAME) \
            and not az_config.getboolean('core', 'disable_confirm_prompt', fallback=False) \
                and not _user_confirmed(confirmation, kwargs):
            raise CLIError('Operation cancelled.')

        client = client_factory(kwargs) if client_factory else None
        try:
            op = get_op_handler(operation)
            result = op(client, **kwargs) if client else op(**kwargs)

            if no_wait_param and kwargs.get(no_wait_param, None):
                return None  # return None for 'no-wait'

            # apply results transform if specified
            if transform_result:
                return transform_result(result)

            # otherwise handle based on return type of results
            if isinstance(result, AzureOperationPoller):
                return LongRunningOperation('Starting {}'.format(name))(result)
            elif isinstance(result, Paged):
                return list(result)
            else:
                return result
        except ClientException as client_exception:
            fault_type = name.replace(' ', '-') + '-client-error'
            telemetry.set_exception(
                client_exception,
                fault_type=fault_type,
                summary='Unexpected client exception during command creation')
            message = getattr(client_exception, 'message', client_exception)
            raise _polish_rp_not_registerd_error(CLIError(message))
        except AzureException as azure_exception:
            fault_type = name.replace(' ', '-') + '-service-error'
            telemetry.set_exception(
                azure_exception,
                fault_type=fault_type,
                summary='Unexpected azure exception during command creation')
            message = re.search(r"([A-Za-z\t .])+", str(azure_exception))
            raise CLIError('\n{}'.format(
                message.group(0) if message else str(azure_exception)))
        except ValueError as value_error:
            fault_type = name.replace(' ', '-') + '-value-error'
            telemetry.set_exception(
                value_error,
                fault_type=fault_type,
                summary='Unexpected value exception during command creation')
            raise CLIError(value_error)
        except CLIError as cli_error:
            raise _polish_rp_not_registerd_error(cli_error)
Esempio n. 17
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):
    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.httpsProxy={}".format(https_proxy),
                        "--set", "global.httpProxy={}".format(http_proxy),
                        "--set", "global.noProxy={}".format(no_proxy),
                        "--set", "global.onboardingPrivateKey={}".format(private_key_pem),
                        "--set", "systemDefaultValues.spnOnboarding=false",
                        "--output", "json"]
    # To set some other helm parameters through file
    if values_file_provided:
        cmd_helm_install.extend(["-f", values_file])
    if proxy_cert != "":
        cmd_helm_install.extend(["--set-file", "global.proxyCert={}".format(proxy_cert)])
    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"))
Esempio n. 18
0
def create_certificate(client, vault_base_url, certificate_name, certificate_policy,
                       disabled=False, expires=None, not_before=None, tags=None):
    from azure.keyvault.generated.models import \
        (CertificateAttributes)
    cert_attrs = CertificateAttributes(not disabled, not_before, expires)
    logger.info("Starting long running operation 'keyvault certificate create'")
    client.create_certificate(
        vault_base_url, certificate_name, certificate_policy, cert_attrs, tags)

    if certificate_policy['issuer_parameters']['name'].lower() == 'unknown':
        # return immediately for a pending certificate
        return client.get_certificate_operation(vault_base_url, certificate_name)

    # otherwise loop until the certificate creation is complete
    while True:
        check = client.get_certificate_operation(vault_base_url, certificate_name)
        if check.status != 'inProgress':
            logger.info("Long running operation 'keyvault certificate create' finished with result %s.", check) # pylint: disable=line-too-long
            return check
        try:
            time.sleep(10)
        except KeyboardInterrupt:
            logger.info("Long running operation wait cancelled.")
            raise
        except Exception as client_exception:
            telemetry.set_exception(exception=client_exception, fault_type='cert-create-error',
                                    summary='Unexpected client exception during cert creation')
            message = getattr(client_exception, 'message', client_exception)

            try:
                message = str(message) + ' ' + json.loads(client_exception.response.text) \
                    ['error']['details'][0]['message']
            except: #pylint: disable=bare-except
                pass

            raise CLIError('{}'.format(message))
Esempio n. 19
0
def export_helm_chart(registry_path, chart_export_path, kube_config,
                      kube_context):
    chart_export_path = os.path.join(os.path.expanduser('~'), '.azure',
                                     'AzureArcCharts')
    cmd_helm_chart_export = [
        "helm", "chart", "export", registry_path, "--destination",
        chart_export_path
    ]
    if kube_config:
        cmd_helm_chart_export.extend(["--kubeconfig", kube_config])
    if kube_context:
        cmd_helm_chart_export.extend(["--kube-context", kube_context])
    response_helm_chart_export = subprocess.Popen(cmd_helm_chart_export,
                                                  stdout=PIPE,
                                                  stderr=PIPE)
    _, error_helm_chart_export = response_helm_chart_export.communicate()
    if response_helm_chart_export.returncode != 0:
        telemetry.set_exception(
            exception=error_helm_chart_export.decode("ascii"),
            fault_type=consts.Export_HelmChart_Fault_Type,
            summary='Unable to export helm chart from the registry')
        raise CLIError("Unable to export helm chart from the registry '{}': ".
                       format(registry_path) +
                       error_helm_chart_export.decode("ascii"))
Esempio n. 20
0
    def _execute_command(kwargs):
        from msrest.paging import Paged
        from msrest.exceptions import ClientException
        from msrestazure.azure_operation import AzureOperationPoller
        from azure.common import AzureException

        if confirmation \
            and not kwargs.get(CONFIRM_PARAM_NAME) \
            and not az_config.getboolean('core', 'disable_confirm_prompt', fallback=False) \
                and not _user_confirmed(confirmation, kwargs):
            raise CLIError('Operation cancelled.')

        client = client_factory(kwargs) if client_factory else None
        try:
            op = get_op_handler(operation)
            try:
                result = op(client, **kwargs) if client else op(**kwargs)
            except Exception as ex:  # pylint: disable=broad-except
                if exception_handler:
                    result = exception_handler(ex)
                else:
                    raise ex

            if no_wait_param and kwargs.get(no_wait_param, None):
                return None  # return None for 'no-wait'

            # apply results transform if specified
            if transform_result:
                return transform_result(result)

            # otherwise handle based on return type of results
            if isinstance(result, AzureOperationPoller):
                return LongRunningOperation('Starting {}'.format(name))(result)
            elif isinstance(result, Paged):
                return list(result)
            else:
                return result
        except ClientException as client_exception:
            fault_type = name.replace(' ', '-') + '-client-error'
            telemetry.set_exception(client_exception, fault_type=fault_type,
                                    summary='Unexpected client exception during command creation')
            message = getattr(client_exception, 'message', client_exception)
            raise _polish_rp_not_registerd_error(CLIError(message))
        except AzureException as azure_exception:
            fault_type = name.replace(' ', '-') + '-service-error'
            telemetry.set_exception(azure_exception, fault_type=fault_type,
                                    summary='Unexpected azure exception during command creation')
            message = re.search(r"([A-Za-z\t .])+", str(azure_exception))
            raise CLIError('\n{}'.format(message.group(0) if message else str(azure_exception)))
        except ValueError as value_error:
            fault_type = name.replace(' ', '-') + '-value-error'
            telemetry.set_exception(value_error, fault_type=fault_type,
                                    summary='Unexpected value exception during command creation')
            raise CLIError(value_error)
        except CLIError as cli_error:
            raise _polish_rp_not_registerd_error(cli_error)
Esempio n. 21
0
def get_helm_registry(cmd,
                      config_dp_endpoint,
                      dp_endpoint_dogfood=None,
                      release_train_dogfood=None):
    # Setting uri
    get_chart_location_url = "{}/{}/GetLatestHelmPackagePath?api-version=2019-11-01-preview".format(
        config_dp_endpoint, 'azure-arc-k8sagents')
    release_train = os.getenv('RELEASETRAIN') if os.getenv(
        'RELEASETRAIN') else 'stable'
    if dp_endpoint_dogfood:
        get_chart_location_url = "{}/azure-arc-k8sagents/GetLatestHelmPackagePath?api-version=2019-11-01-preview".format(
            dp_endpoint_dogfood)
        if release_train_dogfood:
            release_train = release_train_dogfood
    uri_parameters = ["releaseTrain={}".format(release_train)]
    resource = cmd.cli_ctx.cloud.endpoints.active_directory_resource_id

    # Sending request
    try:
        r = send_raw_request(cmd.cli_ctx,
                             'post',
                             get_chart_location_url,
                             uri_parameters=uri_parameters,
                             resource=resource)
    except Exception as e:
        telemetry.set_exception(
            exception=e,
            fault_type=consts.Get_HelmRegistery_Path_Fault_Type,
            summary='Error while fetching helm chart registry path')
        raise CLIInternalError(
            "Error while fetching helm chart registry path: " + str(e))
    if r.content:
        try:
            return r.json().get('repositoryPath')
        except Exception as e:
            telemetry.set_exception(
                exception=e,
                fault_type=consts.Get_HelmRegistery_Path_Fault_Type,
                summary='Error while fetching helm chart registry path')
            raise CLIInternalError(
                "Error while fetching helm chart registry path from JSON response: "
                + str(e))
    else:
        telemetry.set_exception(
            exception='No content in response',
            fault_type=consts.Get_HelmRegistery_Path_Fault_Type,
            summary='No content in acr path response')
        raise CLIInternalError(
            "No content was found in helm registry path response.")
Esempio n. 22
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
Esempio n. 23
0
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)
Esempio n. 24
0
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)
Esempio n. 25
0
     'auto-upgrade', 'all', True)
 prompt = az_cli.config.getboolean('auto-upgrade', 'prompt',
                                   True)
 cmd = ['az', 'upgrade', '--all', str(update_all)]
 if prompt:
     from knack.prompting import verify_is_a_tty, NoTTYException  # pylint: disable=ungrouped-imports
     az_upgrade_run = True
     try:
         verify_is_a_tty()
     except NoTTYException:
         az_upgrade_run = False
         err_msg = "Unable to prompt for auto upgrade as no tty available. " \
             "Run 'az config set auto-upgrade.prompt=no' to allow auto upgrade with no prompt."
         logger.warning(err_msg)
         telemetry.set_exception(
             UnclassifiedUserFault(err_msg),
             fault_type='auto-upgrade-failed')
     else:
         upgrade_exit_code = subprocess.call(
             cmd, shell=platform.system() == 'Windows')
 else:
     import os
     devnull = open(os.devnull, 'w')
     cmd.append('-y')
     upgrade_exit_code = subprocess.call(
         cmd,
         shell=platform.system() == 'Windows',
         stdout=devnull)
 if az_upgrade_run and upgrade_exit_code != 0:
     err_msg = "Auto upgrade failed with exit code {}".format(
         exit_code)
Esempio n. 26
0
def arm_exception_handler(ex, fault_type, summary, return_if_not_found=False):
    if isinstance(ex, AuthenticationError):
        telemetry.set_user_fault()
        telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary)
        raise CLIError("Authentication error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary))

    if isinstance(ex, TokenExpiredError):
        telemetry.set_user_fault()
        telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary)
        raise CLIError("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 CLIError("Http operation error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary))

    if isinstance(ex, ValidationError):
        telemetry.set_user_fault()
        telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary)
        raise CLIError("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 CLIError("Cloud error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary))

    telemetry.set_exception(exception=ex, fault_type=fault_type, summary=summary)
    raise CLIError("Error occured while making ARM request: " + str(ex) + "\nSummary: {}".format(summary))
Esempio n. 27
0
az_cli = get_default_cli()

telemetry.set_application(az_cli, ARGCOMPLETE_ENV_NAME)

# 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 and exit_code != 0:
        if az_cli.result.error is not None and not telemetry.has_exceptions():
            telemetry.set_exception(az_cli.result.error, fault_type='')
        telemetry.set_failure()
    else:
        telemetry.set_success()

    sys.exit(exit_code)

except KeyboardInterrupt:
    telemetry.set_user_fault('keyboard interrupt')
    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:
Esempio n. 28
0
 def send_telemetry(self):
     super().send_telemetry()
     telemetry.set_failure(self.error_msg)
     if self.exception_trace:
         telemetry.set_exception(self.exception_trace, '')
Esempio n. 29
0
az_cli = get_default_cli()

telemetry.set_application(az_cli, ARGCOMPLETE_ENV_NAME)

# 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 and exit_code != 0:
        if az_cli.result.error is not None and not telemetry.has_exceptions():
            telemetry.set_exception(az_cli.result.error, fault_type='')
        telemetry.set_failure()
    else:
        telemetry.set_success()

    sys.exit(exit_code)

except KeyboardInterrupt:
    telemetry.set_user_fault('keyboard interrupt')
    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:
Esempio n. 30
0
def get_command_table(module_name=None):
    '''Loads command table(s)
    When `module_name` is specified, only commands from that module will be loaded.
    If the module is not found, all commands are loaded.
    '''
    loaded = False
    # TODO remove module_name != 'sf' once old sf module is deprecated from the repo
    if module_name and module_name not in BLACKLISTED_MODS and module_name != 'sf':
        try:
            import_module('azure.cli.command_modules.' +
                          module_name).load_commands()
            logger.debug("Successfully loaded command table from module '%s'.",
                         module_name)
            loaded = True
        except ImportError:
            logger.debug(
                "Loading all installed modules as module with name '%s' not found.",
                module_name)
        except Exception:  # pylint: disable=broad-except
            pass
    if not loaded:
        installed_command_modules = []
        try:
            mods_ns_pkg = import_module('azure.cli.command_modules')
            installed_command_modules = [
                modname
                for _, modname, _ in pkgutil.iter_modules(mods_ns_pkg.__path__)
                if modname not in BLACKLISTED_MODS
            ]
        except ImportError:
            pass
        logger.debug('Installed command modules %s', installed_command_modules)
        cumulative_elapsed_time = 0
        for mod in installed_command_modules:
            try:
                start_time = timeit.default_timer()
                import_module('azure.cli.command_modules.' +
                              mod).load_commands()
                elapsed_time = timeit.default_timer() - start_time
                logger.debug("Loaded module '%s' in %.3f seconds.", mod,
                             elapsed_time)
                cumulative_elapsed_time += elapsed_time
            except Exception as ex:  # pylint: disable=broad-except
                # Changing this error message requires updating CI script that checks for failed
                # module loading.
                logger.error("Error loading command module '%s'", mod)
                telemetry.set_exception(
                    exception=ex,
                    fault_type='module-load-error-' + mod,
                    summary='Error loading module: {}'.format(mod))
                logger.debug(traceback.format_exc())
        logger.debug(
            "Loaded all modules in %.3f seconds. "
            "(note: there's always an overhead with the first module loaded)",
            cumulative_elapsed_time)
    try:
        # We always load extensions even if the appropriate module has been loaded
        # as an extension could override the commands already loaded.
        _get_command_table_from_extensions()
    except Exception:  # pylint: disable=broad-except
        logger.warning(
            "Unable to load extensions. Use --debug for more information.")
        logger.debug(traceback.format_exc())
    _update_command_definitions(command_table)
    ordered_commands = OrderedDict(command_table)
    return ordered_commands
Esempio n. 31
0
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)
Esempio n. 32
0
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
Esempio n. 33
0
def helm_install_release(chart_path,
                         subscription_id,
                         kubernetes_distro,
                         kubernetes_infra,
                         resource_group_name,
                         cluster_name,
                         location,
                         onboarding_tenant_id,
                         http_proxy,
                         https_proxy,
                         no_proxy,
                         proxy_cert,
                         private_key_pem,
                         kube_config,
                         kube_context,
                         no_wait,
                         values_file_provided,
                         values_file,
                         cloud_name,
                         disable_auto_upgrade,
                         enable_custom_locations,
                         custom_locations_oid,
                         onboarding_timeout="600"):
    cmd_helm_install = [
        "helm", "upgrade", "--install", "azure-arc", chart_path, "--set",
        "global.subscriptionId={}".format(subscription_id), "--set",
        "global.kubernetesDistro={}".format(kubernetes_distro), "--set",
        "global.kubernetesInfra={}".format(kubernetes_infra), "--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), "--set",
        "systemDefaultValues.clusterconnect-agent.enabled=true", "--output",
        "json"
    ]
    # Add custom-locations related params
    if enable_custom_locations:
        cmd_helm_install.extend(
            ["--set", "systemDefaultValues.customLocations.enabled=true"])
        cmd_helm_install.extend([
            "--set", "systemDefaultValues.customLocations.oid={}".format(
                custom_locations_oid)
        ])
    # To set some other helm parameters through file
    if values_file_provided:
        cmd_helm_install.extend(["-f", values_file])
    if disable_auto_upgrade:
        cmd_helm_install.extend([
            "--set",
            "systemDefaultValues.azureArcAgents.autoUpdate={}".format("false")
        ])
    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:
        # Change --timeout format for helm client to understand
        onboarding_timeout = onboarding_timeout + "s"
        cmd_helm_install.extend(
            ["--wait", "--timeout", "{}".format(onboarding_timeout)])
    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 CLIInternalError("Unable to install helm release: " +
                               error_helm_install.decode("ascii"))
Esempio n. 34
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 AzureResponseError(
            "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 AzureResponseError(
            "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)
        if status_code // 100 == 5:
            raise AzureInternalError(
                "Http operation error occured while making ARM request: " +
                str(ex) + "\nSummary: {}".format(summary))
        raise AzureResponseError(
            "Http operation error occured while making ARM request: " +
            str(ex) + "\nSummary: {}".format(summary))

    if isinstance(ex, MSRestValidationError):
        telemetry.set_exception(exception=ex,
                                fault_type=fault_type,
                                summary=summary)
        raise AzureResponseError(
            "Validation error occured while making ARM request: " + str(ex) +
            "\nSummary: {}".format(summary))

    if isinstance(ex, HttpResponseError):
        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)
        if status_code // 100 == 5:
            raise AzureInternalError(
                "Http response error occured while making ARM request: " +
                str(ex) + "\nSummary: {}".format(summary))
        raise AzureResponseError(
            "Http response error occured while making ARM request: " +
            str(ex) + "\nSummary: {}".format(summary))

    if isinstance(ex, ResourceNotFoundError) and return_if_not_found:
        return

    telemetry.set_exception(exception=ex,
                            fault_type=fault_type,
                            summary=summary)
    raise ClientRequestError("Error occured while making ARM request: " +
                             str(ex) + "\nSummary: {}".format(summary))
Esempio n. 35
0
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")

    # Setting kubeconfig
    kube_config = set_kube_config(kube_config)

    # Removing quotes from kubeconfig path. This is necessary for windows OS.
    trim_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=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 release_namespace is None:
        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
        telemetry.set_exception(exception=e, fault_type=Read_ConfigMap_Fault_Type,
                                summary='Unable to read ConfigMap')
        raise CLIError("Unable to read ConfigMap 'azure-clusterconfig' in 'azure-arc' namespace: %s\n" % e)

    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=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)
Esempio n. 36
0
        def _update_command_table_from_modules(args, command_modules=None):
            """Loads command tables from modules and merge into the main command table.

            :param args: Arguments of the command.
            :param list command_modules: Command modules to load, in the format like ['resource', 'profile'].
             If None, will do module discovery and load all modules.
             If [], only ALWAYS_LOADED_MODULES will be loaded.
             Otherwise, the list will be extended using ALWAYS_LOADED_MODULES.
            """

            # As command modules are built-in, the existence of modules in ALWAYS_LOADED_MODULES is NOT checked
            if command_modules is not None:
                command_modules.extend(ALWAYS_LOADED_MODULES)
            else:
                # Perform module discovery
                command_modules = []
                try:
                    mods_ns_pkg = import_module('azure.cli.command_modules')
                    command_modules = [
                        modname for _, modname, _ in pkgutil.iter_modules(
                            mods_ns_pkg.__path__)
                    ]
                    logger.debug('Discovered command modules: %s',
                                 command_modules)
                except ImportError as e:
                    logger.warning(e)

            count = 0
            cumulative_elapsed_time = 0
            cumulative_group_count = 0
            cumulative_command_count = 0
            logger.debug("Loading command modules:")
            logger.debug(self.header_mod)

            for mod in [m for m in command_modules if m not in BLOCKED_MODS]:
                try:
                    start_time = timeit.default_timer()
                    module_command_table, module_group_table = _load_module_command_loader(
                        self, args, mod)
                    for cmd in module_command_table.values():
                        cmd.command_source = mod
                    self.command_table.update(module_command_table)
                    self.command_group_table.update(module_group_table)

                    elapsed_time = timeit.default_timer() - start_time
                    logger.debug(self.item_format_string, mod, elapsed_time,
                                 len(module_group_table),
                                 len(module_command_table))
                    count += 1
                    cumulative_elapsed_time += elapsed_time
                    cumulative_group_count += len(module_group_table)
                    cumulative_command_count += len(module_command_table)
                except Exception as ex:  # pylint: disable=broad-except
                    # Changing this error message requires updating CI script that checks for failed
                    # module loading.
                    import azure.cli.core.telemetry as telemetry
                    logger.error("Error loading command module '%s': %s", mod,
                                 ex)
                    telemetry.set_exception(
                        exception=ex,
                        fault_type='module-load-error-' + mod,
                        summary='Error loading module: {}'.format(mod))
                    logger.debug(traceback.format_exc())
            # Summary line
            logger.debug(self.item_format_string, "Total ({})".format(count),
                         cumulative_elapsed_time, cumulative_group_count,
                         cumulative_command_count)
Esempio n. 37
0
def create_connectedk8s(cmd, client, resource_group_name, cluster_name, 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)

    # Setting user profile
    profile = Profile(cli_ctx=cmd.cli_ctx)

    # 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)

    # Removing quotes from kubeconfig path. This is necessary for windows OS.
    trim_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=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
    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=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

    # Check Release Existance
    release_namespace = get_release_namespace(kube_config, kube_context)
    if release_namespace is not None:
        # 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
            telemetry.set_exception(exception=e, fault_type=Read_ConfigMap_Fault_Type,
                                    summary='Unable to read ConfigMap')
            raise CLIError("Unable to read ConfigMap 'azure-clusterconfig' in 'azure-arc' namespace: %s\n" % e)
        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
                public_key = client.get(configmap_rg_name,
                                        configmap_cluster_name).agent_public_key_certificate
                cc = generate_request_payload(configuration, location, public_key, tags)
                try:
                    return sdk_no_wait(no_wait, client.create, resource_group_name=resource_group_name,
                                       cluster_name=cluster_name, connected_cluster=cc)
                except CloudError as ex:
                    telemetry.set_exception(exception=ex, fault_type=Create_ConnectedCluster_Fault_Type,
                                            summary='Unable to create connected cluster resource')
                    raise CLIError(ex)
            else:
                telemetry.set_user_fault()
                telemetry.set_exception(exception='The kubernetes cluster is already onboarded', fault_type=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=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:
            telemetry.set_exception(exception=e, fault_type=Create_ResourceGroup_Fault_Type,
                                    summary='Failed to create the resource group')
            raise CLIError("Failed to create the resource group {} :".format(resource_group_name) + str(e))

    # Adding helm repo
    if os.getenv('HELMREPONAME') and os.getenv('HELMREPOURL'):
        repo_name = os.getenv('HELMREPONAME')
        repo_url = os.getenv('HELMREPOURL')
        cmd_helm_repo = ["helm", "repo", "add", repo_name, repo_url, "--kubeconfig", kube_config]
        if kube_context:
            cmd_helm_repo.extend(["--kube-context", kube_context])
        response_helm_repo = Popen(cmd_helm_repo, stdout=PIPE, stderr=PIPE)
        _, error_helm_repo = response_helm_repo.communicate()
        if response_helm_repo.returncode != 0:
            telemetry.set_exception(exception=error_helm_repo.decode("ascii"), fault_type=Add_HelmRepo_Fault_Type,
                                    summary='Failed to add helm repository')
            raise CLIError("Unable to add repository {} to helm: ".format(repo_url) + error_helm_repo.decode("ascii"))

    # Retrieving Helm chart OCI Artifact location
    registry_path = os.getenv('HELMREGISTRY') if os.getenv('HELMREGISTRY') else get_helm_registry(profile, location)

    # 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})

    # Pulling helm chart from registry
    os.environ['HELM_EXPERIMENTAL_OCI'] = '1'
    pull_helm_chart(registry_path, kube_config, kube_context)

    # Exporting helm chart
    chart_export_path = os.path.join(os.path.expanduser('~'), '.azure', 'AzureArcCharts')
    export_helm_chart(registry_path, chart_export_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=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=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=PrivateKey_Export_Fault_Type,
                                summary='Failed to export private key')
        raise CLIError("Failed to export private key." + str(e))

    # Helm Install
    helm_chart_path = os.path.join(chart_export_path, 'azure-arc-k8sagents')
    chart_path = os.getenv('HELMCHART') if os.getenv('HELMCHART') else helm_chart_path
    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",
                        "--kubeconfig", kube_config, "--output", "json"]
    if kube_context:
        cmd_helm_install.extend(["--kube-context", kube_context])
    response_helm_install = Popen(cmd_helm_install, stdout=PIPE, stderr=PIPE)
    _, error_helm_install = response_helm_install.communicate()
    if response_helm_install.returncode != 0:
        telemetry.set_exception(exception=error_helm_install.decode("ascii"), fault_type=Install_HelmRelease_Fault_Type,
                                summary='Unable to install helm release')
        raise CLIError("Unable to install helm release: " + error_helm_install.decode("ascii"))

    # Create connected cluster resource
    cc = generate_request_payload(configuration, location, public_key, tags)
    try:
        put_cc_response = sdk_no_wait(no_wait, client.create,
                                      resource_group_name=resource_group_name,
                                      cluster_name=cluster_name, connected_cluster=cc)
        if no_wait:
            return put_cc_response
    except CloudError as ex:
        telemetry.set_exception(exception=ex, fault_type=Create_ConnectedCluster_Fault_Type,
                                summary='Unable to create connected cluster resource')
        raise CLIError(ex)

    # Getting total number of pods scheduled to run in azure-arc namespace
    api_instance = kube_client.CoreV1Api(kube_client.ApiClient(configuration))
    pod_dict = get_pod_dict(api_instance)

    # Checking azure-arc pod statuses
    try:
        check_pod_status(pod_dict)
    except Exception as e:  # pylint: disable=broad-except
        telemetry.set_exception(exception=e, fault_type=Check_PodStatus_Fault_Type,
                                summary='Failed to check arc agent pods statuses')
        logger.warning("Failed to check arc agent pods statuses: %s", e)

    return put_cc_response