gpustack-runtime 0.2.1__tar.gz → 0.2.2__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 (132) hide show
  1. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/PKG-INFO +1 -1
  2. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/_version.py +2 -2
  3. gpustack_runtime-0.2.2/gpustack_runtime/_version_appendix.py +1 -0
  4. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/__types__.py +13 -0
  5. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/kuberentes.py +36 -0
  6. gpustack_runtime-0.2.2/tests/gpustack_runtime/deployer/test_workload_status.py +117 -0
  7. gpustack_runtime-0.2.1/gpustack_runtime/_version_appendix.py +0 -1
  8. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/.codespelldict +0 -0
  9. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/.codespellrc +0 -0
  10. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/.dockerignore +0 -0
  11. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/.gitattributes +0 -0
  12. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/.gitignore +0 -0
  13. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/.pre-commit-config.yaml +0 -0
  14. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/.python-version +0 -0
  15. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/LICENSE +0 -0
  16. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/Makefile +0 -0
  17. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/README.md +0 -0
  18. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/deploy/manifests/docker-compose.yaml +0 -0
  19. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/deploy/manifests/kubernetes.yaml +0 -0
  20. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/docs/index.md +0 -0
  21. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/docs/modules/gpustack_runtime.deployer.md +0 -0
  22. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/docs/modules/gpustack_runtime.detector.md +0 -0
  23. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/docs/modules/gpustack_runtime.md +0 -0
  24. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/__init__.py +0 -0
  25. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/__main__.py +0 -0
  26. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/_version.pyi +0 -0
  27. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/cmds/__init__.py +0 -0
  28. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/cmds/__types__.py +0 -0
  29. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/cmds/deployer.py +0 -0
  30. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/cmds/detector.py +0 -0
  31. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/cmds/images.py +0 -0
  32. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/__init__.py +0 -0
  33. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/__patches__.py +0 -0
  34. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/__utils__.py +0 -0
  35. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/__init__.py +0 -0
  36. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/__types__.py +0 -0
  37. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/__utils__.py +0 -0
  38. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/amd.py +0 -0
  39. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/ascend.py +0 -0
  40. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/hygon.py +0 -0
  41. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/iluvatar.py +0 -0
  42. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/metax.py +0 -0
  43. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/cdi/thead.py +0 -0
  44. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/docker.py +0 -0
  45. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/k8s/devicemanager/__init__.py +0 -0
  46. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/deployer/podman.py +0 -0
  47. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/__init__.py +0 -0
  48. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/__types__.py +0 -0
  49. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/__utils__.py +0 -0
  50. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/amd.py +0 -0
  51. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/ascend.py +0 -0
  52. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/cambricon.py +0 -0
  53. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/hygon.py +0 -0
  54. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/iluvatar.py +0 -0
  55. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/metax.py +0 -0
  56. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/mthreads.py +0 -0
  57. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/nvidia.py +0 -0
  58. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyamdgpu/__init__.py +0 -0
  59. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyamdsmi/__init__.py +0 -0
  60. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pydcmi/__init__.py +0 -0
  61. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyhgml/__init__.py +0 -0
  62. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyhgml/libhgml.so +0 -0
  63. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyhgml/libuki.so +0 -0
  64. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyhsa/__init__.py +0 -0
  65. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyixml/__init__.py +0 -0
  66. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pymtml/__init__.py +0 -0
  67. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pymxsml/__init__.py +0 -0
  68. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pynvml/__init__.py +0 -0
  69. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/pyrocmsmi/__init__.py +0 -0
  70. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/detector/thead.py +0 -0
  71. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/envs.py +0 -0
  72. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/gpustack_runtime/logging.py +0 -0
  73. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/hatch.toml +0 -0
  74. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/mkdocs.yml +0 -0
  75. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/pack/Dockerfile +0 -0
  76. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/pack/Dockerfile.dummy +0 -0
  77. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/pyproject.toml +0 -0
  78. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/pytest.ini +0 -0
  79. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/ruff.toml +0 -0
  80. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/__init__.py +0 -0
  81. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_compare_versions.json +0 -0
  82. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_correct_runner_image.json +0 -0
  83. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json.json +0 -0
  84. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_jsons.json +0 -0
  85. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_yamls.yaml +0 -0
  86. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_json.json +0 -0
  87. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_yaml.yaml +0 -0
  88. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/fixtures/test_nginx_entrypoint.sh +0 -0
  89. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/deployer/test_utils.py +0 -0
  90. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/fixtures/__init__.py +0 -0
  91. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/README.md +0 -0
  92. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi300x.json +0 -0
  93. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi308x.json +0 -0
  94. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_amd_rx7800xt.json +0 -0
  95. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_ascend_310p3.json +0 -0
  96. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_ascend_910b2.json +0 -0
  97. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_hygon_k100ai.json +0 -0
  98. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_metax_c500.json +0 -0
  99. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_gb10.json +0 -0
  100. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100.json +0 -0
  101. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100_mig.json +0 -0
  102. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h200.json +0 -0
  103. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4080super.json +0 -0
  104. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090d.json +0 -0
  105. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx5090d.json +0 -0
  106. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/detect_output_thead_ppu.json +0 -0
  107. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi300x.json +0 -0
  108. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi308x.json +0 -0
  109. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_amd_rx7800xt.json +0 -0
  110. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_ascend_310p3.json +0 -0
  111. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_ascend_910b2.json +0 -0
  112. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_hygon_k100ai.json +0 -0
  113. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_metax_c500.json +0 -0
  114. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_mthreads_s5000.json +0 -0
  115. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100.json +0 -0
  116. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100_mig.json +0 -0
  117. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h200.json +0 -0
  118. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4080super.json +0 -0
  119. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090d.json +0 -0
  120. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx5090d.json +0 -0
  121. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/samples/topology_output_thead_ppu.json +0 -0
  122. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_amd.py +0 -0
  123. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_ascend.py +0 -0
  124. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_cambricon.py +0 -0
  125. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_detector_utils.py +0 -0
  126. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_hygon.py +0 -0
  127. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_iluvatar.py +0 -0
  128. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_metax.py +0 -0
  129. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_mthreads.py +0 -0
  130. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/tests/gpustack_runtime/detector/test_nvidia.py +0 -0
  131. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/uv.lock +0 -0
  132. {gpustack_runtime-0.2.1 → gpustack_runtime-0.2.2}/uv.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gpustack-runtime
