gpustack-runtime 0.1.39.post1__tar.gz → 0.1.39.post3__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.post1 → gpustack_runtime-0.1.39.post3}/PKG-INFO +3 -2
  2. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/__main__.py +6 -0
  3. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/_version.py +2 -2
  4. gpustack_runtime-0.1.39.post3/gpustack_runtime/_version_appendix.py +1 -0
  5. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/cmds/__init__.py +6 -0
  6. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/cmds/deployer.py +170 -40
  7. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/deployer/__init__.py +197 -0
  8. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/deployer/__types__.py +382 -17
  9. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/deployer/__utils__.py +34 -0
  10. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/deployer/docker.py +280 -66
  11. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/deployer/kuberentes.py +288 -45
  12. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/deployer/podman.py +290 -66
  13. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/__utils__.py +23 -0
  14. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/amd.py +18 -10
  15. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/hygon.py +7 -2
  16. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/iluvatar.py +10 -2
  17. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/mthreads.py +8 -12
  18. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/nvidia.py +194 -86
  19. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pyhsa/__init__.py +7 -7
  20. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pyrocmsmi/__init__.py +3 -9
  21. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/envs.py +30 -18
  22. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/pyproject.toml +2 -1
  23. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi300x.json +2 -2
  24. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/uv.lock +14 -4
  25. gpustack_runtime-0.1.39.post1/gpustack_runtime/_version_appendix.py +0 -1
  26. gpustack_runtime-0.1.39.post1/gpustack_runtime/detector/pymtml/__init__.py +0 -770
  27. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/.codespelldict +0 -0
  28. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/.codespellrc +0 -0
  29. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/.dockerignore +0 -0
  30. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/.gitattributes +0 -0
  31. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/.gitignore +0 -0
  32. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/.pre-commit-config.yaml +0 -0
  33. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/.python-version +0 -0
  34. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/LICENSE +0 -0
  35. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/Makefile +0 -0
  36. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/README.md +0 -0
  37. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/deploy/manifests/docker-compose.yaml +0 -0
  38. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/deploy/manifests/kubernetes.yaml +0 -0
  39. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/docs/index.md +0 -0
  40. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/docs/modules/gpustack_runtime.deployer.md +0 -0
  41. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/docs/modules/gpustack_runtime.detector.md +0 -0
  42. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/docs/modules/gpustack_runtime.md +0 -0
  43. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/__init__.py +0 -0
  44. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/_version.pyi +0 -0
  45. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/cmds/__types__.py +0 -0
  46. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/cmds/detector.py +0 -0
  47. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/cmds/images.py +0 -0
  48. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/deployer/__patches__.py +0 -0
  49. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/__init__.py +0 -0
  50. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/__types__.py +0 -0
  51. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/ascend.py +0 -0
  52. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/cambricon.py +0 -0
  53. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/metax.py +0 -0
  54. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pyacl/__init__.py +0 -0
  55. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pyamdgpu/__init__.py +0 -0
  56. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pyamdsmi/__init__.py +0 -0
  57. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pycuda/__init__.py +0 -0
  58. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pydcmi/__init__.py +0 -0
  59. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pyixml/__init__.py +0 -0
  60. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pymxsml/__init__.py +0 -0
  61. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pymxsml/mxsml.py +0 -0
  62. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pymxsml/mxsml_extension.py +0 -0
  63. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pymxsml/mxsml_mcm.py +0 -0
  64. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/detector/pyrocmcore/__init__.py +0 -0
  65. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/gpustack_runtime/logging.py +0 -0
  66. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/hatch.toml +0 -0
  67. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/mkdocs.yml +0 -0
  68. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/pack/Dockerfile +0 -0
  69. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/pack/Dockerfile.dummy +0 -0
  70. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/pytest.ini +0 -0
  71. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/ruff.toml +0 -0
  72. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/deployer/fixtures/__init__.py +0 -0
  73. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/deployer/fixtures/test_compare_versions.json +0 -0
  74. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/deployer/fixtures/test_correct_runner_image.json +0 -0
  75. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/deployer/fixtures/test_make_image_with.json +0 -0
  76. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/deployer/fixtures/test_nginx_entrypoint.sh +0 -0
  77. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/deployer/fixtures/test_replace_image_with.json +0 -0
  78. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/deployer/test_utils.py +0 -0
  79. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/fixtures/__init__.py +0 -0
  80. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/README.md +0 -0
  81. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_amd_rx7800xt.json +0 -0
  82. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_ascend_310p3.json +0 -0
  83. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_ascend_910b2.json +0 -0
  84. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_hygon_k100ai.json +0 -0
  85. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_metax_c500.json +0 -0
  86. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_gb10.json +0 -0
  87. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100.json +0 -0
  88. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h200.json +0 -0
  89. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4080super.json +0 -0
  90. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090d.json +0 -0
  91. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx5090d.json +0 -0
  92. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi300x.json +0 -0
  93. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_amd_rx7800xt.json +0 -0
  94. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_ascend_310p3.json +0 -0
  95. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_ascend_910b2.json +0 -0
  96. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_hygon_k100ai.json +0 -0
  97. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_metax_c500.json +0 -0
  98. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100.json +0 -0
  99. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h200.json +0 -0
  100. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4080super.json +0 -0
  101. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090d.json +0 -0
  102. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx5090d.json +0 -0
  103. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_amd.py +0 -0
  104. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_ascend.py +0 -0
  105. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_cambricon.py +0 -0
  106. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_hygon.py +0 -0
  107. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_iluvatar.py +0 -0
  108. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_metax.py +0 -0
  109. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_mthreads.py +0 -0
  110. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/tests/gpustack_runtime/detector/test_nvidia.py +0 -0
  111. {gpustack_runtime-0.1.39.post1 → gpustack_runtime-0.1.39.post3}/uv.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gpustack-runtime
