Beispiel #1
0
def test_cli_remove_int(tljh_dir):
    config.main(["add-item", "foo.bar", "1"])
    config.main(["add-item", "foo.bar", "2"])
    cfg = configurer.load_config()
    assert cfg['foo']['bar'] == [1, 2]
    config.main(["remove-item", "foo.bar", "1"])
    cfg = configurer.load_config()
    assert cfg['foo']['bar'] == [2]
Beispiel #2
0
def test_cli_unset(tljh_dir):
    config.main(["set", "foo.bar", "1"])
    config.main(["set", "foo.bar2", "2"])
    cfg = configurer.load_config()
    assert cfg['foo'] == {'bar': 1, 'bar2': 2}

    config.main(["unset", "foo.bar"])
    cfg = configurer.load_config()

    assert cfg['foo'] == {'bar2': 2}
def test_cli_unset(tljh_dir):
    config.main(["set", "foo.bar", "1"])
    config.main(["set", "foo.bar2", "2"])
    cfg = configurer.load_config()
    assert cfg["foo"] == {"bar": 1, "bar2": 2}

    config.main(["unset", "foo.bar"])
    cfg = configurer.load_config()

    assert cfg["foo"] == {"bar2": 2}
def ensure_traefik_config(state_dir):
    """Render the traefik.toml config file"""
    config = load_config()
    config['traefik_api']['basic_auth'] = compute_basic_auth(
        config['traefik_api']['username'],
        config['traefik_api']['password'],
    )

    with open(os.path.join(os.path.dirname(__file__),
                           "traefik.toml.tpl")) as f:
        template = Template(f.read())
    new_toml = template.render(config)
    https = config["https"]
    letsencrypt = https["letsencrypt"]
    tls = https["tls"]
    # validate https config
    if https["enabled"]:
        if not tls["cert"] and not letsencrypt["email"]:
            raise ValueError(
                "To enable https, you must set tls.cert+key or letsencrypt.email+domains"
            )
        if (letsencrypt["email"] and not letsencrypt["domains"]) or (
                letsencrypt["domains"] and not letsencrypt["email"]):
            raise ValueError(
                "Both email and domains must be set for letsencrypt")
    with open(os.path.join(state_dir, "traefik.toml"), "w") as f:
        os.fchmod(f.fileno(), 0o600)
        f.write(new_toml)

    with open(os.path.join(state_dir, "rules.toml"), "w") as f:
        os.fchmod(f.fileno(), 0o600)

    # ensure acme.json exists and is private
    with open(os.path.join(state_dir, "acme.json"), "a") as f:
        os.fchmod(f.fileno(), 0o600)
def ensure_traefik_config(state_dir):
    """Render the traefik.toml config file"""
    traefik_std_config_file = os.path.join(state_dir, "traefik.toml")
    traefik_extra_config_dir = os.path.join(CONFIG_DIR, "traefik_config.d")
    traefik_dynamic_config_dir = os.path.join(state_dir, "rules")

    config = load_config()
    config['traefik_api']['basic_auth'] = compute_basic_auth(
        config['traefik_api']['username'],
        config['traefik_api']['password'],
    )

    with open(os.path.join(os.path.dirname(__file__),
                           "traefik.toml.tpl")) as f:
        template = Template(f.read())
    std_config = template.render(config)
    https = config["https"]
    letsencrypt = https["letsencrypt"]
    tls = https["tls"]
    # validate https config
    if https["enabled"]:
        if not tls["cert"] and not letsencrypt["email"]:
            raise ValueError(
                "To enable https, you must set tls.cert+key or letsencrypt.email+domains"
            )
        if (letsencrypt["email"] and not letsencrypt["domains"]) or (
                letsencrypt["domains"] and not letsencrypt["email"]):
            raise ValueError(
                "Both email and domains must be set for letsencrypt")

    # Ensure traefik extra static config dir exists and is private
    os.makedirs(traefik_extra_config_dir, mode=0o700, exist_ok=True)

    # Ensure traefik dynamic config dir exists and is private
    os.makedirs(traefik_dynamic_config_dir, mode=0o700, exist_ok=True)

    try:
        # Load standard config file merge it with the extra config files into a dict
        extra_config = load_extra_config(traefik_extra_config_dir)
        new_toml = _merge_dictionaries(toml.loads(std_config), extra_config)
    except FileNotFoundError:
        new_toml = toml.loads(std_config)

    # Dump the dict into a toml-formatted string and write it to file
    with open(traefik_std_config_file, "w") as f:
        os.fchmod(f.fileno(), 0o600)
        toml.dump(new_toml, f)

    with open(os.path.join(traefik_dynamic_config_dir, "rules.toml"),
              "w") as f:
        os.fchmod(f.fileno(), 0o600)

    # ensure acme.json exists and is private
    with open(os.path.join(state_dir, "acme.json"), "a") as f:
        os.fchmod(f.fileno(), 0o600)
