kubetorch 0.2.0__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 kubetorch might be problematic. Click here for more details.

Files changed (93) hide show
  1. kubetorch/__init__.py +60 -0
  2. kubetorch/cli.py +1985 -0
  3. kubetorch/cli_utils.py +1025 -0
  4. kubetorch/config.py +453 -0
  5. kubetorch/constants.py +18 -0
  6. kubetorch/docs/Makefile +18 -0
  7. kubetorch/docs/__init__.py +0 -0
  8. kubetorch/docs/_ext/json_globaltoc.py +42 -0
  9. kubetorch/docs/api/cli.rst +10 -0
  10. kubetorch/docs/api/python/app.rst +21 -0
  11. kubetorch/docs/api/python/cls.rst +19 -0
  12. kubetorch/docs/api/python/compute.rst +25 -0
  13. kubetorch/docs/api/python/config.rst +11 -0
  14. kubetorch/docs/api/python/fn.rst +19 -0
  15. kubetorch/docs/api/python/image.rst +14 -0
  16. kubetorch/docs/api/python/secret.rst +18 -0
  17. kubetorch/docs/api/python/volumes.rst +13 -0
  18. kubetorch/docs/api/python.rst +101 -0
  19. kubetorch/docs/conf.py +69 -0
  20. kubetorch/docs/index.rst +20 -0
  21. kubetorch/docs/requirements.txt +5 -0
  22. kubetorch/globals.py +285 -0
  23. kubetorch/logger.py +59 -0
  24. kubetorch/resources/__init__.py +0 -0
  25. kubetorch/resources/callables/__init__.py +0 -0
  26. kubetorch/resources/callables/cls/__init__.py +0 -0
  27. kubetorch/resources/callables/cls/cls.py +157 -0
  28. kubetorch/resources/callables/fn/__init__.py +0 -0
  29. kubetorch/resources/callables/fn/fn.py +133 -0
  30. kubetorch/resources/callables/module.py +1416 -0
  31. kubetorch/resources/callables/utils.py +174 -0
  32. kubetorch/resources/compute/__init__.py +0 -0
  33. kubetorch/resources/compute/app.py +261 -0
  34. kubetorch/resources/compute/compute.py +2596 -0
  35. kubetorch/resources/compute/decorators.py +139 -0
  36. kubetorch/resources/compute/rbac.py +74 -0
  37. kubetorch/resources/compute/utils.py +1114 -0
  38. kubetorch/resources/compute/websocket.py +137 -0
  39. kubetorch/resources/images/__init__.py +1 -0
  40. kubetorch/resources/images/image.py +414 -0
  41. kubetorch/resources/images/images.py +74 -0
  42. kubetorch/resources/secrets/__init__.py +2 -0
  43. kubetorch/resources/secrets/kubernetes_secrets_client.py +412 -0
  44. kubetorch/resources/secrets/provider_secrets/__init__.py +0 -0
  45. kubetorch/resources/secrets/provider_secrets/anthropic_secret.py +12 -0
  46. kubetorch/resources/secrets/provider_secrets/aws_secret.py +16 -0
  47. kubetorch/resources/secrets/provider_secrets/azure_secret.py +14 -0
  48. kubetorch/resources/secrets/provider_secrets/cohere_secret.py +12 -0
  49. kubetorch/resources/secrets/provider_secrets/gcp_secret.py +16 -0
  50. kubetorch/resources/secrets/provider_secrets/github_secret.py +13 -0
  51. kubetorch/resources/secrets/provider_secrets/huggingface_secret.py +20 -0
  52. kubetorch/resources/secrets/provider_secrets/kubeconfig_secret.py +12 -0
  53. kubetorch/resources/secrets/provider_secrets/lambda_secret.py +13 -0
  54. kubetorch/resources/secrets/provider_secrets/langchain_secret.py +12 -0
  55. kubetorch/resources/secrets/provider_secrets/openai_secret.py +11 -0
  56. kubetorch/resources/secrets/provider_secrets/pinecone_secret.py +12 -0
  57. kubetorch/resources/secrets/provider_secrets/providers.py +93 -0
  58. kubetorch/resources/secrets/provider_secrets/ssh_secret.py +12 -0
  59. kubetorch/resources/secrets/provider_secrets/wandb_secret.py +11 -0
  60. kubetorch/resources/secrets/secret.py +238 -0
  61. kubetorch/resources/secrets/secret_factory.py +70 -0
  62. kubetorch/resources/secrets/utils.py +209 -0
  63. kubetorch/resources/volumes/__init__.py +0 -0
  64. kubetorch/resources/volumes/volume.py +365 -0
  65. kubetorch/servers/__init__.py +0 -0
  66. kubetorch/servers/http/__init__.py +0 -0
  67. kubetorch/servers/http/distributed_utils.py +3223 -0
  68. kubetorch/servers/http/http_client.py +730 -0
  69. kubetorch/servers/http/http_server.py +1788 -0
  70. kubetorch/servers/http/server_metrics.py +278 -0
  71. kubetorch/servers/http/utils.py +728 -0
  72. kubetorch/serving/__init__.py +0 -0
  73. kubetorch/serving/autoscaling.py +173 -0
  74. kubetorch/serving/base_service_manager.py +363 -0
  75. kubetorch/serving/constants.py +83 -0
  76. kubetorch/serving/deployment_service_manager.py +478 -0
  77. kubetorch/serving/knative_service_manager.py +519 -0
  78. kubetorch/serving/raycluster_service_manager.py +582 -0
  79. kubetorch/serving/service_manager.py +18 -0
  80. kubetorch/serving/templates/deployment_template.yaml +17 -0
  81. kubetorch/serving/templates/knative_service_template.yaml +19 -0
  82. kubetorch/serving/templates/kt_setup_template.sh.j2 +81 -0
  83. kubetorch/serving/templates/pod_template.yaml +194 -0
  84. kubetorch/serving/templates/raycluster_service_template.yaml +42 -0
  85. kubetorch/serving/templates/raycluster_template.yaml +35 -0
  86. kubetorch/serving/templates/service_template.yaml +21 -0
  87. kubetorch/serving/templates/workerset_template.yaml +36 -0
  88. kubetorch/serving/utils.py +377 -0
  89. kubetorch/utils.py +284 -0
  90. kubetorch-0.2.0.dist-info/METADATA +121 -0
  91. kubetorch-0.2.0.dist-info/RECORD +93 -0
  92. kubetorch-0.2.0.dist-info/WHEEL +4 -0
  93. kubetorch-0.2.0.dist-info/entry_points.txt +5 -0