3
- Version: 0.1.39.post1
3
+ Version: 0.1.39.post3
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,8 +15,9 @@ 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.post2
18
+ Requires-Dist: gpustack-runner>=0.1.23.post5
19
19
  Requires-Dist: kubernetes>=33.1.0
20
+ Requires-Dist: mthreads-ml-py>=2.2.10
20
21
  Requires-Dist: nvidia-ml-py>=13.580.65
21
22
  Requires-Dist: podman==5.6.0
22
23
  Requires-Dist: pyyaml
@@ -16,12 +16,15 @@ from .cmds import (
16
16
  DeleteWorkloadsSubCommand,
17
17
  DeleteWorkloadSubCommand,
18
18
  DetectDevicesSubCommand,
19
+ ExecSelfSubCommand,
19
20
  ExecWorkloadSubCommand,
20
21
  GetDevicesTopologySubCommand,
21
22
  GetWorkloadSubCommand,
23
+ InspectSelfSubCommand,
22
24
  InspectWorkloadSubCommand,
23
25
  ListImagesSubCommand,
24
26
  ListWorkloadsSubCommand,
27
+ LogsSelfSubCommand,
25
28
  LogsWorkloadSubCommand,
26
29
  SaveImagesSubCommand,
27
30
  )
@@ -71,6 +74,9 @@ def main():
71
74
  ListImagesSubCommand.register(subcommand_parser)
72
75
  SaveImagesSubCommand.register(subcommand_parser)
73
76
  CopyImagesSubCommand.register(subcommand_parser)
77
+ LogsSelfSubCommand.register(subcommand_parser)
78
+ ExecSelfSubCommand.register(subcommand_parser)
79
+ InspectSelfSubCommand.register(subcommand_parser)
74
80
 
75
81
  # Autocomplete
