Files
jupyter-collection/.hub/jupyter_config_backup.py

90 lines
3.1 KiB
Python
Raw Normal View History

2025-10-22 19:40:22 +08:00
"""
按组挂载 Git 资源只读 + 自动建用户加组
资源路径 /home/zhangyi/jupyter-collection
控制文件 /home/zhangyi/jupyter-collection/.hub/resource_map.ini
"""
2025-10-22 11:20:52 +08:00
import os, grp, pwd, shutil, subprocess, configparser
2025-10-22 11:07:27 +08:00
from pathlib import Path
from jupyterhub.spawner import Spawner
2025-10-22 19:40:22 +08:00
# ---------- 基础路径 ----------
SHARED_ROOT = Path("/home/zhangyi/jupyter-collection") # Git 仓库
USER_HOME = Path("/home/jupyter-{username}")
CTRL_FILE = SHARED_ROOT / ".hub" / "resource_map.ini"
2025-10-22 11:07:27 +08:00
2025-10-22 19:40:22 +08:00
# ---------- 工具:读控制表 ----------
2025-10-22 11:07:27 +08:00
def load_ctrl():
"""返回 组->资源 和 组->用户 两个字典"""
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
2025-10-22 19:40:22 +08:00
# ---------- post_auth_hook自动建用户+加组 ----------
2025-10-22 11:07:27 +08:00
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]
2025-10-22 19:40:22 +08:00
if not wanted_groups:
return authentication # 控制文件里没有就什么都不做
2025-10-22 11:07:27 +08:00
# 确保组存在
for g in wanted_groups:
subprocess.run(["groupadd", "-f", g], check=False)
2025-10-22 19:40:22 +08:00
# 系统账号不存在就创建TLJH 会自己建 home这里只确保用户存在
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)
2025-10-22 11:07:27 +08:00
# 加组(幂等)
for g in wanted_groups:
subprocess.run(["usermod", "-aG", g, username], check=False)
return authentication
c.Authenticator.post_auth_hook = ensure_user_and_groups
2025-10-22 19:40:22 +08:00
# ---------- pre_spawn_hook只清 shared 并挂载 ----------
2025-10-22 11:07:27 +08:00
def prepare_user(spawner: Spawner):
username = spawner.user.name
user_dir = Path(str(USER_HOME).format(username=username))
2025-10-22 19:40:22 +08:00
# 1. 只清 shared 目录,保留用户其它文件
shared = user_dir / "shared"
if shared.exists():
shutil.rmtree(shared)
shared.mkdir(parents=True, exist_ok=True)
shutil.chown(shared, user=username, group=username)
2025-10-22 11:07:27 +08:00
# 2. 读控制表
res_map, _ = load_ctrl()
# 3. 取系统组
try:
2025-10-22 11:20:52 +08:00
pwnam = pwd.getpwnam(username)
gid_list = os.getgrouplist(username, pwnam.pw_gid)
2025-10-22 11:07:27 +08:00
except (KeyError, OSError):
return
user_groups = {grp.getgrgid(g).gr_name for g in gid_list}
2025-10-22 19:40:22 +08:00
# 4. 组装 bind_mountsSystemdSpawner 语法)
bind_list = []
2025-10-22 11:07:27 +08:00
for grp_name in user_groups:
for res in res_map.get(grp_name, []):
res_path = SHARED_ROOT / res
if res_path.is_dir():
2025-10-22 11:24:22 +08:00
bind_list.append({
'source': str(res_path),
'target': str(shared / res),
'read-only': True,
})
2025-10-22 19:40:22 +08:00
spawner.bind_mounts = bind_list
2025-10-22 11:07:27 +08:00
c.Spawner.pre_spawn_hook = prepare_user