mirror of
https://github.com/jupyterhub/the-littlest-jupyterhub.git
synced 2025-12-18 21:54:05 +08:00
Merge pull request #163 from minrk/config-dir
put config in `$tljh/config` directory
This commit is contained in:
@@ -59,8 +59,8 @@ The easiest & safest way to develop & test TLJH is with `Docker <https://www.doc
|
|||||||
you can test it by running ``python3 -m tljh.installer``.
|
you can test it by running ``python3 -m tljh.installer``.
|
||||||
|
|
||||||
* If you changed ``tljh/jupyterhub_config.py``, ``tljh/configurer.py``,
|
* If you changed ``tljh/jupyterhub_config.py``, ``tljh/configurer.py``,
|
||||||
``/opt/tljh/config.yaml`` or any of their dependencies, you only need to
|
``/opt/tljh/config/`` or any of their dependencies, you only need to
|
||||||
restart jupyterhub for them to take effect. ``systemctl restart jupyterhub``
|
restart jupyterhub for them to take effect. ``tljh-config reload hub``
|
||||||
should do that.
|
should do that.
|
||||||
|
|
||||||
:ref:`troubleshooting/logs` has information on looking at various logs in the container
|
:ref:`troubleshooting/logs` has information on looking at various logs in the container
|
||||||
|
|||||||
2
docs/howto/env/notebook-interfaces.rst
vendored
2
docs/howto/env/notebook-interfaces.rst
vendored
@@ -48,7 +48,7 @@ You can change the default interface users get when they log in by modifying
|
|||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
sudo tljh-config reload
|
sudo tljh-config reload hub
|
||||||
|
|
||||||
If this causes problems, check the :ref:`troubleshoot_logs_jupyterhub` for clues
|
If this causes problems, check the :ref:`troubleshoot_logs_jupyterhub` for clues
|
||||||
on what went wrong.
|
on what went wrong.
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ directory that lets you load multiple ``jupyterhub_config.py`` snippets for
|
|||||||
your configuration. You need to create the directory when you use it for
|
your configuration. You need to create the directory when you use it for
|
||||||
the first time.
|
the first time.
|
||||||
|
|
||||||
Any files in ``/opt/tljh/jupyterhub_config.d`` that end in ``.py`` will be
|
Any files in ``/opt/tljh/config/jupyterhub_config.d`` that end in ``.py`` will be
|
||||||
loaded in alphabetical order as python files to provide configuration for
|
loaded in alphabetical order as python files to provide configuration for
|
||||||
JupyterHub. Any config that can go in a regular ``jupyterhub_config.py``
|
JupyterHub. Any config that can go in a regular ``jupyterhub_config.py``
|
||||||
file is valid in these files. They will be loaded *after* any of the config
|
file is valid in these files. They will be loaded *after* any of the config
|
||||||
options specified with ``tljh-config`` are loaded.
|
options specified with ``tljh-config`` are loaded.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Test simplest plugin
|
|||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from tljh.config import CONFIG_FILE, USER_ENV_PREFIX
|
||||||
|
|
||||||
yaml = YAML(typ='rt')
|
yaml = YAML(typ='rt')
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ def test_pip_packages():
|
|||||||
Test extra user pip packages are installed
|
Test extra user pip packages are installed
|
||||||
"""
|
"""
|
||||||
subprocess.check_call([
|
subprocess.check_call([
|
||||||
'/opt/tljh/user/bin/python3',
|
f'{USER_ENV_PREFIX}/bin/python3',
|
||||||
'-c',
|
'-c',
|
||||||
'import django'
|
'import django'
|
||||||
])
|
])
|
||||||
@@ -31,7 +32,7 @@ def test_conda_packages():
|
|||||||
Test extra user conda packages are installed
|
Test extra user conda packages are installed
|
||||||
"""
|
"""
|
||||||
subprocess.check_call([
|
subprocess.check_call([
|
||||||
'/opt/tljh/user/bin/python3',
|
f'{USER_ENV_PREFIX}/bin/python3',
|
||||||
'-c',
|
'-c',
|
||||||
'import hypothesis'
|
'import hypothesis'
|
||||||
])
|
])
|
||||||
@@ -41,7 +42,7 @@ def test_config_hook():
|
|||||||
"""
|
"""
|
||||||
Check config changes are present
|
Check config changes are present
|
||||||
"""
|
"""
|
||||||
with open('/opt/tljh/config.yaml') as f:
|
with open(CONFIG_FILE) as f:
|
||||||
data = yaml.load(f)
|
data = yaml.load(f)
|
||||||
|
|
||||||
assert data['simplest_plugin']['present']
|
assert data['simplest_plugin']['present']
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ def tljh_dir(tmpdir):
|
|||||||
reload(mod)
|
reload(mod)
|
||||||
assert tljh.config.INSTALL_PREFIX == tljh_dir
|
assert tljh.config.INSTALL_PREFIX == tljh_dir
|
||||||
os.makedirs(tljh.config.STATE_DIR)
|
os.makedirs(tljh.config.STATE_DIR)
|
||||||
|
os.makedirs(tljh.config.CONFIG_DIR)
|
||||||
yield tljh_dir
|
yield tljh_dir
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
"""
|
"""
|
||||||
Unit test functions in installer.py
|
Unit test functions in installer.py
|
||||||
"""
|
"""
|
||||||
from tljh import installer
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from tljh import installer
|
||||||
|
|
||||||
|
|
||||||
def test_ensure_node():
|
def test_ensure_node():
|
||||||
installer.ensure_node()
|
installer.ensure_node()
|
||||||
assert os.path.exists('/usr/bin/node')
|
assert os.path.exists('/usr/bin/node')
|
||||||
|
|
||||||
|
|
||||||
|
def test_ensure_config_yaml(tljh_dir):
|
||||||
|
pm = installer.setup_plugins()
|
||||||
|
installer.ensure_config_yaml(pm)
|
||||||
|
assert os.path.exists(installer.CONFIG_FILE)
|
||||||
|
assert os.path.isdir(installer.CONFIG_DIR)
|
||||||
|
assert os.path.isdir(os.path.join(installer.CONFIG_DIR, 'jupyterhub_config.d'))
|
||||||
|
# verify that old config doesn't exist
|
||||||
|
assert not os.path.exists(os.path.join(tljh_dir, 'config.yaml'))
|
||||||
|
|||||||
57
tests/test_migrator.py
Normal file
57
tests/test_migrator.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
"""
|
||||||
|
Unit test functions in installer.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
from tljh import migrator, config
|
||||||
|
|
||||||
|
|
||||||
|
def test_migrate_config(tljh_dir):
|
||||||
|
CONFIG_FILE = config.CONFIG_FILE
|
||||||
|
CONFIG_DIR = config.CONFIG_DIR
|
||||||
|
OLD_CONFIG_FILE = os.path.join(tljh_dir, "config.yaml")
|
||||||
|
OLD_CONFIG_D = os.path.join(tljh_dir, "jupyterhub_config.d")
|
||||||
|
CONFIG_D = os.path.join(config.CONFIG_DIR, "jupyterhub_config.d")
|
||||||
|
old_config_py = os.path.join(OLD_CONFIG_D, "upgrade.py")
|
||||||
|
new_config_py = os.path.join(CONFIG_D, "upgrade.py")
|
||||||
|
|
||||||
|
# initial condition: nothing exists
|
||||||
|
assert not os.path.exists(CONFIG_FILE)
|
||||||
|
assert not os.path.exists(OLD_CONFIG_FILE)
|
||||||
|
assert os.path.isdir(CONFIG_DIR)
|
||||||
|
|
||||||
|
# run migration with old config and no new config
|
||||||
|
upgraded_config = "old: config\n"
|
||||||
|
with open(OLD_CONFIG_FILE, "w") as f:
|
||||||
|
f.write(upgraded_config)
|
||||||
|
os.makedirs(OLD_CONFIG_D, exist_ok=True)
|
||||||
|
with open(old_config_py, "w") as f:
|
||||||
|
f.write("c.JupyterHub.log_level = 10")
|
||||||
|
|
||||||
|
migrator.migrate_config_files()
|
||||||
|
assert os.path.exists(CONFIG_FILE)
|
||||||
|
assert not os.path.exists(OLD_CONFIG_FILE)
|
||||||
|
with open(CONFIG_FILE) as f:
|
||||||
|
assert f.read() == upgraded_config
|
||||||
|
assert os.path.exists(new_config_py)
|
||||||
|
assert not os.path.exists(OLD_CONFIG_D)
|
||||||
|
|
||||||
|
# run again, this time with both old and new config
|
||||||
|
duplicate_config = "dupe: config\n"
|
||||||
|
with open(OLD_CONFIG_FILE, "w") as f:
|
||||||
|
f.write(duplicate_config)
|
||||||
|
migrator.migrate_config_files()
|
||||||
|
assert os.path.exists(CONFIG_FILE)
|
||||||
|
assert not os.path.exists(OLD_CONFIG_FILE)
|
||||||
|
# didn't clobber config:
|
||||||
|
with open(CONFIG_FILE) as f:
|
||||||
|
assert f.read() == upgraded_config
|
||||||
|
|
||||||
|
# preserved old config
|
||||||
|
backup_config = CONFIG_FILE + f".old.{date.today().isoformat()}"
|
||||||
|
assert os.path.exists(backup_config)
|
||||||
|
with open(backup_config) as f:
|
||||||
|
assert f.read() == duplicate_config
|
||||||
|
|
||||||
|
# migrate jupyterhub_con
|
||||||
@@ -27,7 +27,8 @@ INSTALL_PREFIX = os.environ.get('TLJH_INSTALL_PREFIX', '/opt/tljh')
|
|||||||
HUB_ENV_PREFIX = os.path.join(INSTALL_PREFIX, 'hub')
|
HUB_ENV_PREFIX = os.path.join(INSTALL_PREFIX, 'hub')
|
||||||
USER_ENV_PREFIX = os.path.join(INSTALL_PREFIX, 'user')
|
USER_ENV_PREFIX = os.path.join(INSTALL_PREFIX, 'user')
|
||||||
STATE_DIR = os.path.join(INSTALL_PREFIX, 'state')
|
STATE_DIR = os.path.join(INSTALL_PREFIX, 'state')
|
||||||
CONFIG_FILE = os.path.join(INSTALL_PREFIX, 'config.yaml')
|
CONFIG_DIR = os.path.join(INSTALL_PREFIX, 'config')
|
||||||
|
CONFIG_FILE = os.path.join(CONFIG_DIR, 'config.yaml')
|
||||||
|
|
||||||
|
|
||||||
def set_item_in_config(config, property_path, value):
|
def set_item_in_config(config, property_path, value):
|
||||||
@@ -221,6 +222,9 @@ def main(argv=None):
|
|||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
|
|
||||||
|
from .log import init_logging
|
||||||
|
init_logging()
|
||||||
|
|
||||||
argparser = argparse.ArgumentParser()
|
argparser = argparse.ArgumentParser()
|
||||||
argparser.add_argument(
|
argparser.add_argument(
|
||||||
'--config-path',
|
'--config-path',
|
||||||
|
|||||||
@@ -1,38 +1,44 @@
|
|||||||
|
"""Installation logic for TLJH"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import itertools
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
import subprocess
|
import subprocess
|
||||||
import itertools
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
|
||||||
from urllib.error import HTTPError
|
from urllib.error import HTTPError
|
||||||
from urllib.request import urlopen, URLError
|
from urllib.request import urlopen, URLError
|
||||||
import pluggy
|
|
||||||
|
|
||||||
|
import pluggy
|
||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
|
|
||||||
from tljh import conda, systemd, traefik, user, apt, hooks
|
from tljh import (
|
||||||
from tljh.config import INSTALL_PREFIX, HUB_ENV_PREFIX, USER_ENV_PREFIX, STATE_DIR, CONFIG_FILE
|
apt,
|
||||||
|
conda,
|
||||||
|
hooks,
|
||||||
|
migrator,
|
||||||
|
systemd,
|
||||||
|
traefik,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
||||||
|
from tljh.config import (
|
||||||
|
CONFIG_DIR,
|
||||||
|
CONFIG_FILE,
|
||||||
|
HUB_ENV_PREFIX,
|
||||||
|
INSTALL_PREFIX,
|
||||||
|
STATE_DIR,
|
||||||
|
USER_ENV_PREFIX,
|
||||||
|
)
|
||||||
|
|
||||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
rt_yaml = YAML()
|
rt_yaml = YAML()
|
||||||
|
|
||||||
# Set up logging to print to a file and to stderr
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
os.makedirs(INSTALL_PREFIX, exist_ok=True)
|
|
||||||
file_logger = logging.FileHandler(os.path.join(INSTALL_PREFIX, 'installer.log'))
|
|
||||||
file_logger.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
|
|
||||||
logger.addHandler(file_logger)
|
|
||||||
|
|
||||||
stderr_logger = logging.StreamHandler()
|
|
||||||
stderr_logger.setFormatter(logging.Formatter('%(message)s'))
|
|
||||||
logger.addHandler(stderr_logger)
|
|
||||||
logger.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_node():
|
def ensure_node():
|
||||||
"""
|
"""
|
||||||
Ensure nodejs from nodesource is installed
|
Ensure nodejs from nodesource is installed
|
||||||
@@ -254,7 +260,7 @@ def ensure_admins(admins):
|
|||||||
if not admins:
|
if not admins:
|
||||||
return
|
return
|
||||||
logger.info("Setting up admin users")
|
logger.info("Setting up admin users")
|
||||||
config_path = os.path.join(INSTALL_PREFIX, 'config.yaml')
|
config_path = CONFIG_FILE
|
||||||
if os.path.exists(config_path):
|
if os.path.exists(config_path):
|
||||||
with open(config_path, 'r') as f:
|
with open(config_path, 'r') as f:
|
||||||
config = rt_yaml.load(f)
|
config = rt_yaml.load(f)
|
||||||
@@ -324,7 +330,7 @@ def ensure_symlinks(prefix):
|
|||||||
os.symlink(tljh_config_src, tljh_config_dest)
|
os.symlink(tljh_config_src, tljh_config_dest)
|
||||||
|
|
||||||
|
|
||||||
def setup_plugins(plugins):
|
def setup_plugins(plugins=None):
|
||||||
"""
|
"""
|
||||||
Install plugins & setup a pluginmanager
|
Install plugins & setup a pluginmanager
|
||||||
"""
|
"""
|
||||||
@@ -339,6 +345,7 @@ def setup_plugins(plugins):
|
|||||||
|
|
||||||
return pm
|
return pm
|
||||||
|
|
||||||
|
|
||||||
def run_plugin_actions(plugin_manager, plugins):
|
def run_plugin_actions(plugin_manager, plugins):
|
||||||
"""
|
"""
|
||||||
Run installer hooks defined in plugins
|
Run installer hooks defined in plugins
|
||||||
@@ -373,6 +380,12 @@ def ensure_config_yaml(plugin_manager):
|
|||||||
"""
|
"""
|
||||||
Ensure we have a config.yaml present
|
Ensure we have a config.yaml present
|
||||||
"""
|
"""
|
||||||
|
# ensure config dir exists and is private
|
||||||
|
for path in [CONFIG_DIR, os.path.join(CONFIG_DIR, 'jupyterhub_config.d')]:
|
||||||
|
os.makedirs(path, mode=0o700, exist_ok=True)
|
||||||
|
|
||||||
|
migrator.migrate_config_files()
|
||||||
|
|
||||||
if os.path.exists(CONFIG_FILE):
|
if os.path.exists(CONFIG_FILE):
|
||||||
with open(CONFIG_FILE, 'r') as f:
|
with open(CONFIG_FILE, 'r') as f:
|
||||||
config = rt_yaml.load(f)
|
config = rt_yaml.load(f)
|
||||||
@@ -387,6 +400,9 @@ def ensure_config_yaml(plugin_manager):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
from .log import init_logging
|
||||||
|
init_logging()
|
||||||
|
|
||||||
argparser = argparse.ArgumentParser()
|
argparser = argparse.ArgumentParser()
|
||||||
argparser.add_argument(
|
argparser.add_argument(
|
||||||
'--admin',
|
'--admin',
|
||||||
@@ -407,8 +423,8 @@ def main():
|
|||||||
|
|
||||||
pm = setup_plugins(args.plugin)
|
pm = setup_plugins(args.plugin)
|
||||||
|
|
||||||
|
ensure_config_yaml(pm)
|
||||||
ensure_admins(args.admin)
|
ensure_admins(args.admin)
|
||||||
|
|
||||||
ensure_usergroups()
|
ensure_usergroups()
|
||||||
ensure_user_environment(args.user_requirements_txt_url)
|
ensure_user_environment(args.user_requirements_txt_url)
|
||||||
|
|
||||||
@@ -416,7 +432,6 @@ def main():
|
|||||||
ensure_node()
|
ensure_node()
|
||||||
ensure_jupyterhub_package(HUB_ENV_PREFIX)
|
ensure_jupyterhub_package(HUB_ENV_PREFIX)
|
||||||
ensure_chp_package(HUB_ENV_PREFIX)
|
ensure_chp_package(HUB_ENV_PREFIX)
|
||||||
ensure_config_yaml(pm)
|
|
||||||
ensure_jupyterlab_extensions()
|
ensure_jupyterlab_extensions()
|
||||||
ensure_jupyterhub_service(HUB_ENV_PREFIX)
|
ensure_jupyterhub_service(HUB_ENV_PREFIX)
|
||||||
ensure_jupyterhub_running()
|
ensure_jupyterhub_running()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from glob import glob
|
|||||||
|
|
||||||
from systemdspawner import SystemdSpawner
|
from systemdspawner import SystemdSpawner
|
||||||
from tljh import user, configurer
|
from tljh import user, configurer
|
||||||
from tljh.config import INSTALL_PREFIX, USER_ENV_PREFIX
|
from tljh.config import INSTALL_PREFIX, USER_ENV_PREFIX, CONFIG_DIR
|
||||||
|
|
||||||
|
|
||||||
class CustomSpawner(SystemdSpawner):
|
class CustomSpawner(SystemdSpawner):
|
||||||
@@ -43,7 +43,7 @@ c.SystemdSpawner.default_shell = '/bin/bash'
|
|||||||
# Drop the '-singleuser' suffix present in the default template
|
# Drop the '-singleuser' suffix present in the default template
|
||||||
c.SystemdSpawner.unit_name_template = 'jupyter-{USERNAME}'
|
c.SystemdSpawner.unit_name_template = 'jupyter-{USERNAME}'
|
||||||
|
|
||||||
config_overrides_path = os.path.join(INSTALL_PREFIX, 'config.yaml')
|
config_overrides_path = os.path.join(CONFIG_DIR, 'config.yaml')
|
||||||
if os.path.exists(config_overrides_path):
|
if os.path.exists(config_overrides_path):
|
||||||
with open(config_overrides_path) as f:
|
with open(config_overrides_path) as f:
|
||||||
config_overrides = yaml.safe_load(f)
|
config_overrides = yaml.safe_load(f)
|
||||||
@@ -53,6 +53,6 @@ configurer.apply_config(config_overrides, c)
|
|||||||
|
|
||||||
# Load arbitrary .py config files if they exist.
|
# Load arbitrary .py config files if they exist.
|
||||||
# This is our escape hatch
|
# This is our escape hatch
|
||||||
extra_configs = sorted(glob(os.path.join(INSTALL_PREFIX, 'jupyterhub_config.d', '*.py')))
|
extra_configs = sorted(glob(os.path.join(CONFIG_DIR, 'jupyterhub_config.d', '*.py')))
|
||||||
for ec in extra_configs:
|
for ec in extra_configs:
|
||||||
load_subconfig(ec)
|
load_subconfig(ec)
|
||||||
|
|||||||
19
tljh/log.py
Normal file
19
tljh/log.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
"""Setup tljh logging"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from .config import INSTALL_PREFIX
|
||||||
|
|
||||||
|
|
||||||
|
def init_logging():
|
||||||
|
"""Setup default tljh logger"""
|
||||||
|
logger = logging.getLogger("tljh")
|
||||||
|
os.makedirs(INSTALL_PREFIX, exist_ok=True)
|
||||||
|
file_logger = logging.FileHandler(os.path.join(INSTALL_PREFIX, "installer.log"))
|
||||||
|
file_logger.setFormatter(logging.Formatter("%(asctime)s %(message)s"))
|
||||||
|
logger.addHandler(file_logger)
|
||||||
|
|
||||||
|
stderr_logger = logging.StreamHandler()
|
||||||
|
stderr_logger.setFormatter(logging.Formatter("%(message)s"))
|
||||||
|
logger.addHandler(stderr_logger)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
69
tljh/migrator.py
Normal file
69
tljh/migrator.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
"""Migration utilities for upgrading tljh"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from datetime import date
|
||||||
|
import logging
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from tljh.config import (
|
||||||
|
CONFIG_DIR,
|
||||||
|
CONFIG_FILE,
|
||||||
|
INSTALL_PREFIX,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_file(old_path, new_path):
|
||||||
|
"""Migrate one file from an old location to a new one
|
||||||
|
|
||||||
|
avoids collisions if the new file exists
|
||||||
|
"""
|
||||||
|
if not os.path.exists(old_path):
|
||||||
|
return
|
||||||
|
if os.path.exists(new_path):
|
||||||
|
# new config file already created! still move the config,
|
||||||
|
# but avoid collision
|
||||||
|
timestamp = date.today().isoformat()
|
||||||
|
dest = dest_base = f"{new_path}.old.{timestamp}"
|
||||||
|
i = 0
|
||||||
|
while os.path.exists(dest):
|
||||||
|
# avoid collisions
|
||||||
|
dest = dest_base + f".{i}"
|
||||||
|
i += 1
|
||||||
|
logger.warning(f"Found file in both old ({old_path}) and new ({new_path}).")
|
||||||
|
logger.warning(
|
||||||
|
f"Moving {old_path} to {dest} to avoid clobbering. Its contents will be ignored."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
dest = new_path
|
||||||
|
shutil.move(old_path, dest)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_directory(old_dir, new_dir):
|
||||||
|
"""Migrate a directory to a new location"""
|
||||||
|
if not os.path.exists(old_dir):
|
||||||
|
return
|
||||||
|
if os.path.exists(new_dir):
|
||||||
|
# both dirs exist
|
||||||
|
for f in os.listdir(old_dir):
|
||||||
|
src = os.path.join(old_dir, f)
|
||||||
|
dest = os.path.join(new_dir, f)
|
||||||
|
if os.path.isdir(src):
|
||||||
|
migrate_directory(src, dest)
|
||||||
|
else:
|
||||||
|
migrate_file(src, dest)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Moving directory to new location {old_dir} -> {new_dir}")
|
||||||
|
shutil.move(old_dir, new_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_config_files():
|
||||||
|
"""Migrate config files to their new locations"""
|
||||||
|
# handle old TLJH_DIR/config.yaml location
|
||||||
|
migrate_file(os.path.join(INSTALL_PREFIX, "config.yaml"), CONFIG_FILE)
|
||||||
|
migrate_directory(
|
||||||
|
os.path.join(INSTALL_PREFIX, "jupyterhub_config.d"),
|
||||||
|
os.path.join(CONFIG_DIR, "jupyterhub_config.d"),
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user