Example #1
0
    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)}
Example #2
0
    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
Example #3
0
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
Example #4
0
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
Example #5
0
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
Example #6
0
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
Example #7
0
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
Example #8
0
    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:
Example #10
0
    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}")
Example #11
0
def unescape_dns_label(input):
    output = escapism.unescape(input, escape_char='-')
    return output