gpustack-runtime 0.1.39__tar.gz → 0.1.39.post1__tar.gz

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.
Files changed (111) hide show
  1. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/PKG-INFO +2 -2
  2. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/__main__.py +0 -2
  3. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/_version.py +2 -2
  4. gpustack_runtime-0.1.39.post1/gpustack_runtime/_version_appendix.py +1 -0
  5. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/cmds/__init__.py +0 -2
  6. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/cmds/deployer.py +17 -248
  7. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/deployer/docker.py +38 -103
  8. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/deployer/podman.py +38 -103
  9. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/envs.py +0 -25
  10. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/pyproject.toml +1 -1
  11. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/uv.lock +4 -4
  12. gpustack_runtime-0.1.39/gpustack_runtime/_version_appendix.py +0 -1
  13. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/.codespelldict +0 -0
  14. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/.codespellrc +0 -0
  15. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/.dockerignore +0 -0
  16. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/.gitattributes +0 -0
  17. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/.gitignore +0 -0
  18. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/.pre-commit-config.yaml +0 -0
  19. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/.python-version +0 -0
  20. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/LICENSE +0 -0
  21. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/Makefile +0 -0
  22. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/README.md +0 -0
  23. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/deploy/manifests/docker-compose.yaml +0 -0
  24. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/deploy/manifests/kubernetes.yaml +0 -0
  25. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/docs/index.md +0 -0
  26. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/docs/modules/gpustack_runtime.deployer.md +0 -0
  27. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/docs/modules/gpustack_runtime.detector.md +0 -0
  28. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/docs/modules/gpustack_runtime.md +0 -0
  29. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/__init__.py +0 -0
  30. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/_version.pyi +0 -0
  31. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/cmds/__types__.py +0 -0
  32. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/cmds/detector.py +0 -0
  33. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/cmds/images.py +0 -0
  34. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/deployer/__init__.py +0 -0
  35. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/deployer/__patches__.py +0 -0
  36. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/deployer/__types__.py +0 -0
  37. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/deployer/__utils__.py +0 -0
  38. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/deployer/kuberentes.py +0 -0
  39. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/__init__.py +0 -0
  40. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/__types__.py +0 -0
  41. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/__utils__.py +0 -0
  42. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/amd.py +0 -0
  43. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/ascend.py +0 -0
  44. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/cambricon.py +0 -0
  45. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/hygon.py +0 -0
  46. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/iluvatar.py +0 -0
  47. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/metax.py +0 -0
  48. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/mthreads.py +0 -0
  49. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/nvidia.py +0 -0
  50. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pyacl/__init__.py +0 -0
  51. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pyamdgpu/__init__.py +0 -0
  52. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pyamdsmi/__init__.py +0 -0
  53. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pycuda/__init__.py +0 -0
  54. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pydcmi/__init__.py +0 -0
  55. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pyhsa/__init__.py +0 -0
  56. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pyixml/__init__.py +0 -0
  57. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pymtml/__init__.py +0 -0
  58. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pymxsml/__init__.py +0 -0
  59. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pymxsml/mxsml.py +0 -0
  60. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pymxsml/mxsml_extension.py +0 -0
  61. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pymxsml/mxsml_mcm.py +0 -0
  62. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pyrocmcore/__init__.py +0 -0
  63. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/detector/pyrocmsmi/__init__.py +0 -0
  64. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/gpustack_runtime/logging.py +0 -0
  65. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/hatch.toml +0 -0
  66. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/mkdocs.yml +0 -0
  67. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/pack/Dockerfile +0 -0
  68. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/pack/Dockerfile.dummy +0 -0
  69. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/pytest.ini +0 -0
  70. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/ruff.toml +0 -0
  71. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/deployer/fixtures/__init__.py +0 -0
  72. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/deployer/fixtures/test_compare_versions.json +0 -0
  73. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/deployer/fixtures/test_correct_runner_image.json +0 -0
  74. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/deployer/fixtures/test_make_image_with.json +0 -0
  75. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/deployer/fixtures/test_nginx_entrypoint.sh +0 -0
  76. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/deployer/fixtures/test_replace_image_with.json +0 -0
  77. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/deployer/test_utils.py +0 -0
  78. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/fixtures/__init__.py +0 -0
  79. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/README.md +0 -0
  80. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi300x.json +0 -0
  81. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_amd_rx7800xt.json +0 -0
  82. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_ascend_310p3.json +0 -0
  83. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_ascend_910b2.json +0 -0
  84. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_hygon_k100ai.json +0 -0
  85. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_metax_c500.json +0 -0
  86. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_gb10.json +0 -0
  87. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100.json +0 -0
  88. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h200.json +0 -0
  89. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4080super.json +0 -0
  90. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090d.json +0 -0
  91. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx5090d.json +0 -0
  92. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi300x.json +0 -0
  93. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_amd_rx7800xt.json +0 -0
  94. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_ascend_310p3.json +0 -0
  95. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_ascend_910b2.json +0 -0
  96. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_hygon_k100ai.json +0 -0
  97. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_metax_c500.json +0 -0
  98. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100.json +0 -0
  99. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h200.json +0 -0
  100. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4080super.json +0 -0
  101. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090d.json +0 -0
  102. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx5090d.json +0 -0
  103. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_amd.py +0 -0
  104. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_ascend.py +0 -0
  105. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_cambricon.py +0 -0
  106. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_hygon.py +0 -0
  107. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_iluvatar.py +0 -0
  108. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_metax.py +0 -0
  109. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_mthreads.py +0 -0
  110. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/tests/gpustack_runtime/detector/test_nvidia.py +0 -0
  111. {gpustack_runtime-0.1.39 → gpustack_runtime-0.1.39.post1}/uv.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gpustack-runtime
