gpustack-runtime 0.2.5.post3__tar.gz → 0.2.5.post4__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.
- gpustack_runtime-0.2.5.post4/ADOPTERS.md +12 -0
- gpustack_runtime-0.2.5.post4/AGENTS.md +85 -0
- gpustack_runtime-0.2.5.post4/DCO +34 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/LICENSE +1 -1
- gpustack_runtime-0.2.5.post4/PKG-INFO +82 -0
- gpustack_runtime-0.2.5.post4/README.md +55 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/_version.py +2 -2
- gpustack_runtime-0.2.5.post4/gpustack_runtime/_version_appendix.py +1 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/__init__.py +3 -1
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/k8s/devicemanager/__init__.py +56 -6
- gpustack_runtime-0.2.5.post3/gpustack_runtime/deployer/kuberentes.py → gpustack_runtime-0.2.5.post4/gpustack_runtime/deployer/kubernetes.py +81 -13
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/mkdocs.yml +1 -1
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/specs/2026-08-14-detector-alignment-and-workload-exit-status.md +4 -4
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_kubernetes_status.py +1 -1
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_logs_options.py +2 -2
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_privileged.py +1 -1
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_resource_injection_policy.py +1 -1
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_runtime_class.py +1 -1
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_termination_grace_period.py +3 -3
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_visible_devices_ordering.py +3 -3
- gpustack_runtime-0.2.5.post4/tests/gpustack_runtime/deployer/test_workload_resource_injection_policy.py +347 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_workload_status.py +2 -2
- gpustack_runtime-0.2.5.post3/PKG-INFO +0 -64
- gpustack_runtime-0.2.5.post3/README.md +0 -37
- gpustack_runtime-0.2.5.post3/gpustack_runtime/_version_appendix.py +0 -1
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/.codespelldict +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/.codespellrc +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/.dockerignore +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/.gitattributes +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/.gitignore +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/.pre-commit-config.yaml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/.python-version +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/Makefile +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/deploy/manifests/docker-compose.yaml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/deploy/manifests/kubernetes.yaml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/docs/index.md +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/docs/modules/gpustack_runtime.deployer.md +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/docs/modules/gpustack_runtime.detector.md +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/docs/modules/gpustack_runtime.md +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/__main__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/_version.pyi +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/cmds/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/cmds/__types__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/cmds/deployer.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/cmds/detector.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/cmds/images.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/__patches__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/__types__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/__utils__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/__types__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/__utils__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/amd.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/ascend.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/hygon.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/iluvatar.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/metax.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/cdi/thead.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/docker.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/podman.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/__types__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/__utils__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/amd.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/ascend.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/cambricon.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/hygon.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/iluvatar.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/metax.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/mthreads.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/nvidia.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyamdgpu/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyamdsmi/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pycndev/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pydcmi/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pydmi/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyhgml/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyhgml/libhgml.so +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyhgml/libuki.so +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyhsa/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyixml/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pymtml/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pymxsml/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pynvml/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/pyrocmsmi/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/detector/thead.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/envs.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/logging.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/hatch.toml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/pack/Dockerfile +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/pack/Dockerfile.dummy +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/pyproject.toml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/pytest.ini +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/ruff.toml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_compare_versions.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_correct_runner_image.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_jsons.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_multiple_yamls.yaml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_json.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_load_yaml_or_json_single_yaml.yaml +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/fixtures/test_nginx_entrypoint.sh +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_ascend_ranktable.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_docker_status.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_podman_status.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/deployer/test_utils.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/fixtures/__init__.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/README.md +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi300x.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_amd_mi308x.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_amd_rx7800xt.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_ascend_310p3.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_ascend_910b2.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_hygon_k100ai.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_metax_c500.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_gb10.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h100_mig.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_h200.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4080super.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090_48g.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx4090d.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_nvidia_rtx5090d.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/detect_output_thead_ppu.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi300x.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_amd_mi308x.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_amd_rx7800xt.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_ascend_310p3.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_ascend_910b2.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_hygon_k100ai.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_metax_c500.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_mthreads_s5000.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h100_mig.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_h200.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4080super.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090_48g.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx4090d.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_nvidia_rtx5090d.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/samples/topology_output_thead_ppu.json +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_amd.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_ascend.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_cambricon.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_detector_cli.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_detector_types.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_detector_utils.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_hygon.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_iluvatar.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_metax.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_mig_devices.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_mthreads.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_nvidia.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_pycndev.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_pydmi.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_samples.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/tests/gpustack_runtime/detector/test_thead.py +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/uv.lock +0 -0
- {gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/uv.toml +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Who's using GPUStack Runtime
|
|
2
|
+
|
|
3
|
+
GPUStack Runtime is used by a growing range of companies and projects to detect accelerator (GPU/NPU) resources and run accelerator workloads on Docker, Kubernetes and Podman.
|
|
4
|
+
|
|
5
|
+
If you are using GPUStack Runtime and your name is not on this list, please send us a PR!
|
|
6
|
+
|
|
7
|
+
## Companies/Projects using GPUStack Runtime in production
|
|
8
|
+
|
|
9
|
+
These companies and projects are using GPUStack Runtime in production. If your company is using GPUStack Runtime, open a PR to add your company or project name and a description of how you are using GPUStack Runtime.
|
|
10
|
+
|
|
11
|
+
| Company / Project | Use Case |
|
|
12
|
+
| --- | --- |
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# GPUStack Runtime
|
|
2
|
+
|
|
3
|
+
A Python library with a unified interface for detecting accelerator (GPU/NPU) resources and
|
|
4
|
+
launching and managing GPU workloads on Docker, Kubernetes and Podman.
|
|
5
|
+
|
|
6
|
+
## Project Structure
|
|
7
|
+
|
|
8
|
+
- `gpustack_runtime/` — the library.
|
|
9
|
+
- `detector/` — one detector per vendor (`nvidia.py`, `amd.py`, `ascend.py`, `cambricon.py`,
|
|
10
|
+
`hygon.py`, `iluvatar.py`, `metax.py`, `mthreads.py`, `thead.py`), plus the `py*` packages,
|
|
11
|
+
ctypes bindings to vendor management libraries (`pynvml`, `pydcmi`, `pycndev`, ...).
|
|
12
|
+
- `deployer/` — one deployer per container runtime (`docker.py`, `kubernetes.py`, `podman.py`),
|
|
13
|
+
`cdi/` for vendor CDI spec handling, `k8s/` for Kubernetes helpers, and `__patches__.py` for
|
|
14
|
+
the vendored Podman client patch.
|
|
15
|
+
- `cmds/` — CLI subcommands behind the `gpustack-runtime` entrypoint (`__main__.py`).
|
|
16
|
+
- `envs.py` — environment variables; `logging.py` — logging helpers.
|
|
17
|
+
- `tests/gpustack_runtime/` — pytest suite mirroring the package; `detector/samples/` holds
|
|
18
|
+
captured vendor-tool outputs used as fixtures.
|
|
19
|
+
- `docs/` — MkDocs site; `docs/modules/` is API reference rendered from docstrings by
|
|
20
|
+
mkdocstrings.
|
|
21
|
+
- `specs/` — design records.
|
|
22
|
+
- `pack/` / `deploy/` — container image build and deployment manifests.
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
Detection: each vendor detector in `gpustack_runtime/detector/` probes its vendor library through
|
|
27
|
+
the matching `py*` binding and reports `Device`s with topology. `detector/__init__.py` owns the
|
|
28
|
+
registry (`_DETECTORS`) and the aggregate entry points (`supported_list()`, `detect_devices()`).
|
|
29
|
+
|
|
30
|
+
Deployment: `deployer/__init__.py` owns the registry (`_DEPLOYERS`) and picks the first available
|
|
31
|
+
runtime (Docker, Kubernetes, Podman). A `WorkloadPlan` describes the desired containers; the
|
|
32
|
+
deployer injects vendor resources (CDI devices, environment variables, mounts) and returns a
|
|
33
|
+
workload handle for inspect/logs/exec/delete. Shared types live in `deployer/__types__.py`,
|
|
34
|
+
including the `OperationError` / `UnsupportedError` error contract.
|
|
35
|
+
|
|
36
|
+
## Finding sources
|
|
37
|
+
|
|
38
|
+
- Confirm the checkout, branch and uncommitted changes before reasoning about behavior. The
|
|
39
|
+
published wheel or a merged PR may describe a different revision.
|
|
40
|
+
- Docstrings are the usage contract: `make docs` renders them into `site/`. Specs under
|
|
41
|
+
`specs/` own design rationale; current code establishes what this checkout implements. Report
|
|
42
|
+
disagreement rather than silently choosing one.
|
|
43
|
+
- For a vendor behavior claim, read the matching `py*` binding and the detector tests under
|
|
44
|
+
`tests/gpustack_runtime/detector/`; captured outputs live in `detector/samples/`.
|
|
45
|
+
- For a concrete error, search its stable text locally first. When it concerns an upstream
|
|
46
|
+
dependency (docker, kubernetes, podman clients), check that project's issues and pinned version
|
|
47
|
+
in `pyproject.toml` / `uv.lock`.
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
Setup is `make install` (uv sync plus pre-commit hooks). Day-to-day targets:
|
|
52
|
+
|
|
53
|
+
- `make lint` — pre-commit on all files: ruff check/format, codespell, commitizen.
|
|
54
|
+
- `make test` — pytest.
|
|
55
|
+
- `make docs` — build the MkDocs site into `site/` (git-ignored).
|
|
56
|
+
- `make build` — build wheel and sdist into `dist/` (git-ignored).
|
|
57
|
+
- `make package` — build the container image with docker buildx; `PACKAGE_PUSH=true` pushes.
|
|
58
|
+
|
|
59
|
+
`gpustack_runtime/_version.py` is generated by hatch-vcs and `_version_appendix.py` by
|
|
60
|
+
`make prepare`; never hand-edit either.
|
|
61
|
+
|
|
62
|
+
### Python conventions
|
|
63
|
+
|
|
64
|
+
- Prefer clarity over cleverness to simplify long-term maintenance.
|
|
65
|
+
- Start every package module with `from __future__ import annotations as __future_annotations__`.
|
|
66
|
+
- Follow the ruff configuration in `ruff.toml`; run `make lint` whenever modifying Python source.
|
|
67
|
+
- Type-annotate public APIs and re-export them from the package `__init__.py` (`__all__`).
|
|
68
|
+
- Write Google-style docstrings; they render into the published API reference.
|
|
69
|
+
- Handle errors explicitly. Deployers raise `OperationError` or `UnsupportedError` from
|
|
70
|
+
`deployer.__types__` instead of leaking runtime-client exceptions. Detectors degrade to an
|
|
71
|
+
empty result and log at debug level rather than failing on a host without that vendor.
|
|
72
|
+
- Keep private helpers in double-underscore modules (`__types__.py`, `__utils__.py`); import them
|
|
73
|
+
through the package, not across subpackages.
|
|
74
|
+
- Keep concurrency simple, safe and justified; minimize mutable shared state.
|
|
75
|
+
- Keep comments plain and short: no emoji, no decorative symbols. State the point in words.
|
|
76
|
+
|
|
77
|
+
### Testing conventions
|
|
78
|
+
|
|
79
|
+
- pytest; tests mirror the package layout under `tests/gpustack_runtime/`.
|
|
80
|
+
- Never require real hardware or a real container runtime. Detector tests fake the `py*` vendor
|
|
81
|
+
binding in the test module; deployer tests fake the runtime client.
|
|
82
|
+
- Reuse captured vendor outputs from `tests/gpustack_runtime/detector/samples/` and shared
|
|
83
|
+
fixtures from `fixtures/` instead of inventing new sample data.
|
|
84
|
+
- Verify one behavior or contract per test; assert observable state, not implementation details.
|
|
85
|
+
- Keep tests deterministic and repeatable: no wall-clock, ordering or randomness dependence.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Developer Certificate of Origin
|
|
2
|
+
Version 1.1
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
|
5
|
+
|
|
6
|
+
Everyone is permitted to copy and distribute verbatim copies of this
|
|
7
|
+
license document, but changing it is not allowed.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Developer's Certificate of Origin 1.1
|
|
11
|
+
|
|
12
|
+
By making a contribution to this project, I certify that:
|
|
13
|
+
|
|
14
|
+
(a) The contribution was created in whole or in part by me and I
|
|
15
|
+
have the right to submit it under the open source license
|
|
16
|
+
indicated in the file; or
|
|
17
|
+
|
|
18
|
+
(b) The contribution is based upon previous work that, to the best
|
|
19
|
+
of my knowledge, is covered under an appropriate open source
|
|
20
|
+
license and I have the right under that license to submit that
|
|
21
|
+
work with modifications, whether created in whole or in part
|
|
22
|
+
by me, under the same open source license (unless I am
|
|
23
|
+
permitted to submit under a different license), as indicated
|
|
24
|
+
in the file; or
|
|
25
|
+
|
|
26
|
+
(c) The contribution was provided directly to me by some other
|
|
27
|
+
person who certified (a), (b) or (c) and I have not modified
|
|
28
|
+
it.
|
|
29
|
+
|
|
30
|
+
(d) I understand and agree that this project and the contribution
|
|
31
|
+
are public and that a record of the contribution (including all
|
|
32
|
+
personal information I submit with it, including my sign-off) is
|
|
33
|
+
maintained indefinitely and may be redistributed consistent with
|
|
34
|
+
this project or the open source license(s) involved.
|
|
@@ -175,7 +175,7 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
|
175
175
|
|
|
176
176
|
END OF TERMS AND CONDITIONS
|
|
177
177
|
|
|
178
|
-
Copyright (c)
|
|
178
|
+
Copyright (c) 2026 The GPUStack Authors
|
|
179
179
|
|
|
180
180
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
181
|
you may not use this file except in compliance with the License.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gpustack-runtime
|
|
3
|
+
Version: 0.2.5.post4
|
|
4
|
+
Summary: GPUStack Runtime is library for detecting GPU resources and launching GPU workloads.
|
|
5
|
+
Project-URL: Homepage, https://github.com/gpustack/runtime
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/gpustack/gpustack/issues
|
|
7
|
+
Author-email: GPUStack Authors <contact@gpustack.ai>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: argcomplete>=3.6.3
|
|
17
|
+
Requires-Dist: cachetools>=5.5.2
|
|
18
|
+
Requires-Dist: docker>=7.1.0
|
|
19
|
+
Requires-Dist: gpustack-runner>=0.1.29
|
|
20
|
+
Requires-Dist: kubernetes>=33.1.0
|
|
21
|
+
Requires-Dist: mthreads-ml-py>=2.2.10
|
|
22
|
+
Requires-Dist: nvidia-ml-py>=13.580.65
|
|
23
|
+
Requires-Dist: podman==5.6.0
|
|
24
|
+
Requires-Dist: pyyaml
|
|
25
|
+
Requires-Dist: tqdm
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# GPUStack Runtime
|
|
29
|
+
|
|
30
|
+
[](./LICENSE)
|
|
31
|
+
[](https://pypi.org/project/gpustack-runtime/)
|
|
32
|
+
[](https://github.com/gpustack/runtime/releases/latest)
|
|
33
|
+
[](https://hub.docker.com/r/gpustack/runtime)
|
|
34
|
+
[](https://deepwiki.com/gpustack/runtime)
|
|
35
|
+
|
|
36
|
+
GPUStack Runtime offers a unified interface for detecting GPU resources and managing GPU workloads.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- Detect a wide range of GPU and accelerator resources:
|
|
41
|
+
|
|
42
|
+
- AMD GPU
|
|
43
|
+
- Ascend NPU
|
|
44
|
+
- Cambricon MLU
|
|
45
|
+
- Hygon DCU
|
|
46
|
+
- Iluvatar GPU
|
|
47
|
+
- MetaX GPU
|
|
48
|
+
- Moore Threads GPU
|
|
49
|
+
- NVIDIA GPU
|
|
50
|
+
- T-Head PPU
|
|
51
|
+
|
|
52
|
+
- Manage GPU workloads on the following platforms:
|
|
53
|
+
|
|
54
|
+
- Docker
|
|
55
|
+
- Kubernetes
|
|
56
|
+
- Podman (>=4.9, experimental support via the `CONTAINER_HOST=http+unix:///path/to/podman/socket` environment variable)
|
|
57
|
+
|
|
58
|
+
Contributions to support additional GPU resources are welcome!
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install gpustack-runtime
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Documentation
|
|
67
|
+
|
|
68
|
+
The API reference renders from docstrings. Build the site locally with `make docs` and open
|
|
69
|
+
`site/index.html`.
|
|
70
|
+
|
|
71
|
+
## Contributing
|
|
72
|
+
|
|
73
|
+
Contributions are welcome. Commits must carry a `Signed-off-by` line certifying the
|
|
74
|
+
[Developer Certificate of Origin](./DCO).
|
|
75
|
+
|
|
76
|
+
Using GPUStack Runtime in production? Add your company or project to [ADOPTERS.md](./ADOPTERS.md)
|
|
77
|
+
via pull request.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
Copyright (c) 2026 The GPUStack Authors. Licensed under the
|
|
82
|
+
[Apache License 2.0](./LICENSE).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# GPUStack Runtime
|
|
2
|
+
|
|
3
|
+
[](./LICENSE)
|
|
4
|
+
[](https://pypi.org/project/gpustack-runtime/)
|
|
5
|
+
[](https://github.com/gpustack/runtime/releases/latest)
|
|
6
|
+
[](https://hub.docker.com/r/gpustack/runtime)
|
|
7
|
+
[](https://deepwiki.com/gpustack/runtime)
|
|
8
|
+
|
|
9
|
+
GPUStack Runtime offers a unified interface for detecting GPU resources and managing GPU workloads.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Detect a wide range of GPU and accelerator resources:
|
|
14
|
+
|
|
15
|
+
- AMD GPU
|
|
16
|
+
- Ascend NPU
|
|
17
|
+
- Cambricon MLU
|
|
18
|
+
- Hygon DCU
|
|
19
|
+
- Iluvatar GPU
|
|
20
|
+
- MetaX GPU
|
|
21
|
+
- Moore Threads GPU
|
|
22
|
+
- NVIDIA GPU
|
|
23
|
+
- T-Head PPU
|
|
24
|
+
|
|
25
|
+
- Manage GPU workloads on the following platforms:
|
|
26
|
+
|
|
27
|
+
- Docker
|
|
28
|
+
- Kubernetes
|
|
29
|
+
- Podman (>=4.9, experimental support via the `CONTAINER_HOST=http+unix:///path/to/podman/socket` environment variable)
|
|
30
|
+
|
|
31
|
+
Contributions to support additional GPU resources are welcome!
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install gpustack-runtime
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Documentation
|
|
40
|
+
|
|
41
|
+
The API reference renders from docstrings. Build the site locally with `make docs` and open
|
|
42
|
+
`site/index.html`.
|
|
43
|
+
|
|
44
|
+
## Contributing
|
|
45
|
+
|
|
46
|
+
Contributions are welcome. Commits must carry a `Signed-off-by` line certifying the
|
|
47
|
+
[Developer Certificate of Origin](./DCO).
|
|
48
|
+
|
|
49
|
+
Using GPUStack Runtime in production? Add your company or project to [ADOPTERS.md](./ADOPTERS.md)
|
|
50
|
+
via pull request.
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
Copyright (c) 2026 The GPUStack Authors. Licensed under the
|
|
55
|
+
[Apache License 2.0](./LICENSE).
|
|
@@ -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.5.
|
|
31
|
-
__version_tuple__ = version_tuple = (0, 2, 5, '
|
|
30
|
+
__version__ = version = '0.2.5.post4'
|
|
31
|
+
__version_tuple__ = version_tuple = (0, 2, 5, 'post4')
|
|
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 = "089358e"
|
{gpustack_runtime-0.2.5.post3 → gpustack_runtime-0.2.5.post4}/gpustack_runtime/deployer/__init__.py
RENAMED
|
@@ -36,7 +36,8 @@ from .docker import (
|
|
|
36
36
|
DockerWorkloadPlan,
|
|
37
37
|
DockerWorkloadStatus,
|
|
38
38
|
)
|
|
39
|
-
from .
|
|
39
|
+
from .k8s.devicemanager import KubernetesResourceInjectionPolicyEnum
|
|
40
|
+
from .kubernetes import (
|
|
40
41
|
KubernetesDeployer,
|
|
41
42
|
KubernetesWorkloadPlan,
|
|
42
43
|
KubernetesWorkloadStatus,
|
|
@@ -595,6 +596,7 @@ __all__ = [
|
|
|
595
596
|
"ContainerSecurity",
|
|
596
597
|
"DockerWorkloadPlan",
|
|
597
598
|
"DockerWorkloadStatus",
|
|
599
|
+
"KubernetesResourceInjectionPolicyEnum",
|
|
598
600
|
"KubernetesWorkloadPlan",
|
|
599
601
|
"KubernetesWorkloadStatus",
|
|
600
602
|
"OperationError",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations as __future_annotations__
|
|
2
2
|
|
|
3
3
|
import stat
|
|
4
|
+
from enum import Enum
|
|
4
5
|
from functools import lru_cache
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import TYPE_CHECKING, Any, Literal
|
|
@@ -11,6 +12,36 @@ if TYPE_CHECKING:
|
|
|
11
12
|
from collections.abc import Callable, Mapping
|
|
12
13
|
|
|
13
14
|
|
|
15
|
+
class KubernetesResourceInjectionPolicyEnum(str, Enum):
|
|
16
|
+
"""
|
|
17
|
+
Device injection policies for Kubernetes workloads.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
AUTO = "Auto"
|
|
21
|
+
"""
|
|
22
|
+
Detect the policy from the target node's allocatable resources.
|
|
23
|
+
"""
|
|
24
|
+
ENV = "Env"
|
|
25
|
+
"""
|
|
26
|
+
Inject runtime-visible device environment variables without device-plugin requests.
|
|
27
|
+
"""
|
|
28
|
+
KDP = "KDP"
|
|
29
|
+
"""
|
|
30
|
+
Request device-plugin resources.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def _missing_(cls, value: object) -> KubernetesResourceInjectionPolicyEnum | None:
|
|
35
|
+
if isinstance(value, str):
|
|
36
|
+
for member in cls:
|
|
37
|
+
if member.value.lower() == value.lower():
|
|
38
|
+
return member
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
def __str__(self) -> str:
|
|
42
|
+
return self.value
|
|
43
|
+
|
|
44
|
+
|
|
14
45
|
def is_kubelet_socket_accessible(
|
|
15
46
|
kubelet_endpoint: Path | None = None,
|
|
16
47
|
) -> bool:
|
|
@@ -79,13 +110,16 @@ def node_has_device_plugin_resources(
|
|
|
79
110
|
|
|
80
111
|
def get_resource_injection_policy(
|
|
81
112
|
probe_node_allocatable: Callable[[], Mapping[str, Any] | None] | None = None,
|
|
113
|
+
*,
|
|
114
|
+
policy: KubernetesResourceInjectionPolicyEnum | None = None,
|
|
82
115
|
) -> Literal["env", "kdp"]:
|
|
83
116
|
"""
|
|
84
117
|
Get the resource injection policy (in lowercase) for the deployer.
|
|
85
118
|
|
|
86
|
-
|
|
87
|
-
cluster, not to the process doing the deploying:
|
|
88
|
-
orchestrates remotely, so whether *it* can reach
|
|
119
|
+
A workload policy overrides the environment setting. Under "auto" the
|
|
120
|
+
decision belongs to the cluster, not to the process doing the deploying:
|
|
121
|
+
the Kubernetes deployer orchestrates remotely, so whether *it* can reach
|
|
122
|
+
a kubelet socket says
|
|
89
123
|
nothing about whether the *target* node runs a device plugin. So the probe
|
|
90
124
|
reads that node's allocatable resources and looks for a device-plugin
|
|
91
125
|
resource family there.
|
|
@@ -99,14 +133,29 @@ def get_resource_injection_policy(
|
|
|
99
133
|
probe_node_allocatable:
|
|
100
134
|
Called only under the "auto" policy, to read the target node's
|
|
101
135
|
allocatable resources. Returns None when the node cannot be read.
|
|
136
|
+
policy:
|
|
137
|
+
Workload policy override. None inherits the environment setting;
|
|
138
|
+
Auto probes the node even if the environment selects Env or KDP.
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
ValueError:
|
|
142
|
+
If the policy is not Auto, Env or KDP.
|
|
102
143
|
|
|
103
144
|
Returns:
|
|
104
145
|
The resource injection policy.
|
|
105
146
|
|
|
106
147
|
"""
|
|
107
|
-
policy
|
|
108
|
-
|
|
109
|
-
|
|
148
|
+
if policy is None:
|
|
149
|
+
policy = envs.GPUSTACK_RUNTIME_KUBERNETES_RESOURCE_INJECTION_POLICY
|
|
150
|
+
try:
|
|
151
|
+
policy = KubernetesResourceInjectionPolicyEnum(policy)
|
|
152
|
+
except ValueError as e:
|
|
153
|
+
msg = f"Invalid resource injection policy '{policy}'"
|
|
154
|
+
raise ValueError(msg) from e
|
|
155
|
+
if policy == KubernetesResourceInjectionPolicyEnum.ENV:
|
|
156
|
+
return "env"
|
|
157
|
+
if policy == KubernetesResourceInjectionPolicyEnum.KDP:
|
|
158
|
+
return "kdp"
|
|
110
159
|
|
|
111
160
|
if probe_node_allocatable is None:
|
|
112
161
|
return "kdp"
|
|
@@ -142,6 +191,7 @@ def cdi_kind_to_kdp_resource(
|
|
|
142
191
|
|
|
143
192
|
|
|
144
193
|
__all__ = [
|
|
194
|
+
"KubernetesResourceInjectionPolicyEnum",
|
|
145
195
|
"cdi_kind_to_kdp_resource",
|
|
146
196
|
"get_resource_injection_policy",
|
|
147
197
|
"is_kubelet_socket_accessible",
|
|
@@ -7,12 +7,14 @@ import math
|
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
9
|
import time
|
|
10
|
+
from copy import copy
|
|
10
11
|
from dataclasses import dataclass, field
|
|
11
12
|
from datetime import datetime, timezone
|
|
12
13
|
from decimal import Decimal
|
|
13
14
|
from enum import Enum
|
|
14
15
|
from pathlib import Path
|
|
15
|
-
from
|
|
16
|
+
from threading import Lock
|
|
17
|
+
from typing import TYPE_CHECKING, Literal
|
|
16
18
|
|
|
17
19
|
import kubernetes
|
|
18
20
|
import kubernetes.stream.ws_client
|
|
@@ -49,11 +51,16 @@ from .__utils__ import (
|
|
|
49
51
|
sensitive_env_var,
|
|
50
52
|
validate_rfc1123_domain_name,
|
|
51
53
|
)
|
|
52
|
-
from .k8s.devicemanager import
|
|
54
|
+
from .k8s.devicemanager import (
|
|
55
|
+
KubernetesResourceInjectionPolicyEnum,
|
|
56
|
+
get_resource_injection_policy,
|
|
57
|
+
)
|
|
53
58
|
|
|
54
59
|
if TYPE_CHECKING:
|
|
55
60
|
from collections.abc import Callable, Generator
|
|
56
61
|
|
|
62
|
+
from .__types__ import DevicesMaterial
|
|
63
|
+
|
|
57
64
|
logger = logging.getLogger(__name__)
|
|
58
65
|
clogger = logger.getChild("conversion")
|
|
59
66
|
|
|
@@ -117,6 +124,9 @@ class KubernetesWorkloadPlan(WorkloadPlan):
|
|
|
117
124
|
Domain suffix for the cluster. Default is "cluster.local".
|
|
118
125
|
service_type (KubernetesWorkloadServiceTypeEnum):
|
|
119
126
|
Service type for the workload. Default is CLUSTER_IP.
|
|
127
|
+
resource_injection_policy (KubernetesResourceInjectionPolicyEnum | None):
|
|
128
|
+
Workload override for GPUSTACK_RUNTIME_KUBERNETES_RESOURCE_INJECTION_POLICY.
|
|
129
|
+
None inherits the environment setting. Auto probes the target node.
|
|
120
130
|
namespace (str | None):
|
|
121
131
|
Namespace of the workload.
|
|
122
132
|
name (str):
|
|
@@ -159,6 +169,16 @@ class KubernetesWorkloadPlan(WorkloadPlan):
|
|
|
159
169
|
Service type for the workload.
|
|
160
170
|
"""
|
|
161
171
|
|
|
172
|
+
resource_injection_policy: KubernetesResourceInjectionPolicyEnum | None = None
|
|
173
|
+
"""
|
|
174
|
+
Device injection policy for this workload. None inherits
|
|
175
|
+
GPUSTACK_RUNTIME_KUBERNETES_RESOURCE_INJECTION_POLICY (default Auto).
|
|
176
|
+
Env uses runtime-visible device environment variables; KDP requests
|
|
177
|
+
device-plugin resources; Auto detects the policy from the target node.
|
|
178
|
+
This setting does not change the environment or other workloads.
|
|
179
|
+
Env injection of all devices uses the runtime's privileged container path.
|
|
180
|
+
"""
|
|
181
|
+
|
|
162
182
|
def validate_and_default(self):
|
|
163
183
|
"""
|
|
164
184
|
Validate and set defaults for the workload plan.
|
|
@@ -168,6 +188,15 @@ class KubernetesWorkloadPlan(WorkloadPlan):
|
|
|
168
188
|
If the workload plan is invalid.
|
|
169
189
|
|
|
170
190
|
"""
|
|
191
|
+
if self.resource_injection_policy is not None:
|
|
192
|
+
try:
|
|
193
|
+
self.resource_injection_policy = KubernetesResourceInjectionPolicyEnum(
|
|
194
|
+
self.resource_injection_policy,
|
|
195
|
+
)
|
|
196
|
+
except ValueError as e:
|
|
197
|
+
msg = f"Invalid resource injection policy '{self.resource_injection_policy}'"
|
|
198
|
+
raise ValueError(msg) from e
|
|
199
|
+
|
|
171
200
|
if self.labels is None:
|
|
172
201
|
self.labels = {}
|
|
173
202
|
if self.containers is None:
|
|
@@ -1153,6 +1182,9 @@ class KubernetesDeployer(EndoscopicDeployer):
|
|
|
1153
1182
|
self._node_name = node.metadata.name
|
|
1154
1183
|
return node.status.allocatable or {}
|
|
1155
1184
|
|
|
1185
|
+
_resource_injection_policy: Literal["env", "kdp"] | None = None
|
|
1186
|
+
"""Resolved policy on the deployment instance dedicated to one workload."""
|
|
1187
|
+
|
|
1156
1188
|
def _resolve_resource_injection_policy(self) -> str:
|
|
1157
1189
|
"""
|
|
1158
1190
|
Resolve the resource injection policy for this deployer, probing the
|
|
@@ -1162,6 +1194,8 @@ class KubernetesDeployer(EndoscopicDeployer):
|
|
|
1162
1194
|
The resource injection policy.
|
|
1163
1195
|
|
|
1164
1196
|
"""
|
|
1197
|
+
if self._resource_injection_policy is not None:
|
|
1198
|
+
return self._resource_injection_policy
|
|
1165
1199
|
return get_resource_injection_policy(self._probe_node_allocatable)
|
|
1166
1200
|
|
|
1167
1201
|
def _get_default_node_name(self) -> str:
|
|
@@ -1806,14 +1840,33 @@ class KubernetesDeployer(EndoscopicDeployer):
|
|
|
1806
1840
|
self._client = self._get_client()
|
|
1807
1841
|
self._node_name = envs.GPUSTACK_RUNTIME_KUBERNETES_NODE_NAME
|
|
1808
1842
|
self._runtime_uuid_values_allowed: bool | None = None
|
|
1843
|
+
self._deployment_lock = Lock()
|
|
1844
|
+
self._materials_cache: dict[str, dict[str, DevicesMaterial]] = {}
|
|
1845
|
+
self._materials_locks = {policy: Lock() for policy in ("env", "kdp")}
|
|
1846
|
+
|
|
1847
|
+
def _prepare(self):
|
|
1848
|
+
"""
|
|
1849
|
+
Reuse device materials built for the same resolved injection policy.
|
|
1850
|
+
"""
|
|
1851
|
+
if self._materials is not None:
|
|
1852
|
+
return
|
|
1853
|
+
policy = self._resolve_resource_injection_policy()
|
|
1854
|
+
with self._materials_locks[policy]:
|
|
1855
|
+
if policy not in self._materials_cache:
|
|
1856
|
+
self._runtime_uuid_values_allowed = policy != "kdp"
|
|
1857
|
+
try:
|
|
1858
|
+
super()._prepare()
|
|
1859
|
+
except Exception:
|
|
1860
|
+
# Incomplete detection must not become a reusable device map.
|
|
1861
|
+
self._materials = None
|
|
1862
|
+
raise
|
|
1863
|
+
self._materials_cache[policy] = self._materials
|
|
1864
|
+
self._materials = self._materials_cache[policy]
|
|
1809
1865
|
|
|
1810
1866
|
@property
|
|
1811
1867
|
def allowed_runtime_uuid_values(self) -> bool:
|
|
1812
|
-
#
|
|
1813
|
-
#
|
|
1814
|
-
# materials, which are themselves built once, and it is read there once
|
|
1815
|
-
# per manufacturer -- so probing on every read would spend one API call
|
|
1816
|
-
# per manufacturer to answer a question already settled.
|
|
1868
|
+
# Device materials and UUID eligibility belong to the same resolved
|
|
1869
|
+
# policy. Workloads with the same policy reuse the device materials.
|
|
1817
1870
|
if self._runtime_uuid_values_allowed is None:
|
|
1818
1871
|
self._runtime_uuid_values_allowed = (
|
|
1819
1872
|
self._resolve_resource_injection_policy() != "kdp"
|
|
@@ -2143,25 +2196,40 @@ class KubernetesDeployer(EndoscopicDeployer):
|
|
|
2143
2196
|
msg = f"Invalid workload plan type: {type(workload)}"
|
|
2144
2197
|
raise TypeError(msg)
|
|
2145
2198
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
if isinstance(workload, WorkloadPlan):
|
|
2199
|
+
if not isinstance(workload, KubernetesWorkloadPlan):
|
|
2149
2200
|
workload = KubernetesWorkloadPlan(**workload.__dict__)
|
|
2150
2201
|
workload.validate_and_default()
|
|
2202
|
+
|
|
2203
|
+
# Node selection and mirrored options belong to the registered deployer,
|
|
2204
|
+
# including the node used by later workload listing operations.
|
|
2205
|
+
with self._deployment_lock:
|
|
2206
|
+
self._prepare_mirrored_deployment()
|
|
2207
|
+
deployer: KubernetesDeployer = copy(self)
|
|
2208
|
+
|
|
2209
|
+
# Each workload resolves its policy independently while sharing cached
|
|
2210
|
+
# device materials only with workloads using the same policy.
|
|
2211
|
+
deployer._materials = None
|
|
2212
|
+
deployer._resource_injection_policy = get_resource_injection_policy(
|
|
2213
|
+
deployer._probe_node_allocatable,
|
|
2214
|
+
policy=workload.resource_injection_policy,
|
|
2215
|
+
)
|
|
2216
|
+
deployer._runtime_uuid_values_allowed = (
|
|
2217
|
+
deployer._resource_injection_policy != "kdp"
|
|
2218
|
+
)
|
|
2151
2219
|
if logger.isEnabledFor(logging.DEBUG):
|
|
2152
2220
|
logger.debug("Creating workload:\n%s", workload.to_yaml())
|
|
2153
2221
|
|
|
2154
2222
|
# Create ephemeral file if needed,
|
|
2155
2223
|
# (container index, configured path): <actual ConfigMap name>
|
|
2156
2224
|
ephemeral_filename_mapping: dict[tuple[int, str], str] = (
|
|
2157
|
-
|
|
2225
|
+
deployer._create_ephemeral_configmaps(workload)
|
|
2158
2226
|
)
|
|
2159
2227
|
|
|
2160
2228
|
# Create Service if needed.
|
|
2161
|
-
|
|
2229
|
+
deployer._create_service(workload)
|
|
2162
2230
|
|
|
2163
2231
|
# Create Pod.
|
|
2164
|
-
|
|
2232
|
+
deployer._create_pod(
|
|
2165
2233
|
workload,
|
|
2166
2234
|
ephemeral_filename_mapping,
|
|
2167
2235
|
)
|
|
@@ -425,7 +425,7 @@ gpustack_runtime/
|
|
|
425
425
|
│ ├── __types__.py # Container/Workload{Plan,Status}, Deployer ABC, DevicesMaterial
|
|
426
426
|
│ ├── docker.py # DockerWorkloadStatus.parse_state
|
|
427
427
|
│ ├── podman.py
|
|
428
|
-
│ ├──
|
|
428
|
+
│ ├── kubernetes.py # KubernetesWorkloadStatus.parse_state, Pod/Event reads
|
|
429
429
|
│ └── cdi/<vendor>.py # CDI spec generation, consumes Device.index/appendix
|
|
430
430
|
├── cmds/detector.py # `detect` / `topology` sub-commands
|
|
431
431
|
└── envs.py # GPUSTACK_RUNTIME_* env surface
|
|
@@ -769,14 +769,14 @@ implement `detect_info()`.
|
|
|
769
769
|
- [x] **D1 · Pin the device ordering for containers that see every card**
|
|
770
770
|
Blocked by: None
|
|
771
771
|
Owns: `gpustack_runtime/deployer/__types__.py`, `gpustack_runtime/deployer/docker.py`,
|
|
772
|
-
`gpustack_runtime/deployer/podman.py`, `gpustack_runtime/deployer/
|
|
772
|
+
`gpustack_runtime/deployer/podman.py`, `gpustack_runtime/deployer/kubernetes.py`,
|
|
773
773
|
`tests/gpustack_runtime/deployer/test_visible_devices_ordering.py`
|
|
774
774
|
Gate: review
|
|
775
775
|
Scope: add `Deployer.map_visible_devices_ordering(runtime_envs) -> dict[str, str]` beside the
|
|
776
776
|
existing `map_backend_visible_devices` / `map_visible_devices_affinities` — it resolves each
|
|
777
777
|
runtime visible-devices env name to its manufacturer through `get_manufacturer()` and returns
|
|
778
778
|
`{"CUDA_DEVICE_ORDER": "PCI_BUS_ID"}` when NVIDIA is among them, `{}` otherwise. Call it from all
|
|
779
|
-
three deployers' device-request loops (`docker.py`, `podman.py`, `
|
|
779
|
+
three deployers' device-request loops (`docker.py`, `podman.py`, `kubernetes.py`), right beside
|
|
780
780
|
the existing `if r_v != "all" and privileged:` backend-visible-devices block, under the
|
|
781
781
|
complementary condition `r_v == "all" or privileged` — the deployers' own resolution of "this
|
|
782
782
|
container sees every card". Never overwrite a value the container already declares (the
|
|
@@ -823,7 +823,7 @@ implement `detect_info()`.
|
|
|
823
823
|
|
|
824
824
|
- [x] **D5 · Kubernetes: exit status, image-pull failure, Pod Events, RBAC**
|
|
825
825
|
Blocked by: D2
|
|
826
|
-
Owns: `gpustack_runtime/deployer/
|
|
826
|
+
Owns: `gpustack_runtime/deployer/kubernetes.py`, `deploy/manifests/kubernetes.yaml`,
|
|
827
827
|
`tests/gpustack_runtime/deployer/test_kubernetes_status.py`
|
|
828
828
|
Gate: review
|
|
829
829
|
Scope: fill the exit list from `container_statuses` / `init_container_statuses`, reading
|
|
@@ -9,7 +9,7 @@ import kubernetes.client
|
|
|
9
9
|
import pytest
|
|
10
10
|
|
|
11
11
|
from gpustack_runtime.deployer.__types__ import WorkloadStatusStateEnum
|
|
12
|
-
from gpustack_runtime.deployer.
|
|
12
|
+
from gpustack_runtime.deployer.kubernetes import KubernetesWorkloadStatus
|
|
13
13
|
|
|
14
14
|
_REGISTRY_MESSAGE = 'Back-off pulling image "does-not-exist.invalid/x:y"'
|
|
15
15
|
_EVENT_MESSAGE = (
|