diff --git a/README.md b/README.md index 965961f2..5c7ff9e6 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,14 @@ To run and debug your app locally: 5. **Access your app**: Once the container is running, you can access it at `localhost:` where `` is the port you specified in your configuration (e.g., `localhost:8888` for Jupyter, `localhost:7681` for ttyd) +## Container Resource Limits + +Memory and shared memory limits are automatically configured to prevent OOM conditions on the host VM. + +**Memory Limit**: Reserves 10% of total memory for the host, floored at 1GB and capped at 4GB. Container gets the remainder. + +**Shared Memory**: Default is 64m. Override via `shm-size` metadata. + ## How to use The `.devcontainer.json` file in the custom app folder (e.g. r-analysis/) contains the custom app configuration. diff --git a/src/aou-sas/devcontainer-template.json b/src/aou-sas/devcontainer-template.json index 58b588b2..0dac182c 100644 --- a/src/aou-sas/devcontainer-template.json +++ b/src/aou-sas/devcontainer-template.json @@ -20,6 +20,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } } } diff --git a/src/aou-sas/docker-compose.yaml b/src/aou-sas/docker-compose.yaml index 8aae545f..8c65e9c2 100644 --- a/src/aou-sas/docker-compose.yaml +++ b/src/aou-sas/docker-compose.yaml @@ -5,6 +5,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . additional_contexts: diff --git a/src/custom-workbench-jupyter-template/devcontainer-template.json b/src/custom-workbench-jupyter-template/devcontainer-template.json index 71205ed8..1326770c 100644 --- a/src/custom-workbench-jupyter-template/devcontainer-template.json +++ b/src/custom-workbench-jupyter-template/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/custom-workbench-jupyter-template/docker-compose.yaml b/src/custom-workbench-jupyter-template/docker-compose.yaml index 220815bd..f060451f 100644 --- a/src/custom-workbench-jupyter-template/docker-compose.yaml +++ b/src/custom-workbench-jupyter-template/docker-compose.yaml @@ -4,6 +4,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . additional_contexts: diff --git a/src/example/devcontainer-template.json b/src/example/devcontainer-template.json index 80c77016..05cbfdd1 100644 --- a/src/example/devcontainer-template.json +++ b/src/example/devcontainer-template.json @@ -20,6 +20,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } } } diff --git a/src/example/docker-compose.yaml b/src/example/docker-compose.yaml index 54a93ee6..c7bb38cd 100644 --- a/src/example/docker-compose.yaml +++ b/src/example/docker-compose.yaml @@ -3,6 +3,7 @@ services: # The container name must be "application-server" container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" # This can be either a pre-existing image or built from a Dockerfile image: "quay.io/jupyter/base-notebook" # build: diff --git a/src/jupyter-aou/devcontainer-template.json b/src/jupyter-aou/devcontainer-template.json index 91f923a6..37ee9eae 100644 --- a/src/jupyter-aou/devcontainer-template.json +++ b/src/jupyter-aou/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/jupyter-aou/docker-compose.yaml b/src/jupyter-aou/docker-compose.yaml index 939fc4a2..e0321527 100644 --- a/src/jupyter-aou/docker-compose.yaml +++ b/src/jupyter-aou/docker-compose.yaml @@ -5,6 +5,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . additional_contexts: diff --git a/src/jupyter-template/devcontainer-template.json b/src/jupyter-template/devcontainer-template.json index 7f613802..52afc524 100644 --- a/src/jupyter-template/devcontainer-template.json +++ b/src/jupyter-template/devcontainer-template.json @@ -32,6 +32,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/jupyter-template/docker-compose.yaml b/src/jupyter-template/docker-compose.yaml index 30f1a6d3..286c17c3 100644 --- a/src/jupyter-template/docker-compose.yaml +++ b/src/jupyter-template/docker-compose.yaml @@ -3,6 +3,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" image: "${templateOption:containerImage}" user: "jupyter" restart: always diff --git a/src/nemo_jupyter/devcontainer-template.json b/src/nemo_jupyter/devcontainer-template.json index 6d8ed5e2..2c602aa5 100644 --- a/src/nemo_jupyter/devcontainer-template.json +++ b/src/nemo_jupyter/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/nemo_jupyter/docker-compose.yaml b/src/nemo_jupyter/docker-compose.yaml index 8ee3b9f7..d69499d8 100644 --- a/src/nemo_jupyter/docker-compose.yaml +++ b/src/nemo_jupyter/docker-compose.yaml @@ -3,6 +3,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . target: nemo diff --git a/src/nemo_jupyter_aou/devcontainer-template.json b/src/nemo_jupyter_aou/devcontainer-template.json index 22887763..25c76231 100644 --- a/src/nemo_jupyter_aou/devcontainer-template.json +++ b/src/nemo_jupyter_aou/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/nemo_jupyter_aou/docker-compose.yaml b/src/nemo_jupyter_aou/docker-compose.yaml index 6b531286..87da52e6 100644 --- a/src/nemo_jupyter_aou/docker-compose.yaml +++ b/src/nemo_jupyter_aou/docker-compose.yaml @@ -4,6 +4,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: ../nemo_jupyter additional_contexts: diff --git a/src/pgweb/devcontainer-template.json b/src/pgweb/devcontainer-template.json index 6875d8d6..9909ecc3 100644 --- a/src/pgweb/devcontainer-template.json +++ b/src/pgweb/devcontainer-template.json @@ -20,6 +20,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } } } diff --git a/src/pgweb/docker-compose.yaml b/src/pgweb/docker-compose.yaml index a31ad041..c0bfd075 100644 --- a/src/pgweb/docker-compose.yaml +++ b/src/pgweb/docker-compose.yaml @@ -3,6 +3,7 @@ services: # The container name must be "application-server" container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" # This can be either a pre-existing image or built from a Dockerfile build: context: . diff --git a/src/playground/devcontainer-template.json b/src/playground/devcontainer-template.json index 332b2aa1..bea0ff46 100644 --- a/src/playground/devcontainer-template.json +++ b/src/playground/devcontainer-template.json @@ -14,6 +14,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } } } diff --git a/src/playground/docker-compose.yaml b/src/playground/docker-compose.yaml index a0cf1bfc..bf2d821f 100644 --- a/src/playground/docker-compose.yaml +++ b/src/playground/docker-compose.yaml @@ -3,6 +3,7 @@ services: # The container name must be "application-server" container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" image: "caddy:2.11-alpine" restart: always volumes: diff --git a/src/r-analysis-aou/devcontainer-template.json b/src/r-analysis-aou/devcontainer-template.json index 31ab9135..20fe6283 100644 --- a/src/r-analysis-aou/devcontainer-template.json +++ b/src/r-analysis-aou/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/r-analysis-aou/docker-compose.yaml b/src/r-analysis-aou/docker-compose.yaml index b54bdceb..646e52a0 100644 --- a/src/r-analysis-aou/docker-compose.yaml +++ b/src/r-analysis-aou/docker-compose.yaml @@ -4,6 +4,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . additional_contexts: diff --git a/src/r-analysis/devcontainer-template.json b/src/r-analysis/devcontainer-template.json index c18963ad..572104a7 100644 --- a/src/r-analysis/devcontainer-template.json +++ b/src/r-analysis/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/r-analysis/docker-compose.yaml b/src/r-analysis/docker-compose.yaml index 757b09db..9ce8fc0c 100644 --- a/src/r-analysis/docker-compose.yaml +++ b/src/r-analysis/docker-compose.yaml @@ -2,6 +2,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" image: "ghcr.io/rocker-org/devcontainer/tidyverse@sha256:3a9ecbed900f17da528cdb17c3ddc43045fc9b4be7dbd8c61cb7b8a6439bfa6b" restart: always volumes: diff --git a/src/test-app-secrets/devcontainer-template.json b/src/test-app-secrets/devcontainer-template.json index b3d6b4b4..a32804fe 100644 --- a/src/test-app-secrets/devcontainer-template.json +++ b/src/test-app-secrets/devcontainer-template.json @@ -6,7 +6,10 @@ "options": { "cloud": { "type": "string", - "enum": ["gcp", "aws"], + "enum": [ + "gcp", + "aws" + ], "default": "gcp", "description": "Cloud provider (gcp or aws)" }, @@ -20,6 +23,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } } } diff --git a/src/test-app-secrets/docker-compose.yaml b/src/test-app-secrets/docker-compose.yaml index 82226b56..8d3e1ea4 100644 --- a/src/test-app-secrets/docker-compose.yaml +++ b/src/test-app-secrets/docker-compose.yaml @@ -4,6 +4,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . additional_contexts: diff --git a/src/test-app/devcontainer-template.json b/src/test-app/devcontainer-template.json index 69f53c31..6cf5f7b9 100644 --- a/src/test-app/devcontainer-template.json +++ b/src/test-app/devcontainer-template.json @@ -20,6 +20,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } } } diff --git a/src/test-app/docker-compose.yaml b/src/test-app/docker-compose.yaml index feef9177..91889cc3 100644 --- a/src/test-app/docker-compose.yaml +++ b/src/test-app/docker-compose.yaml @@ -3,6 +3,7 @@ services: # The container name must be "application-server" container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" # This can be either a pre-existing image or built from a Dockerfile image: "docker/getting-started" # build: diff --git a/src/ubuntu-example/devcontainer-template.json b/src/ubuntu-example/devcontainer-template.json index 5132089f..fd8c9279 100644 --- a/src/ubuntu-example/devcontainer-template.json +++ b/src/ubuntu-example/devcontainer-template.json @@ -20,6 +20,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } } } diff --git a/src/ubuntu-example/docker-compose.yaml b/src/ubuntu-example/docker-compose.yaml index a58797fb..efb67bed 100644 --- a/src/ubuntu-example/docker-compose.yaml +++ b/src/ubuntu-example/docker-compose.yaml @@ -3,6 +3,7 @@ services: # The container name must be "application-server" container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" # This can be either a pre-existing image or built from a Dockerfile image: "mcr.microsoft.com/devcontainers/base:ubuntu-24.04" # build: diff --git a/src/vscode-docker/devcontainer-template.json b/src/vscode-docker/devcontainer-template.json index 3b5ed52c..6cdcf8ef 100644 --- a/src/vscode-docker/devcontainer-template.json +++ b/src/vscode-docker/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/vscode-docker/docker-compose.yaml b/src/vscode-docker/docker-compose.yaml index ef1f8289..0d07d3f6 100644 --- a/src/vscode-docker/docker-compose.yaml +++ b/src/vscode-docker/docker-compose.yaml @@ -3,6 +3,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . dockerfile: Dockerfile diff --git a/src/vscode-with-llm/devcontainer-template.json b/src/vscode-with-llm/devcontainer-template.json index 59ad885e..4d942893 100644 --- a/src/vscode-with-llm/devcontainer-template.json +++ b/src/vscode-with-llm/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/vscode-with-llm/docker-compose.yaml b/src/vscode-with-llm/docker-compose.yaml index 327d09bd..5f536671 100644 --- a/src/vscode-with-llm/docker-compose.yaml +++ b/src/vscode-with-llm/docker-compose.yaml @@ -3,6 +3,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . dockerfile: Dockerfile diff --git a/src/vscode/devcontainer-template.json b/src/vscode/devcontainer-template.json index b2448ca3..2d794a7a 100644 --- a/src/vscode/devcontainer-template.json +++ b/src/vscode/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/vscode/docker-compose.yaml b/src/vscode/docker-compose.yaml index 327d09bd..5f536671 100644 --- a/src/vscode/docker-compose.yaml +++ b/src/vscode/docker-compose.yaml @@ -3,6 +3,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . dockerfile: Dockerfile diff --git a/src/workbench-jupyter-docker/devcontainer-template.json b/src/workbench-jupyter-docker/devcontainer-template.json index 71205ed8..1326770c 100644 --- a/src/workbench-jupyter-docker/devcontainer-template.json +++ b/src/workbench-jupyter-docker/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/workbench-jupyter-docker/docker-compose.yaml b/src/workbench-jupyter-docker/docker-compose.yaml index 8f94e447..c756bbb1 100644 --- a/src/workbench-jupyter-docker/docker-compose.yaml +++ b/src/workbench-jupyter-docker/docker-compose.yaml @@ -3,6 +3,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" image: "us-central1-docker.pkg.dev/verily-workbench-public/apps/workbench-jupyter:latest" user: "jupyter:${DOCKER_GID}" restart: always diff --git a/src/workbench-jupyter-parabricks-aou/devcontainer-template.json b/src/workbench-jupyter-parabricks-aou/devcontainer-template.json index 7a189068..4388ccf1 100644 --- a/src/workbench-jupyter-parabricks-aou/devcontainer-template.json +++ b/src/workbench-jupyter-parabricks-aou/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/workbench-jupyter-parabricks-aou/docker-compose.yaml b/src/workbench-jupyter-parabricks-aou/docker-compose.yaml index 7afd2720..ec7388fc 100644 --- a/src/workbench-jupyter-parabricks-aou/docker-compose.yaml +++ b/src/workbench-jupyter-parabricks-aou/docker-compose.yaml @@ -4,6 +4,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: ../workbench-jupyter-parabricks additional_contexts: diff --git a/src/workbench-jupyter-parabricks/devcontainer-template.json b/src/workbench-jupyter-parabricks/devcontainer-template.json index 7a189068..4388ccf1 100644 --- a/src/workbench-jupyter-parabricks/devcontainer-template.json +++ b/src/workbench-jupyter-parabricks/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/workbench-jupyter-parabricks/docker-compose.yaml b/src/workbench-jupyter-parabricks/docker-compose.yaml index 58f8d9c1..4b64ac86 100644 --- a/src/workbench-jupyter-parabricks/docker-compose.yaml +++ b/src/workbench-jupyter-parabricks/docker-compose.yaml @@ -3,6 +3,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . target: parabricks diff --git a/src/workbench-jupyter-with-llm/devcontainer-template.json b/src/workbench-jupyter-with-llm/devcontainer-template.json index fb8fb583..353f25d6 100644 --- a/src/workbench-jupyter-with-llm/devcontainer-template.json +++ b/src/workbench-jupyter-with-llm/devcontainer-template.json @@ -22,6 +22,11 @@ "type": "string", "description": "Shared memory size for the application-server container", "default": "64m" + }, + "memoryLimit": { + "type": "string", + "description": "Memory limit for the application-server container", + "default": "8192m" } }, "platforms": ["Any"] diff --git a/src/workbench-jupyter-with-llm/docker-compose.yaml b/src/workbench-jupyter-with-llm/docker-compose.yaml index 220815bd..f060451f 100644 --- a/src/workbench-jupyter-with-llm/docker-compose.yaml +++ b/src/workbench-jupyter-with-llm/docker-compose.yaml @@ -4,6 +4,7 @@ services: app: container_name: "application-server" shm_size: "${templateOption:shmSize}" + mem_limit: "${templateOption:memoryLimit}" build: context: . additional_contexts: diff --git a/startupscript/butane/050-parse-devcontainer.sh b/startupscript/butane/050-parse-devcontainer.sh index cb488bdb..bf0b399a 100755 --- a/startupscript/butane/050-parse-devcontainer.sh +++ b/startupscript/butane/050-parse-devcontainer.sh @@ -18,6 +18,112 @@ function usage { exit 1 } +calculate_container_memory_limit() { + # Query total system memory in bytes + local total_mem_bytes + total_mem_bytes=$(awk '/^MemTotal:/ {print $2 * 1024}' /proc/meminfo) + local total_mem_mb=$((total_mem_bytes / 1024 / 1024)) + + # Reserve memory for host OS, proxy-agent, fluent-bit, and other system processes. + # Reserve 10% of total memory, floored at 1GB and capped at 4GB. + local reserved_10pct=$((total_mem_mb * 10 / 100)) + local reserved_mb=$((reserved_10pct > 1024 ? reserved_10pct : 1024)) + reserved_mb=$((reserved_mb > 4096 ? 4096 : reserved_mb)) + + # Container gets total - reserved + local container_mem_limit_mb=$((total_mem_mb - reserved_mb)) + + # Validate that the container memory limit is not negative + # If negative or zero, don't set a memory limit (let container use available memory) + if [[ "${container_mem_limit_mb}" -le 0 ]]; then + echo "WARNING: Calculated container memory limit is ${container_mem_limit_mb}MB (negative or zero)" >&2 + echo "WARNING: Total memory: ${total_mem_mb}MB, Reserved: ${reserved_mb}MB" >&2 + echo "WARNING: Not setting a memory limit - container will use available system memory" >&2 + echo "System memory: ${total_mem_mb}MB, Reserved for host: ${reserved_mb}MB, Container memory limit: none" >&2 + echo "" # Return empty string (no limit) + else + echo "System memory: ${total_mem_mb}MB, Reserved for host: ${reserved_mb}MB, Container memory limit: ${container_mem_limit_mb}m" >&2 + echo "${container_mem_limit_mb}m" # Return the limit + fi +} + +replace_template_options() { + local TEMPLATE_PATH="$1" + + echo "replacing templateOptions in ${TEMPLATE_PATH}" + sed -i "s|\${templateOption:login}|${LOGIN}|g" "${TEMPLATE_PATH}" + sed -i "s|\${templateOption:cloud}|${CLOUD}|g" "${TEMPLATE_PATH}" + sed -i "s|\${templateOption:containerImage}|${CONTAINER_IMAGE}|g" "${TEMPLATE_PATH}" + sed -i "s|\${templateOption:containerPort}|${CONTAINER_PORT}|g" "${TEMPLATE_PATH}" + sed -i "s|\${templateOption:shmSize}|${SHM_SIZE}|g" "${TEMPLATE_PATH}" + + # Handle memory limit - delete the line if empty + if [[ -z "${CONTAINER_MEM_LIMIT}" ]]; then + sed -i "/\${templateOption:memoryLimit}/d" "${TEMPLATE_PATH}" + else + sed -i "s|\${templateOption:memoryLimit}|${CONTAINER_MEM_LIMIT}|g" "${TEMPLATE_PATH}" + fi +} + +detect_gpu() { + # Detect NVIDIA GPUs + if nvidia-smi > /dev/null 2>&1; then + return 0 # GPU detected + else + return 1 # No GPU detected + fi +} + +handle_container_state_changed() { + # Each argument is a "key=value" pair representing current container state. + # Removes the application-server container if any value has changed since last run. + local rebuild=false + + if [[ ! -f "${CONTAINER_STATE_FILE}" ]]; then + echo "First run, initializing container state" + rebuild=true + else + local pair key value previous_value + for pair in "$@"; do + key="${pair%%=*}" + value="${pair#*=}" + previous_value="$(grep "^${key}=" "${CONTAINER_STATE_FILE}" | cut -d= -f2-)" + if [[ "${value}" != "${previous_value}" ]]; then + echo "Container state changed: ${key} from ${previous_value} to ${value}" + rebuild=true + fi + done + fi + + if [[ "${rebuild}" == "true" ]]; then + docker rm -f application-server + fi + + printf '%s\n' "$@" > "${CONTAINER_STATE_FILE}" +} + +apply_gpu_runtime() { + local DOCKER_COMPOSE_PATH="$1" + local GPU_RUNTIME_BLOCK_PATH="$2" + local TEMP_COMPOSE_PATH="${DOCKER_COMPOSE_PATH}.tmp" + + echo "Applying GPU runtime configuration in ${DOCKER_COMPOSE_PATH}" + + # Use awk to insert the GPU runtime block after the "app:" line in the docker-compose.yaml file + awk -v gpu_config_path="$GPU_RUNTIME_BLOCK_PATH" ' + /^[[:space:]]*app:/ { # Match the line containing "app:" (can be indented) + print $0; # Print the "app:" line as-is + system("cat " gpu_config_path); # Insert the GPU runtime block by reading from the specified file + } + { + print $0; # For all other lines, print them unchanged + } + ' "${DOCKER_COMPOSE_PATH}" > "${TEMP_COMPOSE_PATH}" # Redirect output to a temporary file + + # Replace the original docker-compose.yaml file with the modified temporary file + mv "${TEMP_COMPOSE_PATH}" "${DOCKER_COMPOSE_PATH}" +} + # Check that the required arguments are provided: devcontainer_path, cloud, login if [[ $# -lt 3 ]]; then usage @@ -95,75 +201,9 @@ if [[ ! "${SHM_SIZE}" =~ ^[0-9]+[bBkKmMgG][bB]?$ ]]; then fi readonly SHM_SIZE -replace_template_options() { - local TEMPLATE_PATH="$1" - - echo "replacing templateOptions in ${TEMPLATE_PATH}" - sed -i "s|\${templateOption:login}|${LOGIN}|g" "${TEMPLATE_PATH}" - sed -i "s|\${templateOption:cloud}|${CLOUD}|g" "${TEMPLATE_PATH}" - sed -i "s|\${templateOption:containerImage}|${CONTAINER_IMAGE}|g" "${TEMPLATE_PATH}" - sed -i "s|\${templateOption:containerPort}|${CONTAINER_PORT}|g" "${TEMPLATE_PATH}" - sed -i "s|\${templateOption:shmSize}|${SHM_SIZE}|g" "${TEMPLATE_PATH}" -} - -detect_gpu() { - # Detect NVIDIA GPUs - if nvidia-smi > /dev/null 2>&1; then - return 0 # GPU detected - else - return 1 # No GPU detected - fi -} - -handle_container_state_changed() { - # Each argument is a "key=value" pair representing current container state. - # Removes the application-server container if any value has changed since last run. - local rebuild=false - - if [[ ! -f "${CONTAINER_STATE_FILE}" ]]; then - echo "First run, initializing container state" - rebuild=true - else - local pair key value previous_value - for pair in "$@"; do - key="${pair%%=*}" - value="${pair#*=}" - previous_value="$(grep "^${key}=" "${CONTAINER_STATE_FILE}" | cut -d= -f2-)" - if [[ "${value}" != "${previous_value}" ]]; then - echo "Container state changed: ${key} from ${previous_value} to ${value}" - rebuild=true - fi - done - fi - - if [[ "${rebuild}" == "true" ]]; then - docker rm -f application-server - fi - - printf '%s\n' "$@" > "${CONTAINER_STATE_FILE}" -} - -apply_gpu_runtime() { - local DOCKER_COMPOSE_PATH="$1" - local GPU_RUNTIME_BLOCK_PATH="$2" - local TEMP_COMPOSE_PATH="${DOCKER_COMPOSE_PATH}.tmp" - - echo "Applying GPU runtime configuration in ${DOCKER_COMPOSE_PATH}" - - # Use awk to insert the GPU runtime block after the "app:" line in the docker-compose.yaml file - awk -v gpu_config_path="$GPU_RUNTIME_BLOCK_PATH" ' - /^[[:space:]]*app:/ { # Match the line containing "app:" (can be indented) - print $0; # Print the "app:" line as-is - system("cat " gpu_config_path); # Insert the GPU runtime block by reading from the specified file - } - { - print $0; # For all other lines, print them unchanged - } - ' "${DOCKER_COMPOSE_PATH}" > "${TEMP_COMPOSE_PATH}" # Redirect output to a temporary file - - # Replace the original docker-compose.yaml file with the modified temporary file - mv "${TEMP_COMPOSE_PATH}" "${DOCKER_COMPOSE_PATH}" -} +# Calculate memory limit for application-server container +CONTAINER_MEM_LIMIT=$(calculate_container_memory_limit) +readonly CONTAINER_MEM_LIMIT # Substitute template options in devcontainer.json and docker-compose.yaml replace_template_options "${DEVCONTAINER_CONFIG_PATH}" diff --git a/tests/common/vm-metadata.sh b/tests/common/vm-metadata.sh index 5644e539..52dd0c9f 100755 --- a/tests/common/vm-metadata.sh +++ b/tests/common/vm-metadata.sh @@ -27,3 +27,10 @@ function get_guest_attribute() { } readonly -f get_guest_attribute +function set_metadata() { + local key="$1" + local value="$2" + echo "Mock: Setting metadata ${key} to ${value}" +} +readonly -f set_metadata +