tmp
This commit is contained in:
@@ -1,15 +1,18 @@
|
|||||||
|
"""
|
||||||
|
按组挂载 Git 资源(只读) + 自动建用户加组
|
||||||
|
资源路径 /home/zhangyi/jupyter-collection
|
||||||
|
控制文件 /home/zhangyi/jupyter-collection/.hub/resource_map.ini
|
||||||
|
"""
|
||||||
import os, grp, pwd, shutil, subprocess, configparser
|
import os, grp, pwd, shutil, subprocess, configparser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from jupyterhub.spawner import Spawner
|
from jupyterhub.spawner import Spawner
|
||||||
|
|
||||||
# host端的仓库目录
|
# ---------- 基础路径 ----------
|
||||||
SHARED_ROOT = Path("/home/zhangyi/jupyter-collection")
|
SHARED_ROOT = Path("/home/zhangyi/jupyter-collection") # Git 仓库
|
||||||
# jyputerhub用户home目录
|
USER_HOME = Path("/home/jupyter-{username}")
|
||||||
USER_HOME = Path("/home/jupyter-{username}")
|
CTRL_FILE = SHARED_ROOT / ".hub" / "resource_map.ini"
|
||||||
# 分组信息路径
|
|
||||||
CTRL_FILE = SHARED_ROOT / ".hub" / "resource_map.ini"
|
|
||||||
|
|
||||||
# ========== 2. 读控制表 ==========
|
# ---------- 工具:读控制表 ----------
|
||||||
def load_ctrl():
|
def load_ctrl():
|
||||||
"""返回 组->资源 和 组->用户 两个字典"""
|
"""返回 组->资源 和 组->用户 两个字典"""
|
||||||
cfg = configparser.ConfigParser()
|
cfg = configparser.ConfigParser()
|
||||||
@@ -20,26 +23,25 @@ def load_ctrl():
|
|||||||
for g, v in cfg.items("users")}
|
for g, v in cfg.items("users")}
|
||||||
return res_map, usr_map
|
return res_map, usr_map
|
||||||
|
|
||||||
# ========== 3. post_auth_hook:自动建用户+加组 ==========
|
# ---------- post_auth_hook:自动建用户+加组 ----------
|
||||||
def ensure_user_and_groups(authenticator, handler, authentication):
|
def ensure_user_and_groups(authenticator, handler, authentication):
|
||||||
username = authentication['name']
|
username = authentication['name']
|
||||||
res_map, usr_map = load_ctrl()
|
res_map, usr_map = load_ctrl()
|
||||||
|
|
||||||
# 该用户应属的组
|
|
||||||
wanted_groups = [g for g, users in usr_map.items() if username in users]
|
wanted_groups = [g for g, users in usr_map.items() if username in users]
|
||||||
if not wanted_groups: # 控制文件里没写就什么都不做
|
if not wanted_groups:
|
||||||
return authentication
|
return authentication # 控制文件里没有就什么都不做
|
||||||
|
|
||||||
# 确保组存在
|
# 确保组存在
|
||||||
for g in wanted_groups:
|
for g in wanted_groups:
|
||||||
subprocess.run(["groupadd", "-f", g], check=False)
|
subprocess.run(["groupadd", "-f", g], check=False)
|
||||||
|
|
||||||
# 若系统账号不存在则创建(Hub 自己会创 home,这里确保 shell 账号)
|
# 系统账号不存在就创建(TLJH 会自己建 home,这里只确保用户存在)
|
||||||
#if subprocess.run(["id", "-u", username], capture_output=True).returncode != 0:
|
if subprocess.run(["id", "-u", username], capture_output=True).returncode != 0:
|
||||||
# subprocess.run([
|
subprocess.run([
|
||||||
# "useradd", "-m", "-s", "/bin/bash",
|
"useradd", "-m", "-s", "/bin/bash",
|
||||||
# "-d", str(USER_HOME).format(username=username), username
|
"-d", str(USER_HOME).format(username=username), username
|
||||||
# ], check=False)
|
], check=False)
|
||||||
|
|
||||||
# 加组(幂等)
|
# 加组(幂等)
|
||||||
for g in wanted_groups:
|
for g in wanted_groups:
|
||||||
@@ -49,17 +51,17 @@ def ensure_user_and_groups(authenticator, handler, authentication):
|
|||||||
|
|
||||||
c.Authenticator.post_auth_hook = ensure_user_and_groups
|
c.Authenticator.post_auth_hook = ensure_user_and_groups
|
||||||
|
|
||||||
# ========== 4. pre_spawn_hook:重建干净 home + 只读挂 Git 资源 ==========
|
# ---------- pre_spawn_hook:只清 shared 并挂载 ----------
|
||||||
def prepare_user(spawner: Spawner):
|
def prepare_user(spawner: Spawner):
|
||||||
username = spawner.user.name
|
username = spawner.user.name
|
||||||
user_dir = Path(str(USER_HOME).format(username=username))
|
user_dir = Path(str(USER_HOME).format(username=username))
|
||||||
|
|
||||||
# 1. 重建空 home(保证无残留)
|
# 1. 只清 shared 目录,保留用户其它文件
|
||||||
if user_dir.exists():
|
shared = user_dir / "shared"
|
||||||
shutil.rmtree(user_dir)
|
if shared.exists():
|
||||||
user_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
|
shutil.rmtree(shared)
|
||||||
# 关键:让 systemd 能进去
|
shared.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.chown(user_dir, user=username, group=username)
|
shutil.chown(shared, user=username, group=username)
|
||||||
|
|
||||||
# 2. 读控制表
|
# 2. 读控制表
|
||||||
res_map, _ = load_ctrl()
|
res_map, _ = load_ctrl()
|
||||||
@@ -72,10 +74,8 @@ def prepare_user(spawner: Spawner):
|
|||||||
return
|
return
|
||||||
user_groups = {grp.getgrgid(g).gr_name for g in gid_list}
|
user_groups = {grp.getgrgid(g).gr_name for g in gid_list}
|
||||||
|
|
||||||
# 4. 挂载该用户该看的资源(只读)
|
# 4. 组装 bind_mounts(SystemdSpawner 语法)
|
||||||
shared = user_dir / "shared"
|
bind_list = []
|
||||||
shared.mkdir(exist_ok=True)
|
|
||||||
bind_list = [] # 先攒列表
|
|
||||||
for grp_name in user_groups:
|
for grp_name in user_groups:
|
||||||
for res in res_map.get(grp_name, []):
|
for res in res_map.get(grp_name, []):
|
||||||
res_path = SHARED_ROOT / res
|
res_path = SHARED_ROOT / res
|
||||||
@@ -85,6 +85,6 @@ def prepare_user(spawner: Spawner):
|
|||||||
'target': str(shared / res),
|
'target': str(shared / res),
|
||||||
'read-only': True,
|
'read-only': True,
|
||||||
})
|
})
|
||||||
spawner.bind_mounts = bind_list
|
spawner.bind_mounts = bind_list
|
||||||
|
|
||||||
c.Spawner.pre_spawn_hook = prepare_user
|
c.Spawner.pre_spawn_hook = prepare_user
|
||||||
Reference in New Issue
Block a user