def get_all_routes(self): routes = {} items = self.client.read(self.prefix, recursive=True) for item in items.children: key = item.key[len(self.prefix):] if key.startswith("/backends"): routespec = unescape(key.split("/")[1], escape_char='-') target = item.value if routespec not in routes: routes[routespec] = {} routes[routespec].update({ 'routespec': routespec, 'target': target }) elif key.startswith("/frontends"): parts = key.split("/") if parts[-1] == 'extra': routespec = unescape(parts[1], escape_char='-') if routespec not in routes: routes[routespec] = {} routes[routespec].update({ 'routespec': routespec, 'data': json.loads(item.value) }) else: continue return {k: v for k, v in routes.items if v['data'].get('jupyterhub-route', False)}
async def get_all_routes(self): """Fetch and return all the routes associated by JupyterHub from the proxy. **Subclasses must define this method** Should return a dictionary of routes, where the keys are routespecs and each value is a dict of the form:: { 'routespec': the route specification ([host]/path/) 'target': the target host URL (proto://host) for this route 'data': the attached data dict for this route (as specified in add_route) } """ all_routes = {} async with self.mutex: for key, value in self.routes_cache["frontends"].items(): escaped_routespec = "".join(key.split("_", 1)[1:]) traefik_routespec = escapism.unescape(escaped_routespec) routespec = self._routespec_from_traefik_path(traefik_routespec) all_routes[routespec] = self._get_route_unsafe(traefik_routespec) return all_routes
def test_escape_default(): for s in test_strings: e = escape(s) assert isinstance(e, text) u = unescape(e) assert isinstance(u, text) assert u == s
def test_safe_escape_char(): escape_char = "-" safe = SAFE.union({escape_char}) with pytest.warns(RuntimeWarning): e = escape(escape_char, safe=safe, escape_char=escape_char) assert e == "{}{:02X}".format(escape_char, ord(escape_char)) u = unescape(e, escape_char=escape_char) assert u == escape_char
def test_escape_custom_char(): for escape_char in r'\-%+_': for s in test_strings: e = escape(s, escape_char=escape_char) assert isinstance(e, text) u = unescape(e, escape_char=escape_char) assert isinstance(u, text) assert u == s
def derive_user(filename): ''' Derive the username given an archive filename. For example, given foo-2ebar.tar.gz, return foo.bar. ''' userpart = filename.replace(args.file_suffix, '') if not args.no_unescape: # default userpart = escapism.unescape(userpart, escape_char='-') return userpart
def test_escape_custom_safe(): safe = 'ABCDEFabcdef0123456789' escape_char = '\\' safe_set = set(safe + '\\') for s in test_strings: e = escape(s, safe=safe, escape_char=escape_char) assert all(c in safe_set for c in e) u = unescape(e, escape_char=escape_char) assert u == s
async def _kv_get_route_parts(self, kv_entry): key = escapism.unescape(kv_entry["KV"]["Key"]) value = kv_entry["KV"]["Value"] # Strip the "/jupyterhub" prefix from the routespec routespec = key.replace(self.kv_jupyterhub_prefix, "") target = base64.b64decode(value.encode()).decode() data = await self._kv_get_data(target) return routespec, target, data
snapshot_id = snap['SnapshotId'] print(colorama.Fore.GREEN + f"{i}/{num_snapshots}. Checking snapshot '{snapshot_id}'...") tags = snap['Tags'] claim_name = get_tag_value( tags, 'kubernetes.io/created-for/pvc/name') # claim-hello stopping_time = get_tag_value( tags, 'jupyter-volume-stopping-time') # 2020-01-10 01:00:52.648872 username = claim_name.split("claim-")[1] try: username_esc = escapism.unescape(username, escape_char='-') except Exception as e: print( colorama.Fore.RED + f"Escaping username {username} failed: {e}. Skipping to next..." ) continue last_stopped = datetime.datetime.strptime(stopping_time, '%Y-%m-%d %H:%M:%S.%f') now = datetime.datetime.now() if (now - last_stopped).days <= max_days_since_last_used: active_usernames.append(username_esc) except BadTagsException as e:
def _send_email_if_expired_and_maybe_delete(self, snapshot): pvc_name = self._get_tags(snapshot, 'kubernetes.io/created-for/pvc/name') if len(pvc_name) == 0: raise Exception( f"Something went wrong with getting username for {snapshot['Tags']}" ) username = pvc_name[0].replace('claim-', '') if username == 'hub-db-dir': print("Database volume found. Do not delete. Skipping...") return # 'escapism' is the custom library used by JupyterHub to escape server names username = escapism.unescape(username, escape_char='-') print(f"Checking snapshot of {username} for expiration...") try: # Send email deletion warning and/or delete as needed if self._get_tags(snapshot, 'do-not-delete'): print("Do-not-delete tag found. Skipping...") return volume_stopped_tag_date = self._get_tags( snapshot, 'jupyter-volume-stopping-time') if len(volume_stopped_tag_date) == 0: print("volume_stopped_tag_date is not present. Skipping...") return volume_stopped_tag_date = volume_stopped_tag_date[0] today = datetime.datetime.utcnow() days_inactive = today - datetime.datetime.strptime( volume_stopped_tag_date, '%Y-%m-%d %H:%M:%S.%f') days_inactive = days_inactive.days print(f"Days inactive ({days_inactive}) for username '{username}'") # Double-check if any volumes exist. If so, then something went wrong with volume delete or snapshot management. if self._volume_still_exists(pvc_name[0], snapshot): print( f"Volume still present for snapshot for {pvc_name}. Skipping..." ) return user_email_address = self._cognito_get_email_address(username) only_email_threshold = self.days_since_activity_thresholds[0:-1] email_and_action_threshold = self.days_since_activity_thresholds[ -1] if days_inactive in only_email_threshold: num_days_left = email_and_action_threshold - days_inactive if num_days_left < 0: num_days_left = 0 email_meta = { 'SENDER': "*****@*****.**", 'RECIPIENT': '<{user_email_address}>'.format( user_email_address=user_email_address), 'SUBJECT': "OpenSARlab Account Notification", 'BODY_HTML': """<html> <head></head> <body> <p>The OpenSARlab account for {username} will be deactivated in {num_days_left} days due to inactivity. Any user data will be permanently deleted.</p> <p>To stop this action, please sign back into your OpenSARlab account and start your server.</p> <p>If you have any questions please don't hesitate to email the <a href="mailto:[email protected]">OpenSARlab Admin</a>.<p> </body> </html>""".format(username=username, num_days_left=num_days_left) } self._send_email(email_meta) elif days_inactive >= email_and_action_threshold: # Disable user self._disable_user(username) # Delete remaining snapshot self._delete_snapshot(snapshot) email_meta = { 'SENDER': "*****@*****.**", 'RECIPIENT': '<{user_email_address}>'.format( user_email_address=user_email_address), 'SUBJECT': "OpenSARlab Account Notification", 'BODY_HTML': """<html> <head></head> <body> <p>The OpenSARlab account for {username} has been deactivated due to {days} days of inactivity. All user data has been permanently deleted and cannot be recovered.</p> <p>If you would like to activate your account or have any questions, please don't hesitate to email the <a href="mailto:[email protected]">OpenSARlab Admin</a>.<p> </body> </html>""".format(username=username, days=email_and_action_threshold) } self._send_email(email_meta) else: print( f"Days inactive ({days_inactive}) do not match any thresholds ({self.days_since_activity_thresholds}). Not performing any action." ) except Exception as e: raise Exception(f"Username {username} had a snapshot issue. {e}")
def unescape_dns_label(input): output = escapism.unescape(input, escape_char='-') return output