mirror of
https://github.com/Monadical-SAS/cubbi.git
synced 2025-12-20 20:29:06 +00:00
feat(volume): add the possibilty to mount local directory into the container (like docker volume)
This commit is contained in:
@@ -46,6 +46,10 @@ mc session create --driver goose
|
|||||||
# Create a session with environment variables
|
# Create a session with environment variables
|
||||||
mc session create -e VAR1=value1 -e VAR2=value2
|
mc session create -e VAR1=value1 -e VAR2=value2
|
||||||
|
|
||||||
|
# Mount custom volumes (similar to Docker's -v flag)
|
||||||
|
mc session create -v /local/path:/container/path
|
||||||
|
mc session create -v ~/data:/data -v ./configs:/etc/app/config
|
||||||
|
|
||||||
# Shorthand for creating a session with a project repository
|
# Shorthand for creating a session with a project repository
|
||||||
mc github.com/username/repo
|
mc github.com/username/repo
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ def create_session(
|
|||||||
env: List[str] = typer.Option(
|
env: List[str] = typer.Option(
|
||||||
[], "--env", "-e", help="Environment variables (KEY=VALUE)"
|
[], "--env", "-e", help="Environment variables (KEY=VALUE)"
|
||||||
),
|
),
|
||||||
|
volume: List[str] = typer.Option(
|
||||||
|
[], "--volume", "-v", help="Mount volumes (LOCAL_PATH:CONTAINER_PATH)"
|
||||||
|
),
|
||||||
name: Optional[str] = typer.Option(None, "--name", "-n", help="Session name"),
|
name: Optional[str] = typer.Option(None, "--name", "-n", help="Session name"),
|
||||||
no_connect: bool = typer.Option(
|
no_connect: bool = typer.Option(
|
||||||
False, "--no-connect", help="Don't automatically connect to the session"
|
False, "--no-connect", help="Don't automatically connect to the session"
|
||||||
@@ -139,6 +142,29 @@ def create_session(
|
|||||||
f"[yellow]Warning: Ignoring invalid environment variable format: {var}[/yellow]"
|
f"[yellow]Warning: Ignoring invalid environment variable format: {var}[/yellow]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Parse volume mounts
|
||||||
|
volume_mounts = {}
|
||||||
|
for vol in volume:
|
||||||
|
if ":" in vol:
|
||||||
|
local_path, container_path = vol.split(":", 1)
|
||||||
|
# Convert to absolute path if relative
|
||||||
|
if not os.path.isabs(local_path):
|
||||||
|
local_path = os.path.abspath(local_path)
|
||||||
|
|
||||||
|
# Validate local path exists
|
||||||
|
if not os.path.exists(local_path):
|
||||||
|
console.print(
|
||||||
|
f"[yellow]Warning: Local path '{local_path}' does not exist. Volume will not be mounted.[/yellow]"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add to volume mounts
|
||||||
|
volume_mounts[local_path] = {"bind": container_path, "mode": "rw"}
|
||||||
|
else:
|
||||||
|
console.print(
|
||||||
|
f"[yellow]Warning: Ignoring invalid volume format: {vol}. Use LOCAL_PATH:CONTAINER_PATH.[/yellow]"
|
||||||
|
)
|
||||||
|
|
||||||
with console.status(f"Creating session with driver '{driver}'..."):
|
with console.status(f"Creating session with driver '{driver}'..."):
|
||||||
session = container_manager.create_session(
|
session = container_manager.create_session(
|
||||||
driver_name=driver,
|
driver_name=driver,
|
||||||
@@ -146,6 +172,7 @@ def create_session(
|
|||||||
environment=environment,
|
environment=environment,
|
||||||
session_name=name,
|
session_name=name,
|
||||||
mount_local=not no_mount and user_config.get("defaults.mount_local", True),
|
mount_local=not no_mount and user_config.get("defaults.mount_local", True),
|
||||||
|
volumes=volume_mounts,
|
||||||
)
|
)
|
||||||
|
|
||||||
if session:
|
if session:
|
||||||
@@ -284,6 +311,9 @@ def quick_create(
|
|||||||
env: List[str] = typer.Option(
|
env: List[str] = typer.Option(
|
||||||
[], "--env", "-e", help="Environment variables (KEY=VALUE)"
|
[], "--env", "-e", help="Environment variables (KEY=VALUE)"
|
||||||
),
|
),
|
||||||
|
volume: List[str] = typer.Option(
|
||||||
|
[], "--volume", "-v", help="Mount volumes (LOCAL_PATH:CONTAINER_PATH)"
|
||||||
|
),
|
||||||
name: Optional[str] = typer.Option(None, "--name", "-n", help="Session name"),
|
name: Optional[str] = typer.Option(None, "--name", "-n", help="Session name"),
|
||||||
no_connect: bool = typer.Option(
|
no_connect: bool = typer.Option(
|
||||||
False, "--no-connect", help="Don't automatically connect to the session"
|
False, "--no-connect", help="Don't automatically connect to the session"
|
||||||
@@ -303,6 +333,7 @@ def quick_create(
|
|||||||
driver=driver,
|
driver=driver,
|
||||||
project=project,
|
project=project,
|
||||||
env=env,
|
env=env,
|
||||||
|
volume=volume,
|
||||||
name=name,
|
name=name,
|
||||||
no_connect=no_connect,
|
no_connect=no_connect,
|
||||||
no_mount=no_mount,
|
no_mount=no_mount,
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ class ContainerManager:
|
|||||||
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 = True,
|
mount_local: bool = True,
|
||||||
|
volumes: Optional[Dict[str, Dict[str, str]]] = None,
|
||||||
) -> Optional[Session]:
|
) -> Optional[Session]:
|
||||||
"""Create a new MC session
|
"""Create a new MC session
|
||||||
|
|
||||||
@@ -134,6 +135,7 @@ class ContainerManager:
|
|||||||
environment: Optional environment variables
|
environment: Optional environment variables
|
||||||
session_name: Optional session name
|
session_name: Optional session name
|
||||||
mount_local: Whether to mount the current directory to /app
|
mount_local: Whether to mount the current directory to /app
|
||||||
|
volumes: Optional additional volumes to mount (dict of {host_path: {"bind": container_path, "mode": mode}})
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Validate driver exists
|
# Validate driver exists
|
||||||
@@ -178,25 +180,46 @@ class ContainerManager:
|
|||||||
self.client.images.pull(driver.image)
|
self.client.images.pull(driver.image)
|
||||||
|
|
||||||
# Set up volume mounts
|
# Set up volume mounts
|
||||||
volumes = {}
|
session_volumes = {}
|
||||||
|
|
||||||
# If project URL is provided, don't mount local directory (will clone into /app)
|
# If project URL is provided, don't mount local directory (will clone into /app)
|
||||||
# If no project URL and mount_local is True, mount local directory to /app
|
# If no project URL and mount_local is True, mount local directory to /app
|
||||||
if not project and mount_local:
|
if not project and mount_local:
|
||||||
# Mount current directory to /app in the container
|
# Mount current directory to /app in the container
|
||||||
current_dir = os.getcwd()
|
current_dir = os.getcwd()
|
||||||
volumes[current_dir] = {"bind": "/app", "mode": "rw"}
|
session_volumes[current_dir] = {"bind": "/app", "mode": "rw"}
|
||||||
print(f"Mounting local directory {current_dir} to /app")
|
print(f"Mounting local directory {current_dir} to /app")
|
||||||
elif project:
|
elif project:
|
||||||
print(
|
print(
|
||||||
f"Project URL provided - container will clone {project} into /app during initialization"
|
f"Project URL provided - container will clone {project} into /app during initialization"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add user-specified volumes
|
||||||
|
if volumes:
|
||||||
|
for host_path, mount_spec in volumes.items():
|
||||||
|
container_path = mount_spec["bind"]
|
||||||
|
# Check for conflicts with /app mount
|
||||||
|
if container_path == "/app" and not project and mount_local:
|
||||||
|
print(
|
||||||
|
"[yellow]Warning: Volume mount to /app conflicts with automatic local directory mount. User-specified mount takes precedence.[/yellow]"
|
||||||
|
)
|
||||||
|
# Remove the automatic mount if there's a conflict
|
||||||
|
if current_dir in session_volumes:
|
||||||
|
del session_volumes[current_dir]
|
||||||
|
|
||||||
|
# Add the volume
|
||||||
|
session_volumes[host_path] = mount_spec
|
||||||
|
print(f"Mounting volume: {host_path} -> {container_path}")
|
||||||
|
|
||||||
# Set up persistent project configuration
|
# Set up persistent project configuration
|
||||||
project_config_path = self._get_project_config_path(project)
|
project_config_path = self._get_project_config_path(project)
|
||||||
print(f"Using project configuration directory: {project_config_path}")
|
print(f"Using project configuration directory: {project_config_path}")
|
||||||
|
|
||||||
# Mount the project configuration directory
|
# Mount the project configuration directory
|
||||||
volumes[str(project_config_path)] = {"bind": "/mc-config", "mode": "rw"}
|
session_volumes[str(project_config_path)] = {
|
||||||
|
"bind": "/mc-config",
|
||||||
|
"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"
|
||||||
@@ -230,7 +253,7 @@ class ContainerManager:
|
|||||||
tty=True,
|
tty=True,
|
||||||
stdin_open=True,
|
stdin_open=True,
|
||||||
environment=env_vars,
|
environment=env_vars,
|
||||||
volumes=volumes,
|
volumes=session_volumes,
|
||||||
labels={
|
labels={
|
||||||
"mc.session": "true",
|
"mc.session": "true",
|
||||||
"mc.session.id": session_id,
|
"mc.session.id": session_id,
|
||||||
|
|||||||
Reference in New Issue
Block a user