3
- Version: 0.1.39
3
+ Version: 0.1.39.post1
4
4
  Summary: GPUStack Runtime is library for detecting GPU resources and launching GPU workloads.
5
5
  Project-URL: Homepage, https://github.com/gpustack/runtime
6
6
  Project-URL: Bug Tracker, https://github.com/gpustack/gpustack/issues
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
15
15
  Requires-Python: >=3.10
16
16
  Requires-Dist: argcomplete>=3.6.3
17
17
  Requires-Dist: docker>=7.1.0
18
- Requires-Dist: gpustack-runner>=0.1.23.post1
18
+ Requires-Dist: gpustack-runner>=0.1.23.post2
19
19
  Requires-Dist: kubernetes>=33.1.0
20
20
  Requires-Dist: nvidia-ml-py>=13.580.65
21
21
  Requires-Dist: podman==5.6.0
@@ -12,7 +12,6 @@ from . import deployer, detector
12
12
  from ._version import commit_id, version
13
13
  from .cmds import (
14
14
  CopyImagesSubCommand,
15
- CreateRunnerWorkloadSubCommand,
16
15
  CreateWorkloadSubCommand,
17
16
  DeleteWorkloadsSubCommand,
18
17
  DeleteWorkloadSubCommand,
@@ -59,7 +58,6 @@ def main():
59
58
  subcommand_parser = parser.add_subparsers(
60
59
  help="gpustack-runtime command helpers",
61
60
  )
62
- CreateRunnerWorkloadSubCommand.register(subcommand_parser)
63
61
  CreateWorkloadSubCommand.register(subcommand_parser)
64
62
  DeleteWorkloadSubCommand.register(subcommand_parser)
65
63
  DeleteWorkloadsSubCommand.register(subcommand_parser)
@@ -27,8 +27,8 @@ version_tuple: VERSION_TUPLE
27
27
  __commit_id__: COMMIT_ID
28
28
  commit_id: COMMIT_ID
29
29
 
30
- __version__ = version = '0.1.39'
31
- __version_tuple__ = version_tuple = (0, 1, 39)
30
+ __version__ = version = '0.1.39.post1'
31
+ __version_tuple__ = version_tuple = (0, 1, 39, 'post1')
32
32
  try:
33
33
  from ._version_appendix import git_commit
34
34
  __commit_id__ = commit_id = git_commit
@@ -0,0 +1 @@
1
+ git_commit = "dea7237"
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from .deployer import (
4
- CreateRunnerWorkloadSubCommand,
5
4
  CreateWorkloadSubCommand,
6
5
  DeleteWorkloadsSubCommand,
7
6
  DeleteWorkloadSubCommand,
@@ -23,7 +22,6 @@ from .images import (
23
22
 
24
23
  __all__ = [
25
24
  "CopyImagesSubCommand",
26
- "CreateRunnerWorkloadSubCommand",
27
25
  "CreateWorkloadSubCommand",
28
26
  "DeleteWorkloadSubCommand",
29
27
  "DeleteWorkloadsSubCommand",
@@ -92,238 +92,6 @@ _IGNORE_SENSITIVE_ENVS_SUFFIX = (
92
92
  )
93
93
 
94
94
 
95
- class CreateRunnerWorkloadSubCommand(SubCommand):
96
- """
97
- Command to create a runner workload deployment.
98
- """
99
-
100
- backend: str
101
- device: str
102
- command_script: str | None
103
- port: int
104
- host_network: bool
105
- check: bool
106
- namespace: str
107
- service: str
108
- version: str
109
- name: str
110
- volume: str
111
- extra_args: list[str]
112
-
113
- @staticmethod
114
- def register(parser: _SubParsersAction):
115
- deploy_parser = parser.add_parser(
116
- "create-runner",
117
- help="Create a runner workload deployment",
118
- )
119
-
120
- deploy_parser.add_argument(
121
- "--backend",
122
- type=str,
123
- help="Backend to use (default: detect from current environment)",
124
- choices=supported_backends(),
125
- )
126
-
127
- deploy_parser.add_argument(
128
- "--device",
129
- type=str,
130
- help="Device to use, multiple devices join by comma (default: all devices)",
131
- default="all",
132
- )
133
-
134
- deploy_parser.add_argument(
135
- "--command-script-file",
136
- type=str,
137
- help="Path of command script for the workload",
138
- )
139
-
140
- deploy_parser.add_argument(
141
- "--port",
142
- type=int,
143
- help="Port to expose",
144
- )
145
-
146
- deploy_parser.add_argument(
147
- "--host-network",
148
- action="store_true",
149
- help="Use host network (default: False)",
150
- default=False,
151
- )
152
-
153
- deploy_parser.add_argument(
154
- "--check",
155
- action="store_true",
156
- help="Enable health check, needs --port (default: False)",
157
- default=False,
158
- )
159
-
160
- deploy_parser.add_argument(
161
- "--namespace",
162
- type=str,
163
- help="Namespace of the runner",
164
- )
165
-
166
- deploy_parser.add_argument(
167
- "service",
168
- type=str,
169
- help="Service of the runner",
170
- )
171
-
172
- deploy_parser.add_argument(
173
- "version",
174
- type=str,
175
- help="Version of the runner",
176
- )
177
-
178
- deploy_parser.add_argument(
179
- "volume",
180
- type=str,
181
- help="Volume to mount",
182
- )
183
-
184
- deploy_parser.add_argument(
185
- "extra_args",
186
- nargs=REMAINDER,
187
- help="Extra arguments for the runner",
188
- )
189
-
190
- deploy_parser.set_defaults(func=CreateRunnerWorkloadSubCommand)
191
-
192
- def __init__(self, args: Namespace):
193
- self.backend = args.backend
194
- self.device = args.device
195
- self.command_script = None
196
- self.port = args.port
197
- self.host_network = args.host_network
198
- self.check = args.check
199
- self.namespace = args.namespace
200
- self.service = args.service
201
- self.version = args.version
202
- self.name = f"{args.service}-{args.version}".lower().replace(".", "-")
203
- self.volume = args.volume
204
- self.extra_args = args.extra_args
205
-
206
- if not self.name or not self.volume:
207
- msg = "The name and volume arguments are required."
208
- raise ValueError(msg)
209
-
210
- if args.command_script_file:
211
- command_script_file = Path(args.command_script_file)
212
- if not command_script_file.is_file():
213
- msg = f"The command script file '{command_script_file}' does not exist."
214
- raise ValueError(msg)
215
- self.command_script = command_script_file.read_text(
216
- encoding="utf-8",
217
- ).strip()
218
-
219
- def run(self):
220
- env = [
221
- ContainerEnv(
222
- name=name,
223
- value=value,
224
- )
225
- for name, value in os.environ.items()
226
- if not name.startswith(_IGNORE_ENVS_PREFIX)
227
- and not name.endswith(_IGNORE_ENVS_SUFFIX)
228
- ]
229
- if self.backend:
230
- resources = ContainerResources(
231
- **{
232
- v: self.device
233
- for k, v in envs.GPUSTACK_RUNTIME_DETECT_BACKEND_MAP_RESOURCE_KEY.items()
234
- if k == self.backend
235
- },
236
- )
237
- else:
238
- resources = ContainerResources(
239
- **{
240
- envs.GPUSTACK_RUNTIME_DEPLOY_AUTOMAP_RESOURCE_KEY: self.device,
241
- },
242
- )
243
- mounts = [
244
- ContainerMount(
245
- path=self.volume,
246
- ),
247
- ]
248
- execution = ContainerExecution(
249
- command_script=self.command_script,
250
- args=self.extra_args,
251
- )
252
- ports = (
253
- [
254
- ContainerPort(
255
- internal=self.port,
256
- ),
257
- ]
258
- if self.port
259
- else None
260
- )
261
- checks = (
262
- [
263
- ContainerCheck(
264
- delay=60,
265
- interval=10,
266
- timeout=5,
267
- retries=6,
268
- tcp=ContainerCheckTCP(port=self.port),
269
- teardown=True,
270
- ),
271
- ]
272
- if self.check and self.port
273
- else None
274
- )
275
- plan = WorkloadPlan(
276
- name=self.name,
277
- namespace=self.namespace,
278
- host_network=self.host_network,
279
- containers=[
280
- Container(
281
- restart_policy=(
282
- ContainerRestartPolicyEnum.NEVER
283
- if not self.check
284
- else ContainerRestartPolicyEnum.ALWAYS
285
- ),
286
- image=f"gpustack/runner:{self.backend if self.backend else 'Host'}X.Y-{self.service}{self.version}",
287
- name=self.name,
288
- envs=env,
289
- resources=resources,
290
- mounts=mounts,
291
- execution=execution,
292
- ports=ports,
293
- checks=checks,
294
- ),
295
- ],
296
- )
297
- create_workload(plan)
298
- print(f"Created workload '{self.name}'.")
299
-
300
- while True:
301
- st = get_workload(
302
- name=self.name,
303
- namespace=self.namespace,
304
- )
305
- if st and st.state not in (
306
- WorkloadStatusStateEnum.PENDING,
307
- WorkloadStatusStateEnum.INITIALIZING,
308
- ):
309
- break
310
- time.sleep(1)
311
-
312
- print("\033[2J\033[H", end="")
313
-
314
- async def stream_logs():
315
- logs_result = await async_logs_workload(
316
- name=self.name,
317
- namespace=self.namespace,
318
- tail=-1,
319
- follow=True,
320
- )
321
- async for line in logs_result:
322
- print(line.decode("utf-8").rstrip())
323
-
324
- asyncio.run(stream_logs())
325
-
326
-
327
95
  class CreateWorkloadSubCommand(SubCommand):
328
96
  """
329
97
  Command to create a workload deployment.
@@ -358,8 +126,7 @@ class CreateWorkloadSubCommand(SubCommand):
358
126
  deploy_parser.add_argument(
359
127
  "--device",
360
128
  type=str,
361
- help="Device to use, multiple devices join by comma (default: all devices)",
362
- default="all",
129
+ help="Device to use, multiple devices join by comma, all for all devices",
363
130
  )
364
131
 
365
132
  deploy_parser.add_argument(
@@ -456,20 +223,22 @@ class CreateWorkloadSubCommand(SubCommand):
456
223
  if not name.startswith(_IGNORE_ENVS_PREFIX)
457
224
  and not name.endswith(_IGNORE_ENVS_SUFFIX)
458
225
  ]
459
- if self.backend:
460
- resources = ContainerResources(
461
- **{
462
- v: self.device
463
- for k, v in envs.GPUSTACK_RUNTIME_DETECT_BACKEND_MAP_RESOURCE_KEY.items()
464
- if k == self.backend
465
- },
466
- )
467
- else:
468
- resources = ContainerResources(
469
- **{
470
- envs.GPUSTACK_RUNTIME_DEPLOY_AUTOMAP_RESOURCE_KEY: self.device,
471
- },
472
- )
226
+ resources = None
227
+ if self.device:
228
+ if self.backend:
229
+ resources = ContainerResources(
230
+ **{
231
+ v: self.device
232
+ for k, v in envs.GPUSTACK_RUNTIME_DETECT_BACKEND_MAP_RESOURCE_KEY.items()
233
+ if k == self.backend
234
+ },
235
+ )
236
+ else:
237
+ resources = ContainerResources(
238
+ **{
239
+ envs.GPUSTACK_RUNTIME_DEPLOY_AUTOMAP_RESOURCE_KEY: self.device,
240
+ },
241
+ )
473
242
  mounts = [
474
243
  ContainerMount(
475
244
  path=self.volume,
@@ -1,12 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import contextlib
4
+ import io
4
5
  import json
5
6
  import logging
6
7
  import operator
7
8
  import os
8
9
  import socket
9
10
  import sys
11
+ import tarfile
10
12
  from dataclasses import dataclass, field
11
13
  from functools import lru_cache, reduce
12
14
  from math import ceil
@@ -303,10 +305,6 @@ class DockerDeployer(Deployer):
303
305
  """
304
306
  Client for interacting with the Docker daemon.
305
307
  """
306
- _container_ephemeral_files_dir: Path | None = None
307
- """
308
- Directory for ephemeral files inside containers, internal use only.
309
- """
310
308
  _mutate_create_options: Callable[[dict[str, Any]], dict[str, Any]] | None = None
311
309
  """
312
310
  Function to handle mirrored deployment, internal use only.
@@ -383,48 +381,6 @@ class DockerDeployer(Deployer):
383
381
 
384
382
  return wrapper
385
383
 
386
- @staticmethod
387
- def _create_ephemeral_files(
388
- workload: DockerWorkloadPlan,
389
- ) -> dict[tuple[int, str], str]:
390
- """
391
- Create ephemeral files as local file for the workload.
392
-
393
- Returns:
394
- A mapping from (container index, configured path) to actual filename.
395
-
396
- Raises:
397
- OperationError:
398
- If the ephemeral files fail to create.
399
-
400
- """
401
- # Map (container index, configured path) to actual filename.
402
- ephemeral_filename_mapping: dict[tuple[int, str], str] = {}
403
- ephemeral_files: list[tuple[str, str, int]] = []
404
- for ci, c in enumerate(workload.containers):
405
- for fi, f in enumerate(c.files or []):
406
- if f.content is not None:
407
- fn = f"{workload.name}-{ci}-{fi}"
408
- ephemeral_filename_mapping[(ci, f.path)] = fn
409
- ephemeral_files.append((fn, f.content, f.mode))
410
- if not ephemeral_filename_mapping:
411
- return ephemeral_filename_mapping
412
-
413
- # Create ephemeral files directory if not exists.
414
- try:
415
- for fn, fc, fm in ephemeral_files:
416
- fp = envs.GPUSTACK_RUNTIME_DOCKER_EPHEMERAL_FILES_DIR.joinpath(fn)
417
- with fp.open("w", encoding="utf-8") as f:
418
- f.write(fc)
419
- f.flush()
420
- fp.chmod(fm)
421
- logger.debug("Created local file %s with mode %s", fp, oct(fm))
422
- except OSError as e:
423
- msg = "Failed to create ephemeral files"
424
- raise OperationError(msg) from e
425
-
426
- return ephemeral_filename_mapping
427
-
428
384
  def _create_ephemeral_volumes(self, workload: DockerWorkloadPlan) -> dict[str, str]:
429
385
  """
430
386
  Create ephemeral volumes for the workload.
@@ -717,12 +673,10 @@ class DockerDeployer(Deployer):
717
673
  else:
718
674
  return d_container
719
675
 
676
+ @staticmethod
720
677
  def _append_container_mounts(
721
- self,
722
678
  create_options: dict[str, Any],
723
679
  c: Container,
724
- ci: int,
725
- ephemeral_filename_mapping: dict[tuple[int, str] : str],
726
680
  ephemeral_volume_name_mapping: dict[str, str],
727
681
  ):
728
682
  """
@@ -738,17 +692,7 @@ class DockerDeployer(Deployer):
738
692
  target="",
739
693
  )
740
694
 
741
- if f.content is not None:
742
- # Ephemeral file, use from local ephemeral files directory.
743
- if (ci, f.path) not in ephemeral_filename_mapping:
744
- continue
745
- fn = ephemeral_filename_mapping[(ci, f.path)]
746
- path = str(
747
- self._container_ephemeral_files_dir.joinpath(fn),
748
- )
749
- binding["Source"] = path
750
- binding["Target"] = f"/{f.path.lstrip('/')}"
751
- elif f.path:
695
+ if f.content is None and f.path:
752
696
  # Host file, bind directly.
753
697
  binding["Source"] = f.path
754
698
  binding["Target"] = f.path
@@ -860,10 +804,39 @@ class DockerDeployer(Deployer):
860
804
 
861
805
  return healthcheck
862
806
 
807
+ @staticmethod
808
+ def _upload_ephemeral_files(
809
+ c: Container,
810
+ container: docker.models.containers.Container,
811
+ ):
812
+ if not c.files:
813
+ return
814
+
815
+ f_tar = io.BytesIO()
816
+ with tarfile.open(fileobj=f_tar, mode="w") as tar:
817
+ for f in c.files:
818
+ if f.content is None or not f.path:
819
+ continue
820
+ fc_bytes = f.content.encode("utf-8")
821
+ info = tarfile.TarInfo(name=f.path.lstrip("/"))
822
+ info.size = len(fc_bytes)
823
+ info.mode = f.mode
824
+ tar.addfile(tarinfo=info, fileobj=io.BytesIO(fc_bytes))
825
+ if f_tar.getbuffer().nbytes == 0:
826
+ return
827
+
828
+ f_tar.seek(0)
829
+ uploaded = container.put_archive(
830
+ path="/",
831
+ data=f_tar.getvalue(),
832
+ )
833
+ if not uploaded:
834
+ msg = f"Failed to upload ephemeral files to container {container.name}"
835
+ raise OperationError(msg)
836
+
863
837
  def _create_containers(
864
838
  self,
865
839
  workload: DockerWorkloadPlan,
866
- ephemeral_filename_mapping: dict[tuple[int, str] : str],
867
840
  ephemeral_volume_name_mapping: dict[str, str],
868
841
  pause_container: docker.models.containers.Container,
869
842
  ) -> (
@@ -1106,8 +1079,6 @@ class DockerDeployer(Deployer):
1106
1079
  self._append_container_mounts(
1107
1080
  create_options,
1108
1081
  c,
1109
- ci,
1110
- ephemeral_filename_mapping,
1111
1082
  ephemeral_volume_name_mapping,
1112
1083
  )
1113
1084
 
@@ -1149,6 +1120,10 @@ class DockerDeployer(Deployer):
1149
1120
  detach=detach,
1150
1121
  **create_options,
1151
1122
  )
1123
+
1124
+ # Upload ephemeral files into the container.
1125
+ self._upload_ephemeral_files(c, d_container)
1126
+
1152
1127
  except docker.errors.APIError as e:
1153
1128
  msg = f"Failed to create container {container_name}{_detail_api_call_error(e)}"
1154
1129
  raise OperationError(msg) from e
@@ -1198,9 +1173,6 @@ class DockerDeployer(Deployer):
1198
1173
  def __init__(self):
1199
1174
  super().__init__(_NAME)
1200
1175
  self._client = self._get_client()
1201
- self._container_ephemeral_files_dir = (
1202
- envs.GPUSTACK_RUNTIME_DOCKER_EPHEMERAL_FILES_DIR
1203
- )
1204
1176
 
1205
1177
  def _prepare_create(self):
1206
1178
  """
@@ -1434,25 +1406,6 @@ class DockerDeployer(Deployer):
1434
1406
 
1435
1407
  self._mutate_create_options = mutate_create_options
1436
1408
 
1437
- # Extract ephemeral files dir mutation if any.
1438
- if mirrored_mounts:
1439
- e_target = str(envs.GPUSTACK_RUNTIME_DOCKER_EPHEMERAL_FILES_DIR)
1440
- b_source = ""
1441
- b_target = ""
1442
- for m in mirrored_mounts:
1443
- c_target = m.get("Destination", "///")
1444
- if (
1445
- e_target == c_target or e_target.startswith(f"{c_target}/")
1446
- ) and len(c_target) >= len(b_target):
1447
- b_source = m.get("Source")
1448
- b_target = c_target
1449
- if b_source:
1450
- result = Path(b_source)
1451
- if e_target != b_target:
1452
- b_subpath = e_target.removeprefix(b_target)
1453
- result = result.joinpath(b_subpath.lstrip("/"))
1454
- self._container_ephemeral_files_dir = result
1455
-
1456
1409
  def _find_self_container(
1457
1410
  self,
1458
1411
  self_container_id: str,
@@ -1536,12 +1489,6 @@ class DockerDeployer(Deployer):
1536
1489
  if logger.isEnabledFor(logging.DEBUG):
1537
1490
  logger.debug("Creating workload:\n%s", workload.to_yaml())
1538
1491
 
1539
- # Create ephemeral file if needed,
1540
- # (container index, configured path): <actual filename>
1541
- ephemeral_filename_mapping: dict[tuple[int, str] : str] = (
1542
- self._create_ephemeral_files(workload)
1543
- )
1544
-
1545
1492
  # Create ephemeral volumes if needed,
1546
1493
  # <configured volume name>: <actual volume name>
1547
1494
  ephemeral_volume_name_mapping: dict[str, str] = self._create_ephemeral_volumes(
@@ -1554,7 +1501,6 @@ class DockerDeployer(Deployer):
1554
1501
  # Create init/run containers.
1555
1502
  init_containers, run_containers = self._create_containers(
1556
1503
  workload,
1557
- ephemeral_filename_mapping,
1558
1504
  ephemeral_volume_name_mapping,
1559
1505
  pause_container,
1560
1506
  )
@@ -1696,17 +1642,6 @@ class DockerDeployer(Deployer):
1696
1642
  msg = f"Failed to delete volumes for workload {name}{_detail_api_call_error(e)}"
1697
1643
  raise OperationError(msg) from e
1698
1644
 
1699
- # Remove all ephemeral files for the workload.
1700
- try:
1701
- for fp in envs.GPUSTACK_RUNTIME_DOCKER_EPHEMERAL_FILES_DIR.glob(
1702
- f"{name}-*",
1703
- ):
1704
- if fp.is_file():
1705
- fp.unlink(missing_ok=True)
1706
- except OSError as e:
1707
- msg = f"Failed to delete ephemeral files for workload {name}"
1708
- raise OperationError(msg) from e
1709
-
1710
1645
  return workload
1711
1646
 
1712
1647
  @_supported