3
- Version: 0.2.1
3
+ Version: 0.2.2
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
@@ -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.2.1'
31
- __version_tuple__ = version_tuple = (0, 2, 1)
30
+ __version__ = version = '0.2.2'
31
+ __version_tuple__ = version_tuple = (0, 2, 2)
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 = "3ea35d7"
@@ -1135,6 +1135,10 @@ class WorkloadStatus:
1135
1135
  Namespace for the workload.
1136
1136
  labels (dict[str, str] | None):
1137
1137
  Labels for the workload.
1138
+ annotations (dict[str, str] | None):
1139
+ Annotations for the workload.
1140
+ state_message (str):
1141
+ Message accompanying the current state.
1138
1142
  executable (list[WorkloadStatusOperation]):
1139
1143
  The operation for the executable containers of the workload.
1140
1144
  loggable (list[WorkloadStatusOperation]):
@@ -1161,6 +1165,15 @@ class WorkloadStatus:
1161
1165
  """
1162
1166
  Labels for the workload.
1163
1167
  """
1168
+ annotations: dict[str, str] | None = field(default_factory=dict)
1169
+ """
1170
+ Annotations for the workload.
1171
+ """
1172
+ state_message: str = ""
1173
+ """
1174
+ Message accompanying the current state, e.g. the Kubernetes Pod's
1175
+ status message on failure (such as a device-plugin admission rejection).
1176
+ """
1164
1177
  executable: list[WorkloadStatusOperation] | None = field(default_factory=list)
