예제 #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]
예제 #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}
예제 #4
0
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)
예제 #5
0
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"
예제 #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",},
        )
예제 #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"
예제 #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")},
            ),
        ]
    )
예제 #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,), {})
예제 #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]
예제 #13
0
def test_cli_set_int(tljh_dir):
    config.main(["set", "https.port", "123"])
    cfg = configurer.load_config()
    assert cfg['https']['port'] == 123
예제 #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
예제 #15
0
"""
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"}