Skip to content

Commit d256f9d

Browse files
authored
nexus gruops (#31)
* nexus gruops * lock
1 parent 6f96223 commit d256f9d

4 files changed

Lines changed: 51 additions & 27 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "nexusai"
3-
version = "0.4.7"
3+
version = "0.4.8"
44
description = "Add your description here"
55
readme = "README.md"
66
requires-python = ">=3.10"

src/nexus/server/installation/setup.py

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class InstallationInfo:
3535

3636

3737
def get_installation_info() -> InstallationInfo:
38-
# Check system installation but handle permission errors gracefully
3938
try:
4039
if MARKER_SYSTEM.exists():
4140
try:
@@ -136,28 +135,32 @@ def create_directories(server_dir: pl.Path) -> None:
136135
(server_dir / "jobs").mkdir(parents=True, exist_ok=True)
137136

138137

139-
def create_server_user() -> bool:
138+
def create_server_user(sup_groups: list[str] | None = None) -> bool:
139+
user_exists = False
140140
try:
141141
pwd.getpwnam(SERVER_USER)
142-
return False
142+
user_exists = True
143143
except KeyError:
144144
subprocess.run(["useradd", "--system", "--create-home", "--shell", "/bin/bash", SERVER_USER], check=True)
145-
return True
145+
146+
if sup_groups:
147+
for grp in sup_groups:
148+
subprocess.run(["usermod", "-aG", grp, SERVER_USER], check=True)
149+
print(f"Added supplementary groups: {', '.join(sup_groups)} to user {SERVER_USER}.")
150+
151+
return not user_exists
146152

147153

148154
def setup_shared_screen_dir() -> bool:
149-
"""Create a shared screen socket directory that all users can access."""
150155
screen_dir = pl.Path("/tmp/screen_nexus")
151156
if not screen_dir.exists():
152157
screen_dir.mkdir(parents=True, exist_ok=True)
153-
# Mode 1777 = sticky bit + rwxrwxrwx (world-writable with sticky bit)
154158
os.chmod(screen_dir, 0o1777)
155159
return True
156160
return False
157161

158162

159163
def setup_passwordless_nexus_attach() -> bool:
160-
"""Set up passwordless sudo access to attach to nexus screen sessions."""
161164
sudoers_file = pl.Path("/etc/sudoers.d/nexus_attach")
162165
content = "ALL ALL=(nexus) NOPASSWD: /usr/bin/screen -r *\n"
163166

@@ -174,10 +177,10 @@ def set_system_permissions() -> None:
174177
subprocess.run(["chmod", "-R", "770", str(SYSTEM_SERVER_DIR)], check=True)
175178

176179

177-
def setup_systemd_server() -> tuple[bool, str | None]:
180+
def setup_systemd_server(sup_groups: list[str] | None = None) -> tuple[bool, str | None]:
178181
from nexus.server.installation import systemd
179182

180-
server_content = systemd.get_service_file_content()
183+
server_content = systemd.get_service_file_content(sup_groups)
181184
dest_server = SYSTEMD_DIR / SYSTEMD_SERVICE_FILENAME
182185
dest_server.write_text(server_content)
183186
return True, None
@@ -200,8 +203,8 @@ def manage_systemd_server(action: str) -> bool:
200203
return False
201204

202205

203-
def install_system_server() -> None:
204-
server_ok, server_error = setup_systemd_server()
206+
def install_system_server(sup_groups: list[str] | None = None) -> None:
207+
server_ok, server_error = setup_systemd_server(sup_groups)
205208
if not server_ok:
206209
sys.exit(server_error)
207210
print(f"Installed server file to: {SYSTEMD_DIR / SYSTEMD_SERVICE_FILENAME}")
@@ -315,7 +318,6 @@ def remove_installation_files(keep_config: bool) -> None:
315318

316319

317320
def check_running_processes() -> list[int]:
318-
"""Check if there are any processes running as the nexus user."""
319321
try:
320322
result = subprocess.run(["pgrep", "-u", SERVER_USER], capture_output=True, text=True)
321323
if result.returncode == 0:
@@ -326,7 +328,6 @@ def check_running_processes() -> list[int]:
326328

327329

328330
def terminate_user_processes(yes_flag: bool = False) -> bool:
329-
"""Terminate all processes running as the nexus user."""
330331
pids = check_running_processes()
331332
if not pids:
332333
return True
@@ -340,14 +341,10 @@ def terminate_user_processes(yes_flag: bool = False) -> bool:
340341
return False
341342

342343
try:
343-
# First try SIGTERM
344344
subprocess.run(["pkill", "-TERM", "-u", SERVER_USER], check=False)
345-
# Wait a moment
346345
time.sleep(1)
347-
# Check if any processes are still running
348346
remaining_pids = check_running_processes()
349347
if remaining_pids:
350-
# If yes flag is set or user agreed, use SIGKILL
351348
if yes_flag or input("Some processes are still running. Use force kill? [y/N]: ").strip().lower() == "y":
352349
subprocess.run(["pkill", "-KILL", "-u", SERVER_USER], check=False)
353350
time.sleep(0.5)
@@ -401,11 +398,19 @@ def check_installation_prerequisites(force: bool = False) -> None:
401398
sys.exit(f"Nexus server is already installed in system mode (version {info.version}).")
402399

403400

404-
def prepare_system_environment() -> None:
401+
def prompt_for_supplementary_groups() -> list[str]:
402+
groups_input = input(
403+
"Enter supplementary groups to add to the nexus user (comma-separated, or leave empty for none): "
404+
).strip()
405+
if groups_input:
406+
return [grp.strip() for grp in groups_input.split(",") if grp.strip()]
407+
return []
408+
409+
def prepare_system_environment(sup_groups: list[str] | None = None) -> None:
405410
create_directories(SYSTEM_SERVER_DIR)
406411
print(f"Created system directory: {SYSTEM_SERVER_DIR}")
407412

408-
if create_server_user():
413+
if create_server_user(sup_groups):
409414
print(f"Created {SERVER_USER} system user.")
410415
else:
411416
print(f"User '{SERVER_USER}' already exists.")
@@ -436,7 +441,12 @@ def install_system(
436441
check_installation_prerequisites(force)
437442

438443
print("Installing Nexus server in system mode...")
439-
prepare_system_environment()
444+
445+
sup_groups = []
446+
if interactive:
447+
sup_groups = prompt_for_supplementary_groups()
448+
449+
prepare_system_environment(sup_groups)
440450

441451
_config = setup_config(SYSTEM_SERVER_DIR, interactive, config_file)
442452
create_persistent_directory(_config)
@@ -445,7 +455,11 @@ def install_system(
445455
set_system_permissions()
446456
print("Set proper directory permissions.")
447457

448-
install_system_server()
458+
server_ok, server_error = setup_systemd_server(sup_groups)
459+
if not server_ok:
460+
sys.exit(server_error)
461+
print(f"Installed server file to: {SYSTEMD_DIR / SYSTEMD_SERVICE_FILENAME}")
462+
449463
server_started = False
450464
if start_server:
451465
server_started = start_system_server()
@@ -469,7 +483,6 @@ def uninstall(keep_config: bool = False, force: bool = False, yes: bool = False)
469483
remove_system_components()
470484
remove_installation_files(keep_config)
471485

472-
# Check and terminate processes before removing user
473486
if not terminate_user_processes(yes_flag=yes):
474487
print(f"\nCannot remove {SERVER_USER} user while processes are still running.")
475488
print("Please terminate these processes manually or run with --yes to force termination.")
@@ -658,4 +671,4 @@ def initialize_context(server_dir: pl.Path | None) -> context.NexusServerContext
658671
_logger = logger.create_logger(log_dir, name="nexus_server", log_level=_config.log_level)
659672
_db = db.create_connection(_logger, db_path=db_path)
660673

661-
return context.NexusServerContext(db=_db, config=_config, logger=_logger)
674+
return context.NexusServerContext(db=_db, config=_config, logger=_logger)

src/nexus/server/installation/systemd.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,16 @@
2121
SERVICE_FILE_CONTENT = UNIT_SECTION + SERVICE_SECTION + INSTALL_SECTION
2222

2323

24-
def get_service_file_content() -> str:
25-
return SERVICE_FILE_CONTENT
24+
def get_service_file_content(sup_groups: list[str] | None = None) -> str:
25+
if not sup_groups:
26+
return SERVICE_FILE_CONTENT
27+
28+
content_lines = SERVICE_FILE_CONTENT.splitlines()
29+
new_lines = []
30+
31+
for line in content_lines:
32+
new_lines.append(line)
33+
if line.strip() == "[Service]":
34+
new_lines.append(f"SupplementaryGroups={' '.join(sup_groups)}")
35+
36+
return '\n'.join(new_lines)

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)