1165
1178
  """
1166
1179
  The operation for the executable containers of the workload.
@@ -227,12 +227,18 @@ class KubernetesWorkloadStatus(WorkloadStatus):
227
227
  for k, v in (k_pod.metadata.labels or {}).items()
228
228
  if not k.startswith("runtime.gpustack.ai/")
229
229
  }
230
+ annotations = {
231
+ k: v
232
+ for k, v in (k_pod.metadata.annotations or {}).items()
233
+ if not k.startswith("runtime.gpustack.ai/")
234
+ }
230
235
 
231
236
  super().__init__(
232
237
  name=name,
233
238
  created_at=created_at,
234
239
  namespace=k_pod.metadata.namespace,
235
240
  labels=labels,
241
+ annotations=annotations,
236
242
  **kwargs,
237
243
  )
238
244
 
@@ -259,6 +265,11 @@ class KubernetesWorkloadStatus(WorkloadStatus):
259
265
  self.loggable.append(op)
260
266
 
261
267
  self.state = self.parse_state(k_pod)
268
+ if k_pod.status.message:
269
+ # Surface the Pod's status message, e.g. a device-plugin
270
+ # admission rejection ("UnexpectedAdmissionError: Allocate
271
+ # failed ...") on Failed Pods.
272
+ self.state_message = k_pod.status.message
262
273
 
263
274
 
264
275
  _NAME = "kubernetes"
@@ -267,6 +278,29 @@ Name of the Kubernetes deployer.
267
278
  """
268
279
 
269
280
 
281
+ def _pin_pod_for_kueue(
282
+ pod: kubernetes.client.V1Pod,
283
+ node_name: str | None,
284
+ ) -> None:
285
+ """
286
+ Adjust node pinning for Kueue-gated Pods in place.
287
+
288
+ Kueue-gated Pods (queue label present) cannot set spec.nodeName:
289
+ the API forbids nodeName until all schedulingGates have been
290
+ cleared. Pin via nodeSelector instead; kube-scheduler places
291
+ the Pod on the same node once Kueue admits it.
292
+ """
293
+ if not node_name:
294
+ return
295
+ if "kueue.x-k8s.io/queue-name" not in (pod.metadata.labels or {}):
296
+ return
297
+ pod.spec.node_name = None
298
+ pod.spec.node_selector = {
299
+ **(pod.spec.node_selector or {}),
300
+ "kubernetes.io/hostname": node_name,
301
+ }
302
+
303
+
270
304
  class KubernetesDeployer(EndoscopicDeployer):
