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