mirror of
https://github.com/Monadical-SAS/cubbi.git
synced 2025-12-20 20:29:06 +00:00
fix(uid): use symlink instead of volume for persistent volume in the container
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,3 +8,7 @@ wheels/
|
|||||||
|
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
.venv
|
.venv
|
||||||
|
|
||||||
|
# Aider
|
||||||
|
.aider*
|
||||||
|
.goose
|
||||||
|
|||||||
@@ -188,10 +188,6 @@ class ContainerManager:
|
|||||||
if gid is not None:
|
if gid is not None:
|
||||||
env_vars["TARGET_GID"] = str(gid)
|
env_vars["TARGET_GID"] = str(gid)
|
||||||
|
|
||||||
# Add project URL to environment if provided
|
|
||||||
if project:
|
|
||||||
env_vars["MC_PROJECT_URL"] = project
|
|
||||||
|
|
||||||
# Pass API keys from host environment to container for local development
|
# Pass API keys from host environment to container for local development
|
||||||
api_keys = [
|
api_keys = [
|
||||||
"OPENAI_API_KEY",
|
"OPENAI_API_KEY",
|
||||||
@@ -236,6 +232,7 @@ class ContainerManager:
|
|||||||
# Clear project for container environment since we're mounting
|
# Clear project for container environment since we're mounting
|
||||||
project = None
|
project = None
|
||||||
elif is_git_repo:
|
elif is_git_repo:
|
||||||
|
env_vars["MC_PROJECT_URL"] = project
|
||||||
print(
|
print(
|
||||||
f"Git repository URL provided - container will clone {project} into /app during initialization"
|
f"Git repository URL provided - container will clone {project} into /app during initialization"
|
||||||
)
|
)
|
||||||
@@ -273,6 +270,7 @@ class ContainerManager:
|
|||||||
|
|
||||||
# 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
|
||||||
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
|
||||||
@@ -291,13 +289,21 @@ class ContainerManager:
|
|||||||
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
|
||||||
|
|
||||||
# Mount persistent config directly to container path
|
# --- REMOVED adding to session_volumes ---
|
||||||
session_volumes[str(target_dir)] = {
|
# We will create symlinks inside the container instead of direct mounts
|
||||||
"bind": config.source,
|
|
||||||
"mode": "rw",
|
# Store the source and target paths for the init script
|
||||||
}
|
# Note: config.target is the path *within* /mc-config
|
||||||
|
persistent_links_data.append(f"{config.source}:{config.target}")
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f" - Created direct volume mount: {target_dir} -> {config.source}"
|
f" - Prepared host path {target_dir} for symlink target {config.target}"
|
||||||
|
)
|
||||||
|
# Set environment variable with semicolon-separated link pairs
|
||||||
|
if persistent_links_data:
|
||||||
|
env_vars["MC_PERSISTENT_LINKS"] = ";".join(persistent_links_data)
|
||||||
|
print(
|
||||||
|
f"Setting MC_PERSISTENT_LINKS={env_vars['MC_PERSISTENT_LINKS']}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Default MC network
|
# Default MC network
|
||||||
@@ -634,7 +640,7 @@ class ContainerManager:
|
|||||||
return False
|
return False
|
||||||
container_id = session_obj.container_id
|
container_id = session_obj.container_id
|
||||||
print(
|
print(
|
||||||
f"[yellow]Warning: Session data missing for {session_id}. Attaching as default container user.[/yellow]"
|
f"[yellow]Warning: Session data missing for {session_id}. Connecting as default container user.[/yellow]"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
container_id = session_data.get("container_id")
|
container_id = session_data.get("container_id")
|
||||||
@@ -660,18 +666,19 @@ class ContainerManager:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Attach to the container's main process TTY
|
# Use exec instead of attach to avoid container exit on Ctrl+C
|
||||||
# This allows seeing the output of --run command followed by the shell
|
|
||||||
# The user context (UID/GID) is determined when the container is created,
|
|
||||||
# attach respects that context.
|
|
||||||
print(
|
print(
|
||||||
f"Attaching to session {session_id} (container: {container_id[:12]})..."
|
f"Connecting to session {session_id} (container: {container_id[:12]})..."
|
||||||
)
|
)
|
||||||
print("Type 'exit' or Ctrl+P, Ctrl+Q (by default) to detach.")
|
print("Type 'exit' to detach from the session.")
|
||||||
cmd = ["docker", "attach", container_id]
|
|
||||||
|
|
||||||
# Use execvp to replace the current process with docker attach
|
# Use docker exec to start a new bash process in the container
|
||||||
# This provides a more seamless shell experience
|
# This leverages the init-status.sh script in bash.bashrc
|
||||||
|
# which will check initialization status
|
||||||
|
cmd = ["docker", "exec", "-it", container_id, "bash", "-l"]
|
||||||
|
|
||||||
|
# Use execvp to replace the current process with docker exec
|
||||||
|
# This provides a seamless shell experience
|
||||||
os.execvp("docker", cmd)
|
os.execvp("docker", cmd)
|
||||||
# execvp does not return if successful
|
# execvp does not return if successful
|
||||||
return True # Should not be reached if execvp succeeds
|
return True # Should not be reached if execvp succeeds
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Script to check and display initialization status - optimized version
|
# Script to check and display initialization status - optimized version
|
||||||
|
|
||||||
|
# Only proceed if running as root
|
||||||
|
if [ "$(id -u)" != "0" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Quick check instead of full logic
|
# Quick check instead of full logic
|
||||||
if grep -q "INIT_COMPLETE=true" "/init.status" 2>/dev/null; then
|
if grep -q "INIT_COMPLETE=true" "/init.status" 2>/dev/null; then
|
||||||
echo "MC initialization has completed."
|
echo "MC initialization has completed."
|
||||||
@@ -27,3 +32,5 @@ else
|
|||||||
echo "No initialization logs found."
|
echo "No initialization logs found."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
exec gosu mcuser /bin/bash -il
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ persistent_configs:
|
|||||||
target: "/mc-config/goose-app"
|
target: "/mc-config/goose-app"
|
||||||
type: "directory"
|
type: "directory"
|
||||||
description: "Goose memory"
|
description: "Goose memory"
|
||||||
- source: "/root/.config/goose"
|
- source: "/home/mcuser/.config/goose"
|
||||||
target: "/mc-config/goose-config"
|
target: "/mc-config/goose-config"
|
||||||
type: "directory"
|
type: "directory"
|
||||||
description: "Goose configuration"
|
description: "Goose configuration"
|
||||||
|
|||||||
@@ -38,11 +38,9 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create home directory and set permissions if it doesn't exist
|
# Create home directory and set permissions
|
||||||
if [ ! -d "/home/mcuser" ]; then
|
mkdir -p /home/mcuser
|
||||||
mkdir -p /home/mcuser
|
chown $MC_USER_ID:$MC_GROUP_ID /home/mcuser
|
||||||
chown $MC_USER_ID:$MC_GROUP_ID /home/mcuser
|
|
||||||
fi
|
|
||||||
# Ensure /app exists and has correct ownership (important for volume mounts)
|
# Ensure /app exists and has correct ownership (important for volume mounts)
|
||||||
mkdir -p /app
|
mkdir -p /app
|
||||||
chown $MC_USER_ID:$MC_GROUP_ID /app
|
chown $MC_USER_ID:$MC_GROUP_ID /app
|
||||||
@@ -112,7 +110,42 @@ else
|
|||||||
echo "Warning: update-goose-config.sh script not found. Goose configuration will not be updated."
|
echo "Warning: update-goose-config.sh script not found. Goose configuration will not be updated."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "MC driver initialization complete"
|
# Create symlinks for persistent configurations defined in the driver
|
||||||
|
if [ -n "$MC_PERSISTENT_LINKS" ]; then
|
||||||
|
echo "Creating persistent configuration symlinks..."
|
||||||
|
# Split by semicolon
|
||||||
|
IFS=';' read -ra LINKS <<< "$MC_PERSISTENT_LINKS"
|
||||||
|
for link_pair in "${LINKS[@]}"; do
|
||||||
|
# Split by colon
|
||||||
|
IFS=':' read -r source_path target_path <<< "$link_pair"
|
||||||
|
|
||||||
|
if [ -z "$source_path" ] || [ -z "$target_path" ]; then
|
||||||
|
echo "Warning: Invalid link pair format '$link_pair', skipping."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Processing link: $source_path -> $target_path"
|
||||||
|
parent_dir=$(dirname "$source_path")
|
||||||
|
|
||||||
|
# Ensure parent directory of the link source exists and is owned by mcuser
|
||||||
|
if [ ! -d "$parent_dir" ]; then
|
||||||
|
echo "Creating parent directory: $parent_dir"
|
||||||
|
mkdir -p "$parent_dir"
|
||||||
|
echo "Changing ownership of parent $parent_dir to $MC_USER_ID:$MC_GROUP_ID"
|
||||||
|
chown "$MC_USER_ID:$MC_GROUP_ID" "$parent_dir" || echo "Warning: Could not chown parent $parent_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the symlink (force, no-dereference)
|
||||||
|
echo "Creating symlink: ln -sfn $target_path $source_path"
|
||||||
|
ln -sfn "$target_path" "$source_path"
|
||||||
|
|
||||||
|
# Optionally, change ownership of the symlink itself
|
||||||
|
echo "Changing ownership of symlink $source_path to $MC_USER_ID:$MC_GROUP_ID"
|
||||||
|
chown -h "$MC_USER_ID:$MC_GROUP_ID" "$source_path" || echo "Warning: Could not chown symlink $source_path"
|
||||||
|
|
||||||
|
done
|
||||||
|
echo "Persistent configuration symlinks created."
|
||||||
|
fi
|
||||||
|
|
||||||
# Mark initialization as complete
|
# Mark initialization as complete
|
||||||
echo "=== MC Initialization completed at $(date) ==="
|
echo "=== MC Initialization completed at $(date) ==="
|
||||||
@@ -126,21 +159,4 @@ if [ -n "$MC_RUN_COMMAND" ]; then
|
|||||||
echo "--- Initial command finished (exit code: $COMMAND_EXIT_CODE) ---";
|
echo "--- Initial command finished (exit code: $COMMAND_EXIT_CODE) ---";
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
# Determine the final command (the interactive shell)
|
exec gosu mcuser "$@"
|
||||||
FINAL_CMD=("$@")
|
|
||||||
if [ ${#FINAL_CMD[@]} -eq 0 ]; then
|
|
||||||
# Default to /bin/bash if CMD wasn't passed or was empty
|
|
||||||
FINAL_CMD=("/bin/bash")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If the final command is bash, ensure it runs interactively
|
|
||||||
# Check if the first argument is /bin/bash and -i is not already present
|
|
||||||
if [ "${FINAL_CMD[0]}" = "/bin/bash" ] && [[ ! " ${FINAL_CMD[@]} " =~ " -i " ]]; then
|
|
||||||
# Add the -i flag to the command array
|
|
||||||
FINAL_CMD+=("-i")
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "--- Starting interactive shell (${FINAL_CMD[*]}) ---";
|
|
||||||
# Now exec gosu directly into the final command, replacing this script process
|
|
||||||
# "${FINAL_CMD[@]}" ensures arguments are passed correctly (e.g., /bin/bash -i)
|
|
||||||
exec gosu mcuser "${FINAL_CMD[@]}"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user