truefoundry 0.11.11rc1__py3-none-any.whl → 0.11.12__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 truefoundry might be problematic. Click here for more details.
- truefoundry/common/constants.py +1 -8
- truefoundry/common/storage_provider_utils.py +35 -10
- truefoundry/deploy/__init__.py +0 -3
- truefoundry/deploy/_autogen/models.py +144 -191
- truefoundry/deploy/builder/__init__.py +0 -1
- truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py +6 -3
- truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py +84 -347
- truefoundry/deploy/builder/builders/tfy_spark_buildpack/__init__.py +7 -3
- truefoundry/deploy/builder/builders/tfy_spark_buildpack/dockerfile_template.py +18 -18
- truefoundry/deploy/builder/builders/tfy_task_pyspark_buildpack/__init__.py +7 -3
- truefoundry/deploy/builder/builders/tfy_task_pyspark_buildpack/dockerfile_template.py +18 -18
- truefoundry/deploy/builder/constants.py +0 -12
- truefoundry/deploy/builder/utils.py +21 -210
- truefoundry/deploy/lib/util.py +0 -35
- truefoundry/deploy/v2/lib/deploy.py +1 -3
- truefoundry/deploy/v2/lib/patched_models.py +3 -76
- truefoundry/deploy/v2/lib/source.py +0 -4
- truefoundry/ml/artifact/truefoundry_artifact_repo.py +25 -11
- truefoundry/ml/log_types/artifacts/artifact.py +3 -2
- truefoundry/ml/log_types/artifacts/model.py +1 -0
- {truefoundry-0.11.11rc1.dist-info → truefoundry-0.11.12.dist-info}/METADATA +1 -2
- {truefoundry-0.11.11rc1.dist-info → truefoundry-0.11.12.dist-info}/RECORD +24 -24
- {truefoundry-0.11.11rc1.dist-info → truefoundry-0.11.12.dist-info}/WHEEL +0 -0
- {truefoundry-0.11.11rc1.dist-info → truefoundry-0.11.12.dist-info}/entry_points.txt +0 -0
|
@@ -1,150 +1,67 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import Dict, List, Optional, Set, Union
|
|
1
|
+
from typing import Dict
|
|
3
2
|
|
|
4
|
-
from
|
|
3
|
+
from mako.template import Template
|
|
5
4
|
|
|
6
5
|
from truefoundry.common.constants import ENV_VARS, PythonPackageManager
|
|
7
|
-
from truefoundry.deploy._autogen.models import
|
|
6
|
+
from truefoundry.deploy._autogen.models import PythonBuild
|
|
7
|
+
from truefoundry.deploy.builder.constants import (
|
|
8
|
+
PIP_CONF_BUILDKIT_SECRET_MOUNT,
|
|
9
|
+
UV_CONF_BUILDKIT_SECRET_MOUNT,
|
|
10
|
+
)
|
|
8
11
|
from truefoundry.deploy.builder.utils import (
|
|
9
12
|
generate_apt_install_command,
|
|
10
|
-
generate_command_to_install_from_uv_lock,
|
|
11
13
|
generate_pip_install_command,
|
|
12
|
-
generate_poetry_install_command,
|
|
13
|
-
generate_secret_mounts,
|
|
14
14
|
generate_uv_pip_install_command,
|
|
15
|
-
get_available_secrets,
|
|
16
15
|
)
|
|
17
16
|
from truefoundry.deploy.v2.lib.patched_models import (
|
|
18
17
|
CUDAVersion,
|
|
19
|
-
PythonBuild,
|
|
20
18
|
_resolve_requirements_path,
|
|
21
|
-
check_whether_poetry_toml_exists,
|
|
22
19
|
)
|
|
23
|
-
from truefoundry.pydantic_v1 import BaseModel
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class TemplateContext(BaseModel):
|
|
27
|
-
"""Pydantic model for template context used in Dockerfile generation."""
|
|
28
|
-
|
|
29
|
-
# Common fields
|
|
30
|
-
python_image_repo: str
|
|
31
|
-
python_version: str
|
|
32
|
-
apt_install_command: Optional[str] = None
|
|
33
|
-
package_manager_config_secret_mount: str = ""
|
|
34
|
-
|
|
35
|
-
# Pip-specific fields
|
|
36
|
-
requirements_path: Optional[str] = None
|
|
37
|
-
requirements_destination_path: Optional[str] = None
|
|
38
|
-
python_packages_install_command: Optional[str] = None
|
|
39
|
-
|
|
40
|
-
# UV-specific fields
|
|
41
|
-
final_uv_sync_command: Optional[str] = None
|
|
42
|
-
|
|
43
|
-
# Poetry-specific fields
|
|
44
|
-
final_poetry_install_command: Optional[str] = None
|
|
45
|
-
poetry_version_expression: Optional[str] = None
|
|
46
|
-
poetry_toml_exists: bool = False
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
CUDA_BASE_IMAGE_TEMPLATE = """\
|
|
50
|
-
FROM nvidia/cuda:{{ cuda_image_tag }} AS base
|
|
51
|
-
SHELL ["/bin/bash", "-o", "pipefail", "-e", "-u", "-c"]
|
|
52
|
-
|
|
53
|
-
ENV PATH=/virtualenvs/venv/bin:$PATH
|
|
54
|
-
ENV VIRTUAL_ENV=/virtualenvs/venv/
|
|
55
|
-
RUN echo "deb https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu $(cat /etc/os-release | grep UBUNTU_CODENAME | cut -d = -f 2) main" >> /etc/apt/sources.list && \\
|
|
56
|
-
echo "deb-src https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu $(cat /etc/os-release | grep UBUNTU_CODENAME | cut -d = -f 2) main" >> /etc/apt/sources.list && \\
|
|
57
|
-
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F23C5A6CF475977595C89F51BA6932366A755776 && \\
|
|
58
|
-
apt update && \\
|
|
59
|
-
DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends git python{{ python_version }}-dev python{{ python_version }}-venv && \\
|
|
60
|
-
python{{ python_version }} -m venv /virtualenvs/venv/ && \\
|
|
61
|
-
rm -rf /var/lib/apt/lists/* && \\
|
|
62
|
-
python -m pip install -U pip setuptools wheel
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
STANDARD_BASE_IMAGE_TEMPLATE = """\
|
|
66
|
-
FROM {{ python_image_repo }}:{{ python_version }} AS base
|
|
67
|
-
SHELL ["/bin/bash", "-o", "pipefail", "-e", "-u", "-c"]
|
|
68
|
-
|
|
69
|
-
ENV PATH=/virtualenvs/venv/bin:$PATH
|
|
70
|
-
ENV VIRTUAL_ENV=/virtualenvs/venv/
|
|
71
|
-
RUN apt update && \\
|
|
72
|
-
DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends git && \\
|
|
73
|
-
python -m venv /virtualenvs/venv/ && \\
|
|
74
|
-
rm -rf /var/lib/apt/lists/* && \\
|
|
75
|
-
python -m pip install -U pip setuptools wheel
|
|
76
|
-
"""
|
|
77
20
|
|
|
21
|
+
# TODO (chiragjn): Switch to a non-root user inside the container
|
|
78
22
|
|
|
79
|
-
|
|
80
|
-
{% if apt_install_command %}
|
|
81
|
-
RUN {{ apt_install_command }}
|
|
82
|
-
{% endif %}
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
PIP_DEPENDENCIES_TEMPLATE = """\
|
|
87
|
-
{% if requirements_path %}
|
|
88
|
-
COPY {{ requirements_path }} {{ requirements_destination_path }}
|
|
89
|
-
{% endif %}
|
|
90
|
-
|
|
91
|
-
{% if python_packages_install_command %}
|
|
92
|
-
RUN {{ package_manager_config_secret_mount }} {{ python_packages_install_command }}
|
|
93
|
-
{% endif %}
|
|
23
|
+
DEFAULT_PYTHON_IMAGE_REPO = "public.ecr.aws/docker/library/python"
|
|
94
24
|
|
|
25
|
+
_POST_PYTHON_INSTALL_TEMPLATE = """
|
|
26
|
+
% if apt_install_command is not None:
|
|
27
|
+
RUN ${apt_install_command}
|
|
28
|
+
% endif
|
|
29
|
+
% if requirements_path is not None:
|
|
30
|
+
COPY ${requirements_path} ${requirements_destination_path}
|
|
31
|
+
% endif
|
|
32
|
+
% if python_packages_install_command is not None:
|
|
33
|
+
RUN ${package_manager_config_secret_mount} ${python_packages_install_command}
|
|
34
|
+
% endif
|
|
95
35
|
COPY . /app
|
|
96
36
|
WORKDIR /app
|
|
97
37
|
"""
|
|
98
38
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
COPY ./ .
|
|
108
|
-
{% if final_uv_sync_command %}
|
|
109
|
-
RUN {{ package_manager_config_secret_mount }} {{ final_uv_sync_command }}
|
|
110
|
-
{% endif %}
|
|
111
|
-
"""
|
|
112
|
-
|
|
113
|
-
POETRY_DEPENDENCIES_TEMPLATE = """\
|
|
114
|
-
RUN python -m venv /opt/poetry
|
|
115
|
-
ENV PATH="/opt/poetry/bin:$PATH"
|
|
116
|
-
RUN pip install -q --no-cache-dir -U "poetry{{ poetry_version_expression }}"
|
|
117
|
-
|
|
118
|
-
WORKDIR /app
|
|
119
|
-
COPY pyproject.toml poetry.lock .
|
|
120
|
-
{% if poetry_toml_exists %}
|
|
121
|
-
COPY poetry.toml .
|
|
122
|
-
{%- endif %}
|
|
123
|
-
RUN poetry config virtualenvs.create false --local \\
|
|
124
|
-
&& poetry config virtualenvs.in-project false --local
|
|
125
|
-
|
|
126
|
-
ENV PATH="/virtualenvs/venv/bin:$PATH"
|
|
127
|
-
ENV VIRTUAL_ENV="/virtualenvs/venv/"
|
|
128
|
-
|
|
129
|
-
{% if python_packages_install_command %}
|
|
130
|
-
RUN {{ package_manager_config_secret_mount }} {{ python_packages_install_command }}
|
|
131
|
-
{% endif %}
|
|
132
|
-
|
|
133
|
-
COPY ./ .
|
|
134
|
-
|
|
135
|
-
{% if final_poetry_install_command %}
|
|
136
|
-
RUN {{ package_manager_config_secret_mount }} {{ final_poetry_install_command }}
|
|
137
|
-
{% endif %}
|
|
138
|
-
|
|
139
|
-
RUN rm -rf /opt/poetry
|
|
39
|
+
DOCKERFILE_TEMPLATE = Template(
|
|
40
|
+
"""
|
|
41
|
+
FROM ${python_image_repo}:${python_version}
|
|
42
|
+
ENV PATH=/virtualenvs/venv/bin:$PATH
|
|
43
|
+
RUN apt update && \
|
|
44
|
+
DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends git && \
|
|
45
|
+
python -m venv /virtualenvs/venv/ && \
|
|
46
|
+
rm -rf /var/lib/apt/lists/*
|
|
140
47
|
"""
|
|
48
|
+
+ _POST_PYTHON_INSTALL_TEMPLATE
|
|
49
|
+
)
|
|
141
50
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{
|
|
145
|
-
|
|
51
|
+
CUDA_DOCKERFILE_TEMPLATE = Template(
|
|
52
|
+
"""
|
|
53
|
+
FROM nvidia/cuda:${nvidia_cuda_image_tag}
|
|
54
|
+
ENV PATH=/virtualenvs/venv/bin:$PATH
|
|
55
|
+
RUN echo "deb https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu $(cat /etc/os-release | grep UBUNTU_CODENAME | cut -d = -f 2) main" >> /etc/apt/sources.list && \
|
|
56
|
+
echo "deb-src https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu $(cat /etc/os-release | grep UBUNTU_CODENAME | cut -d = -f 2) main" >> /etc/apt/sources.list && \
|
|
57
|
+
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F23C5A6CF475977595C89F51BA6932366A755776 && \
|
|
58
|
+
apt update && \
|
|
59
|
+
DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends git python${python_version}-dev python${python_version}-venv && \
|
|
60
|
+
python${python_version} -m venv /virtualenvs/venv/ && \
|
|
61
|
+
rm -rf /var/lib/apt/lists/*
|
|
146
62
|
"""
|
|
147
|
-
|
|
63
|
+
+ _POST_PYTHON_INSTALL_TEMPLATE
|
|
64
|
+
)
|
|
148
65
|
|
|
149
66
|
CUDA_VERSION_TO_IMAGE_TAG: Dict[str, str] = {
|
|
150
67
|
CUDAVersion.CUDA_11_0_CUDNN8.value: "11.0.3-cudnn8-runtime-ubuntu20.04",
|
|
@@ -172,248 +89,68 @@ CUDA_VERSION_TO_IMAGE_TAG: Dict[str, str] = {
|
|
|
172
89
|
def generate_dockerfile_content(
|
|
173
90
|
build_configuration: PythonBuild,
|
|
174
91
|
package_manager: str = ENV_VARS.TFY_PYTHON_BUILD_PACKAGE_MANAGER,
|
|
175
|
-
|
|
92
|
+
mount_python_package_manager_conf_secret: bool = False,
|
|
176
93
|
) -> str:
|
|
177
|
-
if isinstance(build_configuration, dict):
|
|
178
|
-
build_configuration = PythonBuild(**build_configuration)
|
|
179
|
-
if not build_configuration.python_version:
|
|
180
|
-
raise ValueError(
|
|
181
|
-
"`python_version` is required for `tfy-python-buildpack` builder"
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
# Set up Python dependencies
|
|
185
|
-
python_dependencies = deepcopy(build_configuration.python_dependencies)
|
|
186
|
-
if not python_dependencies:
|
|
187
|
-
python_dependencies = Pip(
|
|
188
|
-
type="pip",
|
|
189
|
-
requirements_path=build_configuration.requirements_path,
|
|
190
|
-
pip_packages=build_configuration.pip_packages,
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
# Get available secrets from docker build extra args
|
|
194
|
-
available_secrets = set()
|
|
195
|
-
if docker_build_extra_args:
|
|
196
|
-
available_secrets = get_available_secrets(docker_build_extra_args)
|
|
197
|
-
|
|
198
|
-
# Prepare template context
|
|
199
|
-
context = _build_template_context(
|
|
200
|
-
build_configuration=build_configuration,
|
|
201
|
-
python_dependencies=python_dependencies,
|
|
202
|
-
package_manager=package_manager,
|
|
203
|
-
available_secrets=available_secrets,
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
# Build sections
|
|
207
|
-
base_image_setup = _build_base_image_section(build_configuration)
|
|
208
|
-
apt_packages_section = _build_apt_packages_section(context.apt_install_command)
|
|
209
|
-
python_dependencies_section = _build_python_dependencies_section(
|
|
210
|
-
python_dependencies.type, context
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
template = Template(DOCKERFILE_TEMPLATE)
|
|
214
|
-
return template.render(
|
|
215
|
-
base_image_setup=base_image_setup,
|
|
216
|
-
apt_packages_section=apt_packages_section,
|
|
217
|
-
python_dependencies_section=python_dependencies_section,
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def _build_template_context(
|
|
222
|
-
build_configuration: PythonBuild,
|
|
223
|
-
python_dependencies: Union[Pip, UV, Poetry],
|
|
224
|
-
package_manager: str,
|
|
225
|
-
available_secrets: Set[str],
|
|
226
|
-
) -> TemplateContext:
|
|
227
|
-
# Set up package manager config secret mount
|
|
228
|
-
python_dependencies_type = python_dependencies.type
|
|
229
|
-
package_manager_config_secret_mount = ""
|
|
230
|
-
if available_secrets:
|
|
231
|
-
package_manager_config_secret_mount = generate_secret_mounts(
|
|
232
|
-
available_secrets=available_secrets,
|
|
233
|
-
python_dependencies_type=python_dependencies_type,
|
|
234
|
-
package_manager=package_manager,
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
# Configure dependencies based on type
|
|
238
|
-
if python_dependencies_type == "uv":
|
|
239
|
-
uv_context = _build_uv_context(
|
|
240
|
-
python_dependencies,
|
|
241
|
-
available_secrets,
|
|
242
|
-
)
|
|
243
|
-
return TemplateContext(
|
|
244
|
-
python_image_repo=ENV_VARS.TFY_PYTHONBUILD_PYTHON_IMAGE_REPO,
|
|
245
|
-
python_version=build_configuration.python_version,
|
|
246
|
-
apt_install_command=generate_apt_install_command(
|
|
247
|
-
apt_packages=build_configuration.apt_packages
|
|
248
|
-
),
|
|
249
|
-
package_manager_config_secret_mount=package_manager_config_secret_mount,
|
|
250
|
-
python_packages_install_command=uv_context[
|
|
251
|
-
"python_packages_install_command"
|
|
252
|
-
],
|
|
253
|
-
final_uv_sync_command=uv_context["final_uv_sync_command"],
|
|
254
|
-
)
|
|
255
|
-
elif python_dependencies_type == "poetry":
|
|
256
|
-
poetry_toml_exists = check_whether_poetry_toml_exists(
|
|
257
|
-
build_context_path=build_configuration.build_context_path,
|
|
258
|
-
)
|
|
259
|
-
poetry_context = _build_poetry_context(python_dependencies, available_secrets)
|
|
260
|
-
return TemplateContext(
|
|
261
|
-
python_image_repo=ENV_VARS.TFY_PYTHONBUILD_PYTHON_IMAGE_REPO,
|
|
262
|
-
python_version=build_configuration.python_version,
|
|
263
|
-
apt_install_command=generate_apt_install_command(
|
|
264
|
-
apt_packages=build_configuration.apt_packages
|
|
265
|
-
),
|
|
266
|
-
package_manager_config_secret_mount=package_manager_config_secret_mount,
|
|
267
|
-
python_packages_install_command=poetry_context[
|
|
268
|
-
"python_packages_install_command"
|
|
269
|
-
],
|
|
270
|
-
final_poetry_install_command=poetry_context["final_poetry_install_command"],
|
|
271
|
-
poetry_version_expression=poetry_context["poetry_version_expression"],
|
|
272
|
-
poetry_toml_exists=poetry_toml_exists,
|
|
273
|
-
)
|
|
274
|
-
elif python_dependencies_type == "pip":
|
|
275
|
-
pip_context = _build_pip_context(
|
|
276
|
-
build_configuration,
|
|
277
|
-
python_dependencies,
|
|
278
|
-
package_manager,
|
|
279
|
-
available_secrets,
|
|
280
|
-
)
|
|
281
|
-
return TemplateContext(
|
|
282
|
-
python_image_repo=ENV_VARS.TFY_PYTHONBUILD_PYTHON_IMAGE_REPO,
|
|
283
|
-
python_version=build_configuration.python_version,
|
|
284
|
-
apt_install_command=generate_apt_install_command(
|
|
285
|
-
apt_packages=build_configuration.apt_packages
|
|
286
|
-
),
|
|
287
|
-
package_manager_config_secret_mount=package_manager_config_secret_mount,
|
|
288
|
-
requirements_path=pip_context["requirements_path"],
|
|
289
|
-
requirements_destination_path=pip_context["requirements_destination_path"],
|
|
290
|
-
python_packages_install_command=pip_context[
|
|
291
|
-
"python_packages_install_command"
|
|
292
|
-
],
|
|
293
|
-
)
|
|
294
|
-
else:
|
|
295
|
-
raise ValueError(f"Unsupported dependency type: {python_dependencies_type}")
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
def _build_base_image_section(build_configuration: PythonBuild) -> str:
|
|
299
|
-
if build_configuration.cuda_version:
|
|
300
|
-
cuda_image_tag = CUDA_VERSION_TO_IMAGE_TAG.get(
|
|
301
|
-
build_configuration.cuda_version, build_configuration.cuda_version
|
|
302
|
-
)
|
|
303
|
-
template = Template(CUDA_BASE_IMAGE_TEMPLATE)
|
|
304
|
-
return template.render(
|
|
305
|
-
cuda_image_tag=cuda_image_tag,
|
|
306
|
-
python_version=build_configuration.python_version,
|
|
307
|
-
)
|
|
308
|
-
else:
|
|
309
|
-
template = Template(STANDARD_BASE_IMAGE_TEMPLATE)
|
|
310
|
-
return template.render(
|
|
311
|
-
python_image_repo=ENV_VARS.TFY_PYTHONBUILD_PYTHON_IMAGE_REPO,
|
|
312
|
-
python_version=build_configuration.python_version,
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
def _build_apt_packages_section(apt_install_command: Optional[str]) -> str:
|
|
317
|
-
template = Template(APT_PACKAGES_TEMPLATE)
|
|
318
|
-
return template.render(apt_install_command=apt_install_command)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
def _build_python_dependencies_section(
|
|
322
|
-
python_dependencies_type: str, context: TemplateContext
|
|
323
|
-
) -> str:
|
|
324
|
-
if python_dependencies_type == "pip":
|
|
325
|
-
template = Template(PIP_DEPENDENCIES_TEMPLATE)
|
|
326
|
-
return template.render(**context.dict())
|
|
327
|
-
elif python_dependencies_type == "uv":
|
|
328
|
-
template = Template(UV_DEPENDENCIES_TEMPLATE)
|
|
329
|
-
return template.render(**context.dict())
|
|
330
|
-
elif python_dependencies_type == "poetry":
|
|
331
|
-
template = Template(POETRY_DEPENDENCIES_TEMPLATE)
|
|
332
|
-
return template.render(**context.dict())
|
|
333
|
-
else:
|
|
334
|
-
raise ValueError(f"Unsupported dependency type: {python_dependencies_type}")
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def _build_uv_context(python_dependencies: UV, available_secrets: Set[str]) -> Dict:
|
|
338
|
-
python_packages_install_command = generate_command_to_install_from_uv_lock(
|
|
339
|
-
sync_options=python_dependencies.sync_options,
|
|
340
|
-
uv_version=python_dependencies.uv_version,
|
|
341
|
-
available_secrets=available_secrets,
|
|
342
|
-
)
|
|
343
|
-
final_uv_sync_command = generate_command_to_install_from_uv_lock(
|
|
344
|
-
sync_options=python_dependencies.sync_options,
|
|
345
|
-
uv_version=python_dependencies.uv_version,
|
|
346
|
-
install_project=True,
|
|
347
|
-
available_secrets=available_secrets,
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
"python_packages_install_command": python_packages_install_command,
|
|
352
|
-
"final_uv_sync_command": final_uv_sync_command,
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def _build_poetry_context(
|
|
357
|
-
python_dependencies: Poetry, available_secrets: Set[str]
|
|
358
|
-
) -> Dict:
|
|
359
|
-
python_packages_install_command = generate_poetry_install_command(
|
|
360
|
-
install_options=python_dependencies.install_options,
|
|
361
|
-
available_secrets=available_secrets,
|
|
362
|
-
)
|
|
363
|
-
final_poetry_install_command = generate_poetry_install_command(
|
|
364
|
-
install_options=python_dependencies.install_options,
|
|
365
|
-
install_project=True,
|
|
366
|
-
available_secrets=available_secrets,
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
poetry_version = python_dependencies.poetry_version
|
|
370
|
-
# Handle "latest" poetry version by using major version constraint
|
|
371
|
-
if poetry_version == "latest":
|
|
372
|
-
major_version = ENV_VARS.TFY_PYTHON_BUILD_LATEST_POETRY_MAJOR_VERSION
|
|
373
|
-
poetry_version_expression = f">={major_version},<{major_version + 1}"
|
|
374
|
-
else:
|
|
375
|
-
# For regular versions, use ~= operator for compatibility
|
|
376
|
-
poetry_version_expression = f"~={poetry_version}"
|
|
377
|
-
|
|
378
|
-
return {
|
|
379
|
-
"python_packages_install_command": python_packages_install_command,
|
|
380
|
-
"final_poetry_install_command": final_poetry_install_command,
|
|
381
|
-
"poetry_version_expression": poetry_version_expression,
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
def _build_pip_context(
|
|
386
|
-
build_configuration: PythonBuild,
|
|
387
|
-
python_dependencies: Pip,
|
|
388
|
-
package_manager: str,
|
|
389
|
-
available_secrets: Set[str],
|
|
390
|
-
) -> Dict:
|
|
391
94
|
# TODO (chiragjn): Handle recursive references to other requirements files e.g. `-r requirements-gpu.txt`
|
|
392
95
|
requirements_path = _resolve_requirements_path(
|
|
393
96
|
build_context_path=build_configuration.build_context_path,
|
|
394
|
-
requirements_path=
|
|
97
|
+
requirements_path=build_configuration.requirements_path,
|
|
395
98
|
)
|
|
396
99
|
requirements_destination_path = (
|
|
397
100
|
"/tmp/requirements.txt" if requirements_path else None
|
|
398
101
|
)
|
|
399
|
-
|
|
102
|
+
if not build_configuration.python_version:
|
|
103
|
+
raise ValueError(
|
|
104
|
+
"`python_version` is required for `tfy-python-buildpack` builder"
|
|
105
|
+
)
|
|
400
106
|
if package_manager == PythonPackageManager.PIP.value:
|
|
401
107
|
python_packages_install_command = generate_pip_install_command(
|
|
402
108
|
requirements_path=requirements_destination_path,
|
|
403
|
-
pip_packages=
|
|
404
|
-
|
|
109
|
+
pip_packages=build_configuration.pip_packages,
|
|
110
|
+
mount_pip_conf_secret=mount_python_package_manager_conf_secret,
|
|
405
111
|
)
|
|
406
112
|
elif package_manager == PythonPackageManager.UV.value:
|
|
407
113
|
python_packages_install_command = generate_uv_pip_install_command(
|
|
408
114
|
requirements_path=requirements_destination_path,
|
|
409
|
-
pip_packages=
|
|
410
|
-
|
|
115
|
+
pip_packages=build_configuration.pip_packages,
|
|
116
|
+
mount_uv_conf_secret=mount_python_package_manager_conf_secret,
|
|
411
117
|
)
|
|
412
118
|
else:
|
|
413
119
|
raise ValueError(f"Unsupported package manager: {package_manager}")
|
|
414
120
|
|
|
415
|
-
|
|
121
|
+
apt_install_command = generate_apt_install_command(
|
|
122
|
+
apt_packages=build_configuration.apt_packages
|
|
123
|
+
)
|
|
124
|
+
template_args = {
|
|
125
|
+
"python_image_repo": ENV_VARS.TFY_PYTHONBUILD_PYTHON_IMAGE_REPO,
|
|
126
|
+
"python_version": build_configuration.python_version,
|
|
127
|
+
"apt_install_command": apt_install_command,
|
|
416
128
|
"requirements_path": requirements_path,
|
|
417
129
|
"requirements_destination_path": requirements_destination_path,
|
|
418
130
|
"python_packages_install_command": python_packages_install_command,
|
|
419
131
|
}
|
|
132
|
+
|
|
133
|
+
if mount_python_package_manager_conf_secret:
|
|
134
|
+
if package_manager == PythonPackageManager.PIP.value:
|
|
135
|
+
template_args["package_manager_config_secret_mount"] = (
|
|
136
|
+
PIP_CONF_BUILDKIT_SECRET_MOUNT
|
|
137
|
+
)
|
|
138
|
+
elif package_manager == PythonPackageManager.UV.value:
|
|
139
|
+
template_args["package_manager_config_secret_mount"] = (
|
|
140
|
+
UV_CONF_BUILDKIT_SECRET_MOUNT
|
|
141
|
+
)
|
|
142
|
+
else:
|
|
143
|
+
raise ValueError(f"Unsupported package manager: {package_manager}")
|
|
144
|
+
else:
|
|
145
|
+
template_args["package_manager_config_secret_mount"] = ""
|
|
146
|
+
|
|
147
|
+
if build_configuration.cuda_version:
|
|
148
|
+
template = CUDA_DOCKERFILE_TEMPLATE
|
|
149
|
+
template_args["nvidia_cuda_image_tag"] = CUDA_VERSION_TO_IMAGE_TAG.get(
|
|
150
|
+
build_configuration.cuda_version, build_configuration.cuda_version
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
template = DOCKERFILE_TEMPLATE
|
|
154
|
+
|
|
155
|
+
dockerfile_content = template.render(**template_args)
|
|
156
|
+
return dockerfile_content
|
|
@@ -17,12 +17,12 @@ __all__ = ["generate_dockerfile_content", "build"]
|
|
|
17
17
|
def _convert_to_dockerfile_build_config(
|
|
18
18
|
build_configuration: SparkBuild,
|
|
19
19
|
dockerfile_path: str,
|
|
20
|
-
|
|
20
|
+
mount_python_package_manager_conf_secret: bool = False,
|
|
21
21
|
) -> DockerFileBuild:
|
|
22
22
|
dockerfile_content = generate_dockerfile_content(
|
|
23
23
|
build_configuration=build_configuration,
|
|
24
|
+
mount_python_package_manager_conf_secret=mount_python_package_manager_conf_secret,
|
|
24
25
|
package_manager=PythonPackageManager.PIP.value,
|
|
25
|
-
docker_build_extra_args=extra_opts,
|
|
26
26
|
)
|
|
27
27
|
with open(dockerfile_path, "w", encoding="utf8") as fp:
|
|
28
28
|
fp.write(dockerfile_content)
|
|
@@ -39,6 +39,10 @@ def build(
|
|
|
39
39
|
build_configuration: SparkBuild,
|
|
40
40
|
extra_opts: Optional[List[str]] = None,
|
|
41
41
|
):
|
|
42
|
+
mount_python_package_manager_conf_secret = (
|
|
43
|
+
has_python_package_manager_conf_secret(extra_opts) if extra_opts else False
|
|
44
|
+
)
|
|
45
|
+
|
|
42
46
|
# Copy tfy_execute_notebook.py to the build context
|
|
43
47
|
execute_notebook_src = os.path.join(
|
|
44
48
|
os.path.dirname(__file__), "tfy_execute_notebook.py"
|
|
@@ -59,7 +63,7 @@ def build(
|
|
|
59
63
|
docker_build_configuration = _convert_to_dockerfile_build_config(
|
|
60
64
|
build_configuration,
|
|
61
65
|
dockerfile_path=os.path.join(local_dir, "Dockerfile"),
|
|
62
|
-
|
|
66
|
+
mount_python_package_manager_conf_secret=mount_python_package_manager_conf_secret,
|
|
63
67
|
)
|
|
64
68
|
dockerfile.build(
|
|
65
69
|
tag=tag,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
from typing import List, Optional
|
|
2
|
-
|
|
3
1
|
from mako.template import Template
|
|
4
2
|
|
|
5
3
|
from truefoundry.common.constants import ENV_VARS, PythonPackageManager
|
|
6
4
|
from truefoundry.deploy._autogen.models import SparkBuild
|
|
5
|
+
from truefoundry.deploy.builder.constants import (
|
|
6
|
+
PIP_CONF_BUILDKIT_SECRET_MOUNT,
|
|
7
|
+
UV_CONF_BUILDKIT_SECRET_MOUNT,
|
|
8
|
+
)
|
|
7
9
|
from truefoundry.deploy.builder.utils import (
|
|
8
10
|
generate_pip_install_command,
|
|
9
|
-
generate_secret_mounts,
|
|
10
11
|
generate_uv_pip_install_command,
|
|
11
|
-
get_available_secrets,
|
|
12
12
|
)
|
|
13
13
|
from truefoundry.deploy.v2.lib.patched_models import (
|
|
14
14
|
_resolve_requirements_path,
|
|
@@ -71,13 +71,8 @@ ADDITIONAL_PIP_PACKAGES = [
|
|
|
71
71
|
def generate_dockerfile_content(
|
|
72
72
|
build_configuration: SparkBuild,
|
|
73
73
|
package_manager: str = ENV_VARS.TFY_PYTHON_BUILD_PACKAGE_MANAGER,
|
|
74
|
-
|
|
74
|
+
mount_python_package_manager_conf_secret: bool = False,
|
|
75
75
|
) -> str:
|
|
76
|
-
# Get available secrets from docker build extra args
|
|
77
|
-
available_secrets = set()
|
|
78
|
-
if docker_build_extra_args:
|
|
79
|
-
available_secrets = get_available_secrets(docker_build_extra_args)
|
|
80
|
-
|
|
81
76
|
# TODO (chiragjn): Handle recursive references to other requirements files e.g. `-r requirements-gpu.txt`
|
|
82
77
|
requirements_path = _resolve_requirements_path(
|
|
83
78
|
build_context_path=build_configuration.build_context_path,
|
|
@@ -95,13 +90,13 @@ def generate_dockerfile_content(
|
|
|
95
90
|
python_packages_install_command = generate_pip_install_command(
|
|
96
91
|
requirements_path=requirements_destination_path,
|
|
97
92
|
pip_packages=ADDITIONAL_PIP_PACKAGES,
|
|
98
|
-
|
|
93
|
+
mount_pip_conf_secret=mount_python_package_manager_conf_secret,
|
|
99
94
|
)
|
|
100
95
|
elif package_manager == PythonPackageManager.UV.value:
|
|
101
96
|
python_packages_install_command = generate_uv_pip_install_command(
|
|
102
97
|
requirements_path=requirements_destination_path,
|
|
103
98
|
pip_packages=ADDITIONAL_PIP_PACKAGES,
|
|
104
|
-
|
|
99
|
+
mount_uv_conf_secret=mount_python_package_manager_conf_secret,
|
|
105
100
|
)
|
|
106
101
|
else:
|
|
107
102
|
raise ValueError(f"Unsupported package manager: {package_manager}")
|
|
@@ -114,12 +109,17 @@ def generate_dockerfile_content(
|
|
|
114
109
|
"python_packages_install_command": python_packages_install_command,
|
|
115
110
|
}
|
|
116
111
|
|
|
117
|
-
if
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
if mount_python_package_manager_conf_secret:
|
|
113
|
+
if package_manager == PythonPackageManager.PIP.value:
|
|
114
|
+
template_args["package_manager_config_secret_mount"] = (
|
|
115
|
+
PIP_CONF_BUILDKIT_SECRET_MOUNT
|
|
116
|
+
)
|
|
117
|
+
elif package_manager == PythonPackageManager.UV.value:
|
|
118
|
+
template_args["package_manager_config_secret_mount"] = (
|
|
119
|
+
UV_CONF_BUILDKIT_SECRET_MOUNT
|
|
120
|
+
)
|
|
121
|
+
else:
|
|
122
|
+
raise ValueError(f"Unsupported package manager: {package_manager}")
|
|
123
123
|
else:
|
|
124
124
|
template_args["package_manager_config_secret_mount"] = ""
|
|
125
125
|
|
|
@@ -15,11 +15,11 @@ __all__ = ["generate_dockerfile_content", "build"]
|
|
|
15
15
|
def _convert_to_dockerfile_build_config(
|
|
16
16
|
build_configuration: TaskPySparkBuild,
|
|
17
17
|
dockerfile_path: str,
|
|
18
|
-
|
|
18
|
+
mount_python_package_manager_conf_secret: bool = False,
|
|
19
19
|
) -> DockerFileBuild:
|
|
20
20
|
dockerfile_content = generate_dockerfile_content(
|
|
21
21
|
build_configuration=build_configuration,
|
|
22
|
-
|
|
22
|
+
mount_python_package_manager_conf_secret=mount_python_package_manager_conf_secret,
|
|
23
23
|
)
|
|
24
24
|
with open(dockerfile_path, "w", encoding="utf8") as fp:
|
|
25
25
|
fp.write(dockerfile_content)
|
|
@@ -35,11 +35,15 @@ def build(
|
|
|
35
35
|
build_configuration: TaskPySparkBuild,
|
|
36
36
|
extra_opts: Optional[List[str]] = None,
|
|
37
37
|
):
|
|
38
|
+
mount_python_package_manager_conf_secret = (
|
|
39
|
+
has_python_package_manager_conf_secret(extra_opts) if extra_opts else False
|
|
40
|
+
)
|
|
41
|
+
|
|
38
42
|
with TemporaryDirectory() as local_dir:
|
|
39
43
|
docker_build_configuration = _convert_to_dockerfile_build_config(
|
|
40
44
|
build_configuration,
|
|
41
45
|
dockerfile_path=os.path.join(local_dir, "Dockerfile"),
|
|
42
|
-
|
|
46
|
+
mount_python_package_manager_conf_secret=mount_python_package_manager_conf_secret,
|
|
43
47
|
)
|
|
44
48
|
dockerfile.build(
|
|
45
49
|
tag=tag,
|