def test_load_secrets(tljh_dir):
    """
    Test loading secret files
    """
    with open(os.path.join(tljh_dir, 'state', 'traefik-api.secret'), 'w') as f:
        f.write("traefik-password")

    tljh_config = configurer.load_config()
    assert tljh_config['traefik_api']['password'] == "traefik-password"
    c = apply_mock_config(tljh_config)
    assert c.TraefikTomlProxy.traefik_api_password == "traefik-password"
Beispiel #7
0
def tljh_custom_jupyterhub_config(c, tljh_config_file=CONFIG_FILE):
    # hub
    c.JupyterHub.cleanup_servers = False
    c.JupyterHub.authenticator_class = PAMAuthenticator
    c.JupyterHub.spawner_class = PlasmaSpawner
    c.JupyterHub.template_paths.insert(
        0, os.path.join(os.path.dirname(__file__), "templates")
    )

    # let the spawner infer the user home directory
    c.PlasmaSpawner.base_volume_path = ""

    # fetch the list of allowed UNIX groups from the TLJH config
    tljh_config = load_config(tljh_config_file)
    include_list = tljh_config.get("plasma", {}).get("groups", [])
    include_groups = set(include_list)

    c.JupyterHub.tornado_settings.update({"include_groups": include_groups})

    # add an extra handler to handle user group permissions
    c.JupyterHub.extra_handlers.extend(
        [
            (r"permissions", PermissionsHandler),
            (r"api/permissions", PermissionsAPIHandler),
            (
                r"permissions-static/(.*)",
                CacheControlStaticFilesHandler,
                {"path": os.path.join(os.path.dirname(__file__), "static")},
            ),
        ]
    )

    # spawner
    # update name template for named servers
    c.PlasmaSpawner.name_template = "{prefix}-{username}-{servername}"
    # increase the timeout to be able to pull larger Docker images
    c.PlasmaSpawner.start_timeout = 120
    c.PlasmaSpawner.pull_policy = "Never"
    c.PlasmaSpawner.remove = True
    c.PlasmaSpawner.default_url = "/lab"
    # TODO: change back to jupyterhub-singleuser
    c.PlasmaSpawner.cmd = ["/srv/conda/envs/notebook/bin/jupyterhub-singleuser"]
    # set the default cpu and memory limits
    c.PlasmaSpawner.args = ["--ResourceUseDisplay.track_cpu_percent=True"]

    # prevent PID 1 running in the Docker container to stop when child processes are killed
    # see https://github.com/plasmabio/plasma/issues/191 for more info
    c.PlasmaSpawner.extra_host_config = {'init': True}

    # register Cockpit as a service if active
    if check_service_active("cockpit"):
        c.JupyterHub.services.append(
            {"name": "cockpit", "url": "http://0.0.0.0:9090",},
        )
Beispiel #8
0
def test_load_secrets(tljh_dir):
    """
    Test loading secret files
    """
    with open(os.path.join(tljh_dir, "state", "traefik-api.secret"), "w") as f:
        f.write("traefik-password")

    tljh_config = configurer.load_config()
    assert tljh_config["traefik_api"]["password"] == "traefik-password"
    c = apply_mock_config(tljh_config)
    assert c.TraefikTomlProxy.traefik_api_password == "traefik-password"
Beispiel #9
0
def tljh_custom_jupyterhub_config(c):
    # hub
    c.JupyterHub.hub_ip = public_ips()[0]
    c.JupyterHub.cleanup_servers = False
    c.JupyterHub.spawner_class = Repo2DockerSpawner

    # add extra templates for the service UI
    c.JupyterHub.template_paths.insert(
        0, os.path.join(os.path.dirname(__file__), "templates")
    )

    # spawner
    c.DockerSpawner.cmd = ["jupyterhub-singleuser"]
    c.DockerSpawner.pull_policy = "Never"
    c.DockerSpawner.remove = True

    # fetch limits from the TLJH config
    tljh_config = load_config()
    limits = tljh_config["limits"]
    cpu_limit = limits["cpu"]
    mem_limit = limits["memory"]

    c.JupyterHub.tornado_settings.update(
        {"default_cpu_limit": cpu_limit, "default_mem_limit": mem_limit}
    )

    # register the handlers to manage the user images
    c.JupyterHub.extra_handlers.extend(
        [
            (r"environments", ImagesHandler),
            (r"api/environments", BuildHandler),
            (r"api/environments/([^/]+)/logs", LogsHandler),
            (
                r"environments-static/(.*)",
                CacheControlStaticFilesHandler,
                {"path": os.path.join(os.path.dirname(__file__), "static")},
            ),
        ]
    )