76
82
  argcomplete.autocomplete(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.post1'
31
- __version_tuple__ = version_tuple = (0, 1, 39, 'post1')
30
+ __version__ = version = '0.1.39.post3'
31
+ __version_tuple__ = version_tuple = (0, 1, 39, 'post3')
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 = "d65920e"
@@ -4,10 +4,13 @@ from .deployer import (
4
4
  CreateWorkloadSubCommand,
5
5
  DeleteWorkloadsSubCommand,
6
6
  DeleteWorkloadSubCommand,
7
+ ExecSelfSubCommand,
7
8
  ExecWorkloadSubCommand,
8
9
  GetWorkloadSubCommand,
10
+ InspectSelfSubCommand,
9
11
  InspectWorkloadSubCommand,
10
12
  ListWorkloadsSubCommand,
13
+ LogsSelfSubCommand,
11
14
  LogsWorkloadSubCommand,
12
15
  )
13
16
  from .detector import DetectDevicesSubCommand, GetDevicesTopologySubCommand
@@ -26,12 +29,15 @@ __all__ = [
26
29
  "DeleteWorkloadSubCommand",
27
30
  "DeleteWorkloadsSubCommand",
28
31
  "DetectDevicesSubCommand",
32
+ "ExecSelfSubCommand",
29
33
  "ExecWorkloadSubCommand",
30
34
  "GetDevicesTopologySubCommand",
31
35
  "GetWorkloadSubCommand",
36
+ "InspectSelfSubCommand",
32
37
  "InspectWorkloadSubCommand",
33
38
  "ListImagesSubCommand",
34
39
  "ListWorkloadsSubCommand",
40
+ "LogsSelfSubCommand",
35
41
  "LogsWorkloadSubCommand",
36
42
  "PlatformedImage",
37
43
  "SaveImagesSubCommand",
@@ -23,14 +23,17 @@ from ..deployer import (
23
23
  WorkloadPlan,
24
24
  WorkloadStatus,
25
25
  WorkloadStatusStateEnum,
26
+ async_logs_self,
26
27
  async_logs_workload,
27
28
  create_workload,
28
29
  delete_workload,
30
+ exec_self,
29
31
  exec_workload,
30
32
  get_workload,
33
+ inspect_self,
34
+ inspect_workload,
31
35
  list_workloads,
32
36
  )
33
- from ..deployer.__utils__ import safe_json, safe_yaml
34
37
  from ..detector import supported_backends
35
38
  from .__types__ import SubCommand
36
39
 
@@ -78,19 +81,6 @@ _IGNORE_ENVS_SUFFIX = (
78
81
  "_DRIVER_CAPABILITIES",
79
82
  )
80
83
 
81
- _IGNORE_SENSITIVE_ENVS_SUFFIX = (
82
- "_KEY",
83
- "_key",
84
- "_TOKEN",
85
- "_token",
86
- "_SECRET",
87
- "_secret",
88
- "_PASSWORD",
89
- "_password",
90
- "_PASS",
91
- "_pass",
92
- )
93
-
94
84
 
95
85
  class CreateWorkloadSubCommand(SubCommand):
96
86
  """
@@ -784,35 +774,175 @@ class InspectWorkloadSubCommand(SubCommand):
784
774
  raise ValueError(msg)
785
775
 
786
776
  def run(self):
787
- workload = get_workload(self.name, self.namespace)
788
- if not workload:
777
+ result = inspect_workload(self.name, self.namespace)
778
+ if not result:
789
779
  print(f"Workload '{self.name}' not found.")
790
780
  return
791
781
 
792
- if hasattr(workload, "_d_containers"):
793
- result = []
794
- for c in workload._d_containers: # noqa: SLF001
795
- c_attrs = c.attrs
796
- # Mask sensitive environment variables
797
- if "Env" in c_attrs["Config"]:
798
- for i, env in enumerate(c_attrs["Config"]["Env"] or []):
799
- env_name, _ = env.split("=", maxsplit=1)
800
- if env_name.endswith(_IGNORE_SENSITIVE_ENVS_SUFFIX):
801
- c_attrs["Config"]["Env"][i] = f"{env_name}=******"
802
- result.append(c_attrs)
803
- print(safe_json(result, indent=2))
804
- elif hasattr(workload, "_k_pod"):
805
- k_pod = workload._k_pod # noqa: SLF001
806
- # Remove managed fields to reduce output size
807
- k_pod.metadata.managed_fields = None
808
- # Mask sensitive environment variables
809
- for c in k_pod.spec.containers:
810
- for env in c.env or []:
811
- if env.name.endswith(_IGNORE_SENSITIVE_ENVS_SUFFIX):
812
- env.value = "******"
813
- print(safe_yaml(k_pod, indent=2, sort_keys=False))
814
- else:
815
- print("No detailed inspection information available for this workload.")
782
+ print(result)
783
+
784
+
785
+ class LogsSelfSubCommand(SubCommand):
786
+ """
787
+ Command to get the logs of the deployer itself.
788
+ """
789
+
790
+ tail: int
791
+ follow: bool
792
+
793
+ @staticmethod
794
+ def register(parser: _SubParsersAction):
795
+ logs_parser = parser.add_parser(
796
+ "logs-self",
797
+ help="Get the logs of the deployer itself",
798
+ )
799
+
800
+ logs_parser.add_argument(
801
+ "--tail",
802
+ type=int,
803
+ help="Number of lines to show from the end of the logs (default: -1)",
804
+ default=-1,
805
+ )
806
+
807
+ logs_parser.add_argument(
808
+ "--follow",
809
+ "-f",
810
+ action="store_true",
811
+ help="Follow the logs in real-time",
812
+ )
813
+
814
+ logs_parser.set_defaults(func=LogsSelfSubCommand)
815
+
816
+ def __init__(self, args: Namespace):
817
+ self.tail = args.tail
818
+ self.follow = args.follow
819
+
820
+ def run(self):
821
+ print("\033[2J\033[H", end="")
822
+
823
+ async def stream_logs():
824
+ logs_result = await async_logs_self(
825
+ tail=self.tail,
826
+ follow=self.follow,
827
+ )
828
+ if self.follow:
829
+ async for line in logs_result:
830
+ print(line.decode("utf-8").rstrip())
831
+ elif isinstance(logs_result, str):
832
+ print(logs_result.rstrip())
833
+ else:
834
+ print(logs_result.decode("utf-8").rstrip())
835
+
836
+ asyncio.run(stream_logs())
837
+
838
+
839
+ class ExecSelfSubCommand(SubCommand):
840
+ """
841
+ Command to execute a command in the deployer itself.
842
+ """
843
+
844
+ interactive: bool
845
+ command: list[str]
846
+
847
+ @staticmethod
848
+ def register(parser: _SubParsersAction):
849
+ exec_parser = parser.add_parser(
850
+ "exec-self",
851
+ help="Execute a command in the deployer itself",
852
+ )
853
+
854
+ exec_parser.add_argument(
855
+ "--interactive",
856
+ "-i",
857
+ action="store_true",
858
+ help="Interactive mode",
859
+ )
860
+
861
+ exec_parser.add_argument(
862
+ "command",
863
+ nargs=REMAINDER,
864
+ help="Command to execute in the workload",
865
+ )
866
+
867
+ exec_parser.set_defaults(func=ExecSelfSubCommand)
868
+
869
+ def __init__(self, args: Namespace):
870
+ self.interactive = args.interactive
871
+ self.command = args.command
872
+
873
+ def run(self):
874
+ try:
875
+ if self.interactive:
876
+ from dockerpty import io, pty # noqa: PLC0415
877
+ except ImportError:
878
+ print(
879
+ "dockerpty is required for interactive mode. "
880
+ "Please install it via 'pip install dockerpty'.",
881
+ )
882
+ sys.exit(1)
883
+
884
+ print("\033[2J\033[H", end="")
885
+ exec_result = exec_self(
886
+ detach=not self.interactive,
887
+ command=self.command,
888
+ )
889
+
890
+ # Non-interactive mode: print output and exit with the command's exit code
891
+
892
+ if not self.interactive:
893
+ if isinstance(exec_result, bytes):
894
+ print(exec_result.decode("utf-8").rstrip())
895
+ else:
896
+ print(exec_result)
897
+ return
898
+
899
+ # Interactive mode: use dockerpty to attach to the exec session
900
+
901
+ class ExecOperation(pty.Operation):
902
+ def __init__(self, sock):
903
+ self.stdin = sys.stdin
904
+ self.stdout = sys.stdout
905
+ self.sock = io.Stream(sock)
906
+
907
+ def israw(self, **_):
908
+ return self.stdout.isatty()
909
+
910
+ def start(self, **_):
911
+ sock = self.sockets()
912
+ return [
913
+ io.Pump(io.Stream(self.stdin), sock, wait_for_output=False),
914
+ io.Pump(sock, io.Stream(self.stdout), propagate_close=False),
915
+ ]
916
+
917
+ def resize(self, height, width, **_):
918
+ pass
919
+
920
+ def sockets(self):
921
+ return self.sock
922
+
923
+ exec_op = ExecOperation(exec_result)
924
+ pty.PseudoTerminal(None, exec_op).start()
925
+
926
+
927
+ class InspectSelfSubCommand(SubCommand):
928
+ """
929
+ Command to diagnose the deployer itself.
930
+ """
931
+
932
+ @staticmethod
933
+ def register(parser: _SubParsersAction):
934
+ inspect_parser = parser.add_parser(
935
+ "inspect-self",
936
+ help="Inspect the deployer itself",
937
+ )
938
+
939
+ inspect_parser.set_defaults(func=InspectSelfSubCommand)
940
+
941
+ def __init__(self, args: Namespace):
942
+ pass
943
+
944
+ def run(self):
945
+ print(inspect_self())
816
946
 
817
947
 
818
948
  def format_workloads_json(sts: list[WorkloadStatus]) -> str:
@@ -373,6 +373,198 @@ def exec_workload(
373
373
  raise UnsupportedError(_NO_AVAILABLE_DEPLOYER_MSG)
374
374
 
375
375
 
376
+ def inspect_workload(
377
+ name: WorkloadName,
378
+ namespace: WorkloadNamespace | None = None,
379
+ ) -> str:
380
+ """
381
+ Inspect the given workload.
382
+
383
+ Args:
384
+ name:
385
+ The name of the workload to inspect.
386
+ namespace:
387
+ The namespace of the workload.
388
+
389
+ Returns:
390
+ The inspection data as a dictionary.
391
+
392
+ Raises:
393
+ UnsupportedError:
394
+ If no deployer supports the given workload.
395
+ OperationError:
396
+ If the deployer fails to inspect the workload.
397
+
398
+ """
399
+ for dep in _DEPLOYERS:
400
+ if not dep.is_supported():
401
+ continue
402
+
403
+ return dep.inspect(
404
+ name=name,
405
+ namespace=namespace,
406
+ )
407
+
408
+ raise UnsupportedError(_NO_AVAILABLE_DEPLOYER_MSG)
409
+
410
+
411
+ def logs_self(
412
+ timestamps: bool = False,
413
+ tail: int | None = None,
414
+ since: int | None = None,
415
+ follow: bool = False,
416
+ ) -> Generator[bytes | str, None, None] | bytes | str:
417
+ """
418
+ Get the logs of the deployer itself.
419
+ Only works in mirrored deployment mode.
420
+
421
+ Args:
422
+ timestamps:
423
+ Whether to include timestamps in the logs.
424
+ tail:
425
+ The number of lines from the end of the logs to show.
426
+ since:
427
+ Show logs since a given time (in seconds).
428
+ follow:
429
+ Whether to follow the logs.
430
+
431
+ Returns:
432
+ The logs as a byte string, a string or a generator yielding byte strings or strings if follow is True.
433
+
434
+ Raises:
435
+ UnsupportedError:
436
+ If no deployer supports the given workload.
437
+ OperationError:
438
+ If the deployer fails to get the logs of the workload.
439
+
440
+ """
441
+ for dep in _DEPLOYERS:
442
+ if not dep.is_supported():
443
+ continue
444
+
445
+ if hasattr(dep, "endoscopic_logs"):
446
+ return dep.endoscopic_logs(
447
+ timestamps=timestamps,
448
+ tail=tail,
449
+ since=since,
450
+ follow=follow,
451
+ )
452
+
453
+ raise UnsupportedError(_NO_AVAILABLE_DEPLOYER_MSG)
454
+
455
+
456
+ async def async_logs_self(
457
+ timestamps: bool = False,
458
+ tail: int | None = None,
459
+ since: int | None = None,
460
+ follow: bool = False,
461
+ ) -> AsyncGenerator[bytes | str, None, None] | bytes | str:
462
+ """
463
+ Asynchronously get the logs of the deployer itself.
464
+ Only works in mirrored deployment mode.
465
+
466
+ Args:
467
+ timestamps:
468
+ Whether to include timestamps in the logs.
469
+ tail:
470
+ The number of lines from the end of the logs to show.
471
+ since:
472
+ Show logs since a given time (in seconds).
473
+ follow:
474
+ Whether to follow the logs.
475
+
476
+ Returns:
477
+ The logs as a byte string, a string or a generator yielding byte strings or strings if follow is True.
478
+
479
+ Raises:
480
+ UnsupportedError:
481
+ If no deployer supports the given workload.
482
+ OperationError:
483
+ If the deployer fails to get the logs of the workload.
484
+
485
+ """
486
+ for dep in _DEPLOYERS:
487
+ if not dep.is_supported():
488
+ continue
489
+
490
+ if hasattr(dep, "async_endoscopic_logs"):
491
+ return await dep.async_endoscopic_logs(
492
+ timestamps=timestamps,
493
+ tail=tail,
494
+ since=since,
495
+ follow=follow,
496
+ )
497
+
498
+ raise UnsupportedError(_NO_AVAILABLE_DEPLOYER_MSG)
499
+
500
+
501
+ def exec_self(
502
+ detach: bool = True,
503
+ command: list[str] | None = None,
504
+ args: list[str] | None = None,
505
+ ) -> WorkloadExecStream | bytes | str:
506
+ """
507
+ Execute a command in the deployer itself.
508
+ Only works in mirrored deployment mode.
509
+
510
+ Args:
511
+ detach:
512
+ Whether to detach from the command execution.
513
+ command:
514
+ The command to execute.
515
+ args:
516
+ The arguments to pass to the command.
517
+
518
+ Returns:
519
+ If detach is False, return a WorkloadExecStream.
520
+ otherwise, return the output of the command as a byte string or string.
521
+
522
+ Raises:
523
+ UnsupportedError:
524
+ If no deployer supports the given workload.
525
+ OperationError:
526
+ If the deployer fails to execute the command in the workload.
527
+
528
+ """
529
+ for dep in _DEPLOYERS:
530
+ if not dep.is_supported():
531
+ continue
532
+
533
+ if hasattr(dep, "endoscopic_exec"):
534
+ return dep.endoscopic_exec(
535
+ detach=detach,
536
+ command=command,
537
+ args=args,
538
+ )
539
+
540
+ raise UnsupportedError(_NO_AVAILABLE_DEPLOYER_MSG)
541
+
542
+
543
+ def inspect_self() -> str:
544
+ """
545
+ Inspect the deployer itself.
546
+ Only works in mirrored deployment mode.
547
+
548
+ Returns:
549
+ The inspection data as a dictionary.
550
+
551
+ Raises:
552
+ UnsupportedError:
553
+ If no deployer supports the given workload.
554
+ OperationError:
555
+ If the deployer fails to inspect the workload.
556
+
557
+ """
558
+ for dep in _DEPLOYERS:
559
+ if not dep.is_supported():
560
+ continue
561
+
562
+ if hasattr(dep, "endoscopic_inspect"):
563
+ return dep.endoscopic_inspect()
564
+
565
+ raise UnsupportedError(_NO_AVAILABLE_DEPLOYER_MSG)
566
+
567
+
376
568
  __all__ = [
377
569
  "Container",
378
570
  "ContainerCapabilities",
@@ -408,12 +600,17 @@ __all__ = [
408
600
  "WorkloadSecuritySysctl",
409
601
  "WorkloadStatus",
410
602
  "WorkloadStatusStateEnum",
603
+ "async_logs_self",
411
604
  "async_logs_workload",
412
605
  "create_workload",
413
606
  "delete_workload",
607
+ "exec_self",
414
608
  "exec_workload",
415
609
  "get_workload",
610
+ "inspect_self",
611
+ "inspect_workload",
416
612
  "list_workloads",
613
+ "logs_self",
417
614
  "logs_workload",
418
615
  "supported_list",
419
616
  ]