def test_execute_system_command_unknown_error(mocker): # noinspection PyUnusedLocal def raise_command_exception(*args, **kwargs): raise subprocess.SubprocessError() mocker.patch('subprocess.check_output', new=raise_command_exception) with pytest.raises(subprocess.SubprocessError): execute_system_command(['ls'])
def _show_workflow_logs(workflow: ArgoWorkflow, namespace: str): try: log.debug(f'Worklfow {workflow.name} main container logs:') output, _, _ = execute_system_command(command=[ 'kubectl', 'logs', '-n', namespace, workflow.name, 'main' ]) log.debug(output) log.debug(f'Worklfow {workflow.name} wait container logs:') output, _, _ = execute_system_command(command=[ 'kubectl', 'logs', '-n', namespace, workflow.name, 'wait' ]) log.debug(output) except Exception: log.exception(f'Failed to get {workflow.name} worklfow logs.')
def delete_helm_release(release_name: str, purge=False, namespace: str = None): """ Deletes release of a helm's chart. :param release_name: name of a release to be removed :param purge: if True, helm release will be purged In case of any problems it throws an exception """ if purge: delete_release_command = [ os.path.join(Config().config_path, 'helm'), "delete", "--purge", release_name ] else: delete_release_command = [ os.path.join(Config().config_path, 'helm'), "delete", release_name ] if namespace: delete_release_command += ["--tiller-namespace", namespace] output, err_code, log_output = execute_system_command( delete_release_command) if (f"release \"{release_name}\" deleted" not in output and f"release: \"{release_name}\" not found" not in output): logger.error(log_output) raise RuntimeError( Texts.HELM_RELEASE_REMOVAL_ERROR_MSG.format( release_name=release_name))
def get_top_for_pod(name: str, namespace: str) -> Tuple[str, str]: """ Returns cpu and memory usage for a pod with a given name located in a given namespace :param name: name of a pod :param namespace: namespace where the pod resided. Optional - if not given, function searches the pod in current namespace :return: tuple containing two values - cpu and memory usage expressed in k8s format """ top_command = ["kubectl", "top", "pod", name] if namespace: top_command.extend(["-n", namespace]) output, err_code, log_output = system.execute_system_command(top_command) if err_code: raise KubectlConnectionError( Texts.K8S_CLUSTER_NO_CONNECTION_ERROR_MSG.format( output=log_output)) if output: lines = output.split("\n") if lines and len(lines) > 1: second_line = lines[1] if second_line: split_second_line = second_line.split() if split_second_line and len(split_second_line) > 2: return (split_second_line[1], split_second_line[2]) logger.error(Texts.TOP_COMMAND_ERROR_LOG.format(output=log_output)) raise KubernetesError(Texts.TOP_COMMAND_ERROR)
def _debug_workflow_logs(workflow: ArgoWorkflow, namespace: str): try: output, _, _ = execute_system_command(command=[ 'kubectl', 'logs', '-n', namespace, workflow.name, 'main' ]) log.debug(output) except Exception: log.exception(f'Failed to get {workflow.name} worklfow logs.')
def delete_k8s_object(kind: str, name: str): delete_command = ['kubectl', 'delete', kind, name] logger.debug(delete_command) output, err_code, log_output = system.execute_system_command( delete_command) logger.debug(f"delete_k8s_object - output : {err_code} - {log_output}") if err_code: raise RuntimeError( Texts.K8S_OBJECT_DELETE_ERROR_MSG.format(output=log_output))
def get_mounts_windows(): output, error_code, log_output = execute_system_command(["net", "use"]) if error_code: handle_error(logger, Texts.MOUNTS_LIST_COMMAND_ERROR_MSG, Texts.MOUNTS_LIST_COMMAND_ERROR_MSG) exit(1) host = get_kubectl_host(with_port=False) data = output.split("\n") start_analyzing = False ret_list = [] first_line = None second_line = None for item in data: if start_analyzing: if "The command completed successfully." in item: break else: if not first_line: first_line = item elif not second_line: second_line = item if first_line and second_line: if host in first_line: split_first_line = first_line.split() status = None remote_location = None if len(split_first_line) == 3: status = split_first_line[0].strip() local_folder = split_first_line[1].strip() remote_location = split_first_line[2].strip() elif len(split_first_line) == 2: status = split_first_line[0].strip() local_folder = "" remote_location = split_first_line[1].strip() network = second_line.strip() if status and remote_location: ret_list.append( ShareData(remote_share=remote_location, local_share=local_folder, status=status, network=network)) first_line = None second_line = None elif "--------" in item: start_analyzing = True ret_list.sort(key=lambda x: x.remote_share, reverse=False) click.echo( tabulate([x.windows_tabular_format() for x in ret_list], headers=ShareData.WIN_MOUNTS_LIST_HEADERS, tablefmt=TBLT_TABLE_FORMAT))
def test_execute_system_command_limited_logs(mocker): cop_mock = mocker.patch('subprocess.check_output') cop_mock.return_value = 50 * "a" output, exit_code, log_output = execute_system_command(['ls'], logs_size=20) assert len(log_output) == 20 assert len(output) == 50 # noinspection PyUnresolvedReferences assert subprocess.check_output.call_count == 1
def get_mounts_linux_osx(username: str = "", is_admin: bool = False, osx: bool = False): output, error_code, log_output = execute_system_command(["mount"]) if error_code: handle_error(logger, Texts.MOUNTS_LIST_COMMAND_ERROR_MSG, Texts.MOUNTS_LIST_COMMAND_ERROR_MSG) exit(1) host = get_kubectl_host(with_port=False) if osx: username_string = f"//{username}@" else: username_string = f"username={username}," if osx: mnt_regex = "//(.*)@(.*) on (.*) \(" else: mnt_regex = "(.*) on (.*) type" ret_list = [] if output: for item in [ nauta_item for nauta_item in output.split("\n") if host in nauta_item and ( is_admin or username_string in nauta_item) ]: try: mount_data = re.search(mnt_regex, item) if osx: # Type checking is disabled here - we are catching AttributeError exception anyway username = mount_data.group(1) # type: ignore remote_location = mount_data.group(2) # type: ignore local_folder = mount_data.group(3) # type: ignore else: remote_location = mount_data.group(1) # type: ignore local_folder = mount_data.group(2) # type: ignore username = re.search("username=(.*?),", item).group(1) # type: ignore ret_list.append( ShareData(remote_share=remote_location, local_share=local_folder, username=username)) except Exception: handle_error(logger, Texts.MOUNTS_LIST_COMMAND_ERROR_MSG, Texts.MOUNTS_LIST_COMMAND_ERROR_MSG) ret_list.sort(key=lambda x: x.username, reverse=False) click.echo( tabulate([x.linux_osx_tabular_format() for x in ret_list], headers=ShareData.LINUX_MOUNTS_LIST_HEADERS, tablefmt=TBLT_TABLE_FORMAT))
def check_connection_to_cluster(): check_connection_cmd = ['kubectl', 'get', 'pods'] logger.debug(check_connection_cmd) output, err_code, log_output = system.execute_system_command( check_connection_cmd) logger.debug( f"check_connection_to_cluster - output : {err_code} - {log_output}") if err_code: raise KubectlConnectionError( Texts.K8S_CLUSTER_NO_CONNECTION_ERROR_MSG.format( output=log_output))
def test_execute_system_command_known_error(mocker): bad_exit_code = 1 # noinspection PyUnusedLocal def raise_command_exception(*args, **kwargs): raise subprocess.CalledProcessError(returncode=bad_exit_code, cmd='ls') mocker.patch('subprocess.check_output', new=raise_command_exception) output, exit_code, log_output = execute_system_command(['ls']) assert exit_code == bad_exit_code
def call_draft(args: List[str], cwd: str = None, namespace: str = None, logs_size: int = 0) \ -> (str, int, str): config_path = Config().config_path full_command = [os.path.join(config_path, DRAFT_BIN)] full_command.extend(args) if get_verbosity_level() == logging.DEBUG: full_command.append('--debug') env = os.environ.copy() env['PATH'] = config_path + os.pathsep + env['PATH'] env['DRAFT_HOME'] = os.path.join(config_path, DRAFT_HOME_FOLDER) if namespace: env['TILLER_NAMESPACE'] = namespace return execute_system_command( full_command, env=env, cwd=cwd, logs_size=logs_size)
def install_helm_chart(chart_dirpath: str, release_name: str = None, tiller_namespace: str = None): command = [ os.path.join(Config().config_path, 'helm'), "install", chart_dirpath ] if release_name: command.extend(["--name", release_name]) if tiller_namespace: command.extend(["--tiller-namespace", tiller_namespace]) output, err_code, log_output = execute_system_command(command) logger.debug(f"helm exit code: {err_code} returned: {output}") if err_code != 0: raise RuntimeError(f"helm returned with non-zero code: {err_code}")
def install_helm_chart(chart_dirpath: str, release_name: str = None, tiller_namespace: str = None): command = ["helm", "install", chart_dirpath] if release_name: command.extend(["--name", release_name]) if tiller_namespace: command.extend(["--tiller-namespace", tiller_namespace]) env = os.environ.copy() env['PATH'] = f"{Config().config_path}{os.pathsep}{env['PATH']}" output, err_code, log_output = execute_system_command(command, env=env) logger.debug(f"helm exit code: {err_code} returned: {output}") if err_code != 0: raise RuntimeError(f"helm returned with non-zero code: {err_code}")
def create(state: State, username: str, list_only: bool, filename: str): """ Adds a new user with a name given as a parameter. :param username: name of a new user """ if list_only and filename: handle_error(user_msg=Texts.F_L_OPTIONS_EXCLUSION_ERROR_MSG) exit(1) try: try: validate_user_name(username) except ValueError as exe: handle_error( logger, Texts.NAME_VALIDATION_ERROR_MSG.format(username=username), str(exe), add_verbosity_msg=state.verbosity == 0) exit(1) if not is_current_user_administrator(): handle_error(logger, Texts.USER_NOT_ADMIN_ERROR_MSG, Texts.USER_NOT_ADMIN_ERROR_MSG) exit(1) user_state = check_users_presence(username) if user_state == UserState.ACTIVE: handle_error( logger, Texts.USER_ALREADY_EXISTS_ERROR_MSG.format(username=username), Texts.USER_ALREADY_EXISTS_ERROR_MSG.format(username=username)) exit(1) if user_state == UserState.TERMINATING: handle_error( logger, Texts.USER_BEING_REMOVED_ERROR_MSG.format(username=username), Texts.USER_BEING_REMOVED_ERROR_MSG.format(username=username)) exit(1) except Exception: handle_error( logger, Texts.USER_VERIFICATION_ERROR_MSG.format(username=username), Texts.USER_VERIFICATION_ERROR_MSG.format(username=username), add_verbosity_msg=state.verbosity == 0) exit(1) try: with spinner(text=Texts.CREATING_USER_PROGRESS_MSG.format( username=username)): chart_location = os.path.join(Config().config_path, ADD_USER_CHART_NAME) nauta_config_map = NAUTAConfigMap() tiller_location = nauta_config_map.image_tiller tensorboard_service_location = nauta_config_map.image_tensorboard_service add_user_command = [ "helm", "install", "--wait", "--namespace", username, "--name", username, chart_location, "--set", "global.nauta=nauta", "--set", f"username={username}", "--set", "TillerImage={}".format(tiller_location), "--set", f"TensorboardServiceImage={tensorboard_service_location}" ] env = os.environ.copy() env['PATH'] = Config().config_path + os.pathsep + env['PATH'] _, err_code, log_output = execute_system_command( ' '.join(add_user_command), env=env, shell=True) if err_code: handle_error(logger, log_output, Texts.USER_ADD_ERROR_MSG, add_verbosity_msg=state.verbosity == 0) if not delete_user(username): handle_error(user_msg=Texts.REMOVE_USER_ERROR_MSG.format( username=username)) sys.exit(1) try: users_password = get_users_token(username) except Exception: handle_error(logger, Texts.PASSWORD_GATHER_ERROR_MSG, Texts.PASSWORD_GATHER_ERROR_MSG, add_verbosity_msg=state.verbosity == 0) users_password = "" try: cert = get_certificate(username) except Exception: handle_error(logger, Texts.CERT_GATHER_ERROR_MSG, Texts.CERT_GATHER_ERROR_MSG, add_verbosity_msg=state.verbosity == 0) cert = "" except Exception: handle_error(logger, Texts.USER_ADD_ERROR_MSG.format(username=username), Texts.USER_ADD_ERROR_MSG.format(username=username), add_verbosity_msg=state.verbosity == 0) if not delete_user(username): handle_error(user_msg=Texts.REMOVE_USER_ERROR_MSG.format( username=username)) sys.exit(1) if is_user_created(username, 90): click.echo(Texts.USER_CREATION_SUCCESS_MSG.format(username=username)) else: # if during 90 seconds a user hasn't been created - app displays information about it # but don't step processing the command - config file generated here my be useful later # when user has been created click.echo(Texts.USER_NOT_READY_ERROR_MSG.format(username=username)) try: kubeconfig = generate_kubeconfig(username, username, get_kubectl_host(), users_password, cert) except Exception: handle_error(logger, Texts.CONFIG_CREATION_ERROR_MSG, Texts.CONFIG_CREATION_ERROR_MSG, add_verbosity_msg=state.verbosity == 0) exit(1) if list_only: click.echo(Texts.LIST_ONLY_HEADER) click.echo(kubeconfig) else: if not filename: filename = DEFAULT_FILENAME.format(username) try: with open(filename, "w") as file: file.write(kubeconfig) click.echo(Texts.CONFIG_SAVE_SUCCESS_MSG.format(filename=filename)) except Exception: handle_error(logger, Texts.CONFIG_SAVE_FAIL_MSG, Texts.CONFIG_SAVE_FAIL_MSG, add_verbosity_msg=state.verbosity == 0) click.echo(Texts.CONFIG_SAVE_FAIL_INSTRUCTIONS_MSG) click.echo(kubeconfig) sys.exit(1)
def test_execute_system_command(mocker): mocker.patch('subprocess.check_output') output, exit_code, log_output = execute_system_command(['ls']) # noinspection PyUnresolvedReferences assert subprocess.check_output.call_count == 1