Beispiel #10
0
# Use a high port so users can try this on machines with a JupyterHub already present
c.JupyterHub.hub_port = 15001

c.TraefikTomlProxy.should_start = False

dynamic_conf_file_path = os.path.join(INSTALL_PREFIX, 'state', 'rules.toml')
c.TraefikTomlProxy.toml_dynamic_config_file = dynamic_conf_file_path
c.JupyterHub.proxy_class = TraefikTomlProxy

c.SystemdSpawner.extra_paths = [os.path.join(USER_ENV_PREFIX, 'bin')]
c.SystemdSpawner.default_shell = '/bin/bash'
# Drop the '-singleuser' suffix present in the default template
c.SystemdSpawner.unit_name_template = 'jupyter-{USERNAME}'

tljh_config = configurer.load_config()
configurer.apply_config(tljh_config, c)

# Let TLJH hooks modify `c` if they want

# Set up plugin infrastructure
pm = pluggy.PluginManager('tljh')
pm.add_hookspecs(hooks)
pm.load_setuptools_entrypoints('tljh')
# Call our custom configuration plugin
pm.hook.tljh_custom_jupyterhub_config(c=c)

# Load arbitrary .py config files if they exist.
# This is our escape hatch
extra_configs = sorted(
    glob(os.path.join(CONFIG_DIR, 'jupyterhub_config.d', '*.py')))
        """
        # FIXME: Move this elsewhere? Into the Authenticator?
        system_username = generate_system_username("jupyter-" + self.user.name)

        # FIXME: This is a hack. Allow setting username directly instead
        self.username_template = system_username
        user.ensure_user(system_username)
        user.ensure_user_group(system_username, "jupyterhub-users")
        if self.user.admin:
            user.ensure_user_group(system_username, "jupyterhub-admins")
        else:
            user.remove_user_group(system_username, "jupyterhub-admins")
        if self.user_groups:
            for group, users in self.user_groups.items():
                if self.user.name in users:
                    user.ensure_user_group(system_username, group)
        return super().start()


cfg = configurer.load_config()
# Use the jupyterhub-configurator mixin only if configurator is enabled
# otherwise, any bugs in the configurator backend will stop new user spawns!
if cfg["services"]["configurator"]["enabled"]:
    # Dynamically create the Spawner class using `type`(https://docs.python.org/3/library/functions.html?#type),
    # based on whether or not it should inherit from ConfiguratorSpawnerMixin
    UserCreatingSpawner = type(
        "UserCreatingSpawner", (ConfiguratorSpawnerMixin, CustomSpawner), {}
    )
else:
    UserCreatingSpawner = type("UserCreatingSpawner", (CustomSpawner,), {})
Beispiel #12
0
def test_cli_add_float(tljh_dir):
    config.main(["add-item", "foo.bar", "1.25"])
    cfg = configurer.load_config()
    assert cfg['foo']['bar'] == [1.25]
Beispiel #13
0
def test_cli_set_int(tljh_dir):
    config.main(["set", "https.port", "123"])
    cfg = configurer.load_config()
    assert cfg['https']['port'] == 123
Beispiel #14
0
def test_cli_set_bool(tljh_dir, arg, value):
    config.main(["set", "https.enabled", arg])
    cfg = configurer.load_config()
    assert cfg['https']['enabled'] == value
"""
This file is only used for local development
and overrides some of the default values from the plugin.
"""

import getpass

from jupyterhub.auth import DummyAuthenticator
from tljh.configurer import apply_config, load_config
from tljh_repo2docker import tljh_custom_jupyterhub_config

c.JupyterHub.services = []

# set default limits in the TLJH config in memory
tljh_config = load_config()
tljh_config["limits"]["memory"] = "2G"
tljh_config["limits"]["cpu"] = 2
apply_config(tljh_config, c)

tljh_custom_jupyterhub_config(c)

c.JupyterHub.authenticator_class = DummyAuthenticator

user = getpass.getuser()
c.Authenticator.admin_users = {user, "alice"}