Пример #1
0
def _ensure_path_ready(ngrok_path):
    """
    Ensure the binary for :code:`ngrok` at the given path is ready to be started, raise a relevant
    exception if not.

    :param ngrok_path: The path to the :code:`ngrok` binary.
    """
    if not os.path.exists(ngrok_path):
        raise PyngrokNgrokError(
            "ngrok binary was not found. Be sure to call \"ngrok.ensure_ngrok_installed()\" first for "
            "\"ngrok_path\": {}".format(ngrok_path))

    if ngrok_path in _current_processes:
        raise PyngrokNgrokError("ngrok is already running for the \"ngrok_path\": {}".format(ngrok_path))
Пример #2
0
def set_auth_token(pyngrok_config, token):
    """
    Set the ``ngrok`` auth token in the config file, enabling authenticated features (for instance,
    more concurrent tunnels, custom subdomains, etc.).

    :param pyngrok_config: The ``pyngrok`` configuration to use when interacting with the ``ngrok`` binary.
    :type pyngrok_config: PyngrokConfig
    :param token: The auth token to set.
    :type token: str
    """
    start = [pyngrok_config.ngrok_path, "authtoken", token, "--log=stdout"]
    if pyngrok_config.config_path:
        logger.info("Updating authtoken for \"config_path\": {}".format(
            pyngrok_config.config_path))
        start.append("--config={}".format(pyngrok_config.config_path))
    else:
        logger.info(
            "Updating authtoken for default \"config_path\" of \"ngrok_path\": {}"
            .format(pyngrok_config.ngrok_path))

    result = subprocess.check_output(start)

    if "Authtoken saved" not in str(result):
        raise PyngrokNgrokError(
            "An error occurred when saving the auth token: {}".format(result))
Пример #3
0
def _validate_path(ngrok_path):
    """
    Validate the given path exists, is a ``ngrok`` binary, and is ready to be started, otherwise raise a
    relevant exception.

    :param ngrok_path: The path to the ``ngrok`` binary.
    :type ngrok_path: str
    """
    if not os.path.exists(ngrok_path):
        raise PyngrokNgrokError(
            "ngrok binary was not found. Be sure to call \"ngrok.install_ngrok()\" first for "
            "\"ngrok_path\": {}".format(ngrok_path))

    if ngrok_path in _current_processes:
        raise PyngrokNgrokError(
            "ngrok is already running for the \"ngrok_path\": {}".format(
                ngrok_path))
Пример #4
0
def _start_process(ngrok_path, config_path=None):
    """
    Start a `ngrok` process with no tunnels. This will start the `ngrok` web interface, against
    which HTTP requests can be made to create, interact with, and destroy tunnels.

    :param ngrok_path: The path to the `ngrok` binary.
    :type ngrok_path: string
    :param config_path: A config path override.
    :type config_path: string, optional
    :return: The `ngrok` process.
    :rtype: NgrokProcess
    """
    _ensure_path_ready(ngrok_path)

    start = [ngrok_path, "start", "--none", "--log=stdout"]
    if config_path:
        logger.info("Starting ngrok with config file: {}".format(config_path))

        start.append("--config={}".format(config_path))

    process = subprocess.Popen(start,
                               stdout=subprocess.PIPE,
                               universal_newlines=True)
    atexit.register(_terminate_process, process)

    logger.info("ngrok process started: {}".format(process.pid))

    api_url = None
    tunnel_started = False
    errors = []
    timeout = time.time() + 15
    while time.time() < timeout:
        line = process.stdout.readline()
        logger.debug(line)

        if "starting web service" in line:
            api_url = "http://{}".format(line.split("addr=")[1].strip())
        elif "tunnel session started" in line:
            tunnel_started = True
            break
        elif "lvl=error" in line or "lvl=crit" in line or (
                "err=" in line and "err=nil" not in line
                and 'msg="open config file"' not in line):
            print("GOT ERROR", line.strip())
            errors.append(line.strip())
        elif process.poll() is not None:
            break

    if not api_url or not tunnel_started or len(errors) > 0:
        raise PyngrokNgrokError("The ngrok process was unable to start.",
                                errors)

    logger.info("ngrok web service started: {}".format(api_url))

    ngrok_process = NgrokProcess(ngrok_path, config_path, process, api_url)
    _current_processes[ngrok_path] = ngrok_process

    return ngrok_process
Пример #5
0
def set_auth_token(ngrok_path, token, config_path=None):
    """
    Set the `ngrok` auth token in the config file, enabling authenticated features (for instance,
    more concurrent tunnels, custom subdomains, etc.).

    :param ngrok_path: The path to the `ngrok` binary.
    :type ngrok_path: string
    :param token: The auth token to set.
    :type token: string
    :param config_path: A config path override.
    :type config_path: string, optional
    """
    start = [ngrok_path, "authtoken", token, "--log=stdout"]
    if config_path:
        start.append("--config={}".format(config_path))

    result = subprocess.check_output(start)

    if "Authtoken saved" not in str(result):
        raise PyngrokNgrokError("An error occurred when saving the auth token: {}".format(result))
