Exemplo n.º 1
0
def get_users_samba_password(username: str) -> str:
    """
    Returns samba password of a user with a given username

    :param username: name of a user
    :return: password of a user,
    In case of any problems during gathering of a password it raises KubectlIntError
    If password doesnt exist - it raises ValueError.
    """
    error_message = Texts.GATHERING_PASSWORD_ERROR_MSG
    password = None
    try:
        api = get_k8s_api()

        secret = api.read_namespaced_secret("password", username)

        password = str(base64.b64decode(secret.data["password"]),
                       encoding="utf-8")
    except ApiException as exe:
        if exe.status == HTTPStatus.NOT_FOUND:
            password = None
        else:
            logger.exception(error_message)
            raise KubernetesError(error_message) from exe
    except Exception as exe:
        logger.exception(error_message)
        raise KubernetesError(error_message) from exe

    if password is None:
        raise ValueError(Texts.LACK_OF_PASSWORD_ERROR_MSG)

    return str.strip(password)
Exemplo n.º 2
0
def delete_namespace(namespace: str, propagate: bool = False):
    """
    Removes a namespace with the given name

    :param namespace: namespace to be deleted
    :param propagate: If True - all objects in a namespace will be deleted
    In case of any problems (i.e. lack of privileges) it throws an exception
    """
    try:
        api = get_k8s_api()
        propagation_policy = "Orphan"
        if propagate:
            propagation_policy = "Foreground"
        body = V1DeleteOptions(propagation_policy=propagation_policy)

        response = api.delete_namespace(namespace, body)

        if response.status != "{'phase': 'Terminating'}":
            error_description = Texts.NAMESPACE_DELETE_ERROR_MSG.format(
                namespace=namespace)
            logger.exception(error_description)
            raise KubernetesError(error_description)

    except Exception:
        error_description = Texts.NAMESPACE_DELETE_ERROR_MSG.format(
            namespace=namespace)
        logger.exception(error_description)
        raise KubernetesError(error_description)
Exemplo n.º 3
0
def check_users_presence(username: str) -> UserState:
    """
    Checks whether a user with a given name exists. It searches also for a namespace
    with a name equal to the given username

    :param username: username
    :return: returns a current state of user - as an item for UserState enum
    In case of problems during gathering user's data - it raises an exception.
    """
    namespace = find_namespace(username)

    if namespace != NamespaceStatus.NOT_EXISTS:
        logger.debug("Namespace {} already exists.".format(username))
        return UserState(namespace.value)

    try:
        user_data = User.get(username)

        if user_data and user_data.name == username:
            return UserState.ACTIVE
        else:
            return UserState.NOT_EXISTS

    except Exception as exe:
        error_message = Texts.USER_PRESENCE_CHECK_ERROR_MSG
        logger.error(error_message)
        raise KubernetesError(error_message) from exe
Exemplo n.º 4
0
def get_pod_events(namespace: str, name: str = None) -> List[client.V1Event]:
    try:
        api = get_k8s_api()

        events = []

        try:
            if name:
                event_list: client.V1EventList = api.list_namespaced_event(
                    namespace=namespace,
                    field_selector=f"involvedObject.name={name}")
            else:
                event_list: client.V1EventList = api.list_namespaced_event(
                    namespace=namespace)
            events = event_list.items
        except ApiException as ex:
            if ex.status != HTTPStatus.NOT_FOUND:
                logger.exception('Exception when getting pod events')
                raise

        return events
    except Exception as exe:
        error_message = Texts.GATHERING_EVENTS_ERROR_MSG
        logger.exception(error_message)
        raise KubernetesError(error_message) from exe
Exemplo n.º 5
0
def get_users_token(namespace: str) -> str:
    """
    Gets a default token of a user from a given namespace

    :param namespace: namespace of a user
    :return: encoded token of a user - if it doesn't exist or errors occurred during gathering
    the token - function returns an empty string
    """
    ret_token = ""
    try:
        api = get_k8s_api()
        tokens_list = api.list_namespaced_secret(namespace)

        if tokens_list:
            for token in tokens_list.items:
                if "default-token" in token.metadata.name:
                    ret_token = str(base64.b64decode(token.data.get("token")),
                                    encoding="utf-8")
                    break
            else:
                raise ValueError(Texts.LACK_OF_DEFAULT_TOKEN_ERROR_MSG)
        else:
            raise ValueError(Texts.EMPTY_LIST_OF_TOKENS_ERROR_MSG)

    except Exception as exe:
        error_message = Texts.GATHERING_USERS_TOKEN_ERROR_MSG
        logger.exception(error_message)
        raise KubernetesError(error_message) from exe

    return ret_token
Exemplo n.º 6
0
def get_certificate(namespace: str) -> str:
    """
    Gets a certificate of a user from a given namespace
    :param namespace: namespace of a user
    :return: certificate - if it doesn't exist or errors occurred during gathering
    the certificate - function returns an empty string
    """
    ret_cert = ""
    try:
        api = get_k8s_api()
        secrets_list = api.list_namespaced_secret(namespace)

        if secrets_list:
            for secret in secrets_list.items:
                if "default-token" in secret.metadata.name:
                    ret_cert = str(base64.b64decode(secret.data.get("ca.crt")),
                                   encoding="utf-8")
                    break
            else:
                raise ValueError(Texts.LACK_OF_DEFAULT_TOKEN_ERROR_MSG)
        else:
            raise ValueError(Texts.EMPTY_LIST_OF_TOKENS_ERROR_MSG)

    except Exception as exe:
        error_message = Texts.GATHERING_USER_CERTIFICATE_ERROR_MSG
        logger.exception(error_message)
        raise KubernetesError(error_message) from exe

    return ret_cert