@@ -0,0 +1,137 @@
1
+ import random
2
+ import socket
3
+ import threading
4
+ import time
5
+
6
+ import websocket
7
+
8
+
9
+ class WebSocketRsyncTunnel:
10
+ def __init__(self, local_port: int, ws_url: str):
11
+ self.requested_port = local_port
12
+ self.local_port = None # Will be set in __enter__
13
+ self.ws_url = ws_url
14
+ self.running = False
15
+ self.server_socket = None
16
+
17
+ def __enter__(self):
18
+ self.running = True
19
+
20
+ # Add randomization to reduce concurrent collision probability
21
+ # Try multiple times with different socket instances
22
+ max_attempts = 20
23
+ port_range = 100 # Much wider range to avoid collisions
24
+
25
+ for attempt in range(max_attempts):
26
+ # Add random offset to spread out concurrent requests
27
+ random_offset = random.randint(0, 50)
28
+ start_port = self.requested_port + random_offset
29
+
30
+ # Create a new socket for each attempt
31
+ server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
32
+ server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
33
+
34
+ # Try ports in this range
35
+ for i in range(port_range):
36
+ port = start_port + i
37
+ try:
38
+ server_socket.bind(("127.0.0.1", port))
39
+ server_socket.listen(5)
40
+ # Success! Save the socket and port
41
+ self.server_socket = server_socket
42
+ self.local_port = port
43
+ break
44
+ except OSError as e:
45
+ if e.errno == 98: # Address already in use
46
+ continue
47
+ else:
48
+ server_socket.close()
49
+ raise
50
+ else:
51
+ # No ports available in this range, close socket and try again
52
+ server_socket.close()
53
+ # Add small random delay to reduce simultaneous collision probability
54
+ time.sleep(random.uniform(0.01, 0.05))
55
+ continue
56
+
57
+ # Websocket server successfully bound and is listening
58
+ break
59
+ else:
60
+ raise RuntimeError(
61
+ f"Could not find available port after {max_attempts} attempts starting from {self.requested_port}"
62
+ )
63
+
64
+ threading.Thread(target=self._accept_loop, daemon=True).start()
65
+
66
+ # Wait for ready
67
+ for _ in range(50):
68
+ try:
69
+ with socket.socket() as s:
70
+ s.settimeout(0.1)
71
+ if s.connect_ex(("127.0.0.1", self.local_port)) == 0:
72
+ return self
73
+ except:
74
+ pass
75
+ time.sleep(0.1)
76
+ raise RuntimeError("Tunnel failed to start")
77
+
78
+ def __exit__(self, *args):
79
+ self.running = False
80
+ if self.server_socket:
81
+ try:
82
+ self.server_socket.close()
83
+ except:
84
+ pass # Already closed
85
+
86
+ def _accept_loop(self):
87
+ while self.running:
88
+ try:
89
+ client_sock, _ = self.server_socket.accept()
90
+ threading.Thread(
91
+ target=self._handle_client, args=(client_sock,), daemon=True
92
+ ).start()
93
+ except:
94
+ break
95
+
96
+ def _handle_client(self, client_sock):
97
+ ws = None
98
+ try:
99
+ ws = websocket.create_connection(self.ws_url)
100
+
101
+ def tcp_to_ws():
102
+ while self.running:
103
+ try:
104
+ data = client_sock.recv(65536)
105
+ if not data:
106
+ break
107
+ ws.send_binary(data)
108
+ except:
109
+ break
110
+
111
+ def ws_to_tcp():
112
+ while self.running:
113
+ try:
114
+ data = ws.recv()
115
+ if isinstance(data, bytes):
116
+ client_sock.send(data)
117
+ except:
118
+ break
119
+
120
+ t1 = threading.Thread(target=tcp_to_ws, daemon=True)
121
+ t2 = threading.Thread(target=ws_to_tcp, daemon=True)
122
+ t1.start()
123
+ t2.start()
124
+ t1.join()
125
+ t2.join()
126
+
127
+ except Exception as e:
128
+ print(f"WebSocket connection error: {e}")
129
+
130
+ finally:
131
+ # close both connections
132
+ for conn in [client_sock, ws]:
133
+ if conn:
134
+ try:
135
+ conn.close()
136
+ except:
137
+ pass
@@ -0,0 +1 @@
1
+ from .images import * # noqa: F403
@@ -0,0 +1,414 @@
1
+ from enum import Enum
2
+ from typing import Any, Dict, List, Union
3
+
4
+ # Internal class to represent the image construction process
5
+ class ImageSetupStepType(Enum):
6
+ """Enum for valid Image setup step types"""
7
+
8
+ CMD_RUN = "cmd_run"
9
+ RSYNC = "rsync"
10
+ PIP_INSTALL = "pip_install"
11
+ SYNC_PACKAGE = "sync_package"
12
+ SET_ENV_VARS = "set_env_vars"
13
+
14
+
15
+ class ImageSetupStep:
16
+ def __init__(
17
+ self,
18
+ step_type: ImageSetupStepType,
19
+ **kwargs: Dict[str, Any],
20
+ ):
21
+ """
22
+ A component of the Kubetorch Image, consisting of the step type (e.g. packages, set_env_vars),
23
+ along with arguments to provide to the function corresponding to the step type.
24
+
25
+ Args:
26
+ step_type (ImageSetupStepType): Type of setup step used to provide the Image.
27
+ kwargs (Dict[str, Any]): Please refer to the corresponding functions in ``Image`` to determine
28
+ the correct keyword arguments to provide.
29
+ """
30
+ self.step_type = step_type
31
+ self.kwargs = kwargs
32
+
33
+
34
+ class Image:
35
+ def __init__(
36
+ self,
37
+ name: str = None,
38
+ image_id: str = None,
39
+ python_path: str = None,
40
+ install_cmd: str = None,
41
+ ):
42
+ """
43
+ Kubetorch Image object, specifying cluster setup properties and steps.
44
+
45
+ Args:
46
+ name (str, optional): Name to assign the Kubetorch image.
47
+ image_id (str, optional): Machine image to use, if any. (Default: ``None``)
48
+ python_path (str, optional): Absolute path to the Python executable to use for remote server and installs.
49
+ (Default: ``None``)
50
+ install_cmd (str, optional): Custom pip/uv install command to use for package installations.
51
+ If not provided, will be inferred based on python_path and available tools (preferring to use uv).
52
+ Examples: "uv pip install", "python -m pip install", "/path/to/.venv/bin/python -m uv pip install"
53
+ (Default: ``None``)
54
+
55
+ Note:
56
+ For convenience, Kubetorch provides ready-to-use base images under ``kt.images``.
57
+ These cover common environments like Python, CUDA, and Ray:
58
+
59
+ * ``kt.images.Python310()``, ``kt.images.Python311()``, ``kt.images.Python312()``
60
+ * ``kt.images.Debian()``
61
+ * ``kt.images.Ubuntu()``
62
+ * ``kt.images.Ray()`` (defaults to the latest Ray release)
63
+
64
+ You can also use flexible factories:
65
+
66
+ * ``kt.images.python("3.12")``
67
+ * ``kt.images.pytorch()`` (defaults to "nvcr.io/nvidia/pytorch:23.12-py3")
68
+ * ``kt.images.ray("2.32.0-py311")``
69
+
70
+ These base images can be further customized with methods like
71
+ ``.pip_install()``, ``.set_env_vars()``, ``.sync_package()``, etc.
72
+
73
+ Example:
74
+
75
+ .. code-block:: python
76
+
77
+ import kubetorch as kt
78
+
79
+ custom_image = (
80
+ kt.Image(name="base_image")
81
+ .pip_install(["numpy", "pandas"])
82
+ .set_env_vars({"OMP_NUM_THREADS": 1})
83
+ )
84
+ debian_image = (
85
+ kt.images.Debian()
86
+ .pip_install(["numpy", "pandas"])
87
+ .set_env_vars({"OMP_NUM_THREADS": 1})
88
+ )
89
+ """
90
+
91
+ self.name = name
92
+ self.image_id = image_id
93
+ self.python_path = python_path
94
+ self.install_cmd = install_cmd
95
+
96
+ self.setup_steps = []
97
+ self.docker_secret = None
98
+
99
+ @staticmethod
100
+ def _setup_step_config(step: ImageSetupStep):
101
+ """Get ImageSetupStep config"""
102
+ config = {
103
+ "step_type": step.step_type.value,
104
+ "kwargs": step.kwargs,
105
+ }
106
+ return config
107
+
108
+ @staticmethod
109
+ def _setup_step_from_config(step: Dict):
110
+ """Convert setup step config (dict) to ImageSetupStep object"""
111
+ step_type = step["step_type"]
112
+ kwargs = step["kwargs"]
113
+ return ImageSetupStep(
114
+ step_type=ImageSetupStepType(step_type),
115
+ **kwargs,
116
+ )
117
+
118
+ def from_docker(self, image_id: str):
119
+ """Set up and use an existing Docker image.
120
+
121
+ Args:
122
+ image_id (str): Docker image in the following format ``"<registry>/<image>:<tag>"``
123
+ """
124
+ if self.image_id:
125
+ raise ValueError(
126
+ "Setting both a machine image and docker image is not yet supported."
127
+ )
128
+ self.image_id = image_id
129
+ return self
130
+
131
+ ########################################################
132
+ # Steps to build the image
133
+ ########################################################
134
+
135
+ def pip_install(
136
+ self,
137
+ reqs: List[Union["Package", str]],
138
+ force: bool = False,
139
+ ):
140
+ """Pip install the given packages.
141
+
142
+ Args:
143
+ reqs (List[Package or str]): List of packages to pip install on cluster and env.
144
+ Each string is passed directly to the pip/uv command, allowing full control
145
+ over pip arguments. Examples:
146
+ - Simple package: ``"numpy"``
147
+ - Version constraint: ``"pandas>=1.2.0"``
148
+ - With pip flags: ``"--pre torch==2.0.0rc1"``
149
+ - Multiple flags: ``"--index-url https://pypi.org/simple torch"``
150
+ force (bool, optional): Whether to force re-install a package, if it already exists on the compute. (Default: ``False``)
151
+
152
+ Example:
153
+ .. code-block:: python
154
+
155
+ import kubetorch as kt
156
+
157
+ image = (
158
+ kt.images.Debian()
159
+ .pip_install([
160
+ "numpy>=1.20",
161
+ "pandas",
162
+ "--pre torchmonarch==0.1.0rc7", # Install pre-release
163
+ "--index-url https://test.pypi.org/simple/ mypackage"
164
+ ])
165
+ )
166
+ """
167
+
168
+ self.setup_steps.append(
169
+ ImageSetupStep(
170
+ step_type=ImageSetupStepType.PIP_INSTALL,
171
+ reqs=reqs,
172
+ force=force,
173
+ )
174
+ )
175
+ return self
176
+
177
+ def set_env_vars(self, env_vars: Dict):
178
+ """Set environment variables with support for variable expansion.
179
+
180
+ Environment variables can reference other variables using shell-style expansion:
181
+ - ``$VAR`` or ``${VAR}`` syntax to reference existing variables
182
+ - Variables are expanded when the container starts
183
+ - Variables are expanded in the order they are defined
184
+
185
+ Args:
186
+ env_vars (Dict): Dict of environment variables and values to set.
187
+ Values can include references to other environment variables.
188
+
189
+ Example:
190
+ .. code-block:: python
191
+
192
+ import kubetorch as kt
193
+
194
+ image = (
195
+ kt.images.Debian()
196
+ .set_env_vars({
197
+ "BASE_PATH": "/usr/local",
198
+ "BIN_PATH": "$BASE_PATH/bin", # Expands to /usr/local/bin
199
+ "PATH": "$BIN_PATH:$PATH", # Prepends to existing PATH
200
+ "LD_LIBRARY_PATH": "/opt/lib:${LD_LIBRARY_PATH}", # Appends to existing
201
+ "CUSTOM": "${HOME}/data", # Uses HOME from container
202
+ })
203
+ )
204
+
205
+ Note:
206
+ - Variables are expanded using Python's ``os.path.expandvars()``
207
+ - Undefined variables remain as literal strings (e.g., ``$UNDEFINED`` stays as ``$UNDEFINED``)
208
+ - To include a literal ``$``, escape it with backslash: ``\\$``
209
+ """
210
+ # TODO - support .env files
211
+ self.setup_steps.append(
212
+ ImageSetupStep(
213
+ step_type=ImageSetupStepType.SET_ENV_VARS,
214
+ env_vars=env_vars,
215
+ )
216
+ )
217
+ return self
218
+
219
+ def sync_package(
220
+ self,
221
+ package: str,
222
+ force: bool = False,
223
+ ):
224
+ """Sync local package over and add to path.
225
+
226
+ Args:
227
+ package (Package or str): Package to sync. Either the name of a local editably installed package, or
228
+ the path to the folder to sync over.
229
+ force (bool, optional): Whether to re-sync the package over, if already previously synced over. (Default: ``False``)
230
+ """
231
+ self.setup_steps.append(
232
+ ImageSetupStep(
233
+ step_type=ImageSetupStepType.SYNC_PACKAGE,
234
+ package=package,
235
+ force=force,
236
+ )
237
+ )
238
+ return self
239
+
240
+ def run_bash(
241
+ self,
242
+ command: str,
243
+ force: bool = False,
244
+ ):
245
+ """Run bash commands during image setup.
246
+
247
+ Executes shell commands during container initialization. Commands run in the
248
+ order they are defined and can be used to install software, configure the
249
+ environment, or start background services.
250
+
251
+ Args:
252
+ command (str): Shell command(s) to run on the cluster. Supports:
253
+ - Single commands: ``"apt-get update"``
254
+ - Chained commands: ``"apt-get update && apt-get install -y curl"``
255
+ - Background processes: ``"jupyter notebook --no-browser &"``
256
+ force (bool): Whether to rerun the command on the cluster, if previously run in image setup already. (Default: ``False``)
257
+
258
+ Example:
259
+ .. code-block:: python
260
+
261
+ import kubetorch as kt
262
+
263
+ image = (
264
+ kt.images.Debian()
265
+ .run_bash("apt-get update && apt-get install -y vim")
266
+ .run_bash("pip install jupyter")
267
+ .run_bash("jupyter notebook --no-browser --port=8888 &") # Runs in background
268
+ )
269
+
270
+ Note:
271
+ - Commands ending with ``&`` run in the background and won't block image setup
272
+ - Background processes continue running after setup completes
273
+ - The setup waits 0.5s to catch immediate failures in background processes
274
+ - Use ``&&`` to chain commands that depend on each other
275
+ - Use ``;`` to run commands sequentially regardless of success/failure
276
+ """
277
+ self.setup_steps.append(
278
+ ImageSetupStep(
279
+ step_type=ImageSetupStepType.CMD_RUN,
280
+ command=command,
281
+ force=force,
282
+ )
283
+ )
284
+ return self
285
+
286
+ def rsync(
287
+ self,
288
+ source: str,
289
+ dest: str = None,
290
+ contents: bool = False,
291
+ filter_options: str = None,
292
+ force: bool = False,
293
+ ):
294
+ """Sync files or directories from local machine to the remote container.
295
+
296
+ This method efficiently transfers files to remote containers using rsync,
297
+ which only copies changed files and supports compression. Files are first
298
+ uploaded to a jump pod and then distributed to worker pods on startup.
299
+
300
+ Args:
301
+ source (str): Path to the local file or directory to sync. Supports:
302
+ - Absolute paths: ``/path/to/file``
303
+ - Relative paths: ``./data/file.txt``
304
+ - Home directory paths: ``~/documents/data.csv``
305
+ dest (str, optional): Target path on the remote container. Supports:
306
+ - Absolute paths: ``/data/config.yaml`` (places file at exact location)
307
+ - Relative paths: ``configs/settings.json`` (relative to working directory)
308
+ - Tilde paths: ``~/results/output.txt`` (relative to working directory, ~ is stripped)
309
+ - None: Uses the basename of source in working directory
310
+ contents (bool, optional): For directories only - whether to copy the contents
311
+ or the directory itself.
312
+ If ``True`` the contents of the source directory are copied to the destination,
313
+ and the source directory itself is not created at the destination.
314
+ If ``False`` the source directory along with its contents are copied to the
315
+ destination, creating an additional directory layer at the destination.
316
+ (Default: ``False``)
317
+ filter_options (str, optional): Additional rsync filter options. These are added
318
+ to (not replacing) the default filters. By default, rsync excludes:
319
+
320
+ - Files from ``.gitignore`` (if present)
321
+ - Files from ``.ktignore`` (if present)
322
+ - Common Python artifacts: ``*.pyc``, ``__pycache__``
323
+ - Virtual environments: ``.venv``
324
+ - Git metadata: ``.git``
325
+
326
+ Your filter_options are appended after these defaults. Examples:
327
+
328
+ - Exclude more patterns: ``"--exclude='*.log' --exclude='temp/'"``
329
+ - Include specific files: ``"--include='important.log' --exclude='*.log'"``
330
+ - Override all defaults: Set ``KT_RSYNC_FILTERS`` environment variable
331
+
332
+ (Default: ``None``)
333
+ force (bool, optional): When ``True``, forces rsync to transfer all files
334
+ regardless of modification times by using ``--ignore-times`` flag. This ensures
335
+ all files are copied even if timestamps suggest they haven't changed.
336
+ Useful when timestamp-based change detection is unreliable.
337
+ Note: Files are always synced when deploying with ``.to()``, this flag just
338
+ affects how rsync determines which files need updating.
339
+ (Default: ``False``)
340
+
341
+ Returns:
342
+ Image: Returns self for method chaining.
343
+
344
+ Examples:
345
+ .. code-block:: python
346
+
347
+ import kubetorch as kt
348
+
349
+ # Basic file sync
350
+ image = (
351
+ kt.images.Debian()
352
+ .rsync("./config.yaml", "app/config.yaml")
353
+ )
354
+
355
+ # Sync to absolute path
356
+ image = (
357
+ kt.images.Python312()
358
+ .rsync("./model_weights.pth", "/models/weights.pth")
359
+ )
360
+
361
+ # No destination specified - uses basename
362
+ image = (
363
+ kt.images.Ubuntu()
364
+ .rsync("/local/data/dataset.csv") # Goes to ./dataset.csv
365
+ )
366
+
367
+ # Directory sync - copy directory itself
368
+ image = (
369
+ kt.images.Debian()
370
+ .rsync("./src", "app") # Creates app/src/
371
+ )
372
+
373
+ # Directory sync - copy contents only
374
+ image = (
375
+ kt.images.Debian()
376
+ .rsync("./src", "app", contents=True) # Contents go directly into app/
377
+ )
378
+
379
+ # Multiple rsync operations with filtering
380
+ image = (
381
+ kt.images.Python312()
382
+ .rsync("./data", "/data", filter_options="--exclude='*.tmp'")
383
+ .rsync("./configs", "~/configs")
384
+ .rsync("./scripts")
385
+ .pip_install(["numpy", "pandas"])
386
+ )
387
+
388
+ # Force re-sync for development
389
+ image = (
390
+ kt.images.Debian()
391
+ .rsync("./rapidly_changing_code", "app", force=True)
392
+ )
393
+
394
+ Note:
395
+ - Absolute destination paths (starting with ``/``) place files at exact locations
396
+ - Relative paths and ``~/`` paths are relative to the container's working directory
397
+ - The ``contents`` parameter only affects directory sources, not files
398
+ - Default exclusions include ``.gitignore`` patterns, ``__pycache__``, ``.venv``, and ``.git``
399
+ - User-provided ``filter_options`` are added to (not replacing) the default filters
400
+ - To completely override filters, set the ``KT_RSYNC_FILTERS`` environment variable
401
+ - Use ``force=True`` to bypass timestamp checks and transfer all files
402
+ """
403
+
404
+ self.setup_steps.append(
405
+ ImageSetupStep(
406
+ step_type=ImageSetupStepType.RSYNC,
407
+ source=source,
408
+ dest=dest,
409
+ contents=contents,
410
+ filter_options=filter_options,
411
+ force=force,
412
+ )
413
+ )
414
+ return self
@@ -0,0 +1,74 @@
1
+ from .image import Image
2
+
3
+
4
+ def debian() -> Image:
5
+ """Return a Kubetorch Debian slim image.
6
+
7
+ This uses the default Kubetorch server image, which is built on top of
8
+ a minimal Debian base.
9
+ """
10
+ import kubetorch.serving.constants as serving_constants
11
+ from kubetorch.globals import config
12
+
13
+ if config.tracing_enabled:
14
+ image_id = serving_constants.SERVER_IMAGE_WITH_OTEL
15
+ else:
16
+ image_id = serving_constants.SERVER_IMAGE_MINIMAL
17
+ return Image(name="debian", image_id=image_id)
18
+
19
+
20
+ def ubuntu() -> Image:
21
+ """Return a Kubetorch ubuntu image."""
22
+ import kubetorch.serving.constants as serving_constants
23
+ from kubetorch.globals import config
24
+
25
+ if config.tracing_enabled:
26
+ image_id = serving_constants.UBUNTU_IMAGE_WITH_OTEL
27
+ else:
28
+ image_id = serving_constants.UBUNTU_IMAGE_MINIMAL
29
+ return Image(name="ubuntu", image_id=image_id)
30
+
31
+
32
+ def python(version: str) -> Image:
33
+ """Return a Python slim base image, e.g. ``python('3.12')``."""
34
+ tag = version.replace(".", "")
35
+ return Image(name=f"python{tag}", image_id=f"python:{version}-slim")
36
+
37
+
38
+ def ray(version: str = "latest") -> Image:
39
+ """Return a Ray base image, defaults to ``ray:latest``."""
40
+ return Image(
41
+ name=f"ray{version if version != 'latest' else ''}".strip(),
42
+ image_id=f"rayproject/ray:{version}",
43
+ )
44
+
45
+
46
+ def pytorch(version: str = "23.12-py3") -> Image:
47
+ """Return an NVIDIA PyTorch base image. Defaults to ``nvcr.io/nvidia/pytorch:23.12-py3``."""
48
+ tag = version.replace(".", "").replace("-", "")
49
+ return Image(name=f"pytorch{tag}", image_id=f"nvcr.io/nvidia/pytorch:{version}")
50
+
51
+
52
+ # Predefined convenience aliases for common versions
53
+ Python310 = lambda: python("3.10")
54
+ Python311 = lambda: python("3.11")
55
+ Python312 = lambda: python("3.12")
56
+ Ray = lambda: ray("latest")
57
+ Pytorch2312 = lambda: pytorch("23.12-py3")
58
+ Debian = lambda: debian()
59
+ Ubuntu = lambda: ubuntu()
60
+
61
+ __all__ = [
62
+ "python",
63
+ "ray",
64
+ "pytorch",
65
+ "debian",
66
+ "ubuntu",
67
+ "Python310",
68
+ "Python311",
69
+ "Python312",
70
+ "Ray",
71
+ "Pytorch2312",
72
+ "Debian",
73
+ "Ubuntu",
74
+ ]
@@ -0,0 +1,2 @@
1
+ from .secret import Secret # noqa: F401
2
+ from .secret_factory import secret # noqa: F401