Пример #6
0
def _start_process(ngrok_path, config_path=None, auth_token=None, region=None):
    """
    Start a `ngrok` process with no tunnels. This will start the `ngrok` web interface, against
    which HTTP requests can be made to create, interact with, and destroy tunnels.

    :param ngrok_path: The path to the `ngrok` binary.
    :type ngrok_path: string
    :param config_path: A config path override.
    :type config_path: string, optional
    :param auth_token: An authtoken override.
    :type auth_token: string, optional
    :param region: A region override.
    :type region: string, optional
    :return: The `ngrok` process.
    :rtype: NgrokProcess
    """
    _ensure_path_ready(ngrok_path)

    start = [ngrok_path, "start", "--none", "--log=stdout"]
    if config_path:
        logger.info("Starting ngrok with config file: {}".format(config_path))
        start.append("--config={}".format(config_path))
    if auth_token:
        logger.info("Overriding default auth token")
        start.append("--authtoken={}".format(auth_token))
    if region:
        logger.info("Starting ngrok in region: {}".format(region))
        start.append("--region={}".format(region))

    process = subprocess.Popen(start,
                               stdout=subprocess.PIPE,
                               universal_newlines=True)
    atexit.register(_terminate_process, process)

    logger.info("ngrok process starting: {}".format(process.pid))

    ngrok_process = NgrokProcess(ngrok_path, config_path, process)
    _current_processes[ngrok_path] = ngrok_process

    timeout = time.time() + 15
    while time.time() < timeout:
        line = process.stdout.readline()
        ngrok_process.log_boot_line(line.strip())

        if ngrok_process.healthy():
            logger.info("ngrok process has started: {}".format(
                ngrok_process.api_url))
            break
        elif ngrok_process.startup_error is not None or \
                ngrok_process.proc.poll() is not None:
            break

    if not ngrok_process.healthy():
        # If the process did not come up in a healthy state, clean up the state
        kill_process(ngrok_path)

        if ngrok_process.startup_error is not None:
            raise PyngrokNgrokError("The ngrok process errored on start.",
                                    ngrok_process.startup_logs,
                                    ngrok_process.startup_error)
        else:
            raise PyngrokNgrokError("The ngrok process was unable to start.",
                                    ngrok_process.startup_logs)

    return ngrok_process
Пример #7
0
def _start_process(pyngrok_config):
    """
    Start a ``ngrok`` process with no tunnels. This will start the ``ngrok`` web interface, against
    which HTTP requests can be made to create, interact with, and destroy tunnels.

    :param pyngrok_config: The ``pyngrok`` configuration to use when interacting with the ``ngrok`` binary.
    :type pyngrok_config: PyngrokConfig
    :return: The ``ngrok`` process.
    :rtype: NgrokProcess
    """
    if pyngrok_config.config_path is not None:
        config_path = pyngrok_config.config_path
    else:
        config_path = conf.DEFAULT_NGROK_CONFIG_PATH

    _validate_path(pyngrok_config.ngrok_path)
    _validate_config(config_path)

    start = [pyngrok_config.ngrok_path, "start", "--none", "--log=stdout"]
    if pyngrok_config.config_path:
        logger.info("Starting ngrok with config file: {}".format(
            pyngrok_config.config_path))
        start.append("--config={}".format(pyngrok_config.config_path))
    if pyngrok_config.auth_token:
        logger.info("Overriding default auth token")
        start.append("--authtoken={}".format(pyngrok_config.auth_token))
    if pyngrok_config.region:
        logger.info("Starting ngrok in region: {}".format(
            pyngrok_config.region))
        start.append("--region={}".format(pyngrok_config.region))

    popen_kwargs = {"stdout": subprocess.PIPE, "universal_newlines": True}
    if os.name == "posix":
        popen_kwargs.update(start_new_session=pyngrok_config.start_new_session)
    elif pyngrok_config.start_new_session:
        logger.warning("Ignoring start_new_session=True, which requires POSIX")
    proc = subprocess.Popen(start, **popen_kwargs)
    atexit.register(_terminate_process, proc)

    logger.debug("ngrok process starting with PID: {}".format(proc.pid))

    ngrok_process = NgrokProcess(proc, pyngrok_config)
    _current_processes[pyngrok_config.ngrok_path] = ngrok_process

    timeout = time.time() + pyngrok_config.startup_timeout
    while time.time() < timeout:
        line = proc.stdout.readline()
        ngrok_process._log_startup_line(line)

        if ngrok_process.healthy():
            logger.debug("ngrok process has started with API URL: {}".format(
                ngrok_process.api_url))

            if pyngrok_config.monitor_thread:
                ngrok_process.start_monitor_thread()

            break
        elif ngrok_process.startup_error is not None or \
                ngrok_process.proc.poll() is not None:
            break

    if not ngrok_process.healthy():
        # If the process did not come up in a healthy state, clean up the state
        kill_process(pyngrok_config.ngrok_path)

        if ngrok_process.startup_error is not None:
            raise PyngrokNgrokError(
                "The ngrok process errored on start: {}.".format(
                    ngrok_process.startup_error), ngrok_process.logs,
                ngrok_process.startup_error)
        else:
            raise PyngrokNgrokError("The ngrok process was unable to start.",
                                    ngrok_process.logs)

    return ngrok_process