Exemplo n.º 7
0
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)
Exemplo n.º 8
0
def test_version_with_kubernetes_exception(mocker):
    config_map_mock = mocker.patch('util.config.NAUTAConfigMap.__init__')
    config_map_mock.side_effect = KubernetesError("")
    runner = CliRunner()
    result = runner.invoke(version.version, [])

    assert_version_table_rows(result.output, on_cmd_fail=True)

    assert Texts.KUBECTL_INT_ERROR_MSG + " " + VERBOSE_RERUN_MSG in result.output
Exemplo n.º 9
0
def get_config_map_data(name: str, namespace: str, request_timeout: int = None) -> Dict[str, str]:
    """
    Returns a dictionary taken from data section of a config_map with a given name
    located in the given namespace.
    :param name: name of a config map
    :param namespace: name of a namespace
    :param request_timeout: optional timeout for k8s request. Defaults inside k8s_api to 120 sec.
    :return: dictonary created based on data section of a config map. In case
    of any problems it raises an Exception
    """
    try:
        api = get_k8s_api()
        ret_dict = api.read_namespaced_config_map(name, namespace, _request_timeout=request_timeout).data
    except Exception:
        error_description = Texts.CONFIG_MAP_ACCESS_ERROR_MSG.format(name=name)
        logger.exception(error_description)
        raise KubernetesError(error_description)

    return ret_dict
Exemplo n.º 10
0
def patch_config_map_data(key: str, value: str, name: str, namespace: str):
    """
    Function patches configmap with a given name and located in a given namespace.
    :param key: key identifying valueto be patched
    :param value: value
    :param name: name of a configmap
    :param namespace: namespace where configmap is located
    :return: raises an exception in case of any errors
    """
    api = get_k8s_api()

    try:
        api.patch_namespaced_config_map(name=name,
                                        namespace=namespace,
                                        body={"data": {key: value}})

    except ApiException as exe:
        error_message = Texts.PATCHING_CM_ERROR_MSG
        logger.exception(error_message)
        raise KubernetesError(error_message) from exe
Exemplo n.º 11
0
def find_namespace(namespace: str) -> NamespaceStatus:
    """
    Checks whether a namespace with a given name exists

    :param namespace: name of a namespace to be found
    :return: value from the NamespaceStatus enum
    """
    api = get_k8s_api()
    try:
        namespace_def = api.read_namespace(namespace)

        if namespace_def and namespace_def.metadata and namespace_def.metadata.name == namespace:
            return NamespaceStatus(namespace_def.status.phase)
    except ApiException as e:
        if e.status == 404:
            return NamespaceStatus.NOT_EXISTS
        else:
            error_message = Texts.OTHER_FIND_NAMESPACE_ERROR
            logger.exception(error_message)
            raise KubernetesError(error_message)

    return NamespaceStatus.NOT_EXISTS
Exemplo n.º 12
0
def start_port_forwarding(
        k8s_app_name: NAUTAAppNames,
        port: int = None,
        app_name: str = None,
        number_of_retries: int = 0,
        namespace: str = None) -> Tuple[subprocess.Popen, int, int]:
    """
    Creates a proxy responsible for forwarding requests to and from a
    kubernetes' local docker proxy. In case of any errors during creating the
    process - throws a RuntimeError exception with a short description of
    a cause of a problem.
    When proxy created by this function is no longer needed - it should
    be closed by calling kill() function on a process returned by this
    function.

    :param k8s_app_name: name of kubernetes application for tunnel creation
                         value taken from NAUTAAppNames enum
    :param port: if given - the system will try to use it as a local port. Random port will be used
     if that port is not available
    :return:
        instance of a process with proxy, tunneled port and container port
    """
    logger.debug("Start port forwarding")

    try:
        service_node_port = None
        service_container_port = None

        app_services = get_app_services(nauta_app_name=k8s_app_name,
                                        namespace=namespace,
                                        app_name=app_name)

        if app_services:
            service_node_port = app_services[0].spec.ports[0].node_port
            if service_node_port:
                logger.debug('Service node port pod has been found: {}'.format(
                    service_node_port))

            service_container_port = app_services[0].spec.ports[0].port
            if service_container_port:
                logger.debug(
                    'Service container port has been found: {}'.format(
                        service_container_port))
            service_name = app_services[0].metadata.name
            namespace = app_services[0].metadata.namespace

        if not service_node_port and not service_container_port:
            logger.error(f'Cannot find open ports for {k8s_app_name} app')
            raise KubernetesError(Texts.PROXY_CREATION_MISSING_PORT_ERROR_MSG)

        if port and check_port_availability(port):
            tunnel_port = port
        else:
            tunnel_port = find_random_available_port()

        port_forward_command = [
            'kubectl', 'port-forward', f'--namespace={namespace}',
            f'service/{service_name}',
            f'{tunnel_port}:{service_container_port}', '-v=4'
        ]

        logger.debug(port_forward_command)

        process = None
        if number_of_retries:
            for i in range(number_of_retries - 1):
                try:
                    process = system.execute_subprocess_command(
                        port_forward_command)
                except Exception:
                    logger.exception(
                        "Error during setting up proxy - retrying.")
                else:
                    break
                time.sleep(5)

        if not process:
            process = system.execute_subprocess_command(port_forward_command)

    except KubernetesError as exe:
        raise RuntimeError(exe)
    except LocalPortOccupiedError as exe:
        raise exe
    except Exception:
        raise RuntimeError(Texts.PROXY_CREATION_OTHER_ERROR_MSG)

    logger.info("Port forwarding - proxy set up")
    return process, tunnel_port, service_container_port