Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions truss/templates/base.Dockerfile.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ FROM {{ base_image_name_and_tag }} AS truss_server
{%- set python_executable = config.base_image.python_executable_path or 'python3' %}
ENV PYTHON_EXECUTABLE="{{ python_executable }}"

{%- set bundled_packages_path = "/packages" %} {# path for bundled packages #}
{%- set app_username = "app" %} {# needed later for USER directive#}
{% block user_setup %}
{%- set app_user_uid = 60000 %}
Expand All @@ -30,7 +31,7 @@ ENV DEBIAN_FRONTEND=noninteractive
{# to allow the non-root user to install packages. #}
{%- if non_root_user and enable_model_container_admin_commands %}
RUN apt update && apt install -y sudo
{%- set allowed_admin_commands = ["/usr/bin/apt install *", "/usr/bin/apt update"] %}
{%- set allowed_admin_commands = ["/usr/bin/apt install *", "/usr/bin/apt update", "/usr/bin/apt remove *"] %}
RUN echo "Defaults:{{ app_username }} passwd_tries=0\n{{ app_username }} ALL=(root) NOPASSWD: {{ allowed_admin_commands | join(", ") }}" > /etc/sudoers.d/app-packages
RUN chmod 0440 /etc/sudoers.d/app-packages
{#- optional but good practice: check if the sudoers file is valid #}
Expand Down Expand Up @@ -119,7 +120,7 @@ WORKDIR $APP_HOME

{% block bundled_packages_copy %}
{%- if bundled_packages_dir_exists %}
COPY --chown={{ default_owner }} ./{{ config.bundled_packages_dir }} /packages
COPY --chown={{ default_owner }} ./{{ config.bundled_packages_dir }} {{ bundled_packages_path }}
{%- endif %}
{% endblock %}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import logging
import os
import shutil
import subprocess
from pathlib import Path
from typing import Optional
Expand Down Expand Up @@ -91,27 +93,38 @@ def _apply_python_requirement_patch(
)
action = python_requirement_patch.action

if action == Action.REMOVE:
subprocess.run(
[
self._pip_path,
"uninstall",
"-y",
if self._pip_path == "uv-control-env":
# Use uv pip with the control environment's Python
if action == Action.REMOVE:
subprocess.run([
"uv", "pip", "uninstall", "-y",
python_requirement_patch.requirement,
],
check=True,
)
elif action in [Action.ADD, Action.UPDATE]:
subprocess.run(
[
self._pip_path,
"install",
"--python", "/control/.env/bin/python"
], check=True)
elif action in [Action.ADD, Action.UPDATE]:
subprocess.run([
"uv", "pip", "install",
python_requirement_patch.requirement,
"--upgrade",
],
check=True,
)
"--python", "/control/.env/bin/python"
], check=True)
else:
# Deprecated: use traditional pip executable
# This code block should eventually be removed,
# because we now use uv.
if action == Action.REMOVE:
subprocess.run([
self._pip_path, "uninstall", "-y",
python_requirement_patch.requirement,
], check=True)
elif action in [Action.ADD, Action.UPDATE]:
subprocess.run([
self._pip_path, "install",
python_requirement_patch.requirement,
"--upgrade",
], check=True)

if action not in [Action.REMOVE, Action.ADD, Action.UPDATE]:
raise ValueError(f"Unknown python requirement patch action {action}")

def _apply_system_package_patch(self, system_package_patch: SystemPackagePatch):
Expand All @@ -120,17 +133,34 @@ def _apply_system_package_patch(self, system_package_patch: SystemPackagePatch):
)
action = system_package_patch.action

# Check if we're running as root
# Deprecated: this code should be removed eventually.
is_root = os.getuid() == 0

# If not root, check for sudo availability
if not is_root:
if not shutil.which("sudo"):
raise RuntimeError(
"Cannot install system packages for security reasons, please redeploy the model. "
"System package installation requires elevated privileges that are not available in this environment."
)

# Build the apt command with sudo if needed
apt_prefix = [] if is_root else ["sudo"]

if action == Action.REMOVE:
subprocess.run(
["apt", "remove", "-y", system_package_patch.package], check=True
apt_prefix + ["apt", "remove", "-y", system_package_patch.package],
check=True
)
elif action in [Action.ADD, Action.UPDATE]:
subprocess.run(["apt", "update"], check=True)
subprocess.run(apt_prefix + ["apt", "update"], check=True)
subprocess.run(
["apt", "install", "-y", system_package_patch.package], check=True
apt_prefix + ["apt", "install", "-y", system_package_patch.package],
check=True
)
else:
raise ValueError(f"Unknown python requirement patch action {action}")
raise ValueError(f"Unknown system package patch action {action}")

def _apply_config_patch(self, config_patch: ConfigPatch):
self._app_logger.debug(f"Applying config patch {config_patch.to_dict()}")
Expand Down Expand Up @@ -176,10 +206,17 @@ def _apply_external_data_patch(self, external_data_patch: ExternalDataPatch):


def _identify_pip_path() -> str:
# For uv-managed environments, we don't use a pip executable directly
# Instead, we return a special marker that indicates we should use uv pip
control_python = Path("/control/.env/bin/python")
if control_python.exists():
return "uv-control-env" # Special marker

# Fallback to system pip if control environment doesn't exist
if Path("/usr/local/bin/pip3").exists():
return "/usr/local/bin/pip3"

if Path("/usr/local/bin/pip").exists():
return "/usr/local/bin/pip"

raise RuntimeError("Unable to find pip, make sure it's installed.")
raise RuntimeError("Unable to find pip, make sure it's installed.")
2 changes: 1 addition & 1 deletion truss/templates/server.Dockerfile.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ COPY --chown={{ default_owner }} ./{{ config.model_module_dir }} ${APP_HOME}/mod
{# Macro to change ownership of directories and switch to regular user #}
{%- macro chown_and_switch_to_regular_user_if_enabled(additional_chown_dirs=[]) -%}
{%- if non_root_user %}
RUN chown -R {{ app_username }}:{{ app_username }} {% for dir in additional_chown_dirs %}{{ dir }} {% endfor %}${HOME} ${APP_HOME}
RUN chown -R {{ app_username }}:{{ app_username }} {% for dir in additional_chown_dirs %}{{ dir }} {% endfor %}${HOME} ${APP_HOME}{% if bundled_packages_dir_exists %} {{ bundled_packages_path }}{% endif %}{% if requires_live_reload %} /control{% endif %}
USER {{ app_username }}
{%- endif %} {#- endif non_root_user #}
{%- endmacro -%}
Expand Down
Loading