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 .container import ContainerManager
|
||||||
from .models import SessionStatus
|
from .models import SessionStatus
|
||||||
from .user_config import UserConfigManager
|
from .user_config import UserConfigManager
|
||||||
|
from .session import SessionManager
|
||||||
|
|
||||||
app = typer.Typer(help="Monadical Container Tool")
|
app = typer.Typer(help="Monadical Container Tool")
|
||||||
session_app = typer.Typer(help="Manage MC sessions")
|
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()
|
console = Console()
|
||||||
config_manager = ConfigManager()
|
config_manager = ConfigManager()
|
||||||
user_config = UserConfigManager()
|
user_config = UserConfigManager()
|
||||||
container_manager = ContainerManager(config_manager)
|
session_manager = SessionManager()
|
||||||
|
container_manager = ContainerManager(config_manager, session_manager)
|
||||||
|
|
||||||
|
|
||||||
@app.callback(invoke_without_command=True)
|
@app.callback(invoke_without_command=True)
|
||||||
|
|||||||
@@ -43,10 +43,6 @@ class ConfigManager:
|
|||||||
for driver_name, driver_data in config_data["drivers"].items():
|
for driver_name, driver_data in config_data["drivers"].items():
|
||||||
config.drivers[driver_name] = Driver.model_validate(driver_data)
|
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
|
return config
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error loading config: {e}")
|
print(f"Error loading config: {e}")
|
||||||
@@ -106,21 +102,7 @@ class ConfigManager:
|
|||||||
|
|
||||||
return all_drivers
|
return all_drivers
|
||||||
|
|
||||||
def add_session(self, session_id: str, session_data: dict) -> None:
|
# Session management has been moved to SessionManager in session.py
|
||||||
"""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
|
|
||||||
|
|
||||||
def load_driver_from_dir(self, driver_dir: Path) -> Optional[Driver]:
|
def load_driver_from_dir(self, driver_dir: Path) -> Optional[Driver]:
|
||||||
"""Load a driver configuration from a directory"""
|
"""Load a driver configuration from a directory"""
|
||||||
|
|||||||
@@ -10,11 +10,17 @@ from docker.errors import DockerException, ImageNotFound
|
|||||||
|
|
||||||
from .models import Session, SessionStatus
|
from .models import Session, SessionStatus
|
||||||
from .config import ConfigManager
|
from .config import ConfigManager
|
||||||
|
from .session import SessionManager
|
||||||
|
|
||||||
|
|
||||||
class ContainerManager:
|
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.config_manager = config_manager or ConfigManager()
|
||||||
|
self.session_manager = session_manager or SessionManager()
|
||||||
try:
|
try:
|
||||||
self.client = docker.from_env()
|
self.client = docker.from_env()
|
||||||
# Test connection
|
# Test connection
|
||||||
@@ -329,8 +335,10 @@ class ContainerManager:
|
|||||||
ports=ports,
|
ports=ports,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Save session to config as JSON-compatible dict
|
# Save session to the session manager as JSON-compatible dict
|
||||||
self.config_manager.add_session(session_id, session.model_dump(mode="json"))
|
self.session_manager.add_session(
|
||||||
|
session_id, session.model_dump(mode="json")
|
||||||
|
)
|
||||||
|
|
||||||
return session
|
return session
|
||||||
|
|
||||||
@@ -365,7 +373,6 @@ class ContainerManager:
|
|||||||
|
|
||||||
# Execute interactive shell in container
|
# Execute interactive shell in container
|
||||||
# The init-status.sh script will automatically show logs if needed
|
# 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")
|
os.system(f"docker exec -it {session.container_id} /bin/bash")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -392,7 +399,7 @@ class ContainerManager:
|
|||||||
container = self.client.containers.get(session.container_id)
|
container = self.client.containers.get(session.container_id)
|
||||||
container.stop()
|
container.stop()
|
||||||
container.remove()
|
container.remove()
|
||||||
self.config_manager.remove_session(session.id)
|
self.session_manager.remove_session(session.id)
|
||||||
return True
|
return True
|
||||||
except DockerException as e:
|
except DockerException as e:
|
||||||
print(f"Error closing session {session.id}: {e}")
|
print(f"Error closing session {session.id}: {e}")
|
||||||
@@ -425,8 +432,8 @@ class ContainerManager:
|
|||||||
# Stop and remove container
|
# Stop and remove container
|
||||||
container.stop()
|
container.stop()
|
||||||
container.remove()
|
container.remove()
|
||||||
# Remove from config
|
# Remove from session storage
|
||||||
self.config_manager.remove_session(session.id)
|
self.session_manager.remove_session(session.id)
|
||||||
|
|
||||||
# Notify about completion
|
# Notify about completion
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
|
|||||||
@@ -63,9 +63,6 @@ class Session(BaseModel):
|
|||||||
class Config(BaseModel):
|
class Config(BaseModel):
|
||||||
docker: Dict[str, str] = Field(default_factory=dict)
|
docker: Dict[str, str] = Field(default_factory=dict)
|
||||||
drivers: Dict[str, Driver] = 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(
|
defaults: Dict[str, object] = Field(
|
||||||
default_factory=dict
|
default_factory=dict
|
||||||
) # Can store strings, booleans, or other values
|
) # 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