diff --git a/.hub/jupyter_config_backup.py b/.hub/jupyter_config_backup.py index c84c4d0..314d5eb 100644 --- a/.hub/jupyter_config_backup.py +++ b/.hub/jupyter_config_backup.py @@ -1,101 +1,32 @@ -""" -按组挂载 Git 资源(只读) + 自动建用户加组 -资源路径 /home/zhangyi/jupyter-collection -控制文件 /home/zhangyi/jupyter-collection/.hub/resource_map.ini -""" - -import os, grp, pwd, shutil, subprocess, configparser +import shutil, configparser from pathlib import Path -from jupyterhub.spawner import Spawner -# ---------- 路径常量 ---------- -SHARED_ROOT = Path("/home/zhangyi/jupyter-collection") # Git 资源仓库 -USER_HOME = Path("/home/jupyter-{username}") # 用户家目录模板 -CTRL_FILE = SHARED_ROOT / ".hub" / "resource_map.ini" # 组-资源-用户映射 +# 仓库在host上的地址 +SHARED_ROOT = Path("/home/zhangyi/jupyter-collection") +CTRL_FILE = SHARED_ROOT / ".hub" / "resource_map.ini" -# ---------- 工具:读控制表 ---------- -def load_ctrl(): - """返回 (组->资源, 组->用户) 两个 dict""" +def load_map(): cfg = configparser.ConfigParser() cfg.read(CTRL_FILE) - res_map = {g: [r.strip() for r in v.split(",")] for g, v in cfg.items("map")} - usr_map = {g: [u.strip() for u in v.split(",")] for g, v in cfg.items("users")} - return res_map, usr_map + # 用户名 -> [res1, res2, ...] + return {u: [r.strip() for r in v.split(",")] + for u, v in cfg.items("users")} -# ========== 最早钩子:预创建家目录 ========== -def ensure_homedir(spawner): - """systemd 启动前保证目录存在且属主正确""" +def copy_resources(spawner): username = spawner.user.name - user_dir = Path(str(USER_HOME).format(username=username)) - if not user_dir.exists(): - user_dir.mkdir(mode=0o700, parents=True) - shutil.chown(user_dir, user=username, group=username) + shared = Path(spawner.environment.get("HOME", f"/home/jupyter-{username}")) / "shared" -c.Spawner.pre_spawn_hook = ensure_homedir - -# ========== 认证后钩子:自动建用户+加组 ========== -def ensure_user_and_groups(authenticator, handler, authentication): - username = authentication['name'] - res_map, usr_map = load_ctrl() - wanted_groups = [g for g, users in usr_map.items() if username in users] - if not wanted_groups: - return authentication - - for g in wanted_groups: # 确保组存在 - subprocess.run(["groupadd", "-f", g], check=False) - - if subprocess.run(["id", "-u", username], capture_output=True).returncode != 0: - subprocess.run([ - "useradd", "-m", "-s", "/bin/bash", - "-d", str(USER_HOME).format(username=username), username - ], check=False) - - for g in wanted_groups: # 加组 - subprocess.run(["usermod", "-aG", g, username], check=False) - - return authentication - -c.Authenticator.post_auth_hook = ensure_user_and_groups - -# ========== spawn 钩子:清 shared + 只读挂载 ========== - -def prepare_user(spawner): - username = spawner.user.name - user_dir = Path(str(USER_HOME).format(username=username)) - - # ① 确保家目录 700 + 属主正确(首次/后续都适用) - user_dir.mkdir(mode=0o700, parents=True, exist_ok=True) - shutil.chown(user_dir, user=username, group=username) - - # ② 只清 shared,保留用户其它文件 - shared = user_dir / "shared" + # 清空或新建 shared if shared.exists(): shutil.rmtree(shared) shared.mkdir(parents=True, exist_ok=True) - shutil.chown(shared, user=username, group=username) - # 读取控制表 - res_map, _ = load_ctrl() + # 按 ini 拷贝资源 + for res in load_map().get(username, []): + src = SHARED_ROOT / res + dst = shared / res + if src.is_dir(): + shutil.copytree(src, dst, dirs_exist_ok=True) - # 获取用户系统组 - try: - pwnam = pwd.getpwnam(username) - gid_list = os.getgrouplist(username, pwnam.pw_gid) - except (KeyError, OSError): - return - user_groups = {grp.getgrgid(g).gr_name for g in gid_list} - - # 组装 bind_mounts(SystemdSpawner 语法) - bind_list = [] - for grp_name in user_groups: - for res in res_map.get(grp_name, []): - res_path = SHARED_ROOT / res - if res_path.is_dir(): - bind_list.append({ - 'source': str(res_path), - 'target': str(shared / res), - 'read-only': True, - }) - spawner.bind_mounts = bind_list - -c.Spawner.pre_spawn_hook = prepare_user \ No newline at end of file +# ========== 用户容器启动完成后执行 ========== +c.Spawner.post_spawn_hook = copy_resources \ No newline at end of file diff --git a/.hub/resource_map.ini b/.hub/resource_map.ini index 259c6a0..68e94ee 100644 --- a/.hub/resource_map.ini +++ b/.hub/resource_map.ini @@ -1,12 +1,8 @@ -# 第一个表定义了不同的组别可读取的资源路径名称 -# <组名> = <路径名>, <路径名> -[map] -PrincipleOfGeophysics = geophysics_basic -GeophysicalInversion = geophysics_basic, geophysics_advanced -jupyterhub-admins = geophysics_basic, geophysics_advanced - -# 第二个表定义了每个组内的用户 用逗号区分 +# 这个表定义了每个用户的权限 即能拿到哪些文件夹 逗号分隔 [users] -jupyterhub-admins = zhangyi -PrincipleOfGeophysics = student1, student2, student3 -GeophysicalInversion = student4, student5 \ No newline at end of file +zhangyi = geophysics_basic, geophysics_advanced +student1 = geophysics_basic +student2 = geophysics_basic +student3 = geophysics_basic +student4 = geophysics_basic, geophysics_advanced +student5 = geophysics_basic, geophysics_advanced \ No newline at end of file