feat(volume): add mc config volume command

This commit is contained in:
2025-03-12 12:00:08 -06:00
parent deb5945e40
commit 2caeb42551
3 changed files with 139 additions and 2 deletions

View File

@@ -142,6 +142,23 @@ mc config network add teamnet
mc config network remove teamnet
```
### Default Volumes Configuration
You can configure default volumes that will be automatically mounted in every new session:
```bash
# List default volumes
mc config volume list
# Add a volume to defaults
mc config volume add /local/path:/container/path
# Remove a volume from defaults (will prompt if multiple matches found)
mc config volume remove /local/path
```
Default volumes will be combined with any volumes specified using the `-v` flag when creating a session.
### External Network Connectivity
MC containers can connect to external Docker networks, allowing them to communicate with other services in those networks:

View File

@@ -151,7 +151,14 @@ def create_session(
# Parse volume mounts
volume_mounts = {}
for vol in volume:
# Get default volumes from user config
default_volumes = user_config.get("defaults.volumes", [])
# Combine default volumes with user-specified volumes
all_volumes = default_volumes + list(volume)
for vol in all_volumes:
if ":" in vol:
local_path, container_path = vol.split(":", 1)
# Convert to absolute path if relative
@@ -165,7 +172,7 @@ def create_session(
)
continue
# Add to volume mounts
# Add to volume mounts (later entries override earlier ones with same host path)
volume_mounts[local_path] = {"bind": container_path, "mode": "rw"}
else:
console.print(
@@ -181,6 +188,12 @@ def create_session(
if all_networks:
console.print(f"Networks: {', '.join(all_networks)}")
# Show volumes that will be mounted
if volume_mounts:
console.print("Volumes:")
for host_path, mount_info in volume_mounts.items():
console.print(f" {host_path} -> {mount_info['bind']}")
with console.status(f"Creating session with driver '{driver}'..."):
session = container_manager.create_session(
driver_name=driver,
@@ -473,6 +486,10 @@ def driver_info(
network_app = typer.Typer(help="Manage default networks")
config_app.add_typer(network_app, name="network", no_args_is_help=True)
# Create a volume subcommand for config
volume_app = typer.Typer(help="Manage default volumes")
config_app.add_typer(volume_app, name="volume", no_args_is_help=True)
# Configuration commands
@config_app.command("list")
@@ -620,5 +637,107 @@ def remove_network(
console.print(f"[green]Removed network '{network}' from defaults[/green]")
# Volume configuration commands
@volume_app.command("list")
def list_volumes() -> None:
"""List all default volumes"""
volumes = user_config.get("defaults.volumes", [])
if not volumes:
console.print("No default volumes configured")
return
table = Table(show_header=True, header_style="bold")
table.add_column("Local Path")
table.add_column("Container Path")
for volume in volumes:
if ":" in volume:
local_path, container_path = volume.split(":", 1)
table.add_row(local_path, container_path)
else:
table.add_row(volume, "[yellow]Invalid format[/yellow]")
console.print(table)
@volume_app.command("add")
def add_volume(
volume: str = typer.Argument(
..., help="Volume to add (format: LOCAL_PATH:CONTAINER_PATH)"
),
) -> None:
"""Add a volume to default volumes"""
volumes = user_config.get("defaults.volumes", [])
# Validate format
if ":" not in volume:
console.print(
"[red]Invalid volume format. Use LOCAL_PATH:CONTAINER_PATH.[/red]"
)
return
local_path, container_path = volume.split(":", 1)
# Convert to absolute path if relative
if not os.path.isabs(local_path):
local_path = os.path.abspath(local_path)
volume = f"{local_path}:{container_path}"
# Validate local path exists
if not os.path.exists(local_path):
console.print(
f"[yellow]Warning: Local path '{local_path}' does not exist.[/yellow]"
)
if not typer.confirm("Add anyway?"):
return
# Check if volume is already in defaults
if volume in volumes:
console.print(f"Volume '{volume}' is already in defaults")
return
volumes.append(volume)
user_config.set("defaults.volumes", volumes)
console.print(f"[green]Added volume '{volume}' to defaults[/green]")
@volume_app.command("remove")
def remove_volume(
volume: str = typer.Argument(
..., help="Volume to remove (format: LOCAL_PATH:CONTAINER_PATH)"
),
) -> None:
"""Remove a volume from default volumes"""
volumes = user_config.get("defaults.volumes", [])
# Handle case where user provides just a prefix to match
matching_volumes = [v for v in volumes if v.startswith(volume)]
if not matching_volumes:
console.print(f"No volumes matching '{volume}' found in defaults")
return
if len(matching_volumes) > 1:
console.print(f"Multiple volumes match '{volume}':")
for i, v in enumerate(matching_volumes):
console.print(f" {i + 1}. {v}")
index = typer.prompt(
"Enter the number of the volume to remove (0 to cancel)", type=int
)
if index == 0 or index > len(matching_volumes):
console.print("Volume removal canceled")
return
volume_to_remove = matching_volumes[index - 1]
else:
volume_to_remove = matching_volumes[0]
volumes.remove(volume_to_remove)
user_config.set("defaults.volumes", volumes)
console.print(f"[green]Removed volume '{volume_to_remove}' from defaults[/green]")
if __name__ == "__main__":
app()

View File

@@ -92,6 +92,7 @@ class UserConfigManager:
"connect": True,
"mount_local": True,
"networks": [], # Default networks to connect to (besides mc-network)
"volumes": [], # Default volumes to mount, format: "source:dest"
},
"services": {
"langfuse": {},