def public_url(app, user_or_service=None, path=''): """Return the full, public base URL (including prefix) of the given JupyterHub instance.""" if user_or_service: if app.subdomain_host: host = user_or_service.host else: host = public_host(app) prefix = user_or_service.prefix else: host = public_host(app) prefix = Server.from_url(app.proxy.public_url).base_url if path: return host + ujoin(prefix, path) else: return host + prefix
def start(self): # start tensorboard yield self.start_tb_proxy() public_server = Server.from_url(self.public_url) api_server = Server.from_url(self.api_url) env = os.environ.copy() env['CONFIGPROXY_AUTH_TOKEN'] = self.auth_token cmd = self.command + [ '--ip', public_server.ip, '--port', str(public_server.port), '--api-ip', api_server.ip, '--api-port', str(api_server.port), '--error-target', url_path_join(self.hub.url, 'error'), ] if self.app.subdomain_host: cmd.append('--host-routing') if self.debug: cmd.extend(['--log-level', 'debug']) if self.ssl_key: cmd.extend(['--ssl-key', self.ssl_key]) if self.ssl_cert: cmd.extend(['--ssl-cert', self.ssl_cert]) if self.app.statsd_host: cmd.extend([ '--statsd-host', self.app.statsd_host, '--statsd-port', str(self.app.statsd_port), '--statsd-prefix', self.app.statsd_prefix + '.chp' ]) # Warn if SSL is not used if ' --ssl' not in ' '.join(cmd): self.log.warning( "Running JupyterHub without SSL." " I hope there is SSL termination happening somewhere else..." ) self.log.info("Starting proxy @ %s", public_server.bind_url) self.log.debug("Proxy cmd: %s", cmd) shell = os.name == 'nt' try: self.proxy_process = Popen(cmd, env=env, start_new_session=True, shell=shell) except FileNotFoundError as e: self.log.error( "Failed to find proxy %r\n" "The proxy can be installed with `npm install -g configurable-http-proxy`" % self.command) raise def _check_process(): status = self.proxy_process.poll() if status is not None: e = RuntimeError("Proxy failed to start with exit code %i" % status) # py2-compatible `raise e from None` e.__cause__ = None raise e for server in (public_server, api_server): for i in range(10): _check_process() try: yield server.wait_up(1) except TimeoutError: continue else: break yield server.wait_up(1) _check_process() self.log.debug("Proxy started and appears to be up") pc = PeriodicCallback(self.check_running, 1e3 * self.check_running_interval) pc.start()
def public_host(app): """Return the public *host* (no URL prefix) of the given JupyterHub instance.""" if app.subdomain_host: return app.subdomain_host else: return Server.from_url(app.proxy.public_url).host
def test_ip_port(ip, port, attrs): s = Server(ip=ip, port=port, base_url='/x/') for attr, value in attrs.items(): assert getattr(s, attr) == value
def test_bind_url(bind_url, attrs): s = Server(bind_url=bind_url, base_url='/x/') for attr, value in attrs.items(): assert getattr(s, attr) == value
def start(self): self.server = Server.from_url("http://127.0.0.1:%i" % randint(1025, 65535))
async def check_routes(self, user_dict, service_dict, routes=None): """Check that all users are properly routed on the proxy.""" start = time.perf_counter() # timer starts here when user is created for user in user_dict.values(): if user.authenticator.multiple_instances: await user.authenticator.update_mem(user, "Proxy - check_routes") if not routes: self.log.debug("Fetching routes to check") routes = await self.get_all_routes() # log info-level that we are starting the route-checking # this may help diagnose performance issues, # as we are about self.log.debug("Checking routes") user_routes = { path for path, r in routes.items() if 'user' in r['data'] } futures = [] good_routes = {self.app.hub.routespec} hub = self.hub if self.app.hub.routespec not in routes: futures.append(self.add_hub_route(hub)) else: route = routes[self.app.hub.routespec] if route['target'] != hub.host: self.log.warning("Updating default route %s → %s", route['target'], hub.host) futures.append(self.add_hub_route(hub)) for user in user_dict.values(): for name, spawner in user.spawners.items(): if spawner.ready: spec = spawner.proxy_spec good_routes.add(spec) if spec not in user_routes: self.log.warning("Adding missing route for %s (%s)", spec, spawner.server) futures.append(self.add_user(user, name)) else: route = routes[spec] if route['target'] != spawner.server.host: self.log.warning( "Updating route for %s (%s → %s)", spec, route['target'], spawner.server, ) futures.append(self.add_user(user, name)) elif spawner.pending: # don't consider routes stale if the spawner is in any pending event # wait until after the pending state clears before taking any actions # they could be pending deletion from the proxy! good_routes.add(spawner.proxy_spec) # check service routes service_routes = { r['data']['service']: r for r in routes.values() if 'service' in r['data'] } for service in service_dict.values(): if service.server is None: continue good_routes.add(service.proxy_spec) if service.name not in service_routes: self.log.warning("Adding missing route for %s (%s)", service.name, service.server) futures.append(self.add_service(service)) else: route = service_routes[service.name] if route['target'] != service.server.host: self.log.warning( "Updating route for %s (%s → %s)", route['routespec'], route['target'], service.server.host, ) futures.append(self.add_service(service)) # Now delete the routes that shouldn't be there for routespec in routes: if routespec not in good_routes: route_as_list = list(filter(None, routespec.split('/'))) route_user = None route_servername = None spawn_skip = False try: #self.log.debug("Route as List: {}".format(route_as_list)) if route_as_list[0] == 'integration': if route_as_list[1] == 'hub': if route_as_list[2] == 'api': if route_as_list[3] in [ 'cancel', 'jobstatus', 'token', 'uxnotification' ]: route_user = route_as_list[4] route_servername = route_as_list[5] elif route_as_list[3] == 'users': route_user = route_as_list[4] route_servername = route_as_list[6] elif route_as_list[ 2] == 'spawn-pending' or route_as_list[ 2] == 'spawn': route_user = route_as_list[3] route_servername = route_as_list[4] spawn_skip = True elif route_as_list[1] in ['user', 'spawn']: route_user = route_as_list[2] route_servername = route_as_list[3] try: # skip /tree and /lab , that's the routes we want to deny for dashboards if route_as_list[1] == 'user' and route_as_list[ 4] not in ['lab', 'tree']: spawn_skip = True except: spawn_skip = True else: if route_as_list[0] == 'hub': if route_as_list[1] == 'api': if route_as_list[2] in [ 'cancel', 'jobstatus', 'token', 'uxnotification' ]: route_user = route_as_list[3] route_servername = route_as_list[4] elif route_as_list[2] == 'users': route_user = route_as_list[3] route_servername = route_as_list[5] elif route_as_list[ 1] == 'spawn-pending' or route_as_list[ 1] == 'spawn': route_user = route_as_list[2] route_servername = route_as_list[3] spawn_skip = True elif route_as_list[0] in ['user', 'spawn']: route_user = route_as_list[1] route_servername = route_as_list[2] try: # skip /tree and /lab , that's the routes we want to deny for dashboards if route_as_list[0] == 'user' and route_as_list[ 4] not in ['lab', 'tree']: spawn_skip = True except: spawn_skip = True except: self.log.debug("Err: {}".format(traceback.format_exc())) route_user = None route_servername = None pass delete = False db_spawner = None if route_user: db_user = self.db.query( orm.User).filter(orm.User.name == route_user).first() if db_user: self.db.refresh(db_user) db_spawner = self.db.query(orm.Spawner).filter( orm.Spawner.user_id == db_user.id).filter( orm.Spawner.name == route_servername).first() if db_spawner: self.db.refresh(db_spawner) if (not spawn_skip) and (not db_spawner or not db_spawner.server_id): delete = True else: delete = True else: delete = True if delete: self.log.debug("Deleting stale route %s", routespec) futures.append(self.delete_route(routespec)) else: if db_spawner: db_server = self.db.query(orm.Server).filter( orm.Server.id == db_spawner.server_id).first() if db_server: self.db.refresh(db_server) if route_user and user_dict.get( route_user) and db_server: self.log.debug("Add Server to memory spawner %s", routespec) user_dict.get(route_user).spawners[ db_spawner.name].server = Server( orm_server=db_server) await gen.multi(futures) stop = time.perf_counter() # timer stops here when user is deleted CHECK_ROUTES_DURATION_SECONDS.observe(stop - start) # histogram metric