271
305
  """
272
306
  Deployer implementation for Kubernetes.
@@ -1191,6 +1225,8 @@ class KubernetesDeployer(EndoscopicDeployer):
1191
1225
 
1192
1226
  core_api = kubernetes.client.CoreV1Api(self._client)
1193
1227
  try:
1228
+ _pin_pod_for_kueue(pod, self._node_name)
1229
+
1194
1230
  pod = self._mutate_create_pod(pod)
1195
1231
  if envs.GPUSTACK_RUNTIME_DEPLOY_PRINT_CONVERSION:
1196
1232
  clogger.info(
@@ -0,0 +1,117 @@
1
+ from datetime import datetime, timezone
2
+
3
+ import kubernetes.client
4
+
5
+ from gpustack_runtime.deployer.__types__ import WorkloadStatus
6
+ from gpustack_runtime.deployer.kuberentes import (
7
+ KubernetesWorkloadStatus,
8
+ _pin_pod_for_kueue,
9
+ )
10
+
11
+
12
+ def _pod(annotations=None) -> kubernetes.client.V1Pod:
13
+ return kubernetes.client.V1Pod(
14
+ metadata=kubernetes.client.V1ObjectMeta(
15
+ name="gpustack-test",
16
+ namespace="default",
17
+ creation_timestamp=datetime(2026, 7, 29, 8, 0, 0, tzinfo=timezone.utc),
18
+ labels={"app": "test"},
19
+ annotations=annotations,
20
+ ),
21
+ spec=kubernetes.client.V1PodSpec(
22
+ containers=[kubernetes.client.V1Container(name="run-0")],
23
+ ),
24
+ status=kubernetes.client.V1PodStatus(phase="Pending"),
25
+ )
26
+
27
+
28
+ def test_kubernetes_workload_status_annotations():
29
+ status = KubernetesWorkloadStatus(
30
+ name="test",
31
+ k_pod=_pod(
32
+ annotations={
33
+ "device.gpustack.ai/accelerator.allocated": '{"run-0": {}}',
34
+ "runtime.gpustack.ai/component-run-0-name": "internal",
35
+ },
36
+ ),
37
+ )
38
+ # Third-party annotations (e.g. device-plugin allocation) are surfaced,
39
+ # runtime-internal annotations are filtered out.
40
+ assert status.annotations == {
41
+ "device.gpustack.ai/accelerator.allocated": '{"run-0": {}}',
42
+ }
43
+
44
+
45
+ def test_kubernetes_workload_status_annotations_absent():
46
+ status = KubernetesWorkloadStatus(name="test", k_pod=_pod(annotations=None))
47
+ assert status.annotations == {}
48
+
49
+
50
+ def test_workload_status_annotations_default_empty():
51
+ # Docker/Podman deployers don't populate annotations; the base default is empty.
52
+ status = WorkloadStatus(name="test", created_at="2026-07-29T08:00:00.000000Z")
53
+ assert status.annotations == {}
54
+
55
+
56
+ def test_workload_status_annotations_json_roundtrip():
57
+ status = KubernetesWorkloadStatus(
58
+ name="test",
59
+ k_pod=_pod(annotations={"device.gpustack.ai/accelerator.allocated": "{}"}),
60
+ )
61
+ restored = WorkloadStatus.from_json(status.to_json())
62
+ assert restored.annotations == status.annotations
63
+
64
+
65
+ def _pinning_pod(labels=None) -> kubernetes.client.V1Pod:
66
+ return kubernetes.client.V1Pod(
67
+ metadata=kubernetes.client.V1ObjectMeta(name="p", labels=labels or {}),
68
+ spec=kubernetes.client.V1PodSpec(
69
+ node_name="node-a",
70
+ containers=[kubernetes.client.V1Container(name="run-0")],
71
+ ),
72
+ )
73
+
74
+
75
+ def test_pin_pod_for_kueue_switches_to_node_selector():
76
+ pod = _pinning_pod(labels={"kueue.x-k8s.io/queue-name": "q"})
77
+ _pin_pod_for_kueue(pod, "node-a")
78
+ assert pod.spec.node_name is None
79
+ assert pod.spec.node_selector == {"kubernetes.io/hostname": "node-a"}
80
+
81
+
82
+ def test_pin_pod_for_kueue_merges_existing_node_selector():
83
+ pod = _pinning_pod(labels={"kueue.x-k8s.io/queue-name": "q"})
84
+ pod.spec.node_selector = {"disk": "ssd"}
85
+ _pin_pod_for_kueue(pod, "node-a")
86
+ assert pod.spec.node_selector == {
87
+ "disk": "ssd",
88
+ "kubernetes.io/hostname": "node-a",
89
+ }
90
+
91
+
92
+ def test_pin_pod_for_kueue_noop_without_queue_label():
93
+ pod = _pinning_pod()
94
+ _pin_pod_for_kueue(pod, "node-a")
95
+ assert pod.spec.node_name == "node-a"
96
+ assert pod.spec.node_selector is None
97
+
98
+
99
+ def test_pin_pod_for_kueue_noop_without_node_name():
100
+ pod = _pinning_pod(labels={"kueue.x-k8s.io/queue-name": "q"})
101
+ _pin_pod_for_kueue(pod, None)
102
+ assert pod.spec.node_name == "node-a"
103
+
104
+
105
+ def test_kubernetes_workload_status_state_message():
106
+ pod = _pod()
107
+ pod.status = kubernetes.client.V1PodStatus(
108
+ phase="Failed",
109
+ message="Allocate failed due to no enough GPU devices",
110
+ )
111
+ status = KubernetesWorkloadStatus(name="test", k_pod=pod)
112
+ assert status.state_message == "Allocate failed due to no enough GPU devices"
113
+
114
+
115
+ def test_kubernetes_workload_status_state_message_default():
116
+ status = KubernetesWorkloadStatus(name="test", k_pod=_pod())
117
+ assert status.state_message == ""
@@ -1 +0,0 @@
1
- git_commit = "1d1e894"