def find_random_available_port() -> int: for port in random.sample(range(START_PORT, END_PORT), k=MAX_NUMBER_OF_TRIES): if check_port_availability(port): tunnel_port = port break else: error_msg = Texts.NO_AVAILABLE_PORT_ERROR_MSG logger.error(error_msg) raise LocalPortOccupiedError(error_msg) return tunnel_port
def test_check_port_availability_occupied(mocker): socket_local = mocker.patch("socket.socket.bind") socket_local.side_effect = OSError(errno.EADDRINUSE, "Address in use") assert not check_port_availability(9000)
def test_check_port_availability_failure(mocker): socket_local = mocker.patch("socket.socket.bind") socket_local.side_effect = OSError() with pytest.raises(RuntimeError): check_port_availability(9000)
def test_check_port_availability_success(mocker): mocker.patch("socket.socket") assert check_port_availability(9000)
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