mirror of
https://github.com/Monadical-SAS/cubbi.git
synced 2025-12-21 04:39:07 +00:00
feat(project): explicitely add --project to save information in /mc-config across run.
Containers are now isolated by default.
This commit is contained in:
@@ -86,6 +86,7 @@ def list_sessions() -> None:
|
|||||||
table.add_column("Status")
|
table.add_column("Status")
|
||||||
table.add_column("Ports")
|
table.add_column("Ports")
|
||||||
table.add_column("Project")
|
table.add_column("Project")
|
||||||
|
table.add_column("Project Name")
|
||||||
table.add_column("MCPs")
|
table.add_column("MCPs")
|
||||||
|
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
@@ -119,6 +120,7 @@ def list_sessions() -> None:
|
|||||||
f"[{status_color}]{status_name}[/{status_color}]",
|
f"[{status_color}]{status_name}[/{status_color}]",
|
||||||
ports_str,
|
ports_str,
|
||||||
session.project or "",
|
session.project or "",
|
||||||
|
session.project_name or "",
|
||||||
mcps_str,
|
mcps_str,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -128,11 +130,16 @@ def list_sessions() -> None:
|
|||||||
@session_app.command("create")
|
@session_app.command("create")
|
||||||
def create_session(
|
def create_session(
|
||||||
driver: Optional[str] = typer.Option(None, "--driver", "-d", help="Driver to use"),
|
driver: Optional[str] = typer.Option(None, "--driver", "-d", help="Driver to use"),
|
||||||
project: Optional[str] = typer.Argument(
|
path_or_url: Optional[str] = typer.Argument(
|
||||||
None,
|
None,
|
||||||
help="Local directory path to mount or repository URL to clone",
|
help="Local directory path to mount or repository URL to clone",
|
||||||
show_default=False,
|
show_default=False,
|
||||||
),
|
),
|
||||||
|
project: Optional[str] = typer.Option(
|
||||||
|
None,
|
||||||
|
"--project",
|
||||||
|
help="Project name for configuration persistence (if not specified, no persistent configuration will be used)",
|
||||||
|
),
|
||||||
env: List[str] = typer.Option(
|
env: List[str] = typer.Option(
|
||||||
[], "--env", "-e", help="Environment variables (KEY=VALUE)"
|
[], "--env", "-e", help="Environment variables (KEY=VALUE)"
|
||||||
),
|
),
|
||||||
@@ -174,6 +181,9 @@ def create_session(
|
|||||||
If a local directory path is provided, it will be mounted at /app in the container.
|
If a local directory path is provided, it will be mounted at /app in the container.
|
||||||
If a repository URL is provided, it will be cloned into /app during initialization.
|
If a repository URL is provided, it will be cloned into /app during initialization.
|
||||||
If no path or URL is provided, no local volume will be mounted.
|
If no path or URL is provided, no local volume will be mounted.
|
||||||
|
|
||||||
|
Use --project to specify a project name for configuration persistence.
|
||||||
|
If --project is not specified, no persistent configuration will be used.
|
||||||
"""
|
"""
|
||||||
# Determine UID/GID
|
# Determine UID/GID
|
||||||
target_uid = uid if uid is not None else os.getuid()
|
target_uid = uid if uid is not None else os.getuid()
|
||||||
@@ -254,15 +264,16 @@ def create_session(
|
|||||||
console.print(f" {host_path} -> {mount_info['bind']}")
|
console.print(f" {host_path} -> {mount_info['bind']}")
|
||||||
|
|
||||||
with console.status(f"Creating session with driver '{driver}'..."):
|
with console.status(f"Creating session with driver '{driver}'..."):
|
||||||
# If project is a local directory, we should mount it
|
# If path_or_url is a local directory, we should mount it
|
||||||
# If it's a Git URL or doesn't exist, handle accordingly
|
# If it's a Git URL or doesn't exist, handle accordingly
|
||||||
mount_local = False
|
mount_local = False
|
||||||
if project and os.path.isdir(os.path.expanduser(project)):
|
if path_or_url and os.path.isdir(os.path.expanduser(path_or_url)):
|
||||||
mount_local = True
|
mount_local = True
|
||||||
|
|
||||||
session = container_manager.create_session(
|
session = container_manager.create_session(
|
||||||
driver_name=driver,
|
driver_name=driver,
|
||||||
project=project,
|
project=path_or_url,
|
||||||
|
project_name=project,
|
||||||
environment=environment,
|
environment=environment,
|
||||||
session_name=name,
|
session_name=name,
|
||||||
mount_local=mount_local,
|
mount_local=mount_local,
|
||||||
|
|||||||
@@ -51,37 +51,38 @@ class ContainerManager:
|
|||||||
"""Generate a unique session ID"""
|
"""Generate a unique session ID"""
|
||||||
return str(uuid.uuid4())[:8]
|
return str(uuid.uuid4())[:8]
|
||||||
|
|
||||||
def _get_project_config_path(self, project: Optional[str] = None) -> pathlib.Path:
|
def _get_project_config_path(
|
||||||
|
self, project: Optional[str] = None, project_name: Optional[str] = None
|
||||||
|
) -> Optional[pathlib.Path]:
|
||||||
"""Get the path to the project configuration directory
|
"""Get the path to the project configuration directory
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
project: Optional project repository URL. If None, uses current directory.
|
project: Optional project repository URL or path (only used for mounting).
|
||||||
|
project_name: Optional explicit project name. Only used if specified.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Path to the project configuration directory
|
Path to the project configuration directory, or None if no project_name is provided
|
||||||
"""
|
"""
|
||||||
# Get home directory for the MC config
|
# Get home directory for the MC config
|
||||||
mc_home = pathlib.Path.home() / ".mc"
|
mc_home = pathlib.Path.home() / ".mc"
|
||||||
|
|
||||||
# If no project URL is provided, use the current directory path
|
# Only use project_name if explicitly provided
|
||||||
if not project:
|
if project_name:
|
||||||
# Use current working directory as project identifier
|
# Create a hash of the project name to use as directory name
|
||||||
project_id = os.getcwd()
|
project_hash = hashlib.md5(project_name.encode()).hexdigest()
|
||||||
|
|
||||||
|
# Create the project config directory path
|
||||||
|
config_path = mc_home / "projects" / project_hash / "config"
|
||||||
|
|
||||||
|
# Create the directory if it doesn't exist
|
||||||
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
config_path.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
return config_path
|
||||||
else:
|
else:
|
||||||
# Use project URL as identifier
|
# If no project_name is provided, don't create any config directory
|
||||||
project_id = project
|
# This ensures we don't mount the /mc-config volume for project-less sessions
|
||||||
|
return None
|
||||||
# Create a hash of the project ID to use as directory name
|
|
||||||
project_hash = hashlib.md5(project_id.encode()).hexdigest()
|
|
||||||
|
|
||||||
# Create the project config directory path
|
|
||||||
config_path = mc_home / "projects" / project_hash / "config"
|
|
||||||
|
|
||||||
# Create the directory if it doesn't exist
|
|
||||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
config_path.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
return config_path
|
|
||||||
|
|
||||||
def list_sessions(self) -> List[Session]:
|
def list_sessions(self) -> List[Session]:
|
||||||
"""List all active MC sessions"""
|
"""List all active MC sessions"""
|
||||||
@@ -113,6 +114,7 @@ class ContainerManager:
|
|||||||
container_id=container_id,
|
container_id=container_id,
|
||||||
created_at=container.attrs["Created"],
|
created_at=container.attrs["Created"],
|
||||||
project=labels.get("mc.project"),
|
project=labels.get("mc.project"),
|
||||||
|
project_name=labels.get("mc.project_name"),
|
||||||
model=labels.get("mc.model"),
|
model=labels.get("mc.model"),
|
||||||
provider=labels.get("mc.provider"),
|
provider=labels.get("mc.provider"),
|
||||||
)
|
)
|
||||||
@@ -141,6 +143,7 @@ class ContainerManager:
|
|||||||
self,
|
self,
|
||||||
driver_name: str,
|
driver_name: str,
|
||||||
project: Optional[str] = None,
|
project: Optional[str] = None,
|
||||||
|
project_name: Optional[str] = None,
|
||||||
environment: Optional[Dict[str, str]] = None,
|
environment: Optional[Dict[str, str]] = None,
|
||||||
session_name: Optional[str] = None,
|
session_name: Optional[str] = None,
|
||||||
mount_local: bool = False,
|
mount_local: bool = False,
|
||||||
@@ -159,6 +162,7 @@ class ContainerManager:
|
|||||||
Args:
|
Args:
|
||||||
driver_name: The name of the driver to use
|
driver_name: The name of the driver to use
|
||||||
project: Optional project repository URL or local directory path
|
project: Optional project repository URL or local directory path
|
||||||
|
project_name: Optional explicit project name for configuration persistence
|
||||||
environment: Optional environment variables
|
environment: Optional environment variables
|
||||||
session_name: Optional session name
|
session_name: Optional session name
|
||||||
mount_local: Whether to mount the specified local directory to /app (ignored if project is None)
|
mount_local: Whether to mount the specified local directory to /app (ignored if project is None)
|
||||||
@@ -262,57 +266,62 @@ class ContainerManager:
|
|||||||
session_volumes[host_path] = mount_spec
|
session_volumes[host_path] = mount_spec
|
||||||
print(f"Mounting volume: {host_path} -> {container_path}")
|
print(f"Mounting volume: {host_path} -> {container_path}")
|
||||||
|
|
||||||
# Set up persistent project configuration
|
# Set up persistent project configuration if project_name is provided
|
||||||
project_config_path = self._get_project_config_path(project)
|
project_config_path = self._get_project_config_path(project, project_name)
|
||||||
print(f"Using project configuration directory: {project_config_path}")
|
if project_config_path:
|
||||||
|
print(f"Using project configuration directory: {project_config_path}")
|
||||||
|
|
||||||
# Mount the project configuration directory
|
# Mount the project configuration directory
|
||||||
session_volumes[str(project_config_path)] = {
|
session_volumes[str(project_config_path)] = {
|
||||||
"bind": "/mc-config",
|
"bind": "/mc-config",
|
||||||
"mode": "rw",
|
"mode": "rw",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add environment variables for config path
|
# Add environment variables for config path
|
||||||
env_vars["MC_CONFIG_DIR"] = "/mc-config"
|
env_vars["MC_CONFIG_DIR"] = "/mc-config"
|
||||||
env_vars["MC_DRIVER_CONFIG_DIR"] = f"/mc-config/{driver_name}"
|
env_vars["MC_DRIVER_CONFIG_DIR"] = f"/mc-config/{driver_name}"
|
||||||
|
|
||||||
# Create driver-specific config directories and set up direct volume mounts
|
# Create driver-specific config directories and set up direct volume mounts
|
||||||
if driver.persistent_configs:
|
if driver.persistent_configs:
|
||||||
persistent_links_data = [] # To store "source:target" pairs for symlinks
|
persistent_links_data = [] # To store "source:target" pairs for symlinks
|
||||||
print("Setting up persistent configuration directories:")
|
print("Setting up persistent configuration directories:")
|
||||||
for config in driver.persistent_configs:
|
for config in driver.persistent_configs:
|
||||||
# Get target directory path on host
|
# Get target directory path on host
|
||||||
target_dir = project_config_path / config.target.removeprefix(
|
target_dir = project_config_path / config.target.removeprefix(
|
||||||
"/mc-config/"
|
"/mc-config/"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create directory if it's a directory type config
|
# Create directory if it's a directory type config
|
||||||
if config.type == "directory":
|
if config.type == "directory":
|
||||||
dir_existed = target_dir.exists()
|
dir_existed = target_dir.exists()
|
||||||
target_dir.mkdir(parents=True, exist_ok=True)
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
if not dir_existed:
|
if not dir_existed:
|
||||||
print(f" - Created directory: {target_dir}")
|
print(f" - Created directory: {target_dir}")
|
||||||
# For files, make sure parent directory exists
|
# For files, make sure parent directory exists
|
||||||
elif config.type == "file":
|
elif config.type == "file":
|
||||||
target_dir.parent.mkdir(parents=True, exist_ok=True)
|
target_dir.parent.mkdir(parents=True, exist_ok=True)
|
||||||
# File will be created by the container if needed
|
# File will be created by the container if needed
|
||||||
|
|
||||||
# --- REMOVED adding to session_volumes ---
|
# Store the source and target paths for the init script
|
||||||
# We will create symlinks inside the container instead of direct mounts
|
# Note: config.target is the path *within* /mc-config
|
||||||
|
persistent_links_data.append(f"{config.source}:{config.target}")
|
||||||
|
|
||||||
# Store the source and target paths for the init script
|
print(
|
||||||
# Note: config.target is the path *within* /mc-config
|
f" - Prepared host path {target_dir} for symlink target {config.target}"
|
||||||
persistent_links_data.append(f"{config.source}:{config.target}")
|
)
|
||||||
|
|
||||||
print(
|
# Set up persistent links
|
||||||
f" - Prepared host path {target_dir} for symlink target {config.target}"
|
if persistent_links_data:
|
||||||
)
|
env_vars["MC_PERSISTENT_LINKS"] = ";".join(
|
||||||
# Set environment variable with semicolon-separated link pairs
|
persistent_links_data
|
||||||
if persistent_links_data:
|
)
|
||||||
env_vars["MC_PERSISTENT_LINKS"] = ";".join(persistent_links_data)
|
print(
|
||||||
print(
|
f"Setting MC_PERSISTENT_LINKS={env_vars['MC_PERSISTENT_LINKS']}"
|
||||||
f"Setting MC_PERSISTENT_LINKS={env_vars['MC_PERSISTENT_LINKS']}"
|
)
|
||||||
)
|
else:
|
||||||
|
print(
|
||||||
|
"No project_name provided - skipping configuration directory setup."
|
||||||
|
)
|
||||||
|
|
||||||
# Default MC network
|
# Default MC network
|
||||||
default_network = self.config_manager.config.docker.get(
|
default_network = self.config_manager.config.docker.get(
|
||||||
@@ -504,6 +513,7 @@ class ContainerManager:
|
|||||||
"mc.session.name": session_name,
|
"mc.session.name": session_name,
|
||||||
"mc.driver": driver_name,
|
"mc.driver": driver_name,
|
||||||
"mc.project": project or "",
|
"mc.project": project or "",
|
||||||
|
"mc.project_name": project_name or "",
|
||||||
"mc.mcps": ",".join(mcp_names) if mcp_names else "",
|
"mc.mcps": ",".join(mcp_names) if mcp_names else "",
|
||||||
},
|
},
|
||||||
network=network_list[0], # Connect to the first network initially
|
network=network_list[0], # Connect to the first network initially
|
||||||
@@ -613,6 +623,7 @@ class ContainerManager:
|
|||||||
container_id=container.id,
|
container_id=container.id,
|
||||||
environment=env_vars,
|
environment=env_vars,
|
||||||
project=project,
|
project=project,
|
||||||
|
project_name=project_name,
|
||||||
created_at=container.attrs["Created"],
|
created_at=container.attrs["Created"],
|
||||||
ports=ports,
|
ports=ports,
|
||||||
mcps=mcp_names,
|
mcps=mcp_names,
|
||||||
|
|||||||
@@ -110,6 +110,13 @@ if [ -n "$LANGFUSE_INIT_PROJECT_SECRET_KEY" ] && [ -n "$LANGFUSE_INIT_PROJECT_PU
|
|||||||
export LANGFUSE_URL="${LANGFUSE_URL:-https://cloud.langfuse.com}"
|
export LANGFUSE_URL="${LANGFUSE_URL:-https://cloud.langfuse.com}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Ensure /mc-config directory exists (required for symlinks)
|
||||||
|
if [ ! -d "/mc-config" ]; then
|
||||||
|
echo "Creating /mc-config directory since it doesn't exist"
|
||||||
|
mkdir -p /mc-config
|
||||||
|
chown $MC_USER_ID:$MC_GROUP_ID /mc-config
|
||||||
|
fi
|
||||||
|
|
||||||
# Create symlinks for persistent configurations defined in the driver
|
# Create symlinks for persistent configurations defined in the driver
|
||||||
if [ -n "$MC_PERSISTENT_LINKS" ]; then
|
if [ -n "$MC_PERSISTENT_LINKS" ]; then
|
||||||
echo "Creating persistent configuration symlinks..."
|
echo "Creating persistent configuration symlinks..."
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class Session(BaseModel):
|
|||||||
container_id: Optional[str] = None
|
container_id: Optional[str] = None
|
||||||
environment: Dict[str, str] = Field(default_factory=dict)
|
environment: Dict[str, str] = Field(default_factory=dict)
|
||||||
project: Optional[str] = None
|
project: Optional[str] = None
|
||||||
|
project_name: Optional[str] = None
|
||||||
created_at: str
|
created_at: str
|
||||||
ports: Dict[int, int] = Field(default_factory=dict)
|
ports: Dict[int, int] = Field(default_factory=dict)
|
||||||
mcps: List[str] = Field(default_factory=list)
|
mcps: List[str] = Field(default_factory=list)
|
||||||
|
|||||||
Reference in New Issue
Block a user