truss 0.10.9rc547__py3-none-any.whl → 0.10.9rc549__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of truss might be problematic. Click here for more details.
- truss/contexts/local_loader/docker_build_emulator.py +26 -8
- truss/templates/base.Dockerfile.jinja +9 -6
- truss/templates/cache.Dockerfile.jinja +3 -2
- truss/templates/control/control/helpers/truss_patch/model_container_patch_applier.py +61 -24
- truss/templates/server.Dockerfile.jinja +12 -16
- truss/tests/templates/control/control/test_server_integration.py +64 -41
- truss/tests/templates/server/test_truss_server.py +17 -12
- truss/tests/test_model_inference.py +4 -2
- {truss-0.10.9rc547.dist-info → truss-0.10.9rc549.dist-info}/METADATA +1 -1
- {truss-0.10.9rc547.dist-info → truss-0.10.9rc549.dist-info}/RECORD +13 -13
- {truss-0.10.9rc547.dist-info → truss-0.10.9rc549.dist-info}/WHEEL +0 -0
- {truss-0.10.9rc547.dist-info → truss-0.10.9rc549.dist-info}/entry_points.txt +0 -0
- {truss-0.10.9rc547.dist-info → truss-0.10.9rc549.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from dataclasses import dataclass, field
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Dict, List
|
|
@@ -31,12 +32,26 @@ class DockerBuildEmulator:
|
|
|
31
32
|
self._context_dir = context_dir
|
|
32
33
|
|
|
33
34
|
def run(self, fs_root_dir: Path) -> DockerBuildEmulatorResult:
|
|
34
|
-
def _resolve_env(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
def _resolve_env(in_value: str) -> str:
|
|
36
|
+
# Valid environment variable name pattern
|
|
37
|
+
var_name_pattern = r'[A-Za-z_][A-Za-z0-9_]*'
|
|
38
|
+
|
|
39
|
+
# Handle ${VAR} syntax
|
|
40
|
+
def replace_braced_var(match):
|
|
41
|
+
var_name = match.group(1)
|
|
42
|
+
return result.env.get(var_name, match.group(0)) # Return original if not found
|
|
43
|
+
|
|
44
|
+
# Handle $VAR syntax (word boundary ensures we don't match parts of other vars)
|
|
45
|
+
def replace_simple_var(match):
|
|
46
|
+
var_name = match.group(1)
|
|
47
|
+
return result.env.get(var_name, match.group(0)) # Return original if not found
|
|
48
|
+
|
|
49
|
+
# Replace ${VAR} patterns first, using % substitution to avoid additional braces noise with f-strings
|
|
50
|
+
value = re.sub(r'\$\{(%s)\}' % var_name_pattern, replace_braced_var, in_value)
|
|
51
|
+
# Then replace remaining $VAR patterns (only at word boundaries)
|
|
52
|
+
value = re.sub(r'\$(%s)\b' % var_name_pattern, replace_simple_var, value)
|
|
53
|
+
|
|
54
|
+
return value
|
|
40
55
|
|
|
41
56
|
def _resolve_values(keys: List[str]) -> List[str]:
|
|
42
57
|
return list(map(_resolve_env, keys))
|
|
@@ -53,11 +68,14 @@ class DockerBuildEmulator:
|
|
|
53
68
|
if cmd.instruction == DockerInstruction.ENTRYPOINT:
|
|
54
69
|
result.entrypoint = list(values)
|
|
55
70
|
if cmd.instruction == DockerInstruction.COPY:
|
|
71
|
+
# Filter out --chown flags
|
|
72
|
+
filtered_values = [v for v in values if not v.startswith("--chown")]
|
|
73
|
+
|
|
56
74
|
# NB(nikhil): Skip COPY commands with --from flag (multi-stage builds)
|
|
57
|
-
if len(
|
|
75
|
+
if len(filtered_values) != 2:
|
|
58
76
|
continue
|
|
59
77
|
|
|
60
|
-
src, dst =
|
|
78
|
+
src, dst = filtered_values
|
|
61
79
|
src = src.replace("./", "", 1)
|
|
62
80
|
dst = dst.replace("/", "", 1)
|
|
63
81
|
copy_tree_or_file(self._context_dir / src, fs_root_dir / dst)
|
|
@@ -8,24 +8,27 @@ FROM {{ base_image_name_and_tag }} AS truss_server
|
|
|
8
8
|
{%- set python_executable = config.base_image.python_executable_path or 'python3' %}
|
|
9
9
|
ENV PYTHON_EXECUTABLE="{{ python_executable }}"
|
|
10
10
|
|
|
11
|
+
{%- set bundled_packages_path = "/packages" %} {# path for bundled packages #}
|
|
11
12
|
{%- set app_username = "app" %} {# needed later for USER directive#}
|
|
12
13
|
{% block user_setup %}
|
|
13
14
|
{%- set app_user_uid = 60000 %}
|
|
14
15
|
{%- set control_server_dir = "/control" %}
|
|
15
16
|
{%- set default_owner = "root:root" %}
|
|
16
|
-
# The non-root user's home directory.
|
|
17
|
-
# uv will use $HOME to install packages.
|
|
17
|
+
{# The non-root user's home directory. #}
|
|
18
|
+
{# uv will use $HOME to install packages. #}
|
|
18
19
|
ENV HOME=/home/{{ app_username }}
|
|
19
|
-
# Directory containing inference server code.
|
|
20
|
+
{# Directory containing inference server code. #}
|
|
20
21
|
ENV APP_HOME=/{{ app_username }}
|
|
21
22
|
RUN mkdir -p ${APP_HOME} {{ control_server_dir }}
|
|
22
|
-
# Create a non-root user to run model containers.
|
|
23
|
+
{# Create a non-root user to run model containers. #}
|
|
23
24
|
RUN useradd -u {{ app_user_uid }} -ms /bin/bash {{ app_username }}
|
|
24
25
|
{% endblock %} {#- endblock user_setup #}
|
|
25
26
|
|
|
26
27
|
{#- at the very beginning, set non-interactive mode for apt #}
|
|
27
28
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
28
29
|
|
|
30
|
+
{# If non-root user is enabled and model container admin commands are enabled, install sudo #}
|
|
31
|
+
{# to allow the non-root user to install packages. #}
|
|
29
32
|
{%- if non_root_user and enable_model_container_admin_commands %}
|
|
30
33
|
RUN apt update && apt install -y sudo
|
|
31
34
|
{%- set allowed_admin_commands = ["/usr/bin/apt install *", "/usr/bin/apt update"] %}
|
|
@@ -66,7 +69,7 @@ RUN if ! command -v uv >/dev/null 2>&1; then \
|
|
|
66
69
|
command -v curl >/dev/null 2>&1 || (apt update && apt install -y curl) && \
|
|
67
70
|
curl -LsSf --retry 5 --retry-delay 5 https://astral.sh/uv/{{ UV_VERSION }}/install.sh | sh; \
|
|
68
71
|
fi
|
|
69
|
-
# Add the user's local bin to the path, used by uv.
|
|
72
|
+
{# Add the user's local bin to the path, used by uv. #}
|
|
70
73
|
ENV PATH=${PATH}:${HOME}/.local/bin
|
|
71
74
|
{% endblock %}
|
|
72
75
|
|
|
@@ -117,7 +120,7 @@ WORKDIR $APP_HOME
|
|
|
117
120
|
|
|
118
121
|
{% block bundled_packages_copy %}
|
|
119
122
|
{%- if bundled_packages_dir_exists %}
|
|
120
|
-
COPY --chown={{ default_owner }} ./{{ config.bundled_packages_dir }}
|
|
123
|
+
COPY --chown={{ default_owner }} ./{{ config.bundled_packages_dir }} {{ bundled_packages_path }}
|
|
121
124
|
{%- endif %}
|
|
122
125
|
{% endblock %}
|
|
123
126
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
FROM python:3.11-slim AS cache_warmer
|
|
2
2
|
|
|
3
|
+
ENV APP_HOME=/app
|
|
3
4
|
RUN mkdir -p ${APP_HOME}/model_cache
|
|
4
5
|
WORKDIR ${APP_HOME}
|
|
5
6
|
|
|
@@ -8,9 +9,9 @@ ENV HUGGING_FACE_HUB_TOKEN="{{hf_access_token}}"
|
|
|
8
9
|
{% endif %}
|
|
9
10
|
|
|
10
11
|
RUN apt-get -y update; apt-get -y install curl; curl -s https://baseten-public.s3.us-west-2.amazonaws.com/bin/b10cp-5fe8dc7da-linux-amd64 -o /app/b10cp; chmod +x /app/b10cp
|
|
11
|
-
ENV B10CP_PATH_TRUSS="/
|
|
12
|
+
ENV B10CP_PATH_TRUSS="${APP_HOME}/b10cp"
|
|
12
13
|
COPY --chown={{ default_owner }} ./cache_requirements.txt ${APP_HOME}/cache_requirements.txt
|
|
13
|
-
RUN pip install -r /
|
|
14
|
+
RUN pip install -r ${APP_HOME}/cache_requirements.txt --no-cache-dir && rm -rf /root/.cache/pip
|
|
14
15
|
COPY --chown={{ default_owner }} ./cache_warmer.py /cache_warmer.py
|
|
15
16
|
|
|
16
17
|
{% for credential in credentials_to_cache %}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
2
4
|
import subprocess
|
|
3
5
|
from pathlib import Path
|
|
4
6
|
from typing import Optional
|
|
@@ -91,27 +93,38 @@ class ModelContainerPatchApplier:
|
|
|
91
93
|
)
|
|
92
94
|
action = python_requirement_patch.action
|
|
93
95
|
|
|
94
|
-
if
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"uninstall",
|
|
99
|
-
"-y",
|
|
96
|
+
if self._pip_path == "uv-control-env":
|
|
97
|
+
# Use uv pip with the control environment's Python
|
|
98
|
+
if action == Action.REMOVE:
|
|
99
|
+
subprocess.run([
|
|
100
|
+
"uv", "pip", "uninstall", "-y",
|
|
100
101
|
python_requirement_patch.requirement,
|
|
101
|
-
|
|
102
|
-
check=True
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
[
|
|
107
|
-
self._pip_path,
|
|
108
|
-
"install",
|
|
102
|
+
"--python", "/control/.env/bin/python"
|
|
103
|
+
], check=True)
|
|
104
|
+
elif action in [Action.ADD, Action.UPDATE]:
|
|
105
|
+
subprocess.run([
|
|
106
|
+
"uv", "pip", "install",
|
|
109
107
|
python_requirement_patch.requirement,
|
|
110
108
|
"--upgrade",
|
|
111
|
-
|
|
112
|
-
check=True
|
|
113
|
-
)
|
|
109
|
+
"--python", "/control/.env/bin/python"
|
|
110
|
+
], check=True)
|
|
114
111
|
else:
|
|
112
|
+
# Deprecated: use traditional pip executable
|
|
113
|
+
# This code block should eventually be removed,
|
|
114
|
+
# because we now use uv.
|
|
115
|
+
if action == Action.REMOVE:
|
|
116
|
+
subprocess.run([
|
|
117
|
+
self._pip_path, "uninstall", "-y",
|
|
118
|
+
python_requirement_patch.requirement,
|
|
119
|
+
], check=True)
|
|
120
|
+
elif action in [Action.ADD, Action.UPDATE]:
|
|
121
|
+
subprocess.run([
|
|
122
|
+
self._pip_path, "install",
|
|
123
|
+
python_requirement_patch.requirement,
|
|
124
|
+
"--upgrade",
|
|
125
|
+
], check=True)
|
|
126
|
+
|
|
127
|
+
if action not in [Action.REMOVE, Action.ADD, Action.UPDATE]:
|
|
115
128
|
raise ValueError(f"Unknown python requirement patch action {action}")
|
|
116
129
|
|
|
117
130
|
def _apply_system_package_patch(self, system_package_patch: SystemPackagePatch):
|
|
@@ -120,17 +133,34 @@ class ModelContainerPatchApplier:
|
|
|
120
133
|
)
|
|
121
134
|
action = system_package_patch.action
|
|
122
135
|
|
|
136
|
+
# Check if we're running as root
|
|
137
|
+
# Deprecated: this code should be removed eventually.
|
|
138
|
+
is_root = os.getuid() == 0
|
|
139
|
+
|
|
140
|
+
# If not root, check for sudo availability
|
|
141
|
+
if not is_root:
|
|
142
|
+
if not shutil.which("sudo"):
|
|
143
|
+
raise RuntimeError(
|
|
144
|
+
"Cannot install system packages for security reasons, please redeploy the model. "
|
|
145
|
+
"System package installation requires elevated privileges that are not available in this environment."
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Build the apt command with sudo if needed
|
|
149
|
+
apt_prefix = [] if is_root else ["sudo"]
|
|
150
|
+
|
|
123
151
|
if action == Action.REMOVE:
|
|
124
152
|
subprocess.run(
|
|
125
|
-
["apt", "remove", "-y", system_package_patch.package],
|
|
153
|
+
apt_prefix + ["apt", "remove", "-y", system_package_patch.package],
|
|
154
|
+
check=True
|
|
126
155
|
)
|
|
127
156
|
elif action in [Action.ADD, Action.UPDATE]:
|
|
128
|
-
subprocess.run(["apt", "update"], check=True)
|
|
157
|
+
subprocess.run(apt_prefix + ["apt", "update"], check=True)
|
|
129
158
|
subprocess.run(
|
|
130
|
-
["apt", "install", "-y", system_package_patch.package],
|
|
159
|
+
apt_prefix + ["apt", "install", "-y", system_package_patch.package],
|
|
160
|
+
check=True
|
|
131
161
|
)
|
|
132
162
|
else:
|
|
133
|
-
raise ValueError(f"Unknown
|
|
163
|
+
raise ValueError(f"Unknown system package patch action {action}")
|
|
134
164
|
|
|
135
165
|
def _apply_config_patch(self, config_patch: ConfigPatch):
|
|
136
166
|
self._app_logger.debug(f"Applying config patch {config_patch.to_dict()}")
|
|
@@ -176,10 +206,17 @@ class ModelContainerPatchApplier:
|
|
|
176
206
|
|
|
177
207
|
|
|
178
208
|
def _identify_pip_path() -> str:
|
|
209
|
+
# For uv-managed environments, we don't use a pip executable directly
|
|
210
|
+
# Instead, we return a special marker that indicates we should use uv pip
|
|
211
|
+
control_python = Path("/control/.env/bin/python")
|
|
212
|
+
if control_python.exists():
|
|
213
|
+
return "uv-control-env" # Special marker
|
|
214
|
+
|
|
215
|
+
# Fallback to system pip if control environment doesn't exist
|
|
179
216
|
if Path("/usr/local/bin/pip3").exists():
|
|
180
217
|
return "/usr/local/bin/pip3"
|
|
181
|
-
|
|
218
|
+
|
|
182
219
|
if Path("/usr/local/bin/pip").exists():
|
|
183
220
|
return "/usr/local/bin/pip"
|
|
184
|
-
|
|
185
|
-
raise RuntimeError("Unable to find pip, make sure it's installed.")
|
|
221
|
+
|
|
222
|
+
raise RuntimeError("Unable to find pip, make sure it's installed.")
|
|
@@ -46,7 +46,7 @@ RUN {{ sys_pip_install_command }} -r {{ server_requirements_filename }} --no-cac
|
|
|
46
46
|
|
|
47
47
|
{% block app_copy %}
|
|
48
48
|
{%- if model_cache_v1 %}
|
|
49
|
-
# Copy data before code for better caching
|
|
49
|
+
{# Copy data before code for better caching #}
|
|
50
50
|
{%- include "copy_cache_files.Dockerfile.jinja" -%}
|
|
51
51
|
{%- endif %} {#- endif model_cache_v1 #}
|
|
52
52
|
|
|
@@ -68,7 +68,7 @@ COPY --chown={{ default_owner }} ./{{ config.data_dir }} ${APP_HOME}/data
|
|
|
68
68
|
{%- endif %} {#- endif data_dir_exists #}
|
|
69
69
|
|
|
70
70
|
{%- if model_cache_v2 %}
|
|
71
|
-
# v0.0.9, keep synced with server_requirements.txt
|
|
71
|
+
{# v0.0.9, keep synced with server_requirements.txt #}
|
|
72
72
|
RUN curl -sSL --fail --retry 5 --retry-delay 2 -o /usr/local/bin/truss-transfer-cli https://github.com/basetenlabs/truss/releases/download/v0.10.9rc0/truss-transfer-cli-v0.10.9rc0-linux-x86_64-unknown-linux-musl
|
|
73
73
|
RUN chmod +x /usr/local/bin/truss-transfer-cli
|
|
74
74
|
RUN mkdir /static-bptr
|
|
@@ -101,6 +101,13 @@ COPY --chown={{ default_owner }} ./{{ config.model_module_dir }} ${APP_HOME}/mod
|
|
|
101
101
|
{% endblock %} {#- endblock app_copy #}
|
|
102
102
|
|
|
103
103
|
{% block run %}
|
|
104
|
+
{# Macro to change ownership of directories and switch to regular user #}
|
|
105
|
+
{%- macro chown_and_switch_to_regular_user_if_enabled(additional_chown_dirs=[]) -%}
|
|
106
|
+
{%- if non_root_user %}
|
|
107
|
+
RUN chown -R {{ app_username }}:{{ app_username }} {% for dir in additional_chown_dirs %}{{ dir }} {% endfor %}${HOME} ${APP_HOME} {{ bundled_packages_path }}
|
|
108
|
+
USER {{ app_username }}
|
|
109
|
+
{%- endif %} {#- endif non_root_user #}
|
|
110
|
+
{%- endmacro -%}
|
|
104
111
|
|
|
105
112
|
{%- if config.docker_server %}
|
|
106
113
|
RUN apt-get update -y && apt-get install -y --no-install-recommends \
|
|
@@ -120,13 +127,8 @@ ENV SUPERVISOR_SERVER_URL="{{ supervisor_server_url }}"
|
|
|
120
127
|
ENV SERVER_START_CMD="/docker_server/.venv/bin/supervisord -c {{ supervisor_config_path }}"
|
|
121
128
|
{#- default configuration uses port 80, which requires root privileges, so we remove it #}
|
|
122
129
|
RUN rm -f /etc/nginx/sites-enabled/default
|
|
123
|
-
{%- if non_root_user %}
|
|
124
130
|
{#- nginx writes to /var/lib/nginx, /var/log/nginx, and /run directories #}
|
|
125
|
-
{
|
|
126
|
-
RUN chown -R {{ app_username }}:{{ app_username }} {{ nginx_dirs | join(" ") }}
|
|
127
|
-
RUN chown -R {{ app_username }}:{{ app_username }} ${HOME} ${APP_HOME}
|
|
128
|
-
USER {{ app_username }}
|
|
129
|
-
{%- endif %} {#- endif non_root_user #}
|
|
131
|
+
{{ chown_and_switch_to_regular_user_if_enabled(["/var/lib/nginx", "/var/log/nginx", "/run"]) }}
|
|
130
132
|
ENTRYPOINT ["/docker_server/.venv/bin/supervisord", "-c", "{{ supervisor_config_path }}"]
|
|
131
133
|
|
|
132
134
|
{%- elif requires_live_reload %} {#- elif requires_live_reload #}
|
|
@@ -134,19 +136,13 @@ ENV HASH_TRUSS="{{ truss_hash }}"
|
|
|
134
136
|
ENV CONTROL_SERVER_PORT="8080"
|
|
135
137
|
ENV INFERENCE_SERVER_PORT="8090"
|
|
136
138
|
ENV SERVER_START_CMD="/control/.env/bin/python /control/control/server.py"
|
|
137
|
-
{
|
|
138
|
-
RUN chown -R {{ app_username }}:{{ app_username }} ${HOME} ${APP_HOME}
|
|
139
|
-
USER {{ app_username }}
|
|
140
|
-
{%- endif %} {#- endif non_root_user #}
|
|
139
|
+
{{ chown_and_switch_to_regular_user_if_enabled() }}
|
|
141
140
|
ENTRYPOINT ["/control/.env/bin/python", "/control/control/server.py"]
|
|
142
141
|
|
|
143
142
|
{%- else %} {#- else (default inference server) #}
|
|
144
143
|
ENV INFERENCE_SERVER_PORT="8080"
|
|
145
144
|
ENV SERVER_START_CMD="{{ python_executable }} /app/main.py"
|
|
146
|
-
{
|
|
147
|
-
RUN chown -R {{ app_username }}:{{ app_username }} ${HOME} ${APP_HOME}
|
|
148
|
-
USER {{ app_username }}
|
|
149
|
-
{%- endif %} {#- endif non_root_user #}
|
|
145
|
+
{{ chown_and_switch_to_regular_user_if_enabled() }}
|
|
150
146
|
ENTRYPOINT ["{{ python_executable }}", "/app/main.py"]
|
|
151
147
|
{%- endif %} {#- endif config.docker_server / live_reload #}
|
|
152
148
|
|
|
@@ -21,6 +21,56 @@ from prometheus_client.parser import text_string_to_metric_families
|
|
|
21
21
|
PATCH_PING_MAX_DELAY_SECS = 3
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def _start_truss_server(
|
|
25
|
+
stdout_capture_file_path: str,
|
|
26
|
+
truss_control_container_fs: Path,
|
|
27
|
+
with_patch_ping_flow: bool,
|
|
28
|
+
patch_ping_server_port: int,
|
|
29
|
+
ctrl_port: int,
|
|
30
|
+
inf_port: int,
|
|
31
|
+
):
|
|
32
|
+
"""Module-level function to avoid pickling issues with multiprocessing."""
|
|
33
|
+
if with_patch_ping_flow:
|
|
34
|
+
os.environ["PATCH_PING_URL_TRUSS"] = (
|
|
35
|
+
f"http://localhost:{patch_ping_server_port}"
|
|
36
|
+
)
|
|
37
|
+
sys.stdout = open(stdout_capture_file_path, "w")
|
|
38
|
+
app_path = truss_control_container_fs / "app"
|
|
39
|
+
sys.path.append(str(app_path))
|
|
40
|
+
control_path = truss_control_container_fs / "control" / "control"
|
|
41
|
+
sys.path.append(str(control_path))
|
|
42
|
+
|
|
43
|
+
from server import ControlServer
|
|
44
|
+
|
|
45
|
+
control_server = ControlServer(
|
|
46
|
+
python_executable_path=sys.executable,
|
|
47
|
+
inf_serv_home=str(app_path),
|
|
48
|
+
control_server_port=ctrl_port,
|
|
49
|
+
inference_server_port=inf_port,
|
|
50
|
+
)
|
|
51
|
+
control_server.run()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _start_patch_ping_server(patch_ping_server_port: int):
|
|
55
|
+
"""Module-level function to avoid pickling issues with multiprocessing."""
|
|
56
|
+
import json
|
|
57
|
+
import random
|
|
58
|
+
import time
|
|
59
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
60
|
+
|
|
61
|
+
class Handler(BaseHTTPRequestHandler):
|
|
62
|
+
def do_POST(self):
|
|
63
|
+
time.sleep(random.uniform(0, PATCH_PING_MAX_DELAY_SECS))
|
|
64
|
+
self.send_response(200)
|
|
65
|
+
self.end_headers()
|
|
66
|
+
self.wfile.write(
|
|
67
|
+
bytes(json.dumps({"is_current": True}), encoding="utf-8")
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
httpd = HTTPServer(("localhost", patch_ping_server_port), Handler)
|
|
71
|
+
httpd.serve_forever()
|
|
72
|
+
|
|
73
|
+
|
|
24
74
|
@dataclass
|
|
25
75
|
class ControlServerDetails:
|
|
26
76
|
control_server_process: Process
|
|
@@ -270,51 +320,24 @@ def _configured_control_server(
|
|
|
270
320
|
inf_port = ctrl_port + 1
|
|
271
321
|
patch_ping_server_port = ctrl_port + 2
|
|
272
322
|
|
|
273
|
-
def start_truss_server(stdout_capture_file_path):
|
|
274
|
-
if with_patch_ping_flow:
|
|
275
|
-
os.environ["PATCH_PING_URL_TRUSS"] = (
|
|
276
|
-
f"http://localhost:{patch_ping_server_port}"
|
|
277
|
-
)
|
|
278
|
-
sys.stdout = open(stdout_capture_file_path, "w")
|
|
279
|
-
app_path = truss_control_container_fs / "app"
|
|
280
|
-
sys.path.append(str(app_path))
|
|
281
|
-
control_path = truss_control_container_fs / "control" / "control"
|
|
282
|
-
sys.path.append(str(control_path))
|
|
283
|
-
|
|
284
|
-
from server import ControlServer
|
|
285
|
-
|
|
286
|
-
control_server = ControlServer(
|
|
287
|
-
python_executable_path=sys.executable,
|
|
288
|
-
inf_serv_home=str(app_path),
|
|
289
|
-
control_server_port=ctrl_port,
|
|
290
|
-
inference_server_port=inf_port,
|
|
291
|
-
)
|
|
292
|
-
control_server.run()
|
|
293
|
-
|
|
294
|
-
def start_patch_ping_server():
|
|
295
|
-
import json
|
|
296
|
-
import random
|
|
297
|
-
import time
|
|
298
|
-
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
299
|
-
|
|
300
|
-
class Handler(BaseHTTPRequestHandler):
|
|
301
|
-
def do_POST(self):
|
|
302
|
-
time.sleep(random.uniform(0, PATCH_PING_MAX_DELAY_SECS))
|
|
303
|
-
self.send_response(200)
|
|
304
|
-
self.end_headers()
|
|
305
|
-
self.wfile.write(
|
|
306
|
-
bytes(json.dumps({"is_current": True}), encoding="utf-8")
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
httpd = HTTPServer(("localhost", patch_ping_server_port), Handler)
|
|
310
|
-
httpd.serve_forever()
|
|
311
|
-
|
|
312
323
|
stdout_capture_file = tempfile.NamedTemporaryFile()
|
|
313
|
-
subproc = Process(
|
|
324
|
+
subproc = Process(
|
|
325
|
+
target=_start_truss_server,
|
|
326
|
+
args=(
|
|
327
|
+
stdout_capture_file.name,
|
|
328
|
+
truss_control_container_fs,
|
|
329
|
+
with_patch_ping_flow,
|
|
330
|
+
patch_ping_server_port,
|
|
331
|
+
ctrl_port,
|
|
332
|
+
inf_port,
|
|
333
|
+
),
|
|
334
|
+
)
|
|
314
335
|
subproc.start()
|
|
315
336
|
proc_id = subproc.pid
|
|
316
337
|
if with_patch_ping_flow:
|
|
317
|
-
patch_ping_server_proc = Process(
|
|
338
|
+
patch_ping_server_proc = Process(
|
|
339
|
+
target=_start_patch_ping_server, args=(patch_ping_server_port,)
|
|
340
|
+
)
|
|
318
341
|
patch_ping_server_proc.start()
|
|
319
342
|
try:
|
|
320
343
|
time.sleep(2.0)
|
|
@@ -10,23 +10,28 @@ from pathlib import Path
|
|
|
10
10
|
import pytest
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
def _start_truss_server(stdout_capture_file_path: str, truss_container_fs: Path, port: int):
|
|
14
|
+
"""Module-level function to avoid pickling issues with multiprocessing."""
|
|
15
|
+
sys.stdout = open(stdout_capture_file_path, "w")
|
|
16
|
+
app_path = truss_container_fs / "app"
|
|
17
|
+
sys.path.append(str(app_path))
|
|
18
|
+
os.chdir(app_path)
|
|
19
|
+
|
|
20
|
+
from truss_server import TrussServer
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
app_path = truss_container_fs / "app"
|
|
20
|
-
sys.path.append(str(app_path))
|
|
21
|
-
os.chdir(app_path)
|
|
22
|
+
server = TrussServer(http_port=port, config_or_path=app_path / "config.yaml")
|
|
23
|
+
server.start()
|
|
22
24
|
|
|
23
|
-
from truss_server import TrussServer
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
@pytest.mark.integration
|
|
27
|
+
def test_truss_server_termination(truss_container_fs):
|
|
28
|
+
port = 10123
|
|
27
29
|
|
|
28
30
|
stdout_capture_file = tempfile.NamedTemporaryFile()
|
|
29
|
-
subproc = Process(
|
|
31
|
+
subproc = Process(
|
|
32
|
+
target=_start_truss_server,
|
|
33
|
+
args=(stdout_capture_file.name, truss_container_fs, port)
|
|
34
|
+
)
|
|
30
35
|
subproc.start()
|
|
31
36
|
proc_id = subproc.pid
|
|
32
37
|
time.sleep(2.0)
|
|
@@ -990,8 +990,10 @@ def test_is_healthy_returns_503_on_load_failure():
|
|
|
990
990
|
# when the model goes down, this will throw an exception
|
|
991
991
|
break
|
|
992
992
|
diff = container.diff()
|
|
993
|
-
|
|
994
|
-
|
|
993
|
+
# the crash file is written to the app user's home directory
|
|
994
|
+
crash_file_path = "/home/app/inference_server_crashed.txt"
|
|
995
|
+
assert crash_file_path in diff
|
|
996
|
+
assert diff[crash_file_path] == "A"
|
|
995
997
|
|
|
996
998
|
|
|
997
999
|
@pytest.mark.integration
|
|
@@ -38,7 +38,7 @@ truss/contexts/image_builder/cache_warmer.py,sha256=TGMV1Mh87n2e_dSowH0sf0rZhZra
|
|
|
38
38
|
truss/contexts/image_builder/image_builder.py,sha256=IuRgDeeoHVLzIkJvKtX3807eeqEyaroCs_KWDcIHZUg,1461
|
|
39
39
|
truss/contexts/image_builder/serving_image_builder.py,sha256=yrFBAGmYspt_4rtdrku-zeKQfEkqfHfTzIJEkDNDkng,34226
|
|
40
40
|
truss/contexts/image_builder/util.py,sha256=y2-CjUKv0XV-0w2sr1fUCflysDJLsoU4oPp6tvvoFnk,1203
|
|
41
|
-
truss/contexts/local_loader/docker_build_emulator.py,sha256=
|
|
41
|
+
truss/contexts/local_loader/docker_build_emulator.py,sha256=EBWflUGjlyGcWfZ2SEH3lXbIN3Vfbnty3u3m4EgBQFE,3521
|
|
42
42
|
truss/contexts/local_loader/dockerfile_parser.py,sha256=GoRJ0Af_3ILyLhjovK5lrCGn1rMxz6W3l681ro17ZzI,1344
|
|
43
43
|
truss/contexts/local_loader/load_model_local.py,sha256=7XoIjjDi9-PilI5VUgGbTn4ucxFbQF6eQ-SsIcchtcM,1867
|
|
44
44
|
truss/contexts/local_loader/truss_module_loader.py,sha256=OtewX72XljkA0MPqqf9iPIqvUhG6HwP6q-IpQfm143o,5710
|
|
@@ -66,12 +66,12 @@ truss/remote/baseten/utils/time.py,sha256=Ry9GMjYnbIGYVIGwtmv4V8ljWjvdcaCf5NOQzl
|
|
|
66
66
|
truss/remote/baseten/utils/transfer.py,sha256=d3VptuQb6M1nyS6kz0BAfeOYDLkMKUjatJXpY-mp-As,1548
|
|
67
67
|
truss/templates/README.md.jinja,sha256=N7CJdyldZuJamj5jLh47le0hFBdu9irVsTBqoxhPNPQ,2476
|
|
68
68
|
truss/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
|
-
truss/templates/base.Dockerfile.jinja,sha256=
|
|
70
|
-
truss/templates/cache.Dockerfile.jinja,sha256=
|
|
69
|
+
truss/templates/base.Dockerfile.jinja,sha256=ExTV995fE_1M-o0IXh5bJxqnISgb7rlttvUw7lXRc0M,5708
|
|
70
|
+
truss/templates/cache.Dockerfile.jinja,sha256=1qZqDo1phrcqi-Vwol-VafYJkADsBbQWU6huQ-_1x00,1146
|
|
71
71
|
truss/templates/cache_requirements.txt,sha256=xoPoJ-OVnf1z6oq_RVM3vCr3ionByyqMLj7wGs61nUs,87
|
|
72
72
|
truss/templates/copy_cache_files.Dockerfile.jinja,sha256=Os5zFdYLZ_AfCRGq4RcpVTObOTwL7zvmwYcvOzd_Zqo,126
|
|
73
73
|
truss/templates/docker_server_requirements.txt,sha256=PyhOPKAmKW1N2vLvTfLMwsEtuGpoRrbWuNo7tT6v2Mc,18
|
|
74
|
-
truss/templates/server.Dockerfile.jinja,sha256=
|
|
74
|
+
truss/templates/server.Dockerfile.jinja,sha256=4mVJGgFUcP6wtMEYcEbl5bzregHqOsKxeFIe3BYVNrA,7097
|
|
75
75
|
truss/templates/control/requirements.txt,sha256=Kk0tYID7trPk5gwX38Wrt2-YGWZAXFJCJRcqJ8ZzCjc,251
|
|
76
76
|
truss/templates/control/control/application.py,sha256=jYeta6hWe1SkfLL3W4IDmdYjg3ZuKqI_UagWYs5RB_E,3793
|
|
77
77
|
truss/templates/control/control/endpoints.py,sha256=FM-sgao7I3gMoUTasM3Xq_g2LDoJQe75JxIoaQxzeNo,10031
|
|
@@ -84,7 +84,7 @@ truss/templates/control/control/helpers/inference_server_process_controller.py,s
|
|
|
84
84
|
truss/templates/control/control/helpers/inference_server_starter.py,sha256=Fz2Puijro6Cc5cvTsAqOveNJbBQR_ARYJXl4lvETJ8Y,2633
|
|
85
85
|
truss/templates/control/control/helpers/truss_patch/__init__.py,sha256=CXZdUV_ylqLTJrKuFpvSnUT6PUFrZrMF2y6jiHbdaKU,998
|
|
86
86
|
truss/templates/control/control/helpers/truss_patch/model_code_patch_applier.py,sha256=LTIIADLz0wRn7V49j64dU1U7Hbta9YLde3pb5YZWvzQ,2001
|
|
87
|
-
truss/templates/control/control/helpers/truss_patch/model_container_patch_applier.py,sha256=
|
|
87
|
+
truss/templates/control/control/helpers/truss_patch/model_container_patch_applier.py,sha256=EnPMfoqtw3tjGhp1vQF_hAIhQqslsuM9M4mBcwWAe3Q,9053
|
|
88
88
|
truss/templates/control/control/helpers/truss_patch/requirement_name_identifier.py,sha256=CL3KEAj4B3ApMQShd7TI5umXVbazLZY5StrNlwHwWtc,1995
|
|
89
89
|
truss/templates/control/control/helpers/truss_patch/system_packages.py,sha256=IYh1CVU_kooAvtSGXKQDDWnNdOhlv7ENWagsL1wvhgw,208
|
|
90
90
|
truss/templates/custom/examples.yaml,sha256=2UcCtEdavImWmiCtj31ckBlAKVOwNMC5AwMIIznKDag,48
|
|
@@ -132,7 +132,7 @@ truss/tests/test_context_builder_image.py,sha256=fVZNJSzZNiWa7Dr1X_VhhMJtyJ5HzsL
|
|
|
132
132
|
truss/tests/test_control_truss_patching.py,sha256=lbMuAjLbkeDRLxUxXHWr41BZyhZKHQYoMnbJSj3dqrc,15390
|
|
133
133
|
truss/tests/test_custom_server.py,sha256=GP2qMgnqxJMPRtfEciqbhBcG0_JUK7gNL7nrXPGrSLg,1305
|
|
134
134
|
truss/tests/test_docker.py,sha256=3RI6jEC9CVQsKj83s_gOBl3EkdOaov-KEX4IihfMJW4,523
|
|
135
|
-
truss/tests/test_model_inference.py,sha256=
|
|
135
|
+
truss/tests/test_model_inference.py,sha256=9QfPMa1kjxvKCWg5XKocjwcpfDkKB7pWd8bn4hIkshk,76213
|
|
136
136
|
truss/tests/test_model_schema.py,sha256=Bw28CZ4D0JQOkYdBQJZvgryeW0TRn7Axketp5kvZ_t4,14219
|
|
137
137
|
truss/tests/test_testing_utilities_for_other_tests.py,sha256=YqIKflnd_BUMYaDBSkX76RWiWGWM_UlC2IoT4NngMqE,3048
|
|
138
138
|
truss/tests/test_truss_gatherer.py,sha256=bn288OEkC49YY0mhly4cAl410ktZPfElNdWwZy82WfA,1261
|
|
@@ -162,7 +162,7 @@ truss/tests/remote/baseten/test_remote.py,sha256=y1qSPL1t7dBeYI3xMFn436fttG7wkYd
|
|
|
162
162
|
truss/tests/remote/baseten/test_service.py,sha256=ufZbtQlBNIzFCxRt_iE-APLpWbVw_3ViUpSh6H9W5nU,1945
|
|
163
163
|
truss/tests/templates/control/control/test_endpoints.py,sha256=tGU3w8zOKC8LfWGdhp-TlV7E603KXg2xGwpqDdf8Pnw,3385
|
|
164
164
|
truss/tests/templates/control/control/test_server.py,sha256=r1O3VEK9eoIL2-cg8nYLXYct_H3jf5rGp1wLT1KBdeA,9488
|
|
165
|
-
truss/tests/templates/control/control/test_server_integration.py,sha256=
|
|
165
|
+
truss/tests/templates/control/control/test_server_integration.py,sha256=GSc9dxnrK6c4SCS0kzCP2pgUFbUTEMt_KTNausdPEbM,11867
|
|
166
166
|
truss/tests/templates/control/control/helpers/test_context_managers.py,sha256=3LoonRaKu_UvhaWs1eNmEQCZq-iJ3aIjI0Mn4amC8Bw,283
|
|
167
167
|
truss/tests/templates/control/control/helpers/test_model_container_patch_applier.py,sha256=jhPgExGFF42iuWPM9ry93dnpF765d-CGTCIhbswK0hk,5730
|
|
168
168
|
truss/tests/templates/control/control/helpers/test_requirement_name_identifier.py,sha256=kPYrAb97BtN8Wm0Hofw7iJ412Tew2xHgiteKtXVoC5A,2980
|
|
@@ -171,7 +171,7 @@ truss/tests/templates/core/server/test_lazy_data_resolver_v2.py,sha256=xZNMhfhHa
|
|
|
171
171
|
truss/tests/templates/core/server/test_secrets_resolver.py,sha256=qFHZEKs9t2Gj9JBJKB8xLbIjCgBpIUoNanQ3l5RBoRM,1550
|
|
172
172
|
truss/tests/templates/server/test_model_wrapper.py,sha256=1has4G3wiHd1g4JzDunJCMkT2r6LFUSIt4HaP5PVx5A,9406
|
|
173
173
|
truss/tests/templates/server/test_schema.py,sha256=HfaPGIm8u39qSvfzUwk4Ymtvq38JBczRUlmSYZS8X5I,9527
|
|
174
|
-
truss/tests/templates/server/test_truss_server.py,sha256
|
|
174
|
+
truss/tests/templates/server/test_truss_server.py,sha256=-y2MzmdURj1eOW1lgYy7V3RN3saKedaE6g4a1SwnV0Y,1662
|
|
175
175
|
truss/tests/templates/server/common/test_retry.py,sha256=-7yw0DvDXhCntNhFbTP1liWQQhlbQmZAA4AUW1VFsu0,2039
|
|
176
176
|
truss/tests/test_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
177
177
|
truss/tests/test_data/auto-mpg.data,sha256=SLgw4R_u5VclJfjxaR3bnTjT17cGPtzY_KZywqXhfY0,30286
|
|
@@ -365,8 +365,8 @@ truss_train/definitions.py,sha256=RZs4bCWkq7gBJALDLgmd4QxjlxWk6GMs2a62kiAalvw,67
|
|
|
365
365
|
truss_train/deployment.py,sha256=zmeJ66kg1Wc7l7bwA_cXqv85uMF77hYl7NPHuhc1NPs,2493
|
|
366
366
|
truss_train/loader.py,sha256=0o66EjBaHc2YY4syxxHVR4ordJWs13lNXnKjKq2wq0U,1630
|
|
367
367
|
truss_train/public_api.py,sha256=9N_NstiUlmBuLUwH_fNG_1x7OhGCytZLNvqKXBlStrM,1220
|
|
368
|
-
truss-0.10.
|
|
369
|
-
truss-0.10.
|
|
370
|
-
truss-0.10.
|
|
371
|
-
truss-0.10.
|
|
372
|
-
truss-0.10.
|
|
368
|
+
truss-0.10.9rc549.dist-info/METADATA,sha256=N9YtbH5yEBV6gtiLQFltXblIxRbVlW7kyyGekB35-FI,6674
|
|
369
|
+
truss-0.10.9rc549.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
370
|
+
truss-0.10.9rc549.dist-info/entry_points.txt,sha256=-MwKfHHQHQ6j0HqIgvxrz3CehCmczDLTD-OsRHnjjuU,130
|
|
371
|
+
truss-0.10.9rc549.dist-info/licenses/LICENSE,sha256=FTqGzu85i-uw1Gi8E_o0oD60bH9yQ_XIGtZbA1QUYiw,1064
|
|
372
|
+
truss-0.10.9rc549.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|