SERVER-113070: Use a volume for the devcontainer home directory for p… (#43588)

GitOrigin-RevId: 873ba6fc7e9150bafc9364b67bc279f9817265fb
This commit is contained in:
Eric Lavigne 2025-11-06 15:43:30 -07:00 committed by MongoDB Bot
parent b71ae2b570
commit afd729fc6c
8 changed files with 189 additions and 55 deletions

View File

@ -39,13 +39,6 @@ RUN chmod +x /usr/local/bin/xdg-open-wrapper.sh && \
RUN echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/devcontaineruser && \ RUN echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/devcontaineruser && \
chmod 0440 /etc/sudoers.d/devcontaineruser chmod 0440 /etc/sudoers.d/devcontaineruser
# Persistent bash history
RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
&& mkdir /commandhistory \
&& touch /commandhistory/.bash_history \
&& chown -R $USERNAME /commandhistory \
&& echo "$SNIPPET" >> "/home/$USERNAME/.bashrc"
# Toolchain installation with SHA256 verification # Toolchain installation with SHA256 verification
# Run "python3 toolchain.py generate" to update toolchain_config.env # Run "python3 toolchain.py generate" to update toolchain_config.env
ARG TARGETPLATFORM ARG TARGETPLATFORM
@ -80,35 +73,41 @@ USER $USERNAME
ENV USER=${USERNAME} ENV USER=${USERNAME}
RUN /opt/mongodbtoolchain/revisions/*/scripts/install.sh; echo "Toolchain installation complete" RUN /opt/mongodbtoolchain/revisions/*/scripts/install.sh; echo "Toolchain installation complete"
# Add MongoDB toolchain to PATH # Add MongoDB toolchain to PATH via system-wide profile
ENV PATH="/opt/mongodbtoolchain/v5/bin:${PATH}" USER root
RUN echo 'export PATH="/opt/mongodbtoolchain/v5/bin:${PATH}"' > /etc/profile.d/02-mongodbtoolchain.sh \
# Setup bash profile to source .bashrc && chmod +x /etc/profile.d/02-mongodbtoolchain.sh
RUN echo "" >> "$HOME/.bash_profile" && \ USER $USERNAME
echo "# BEGIN Source .bashrc" >> "$HOME/.bash_profile" && \
echo "if [[ -f ~/.bashrc ]]; then" >> "$HOME/.bash_profile" && \
echo " source ~/.bashrc" >> "$HOME/.bash_profile" && \
echo "fi" >> "$HOME/.bash_profile" && \
echo "# END Source .bashrc" >> "$HOME/.bash_profile"
# Create MongoDB data directory # Create MongoDB data directory
USER root USER root
RUN mkdir -p /data/db && chown -R ${USERNAME}:${USERNAME} /data/db RUN mkdir -p /data/db && chown -R ${USERNAME}:${USERNAME} /data/db
USER $USERNAME USER $USERNAME
# Bazel telemetry # Bazel telemetry - configure system-wide defaults
RUN echo "common --bes_keywords=devcontainer:use=true" >> "$HOME/.bazelrc" && \ # These will be imported by user's .bazelrc in post-create script
echo "common --bes_keywords=devcontainer:image=$BASE_IMAGE" >> "$HOME/.bazelrc" && \ USER root
echo "common --bes_keywords=devcontainer:username=$USERNAME" >> "$HOME/.bazelrc" RUN mkdir -p /etc/devcontainer && \
echo "# MongoDB Devcontainer Bazel Configuration" > /etc/devcontainer/bazelrc && \
echo "common --bes_keywords=devcontainer:use=true" >> /etc/devcontainer/bazelrc && \
echo "common --bes_keywords=devcontainer:image=$BASE_IMAGE" >> /etc/devcontainer/bazelrc && \
chmod 644 /etc/devcontainer/bazelrc
USER $USERNAME
# Install pipx (Python package manager for tools) # Install pipx (Python package manager for tools)
# Add ~/.local/bin to PATH for pipx-installed tools
USER root
RUN echo 'export PATH="$HOME/.local/bin:${PATH}"' > /etc/profile.d/03-local-bin.sh \
&& chmod +x /etc/profile.d/03-local-bin.sh
USER $USERNAME
ENV PATH="/home/${USERNAME}/.local/bin:${PATH}" ENV PATH="/home/${USERNAME}/.local/bin:${PATH}"
RUN /opt/mongodbtoolchain/v5/bin/python3 -m venv /tmp/pipx-venv && \ RUN /opt/mongodbtoolchain/v5/bin/python3 -m venv /tmp/pipx-venv && \
/tmp/pipx-venv/bin/python -m pip install --upgrade "pip<20.3" && \ /tmp/pipx-venv/bin/python -m pip install --upgrade "pip<20.3" && \
/tmp/pipx-venv/bin/python -m pip install pipx && \ /tmp/pipx-venv/bin/python -m pip install pipx && \
/tmp/pipx-venv/bin/pipx install pipx --python /opt/mongodbtoolchain/v5/bin/python3 --force && \ /tmp/pipx-venv/bin/pipx install pipx --python /opt/mongodbtoolchain/v5/bin/python3 --force && \
/tmp/pipx-venv/bin/pipx ensurepath --force && \
rm -rf /tmp/pipx-venv rm -rf /tmp/pipx-venv
# Note: PATH is configured via /etc/profile.d, not ~/.bashrc, to avoid modifying home volume
# Install db-contrib-tool using pipx # Install db-contrib-tool using pipx
RUN /home/${USERNAME}/.local/bin/pipx install db-contrib-tool RUN /home/${USERNAME}/.local/bin/pipx install db-contrib-tool

View File

@ -11,6 +11,11 @@
} }
}, },
"mounts": [ "mounts": [
{
"source": "mongo-dev-home",
"target": "/home",
"type": "volume"
},
{ {
"source": "engflow_auth", "source": "engflow_auth",
"target": "/home/${localEnv:USER}/.config/engflow_auth", "target": "/home/${localEnv:USER}/.config/engflow_auth",
@ -21,15 +26,16 @@
"target": "/home/${localEnv:USER}/.cache", "target": "/home/${localEnv:USER}/.cache",
"type": "volume" "type": "volume"
}, },
{
"source": "mongo-bashhistory",
"target": "/commandhistory",
"type": "volume"
},
{ {
"source": "${containerWorkspaceFolderBasename}-python3-venv", "source": "${containerWorkspaceFolderBasename}-python3-venv",
"target": "${containerWorkspaceFolder}/python3-venv", "target": "${containerWorkspaceFolder}/python3-venv",
"type": "volume" "type": "volume"
},
{
"source": "${localEnv:HOME}/.ssh",
"target": "/home/${localEnv:USER}/.ssh",
"type": "bind",
"readonly": true
} }
], ],
"containerEnv": { "containerEnv": {

View File

@ -8,10 +8,28 @@ set -euo pipefail
echo "Starting MongoDB development container post-create setup..." echo "Starting MongoDB development container post-create setup..."
# Step 1: Fix volume permissions # Setup user's .bazelrc to import system-wide devcontainer config
echo "Setting up Bazel configuration..."
if [ -f "/etc/devcontainer/bazelrc" ]; then
if [ ! -f "${HOME}/.bazelrc" ]; then
# Create new .bazelrc with import
echo "# Import devcontainer system configuration" >"${HOME}/.bazelrc"
echo "try-import /etc/devcontainer/bazelrc" >>"${HOME}/.bazelrc"
echo "[OK] Created .bazelrc with system config import"
elif ! grep -q "try-import /etc/devcontainer/bazelrc" "${HOME}/.bazelrc" 2>/dev/null; then
# Add import to existing .bazelrc
echo "" >>"${HOME}/.bazelrc"
echo "# Import devcontainer system configuration" >>"${HOME}/.bazelrc"
echo "try-import /etc/devcontainer/bazelrc" >>"${HOME}/.bazelrc"
echo "[OK] Added system config import to existing .bazelrc"
else
echo "Info: .bazelrc already imports system config"
fi
fi
# Fix volume permissions
echo "Fixing volume permissions..." echo "Fixing volume permissions..."
sudo chown -R "$(whoami)": "${HOME}/.config/engflow_auth" || echo "Warning: Could not fix engflow_auth permissions" sudo chown -R "$(whoami)": "${HOME}" || echo "Warning: Could not fix home directory permissions"
sudo chown -R "$(whoami)": "${HOME}/.cache" || echo "Warning: Could not fix cache permissions"
sudo chown -R "$(whoami)": "${WORKSPACE_FOLDER}/python3-venv" || echo "Warning: Could not fix python3-venv permissions" sudo chown -R "$(whoami)": "${WORKSPACE_FOLDER}/python3-venv" || echo "Warning: Could not fix python3-venv permissions"
sudo chown "$(whoami)": "${WORKSPACE_FOLDER}/.." || echo "Warning: Could not fix parent directory permissions" sudo chown "$(whoami)": "${WORKSPACE_FOLDER}/.." || echo "Warning: Could not fix parent directory permissions"
@ -28,7 +46,7 @@ echo "Configuring git safe.directory..."
git config --global --add safe.directory "${WORKSPACE_FOLDER}" || echo "Warning: Could not configure git safe.directory" git config --global --add safe.directory "${WORKSPACE_FOLDER}" || echo "Warning: Could not configure git safe.directory"
echo "[OK] Volume and Git permissions fixed" echo "[OK] Volume and Git permissions fixed"
# Step 2: Configure Bazel with Docker information (one-time container setup) # Configure Bazel with Docker information (one-time container setup)
echo "Configuring Bazel with Docker server information..." echo "Configuring Bazel with Docker server information..."
# Helper function to add Bazel keyword if not already present # Helper function to add Bazel keyword if not already present
@ -57,7 +75,7 @@ add_bazel_keyword "docker_server_version" "${DOCKER_VERSION}"
ARCH=$(uname -i 2>/dev/null || echo "unknown") ARCH=$(uname -i 2>/dev/null || echo "unknown")
add_bazel_keyword "arch" "${ARCH}" add_bazel_keyword "arch" "${ARCH}"
# Step 4: Unshallow Git repository (one-time setup) # Unshallow Git repository (one-time setup)
echo "Setting up Git repository..." echo "Setting up Git repository..."
# Helper function to handle git shallow lock # Helper function to handle git shallow lock
@ -100,7 +118,7 @@ else
fi fi
fi fi
# Step 4: Create Python virtual environment and install dependencies # Create Python virtual environment and install dependencies
echo "Creating Python virtual environment..." echo "Creating Python virtual environment..."
venv_created=false venv_created=false
if [ ! -d "${WORKSPACE_FOLDER}/python3-venv/bin" ]; then if [ ! -d "${WORKSPACE_FOLDER}/python3-venv/bin" ]; then
@ -127,7 +145,7 @@ else
echo "Info: Python virtual environment already exists" echo "Info: Python virtual environment already exists"
fi fi
# Step 5: Build clang configuration # Build clang configuration
echo "Building clang configuration..." echo "Building clang configuration..."
# Backup existing .bazelrc.compiledb if it exists, then use our known-good config # Backup existing .bazelrc.compiledb if it exists, then use our known-good config
@ -169,7 +187,7 @@ else
echo "Warning: Failed to build clang configuration" echo "Warning: Failed to build clang configuration"
fi fi
# Step 6: Setup GDB pretty printers # Setup GDB pretty printers
echo "Setting up GDB pretty printers..." echo "Setting up GDB pretty printers..."
cd "${WORKSPACE_FOLDER}/.." cd "${WORKSPACE_FOLDER}/.."
if [ -d "Boost-Pretty-Printer" ]; then if [ -d "Boost-Pretty-Printer" ]; then
@ -189,7 +207,7 @@ else
fi fi
cd "${WORKSPACE_FOLDER}" cd "${WORKSPACE_FOLDER}"
# Step 7: Configure automatic Python virtual environment activation # Configure automatic Python virtual environment activation
echo "Configuring automatic Python virtual environment activation..." echo "Configuring automatic Python virtual environment activation..."
# Helper function to add Python venv activation to shell config # Helper function to add Python venv activation to shell config
@ -220,7 +238,7 @@ else
echo "Warning: Python virtual environment not found at ${WORKSPACE_FOLDER}/python3-venv" echo "Warning: Python virtual environment not found at ${WORKSPACE_FOLDER}/python3-venv"
fi fi
# Step 8: Sync Python dependencies # Sync Python dependencies
echo "Syncing Python dependencies..." echo "Syncing Python dependencies..."
if [ -f "${WORKSPACE_FOLDER}/python3-venv/bin/activate" ]; then if [ -f "${WORKSPACE_FOLDER}/python3-venv/bin/activate" ]; then
# Only run sync if venv was just created OR if we're updating an existing one # Only run sync if venv was just created OR if we're updating an existing one
@ -241,7 +259,7 @@ else
echo "Warning: Python virtual environment not found at ${WORKSPACE_FOLDER}/python3-venv" echo "Warning: Python virtual environment not found at ${WORKSPACE_FOLDER}/python3-venv"
fi fi
# Step 9: Install Node modules # Install Node modules
echo "Installing Node.js dependencies..." echo "Installing Node.js dependencies..."
if bazel run @pnpm//:pnpm --config=local -- --dir "${WORKSPACE_FOLDER}" install; then if bazel run @pnpm//:pnpm --config=local -- --dir "${WORKSPACE_FOLDER}" install; then
echo "[OK] Node.js dependencies installed successfully" echo "[OK] Node.js dependencies installed successfully"

View File

@ -67,18 +67,29 @@ For Linux users, you can use Docker Engine directly.
**Installation:** **Installation:**
Follow the official guide: [docs.docker.com/engine/install](https://docs.docker.com/engine/install/) Follow the official guide: [docs.docker.com/engine/install](https://docs.docker.com/engine/install/)
### 2. Install Visual Studio Code ### 2. Create SSH Directory (Required)
> **⚠️ Critical:** You **must** have a `~/.ssh` directory on your host machine before building the devcontainer. The devcontainer requires this directory to exist, regardless of whether you use SSH or HTTPS to clone the repository.
```bash
# On your HOST machine (not inside the container)
mkdir -p ~/.ssh
```
If you skip this step, you'll encounter bind mount errors when trying to start the devcontainer.
### 3. Install Visual Studio Code
Download and install VS Code from [code.visualstudio.com](https://code.visualstudio.com/) Download and install VS Code from [code.visualstudio.com](https://code.visualstudio.com/)
### 3. Install Dev Containers Extension ### 4. Install Dev Containers Extension
1. Open VS Code 1. Open VS Code
2. Go to Extensions (⌘/Ctrl+Shift+X) 2. Go to Extensions (⌘/Ctrl+Shift+X)
3. Search for "Dev Containers" 3. Search for "Dev Containers"
4. Install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension by Microsoft 4. Install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension by Microsoft
### 4. Configure SSH Keys (Recommended) ### 5. Configure SSH Keys (Recommended)
To clone the repository using SSH (recommended for contributors), you'll need SSH keys configured with GitHub. To clone the repository using SSH (recommended for contributors), you'll need SSH keys configured with GitHub.

View File

@ -32,6 +32,54 @@ Restart VSCode. If you install Rancher Desktop while you already have VSCode ope
## Container Build Issues ## Container Build Issues
### Build Fails with SSH Bind Mount Error
**Symptoms:**
```
Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /Users/username/.ssh
```
Or on macOS/Linux systems using certain Docker providers:
```
Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /socket_mnt/...
```
**Root Cause:**
The devcontainer configuration mounts your `~/.ssh` directory to enable Git operations over SSH. If this directory doesn't exist on your host machine, the container fails to start. **This directory is required even if you plan to use HTTPS instead of SSH for cloning.**
**Solutions:**
1. **Create the .ssh directory on your host machine:**
```bash
# On your HOST machine (not in container)
mkdir -p ~/.ssh
```
2. **Rebuild the container:**
- Command Palette → "Dev Containers: Rebuild Container"
**Note on SSH Agent Forwarding:**
SSH agent forwarding behavior varies by Docker provider on macOS:
- **Docker Desktop**: Automatic SSH agent forwarding built-in
- **OrbStack**: Automatic SSH agent forwarding built-in
- **Rancher Desktop**:
- With dockerd runtime: Automatic agent forwarding
- With containerd runtime: Agent forwarding requires additional setup
To use SSH agent forwarding, ensure your SSH keys are added to your host's SSH agent before starting the container:
```bash
ssh-add ~/.ssh/id_ed25519 # or your key name
ssh-add -l # verify keys are loaded
```
### Build Fails with "No Space Left on Device" ### Build Fails with "No Space Left on Device"
**Symptoms:** **Symptoms:**

View File

@ -89,6 +89,6 @@ buildvariants:
- "evergreen/devcontainer*.sh" - "evergreen/devcontainer*.sh"
- "poetry_requirements.txt" - "poetry_requirements.txt"
run_on: run_on:
- ubuntu2204-small - ubuntu2204-large
tasks: tasks:
- name: devcontainer_test - name: devcontainer_test

View File

@ -26,11 +26,15 @@ fi
echo "npm version:" echo "npm version:"
npm --version npm --version
# Install devcontainer CLI locally in task workdir # Install devcontainer CLI to temp directory
echo "" echo ""
echo "Installing @devcontainers/cli..." echo "Installing @devcontainers/cli..."
INSTALL_DIR="$PWD/.devcontainer-cli" # Use CLI_INSTALL_DIR set by caller
mkdir -p "$INSTALL_DIR" if [ -z "${CLI_INSTALL_DIR:-}" ]; then
echo "ERROR: CLI_INSTALL_DIR not set by caller"
exit 1
fi
INSTALL_DIR="$CLI_INSTALL_DIR"
npm install -g --prefix "$INSTALL_DIR" @devcontainers/cli npm install -g --prefix "$INSTALL_DIR" @devcontainers/cli
# Add to PATH for this script and subsequent commands # Add to PATH for this script and subsequent commands

View File

@ -13,6 +13,19 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT" cd "$REPO_ROOT"
# Create temp directory for devcontainer CLI installation
CLI_INSTALL_DIR=$(mktemp -d)
export CLI_INSTALL_DIR
# Set up cleanup trap to remove temp directory on exit
cleanup() {
if [ -d "$CLI_INSTALL_DIR" ]; then
echo "Cleaning up devcontainer CLI installation..."
rm -rf "$CLI_INSTALL_DIR"
fi
}
trap cleanup EXIT
# Set a non-conflicting username for CI # Set a non-conflicting username for CI
# In CI, USER is often "ubuntu" which conflicts with system users # In CI, USER is often "ubuntu" which conflicts with system users
# Use a distinct name that won't conflict # Use a distinct name that won't conflict
@ -37,8 +50,8 @@ if ! command -v devcontainer &>/dev/null; then
echo "Installing devcontainer CLI..." echo "Installing devcontainer CLI..."
bash "$SCRIPT_DIR/devcontainer_cli_setup.sh" bash "$SCRIPT_DIR/devcontainer_cli_setup.sh"
# Add CLI to PATH (installed locally by cli_setup.sh) # Add CLI to PATH (installed by cli_setup.sh)
export PATH="$PWD/.devcontainer-cli/bin:$PATH" export PATH="${CLI_INSTALL_DIR}/bin:$PATH"
fi fi
echo "Using devcontainer CLI version:" echo "Using devcontainer CLI version:"
@ -76,30 +89,35 @@ echo "Container ID: $CONTAINER_ID"
echo "" echo ""
echo "=== Testing inside devcontainer ===" echo "=== Testing inside devcontainer ==="
# Run commands inside the container using devcontainer exec # Helper function to run commands inside the container
# Pass CI=true to all exec commands so engflow_auth wrapper skips credential helper setup
# Pass the same --id-label so it can find the container we created # Pass the same --id-label so it can find the container we created
devcontainer_run() {
devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" --remote-env CI=true "$@"
}
echo "Checking GCC version..." echo "Checking GCC version..."
devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" gcc --version devcontainer_run gcc --version
echo "" echo ""
echo "Checking Python version..." echo "Checking Python version..."
devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" python3 --version devcontainer_run python3 --version
echo "" echo ""
echo "Checking Python venv..." echo "Checking Python venv..."
devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" bash -c "source python3-venv/bin/activate && python --version" devcontainer_run bash -c "source python3-venv/bin/activate && python --version"
echo "" echo ""
echo "Checking Bazel..." echo "Checking Bazel..."
devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" bazel --version devcontainer_run bazel --version
echo "" echo ""
echo "Checking Git..." echo "Checking Git..."
devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" git --version devcontainer_run git --version
echo "" echo ""
echo "Checking clangd configuration..." echo "Checking clangd configuration..."
if ! devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" test -f compile_commands.json; then if ! devcontainer_run test -f compile_commands.json; then
echo "ERROR: compile_commands.json not found - clangd setup failed" echo "ERROR: compile_commands.json not found - clangd setup failed"
exit 1 exit 1
fi fi
@ -107,12 +125,42 @@ echo "✓ compile_commands.json exists"
echo "" echo ""
echo "Checking .clang-tidy configuration..." echo "Checking .clang-tidy configuration..."
if ! devcontainer exec --workspace-folder . --id-label "test-id=${TEST_ID}" test -f .clang-tidy; then if ! devcontainer_run test -f .clang-tidy; then
echo "ERROR: .clang-tidy not found - clang-tidy setup failed" echo "ERROR: .clang-tidy not found - clang-tidy setup failed"
exit 1 exit 1
fi fi
echo "✓ .clang-tidy exists" echo "✓ .clang-tidy exists"
echo ""
echo "=== Running representative user operations ==="
echo ""
echo "Running C++ unit test..."
devcontainer_run bazel test //src/mongo/bson:bson_test --config=local --test_output=errors
echo ""
echo "Building IDL target (tests code generation)..."
devcontainer_run bazel build //src/mongo/bson:bson_validate --config=local
echo ""
echo "Building install-dist-test target (necessary for resmoke below)..."
devcontainer_run bazel build install-dist-test
echo ""
echo "Running Python test via resmoke..."
devcontainer_run python3 buildscripts/resmoke.py run --suite=core --sanityCheck
echo ""
echo "Checking code formatting..."
devcontainer_run bazel run format
echo ""
echo "Running linter..."
devcontainer_run bazel run lint
echo ""
echo "✅ All representative operations passed"
echo "" echo ""
echo "=== Stopping devcontainer ===" echo "=== Stopping devcontainer ==="