mirror of
https://github.com/Monadical-SAS/cubbi.git
synced 2025-12-20 20:29:06 +00:00
feat(cli): separate session state into its own session.yaml file
This commit is contained in:
@@ -8,6 +8,7 @@ from .config import ConfigManager
|
||||
from .container import ContainerManager
|
||||
from .models import SessionStatus
|
||||
from .user_config import UserConfigManager
|
||||
from .session import SessionManager
|
||||
|
||||
app = typer.Typer(help="Monadical Container Tool")
|
||||
session_app = typer.Typer(help="Manage MC sessions")
|
||||
@@ -20,7 +21,8 @@ app.add_typer(config_app, name="config", no_args_is_help=True)
|
||||
console = Console()
|
||||
config_manager = ConfigManager()
|
||||
user_config = UserConfigManager()
|
||||
container_manager = ContainerManager(config_manager)
|
||||
session_manager = SessionManager()
|
||||
container_manager = ContainerManager(config_manager, session_manager)
|
||||
|
||||
|
||||
@app.callback(invoke_without_command=True)
|
||||
|
||||
@@ -43,10 +43,6 @@ class ConfigManager:
|
||||
for driver_name, driver_data in config_data["drivers"].items():
|
||||
config.drivers[driver_name] = Driver.model_validate(driver_data)
|
||||
|
||||
# Add sessions (stored as simple dictionaries)
|
||||
if "sessions" in config_data:
|
||||
config.sessions = config_data["sessions"]
|
||||
|
||||
return config
|
||||
except Exception as e:
|
||||
print(f"Error loading config: {e}")
|
||||
@@ -106,21 +102,7 @@ class ConfigManager:
|
||||
|
||||
return all_drivers
|
||||
|
||||
def add_session(self, session_id: str, session_data: dict) -> None:
|
||||
"""Add a session to the config"""
|
||||
# Store session data as a dictionary in the config
|
||||
self.config.sessions[session_id] = session_data
|
||||
self.save_config()
|
||||
|
||||
def remove_session(self, session_id: str) -> None:
|
||||
"""Remove a session from the config"""
|
||||
if session_id in self.config.sessions:
|
||||
del self.config.sessions[session_id]
|
||||
self.save_config()
|
||||
|
||||
def list_sessions(self) -> Dict:
|
||||
"""List all sessions in the config"""
|
||||
return self.config.sessions
|
||||
# Session management has been moved to SessionManager in session.py
|
||||
|
||||
def load_driver_from_dir(self, driver_dir: Path) -> Optional[Driver]:
|
||||
"""Load a driver configuration from a directory"""
|
||||
|
||||
@@ -10,11 +10,17 @@ from docker.errors import DockerException, ImageNotFound
|
||||
|
||||
from .models import Session, SessionStatus
|
||||
from .config import ConfigManager
|
||||
from .session import SessionManager
|
||||
|
||||
|
||||
class ContainerManager:
|
||||
def __init__(self, config_manager: Optional[ConfigManager] = None):
|
||||
def __init__(
|
||||
self,
|
||||
config_manager: Optional[ConfigManager] = None,
|
||||
session_manager: Optional[SessionManager] = None,
|
||||
):
|
||||
self.config_manager = config_manager or ConfigManager()
|
||||
self.session_manager = session_manager or SessionManager()
|
||||
try:
|
||||
self.client = docker.from_env()
|
||||
# Test connection
|
||||
@@ -329,8 +335,10 @@ class ContainerManager:
|
||||
ports=ports,
|
||||
)
|
||||
|
||||
# Save session to config as JSON-compatible dict
|
||||
self.config_manager.add_session(session_id, session.model_dump(mode="json"))
|
||||
# Save session to the session manager as JSON-compatible dict
|
||||
self.session_manager.add_session(
|
||||
session_id, session.model_dump(mode="json")
|
||||
)
|
||||
|
||||
return session
|
||||
|
||||
@@ -365,7 +373,6 @@ class ContainerManager:
|
||||
|
||||
# Execute interactive shell in container
|
||||
# The init-status.sh script will automatically show logs if needed
|
||||
print(f"Connecting to session {session_id}...")
|
||||
os.system(f"docker exec -it {session.container_id} /bin/bash")
|
||||
return True
|
||||
|
||||
@@ -392,7 +399,7 @@ class ContainerManager:
|
||||
container = self.client.containers.get(session.container_id)
|
||||
container.stop()
|
||||
container.remove()
|
||||
self.config_manager.remove_session(session.id)
|
||||
self.session_manager.remove_session(session.id)
|
||||
return True
|
||||
except DockerException as e:
|
||||
print(f"Error closing session {session.id}: {e}")
|
||||
@@ -425,8 +432,8 @@ class ContainerManager:
|
||||
# Stop and remove container
|
||||
container.stop()
|
||||
container.remove()
|
||||
# Remove from config
|
||||
self.config_manager.remove_session(session.id)
|
||||
# Remove from session storage
|
||||
self.session_manager.remove_session(session.id)
|
||||
|
||||
# Notify about completion
|
||||
if progress_callback:
|
||||
|
||||
@@ -63,9 +63,6 @@ class Session(BaseModel):
|
||||
class Config(BaseModel):
|
||||
docker: Dict[str, str] = Field(default_factory=dict)
|
||||
drivers: Dict[str, Driver] = Field(default_factory=dict)
|
||||
sessions: Dict[str, dict] = Field(
|
||||
default_factory=dict
|
||||
) # Store as dict to avoid serialization issues
|
||||
defaults: Dict[str, object] = Field(
|
||||
default_factory=dict
|
||||
) # Can store strings, booleans, or other values
|
||||
|
||||
85
mcontainer/session.py
Normal file
85
mcontainer/session.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Session storage management for Monadical Container Tool.
|
||||
"""
|
||||
|
||||
import os
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
DEFAULT_SESSIONS_FILE = Path.home() / ".config" / "mc" / "sessions.yaml"
|
||||
|
||||
|
||||
class SessionManager:
|
||||
"""Manager for container sessions."""
|
||||
|
||||
def __init__(self, sessions_path: Optional[Path] = None):
|
||||
"""Initialize the session manager.
|
||||
|
||||
Args:
|
||||
sessions_path: Optional path to the sessions file.
|
||||
Defaults to ~/.config/mc/sessions.yaml.
|
||||
"""
|
||||
self.sessions_path = sessions_path or DEFAULT_SESSIONS_FILE
|
||||
self.sessions = self._load_sessions()
|
||||
|
||||
def _load_sessions(self) -> Dict[str, dict]:
|
||||
"""Load sessions from file or create an empty sessions file if it doesn't exist."""
|
||||
if not self.sessions_path.exists():
|
||||
# Create directory if it doesn't exist
|
||||
self.sessions_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
# Create empty sessions file
|
||||
with open(self.sessions_path, "w") as f:
|
||||
yaml.safe_dump({}, f)
|
||||
# Set secure permissions
|
||||
os.chmod(self.sessions_path, 0o600)
|
||||
return {}
|
||||
|
||||
# Load existing sessions
|
||||
with open(self.sessions_path, "r") as f:
|
||||
sessions = yaml.safe_load(f) or {}
|
||||
return sessions
|
||||
|
||||
def save(self) -> None:
|
||||
"""Save the sessions to file."""
|
||||
with open(self.sessions_path, "w") as f:
|
||||
yaml.safe_dump(self.sessions, f)
|
||||
|
||||
def add_session(self, session_id: str, session_data: dict) -> None:
|
||||
"""Add a session to storage.
|
||||
|
||||
Args:
|
||||
session_id: The unique session ID
|
||||
session_data: The session data (Session model dump as dict)
|
||||
"""
|
||||
self.sessions[session_id] = session_data
|
||||
self.save()
|
||||
|
||||
def get_session(self, session_id: str) -> Optional[dict]:
|
||||
"""Get a session by ID.
|
||||
|
||||
Args:
|
||||
session_id: The session ID
|
||||
|
||||
Returns:
|
||||
The session data or None if not found
|
||||
"""
|
||||
return self.sessions.get(session_id)
|
||||
|
||||
def list_sessions(self) -> Dict[str, dict]:
|
||||
"""List all sessions.
|
||||
|
||||
Returns:
|
||||
Dict of session ID to session data
|
||||
"""
|
||||
return self.sessions
|
||||
|
||||
def remove_session(self, session_id: str) -> None:
|
||||
"""Remove a session from storage.
|
||||
|
||||
Args:
|
||||
session_id: The session ID to remove
|
||||
"""
|
||||
if session_id in self.sessions:
|
||||
del self.sessions[session_id]
|
||||
self.save()
|
||||
Reference in New Issue
Block a user