config.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import logging
  2. from typing import Any, Dict, get_type_hints, get_args, get_origin
  3. from fastapi import Request
  4. from transformers.hf_argparser import string_to_bool
  5. from gpustack.config.config import Config
  6. WHITELIST_CONFIG_FIELDS = {
  7. "debug",
  8. "system_default_container_registry",
  9. }
  10. READ_ONLY_CONFIG_FIELDS = WHITELIST_CONFIG_FIELDS.union(
  11. {
  12. "server_external_url",
  13. "grafana_url",
  14. "disable_builtin_observability",
  15. "grafana_worker_dashboard_uid",
  16. "grafana_model_dashboard_uid",
  17. }
  18. )
  19. logger = logging.getLogger(__name__)
  20. def _unwrap_optional(tp):
  21. origin = get_origin(tp)
  22. if origin is None:
  23. return tp
  24. args = get_args(tp)
  25. non_none = [a for a in args if a is not type(None)]
  26. return non_none[0] if non_none else tp
  27. def coerce_value_by_field(field: str, v):
  28. hints = get_type_hints(Config)
  29. tp = hints.get(field)
  30. if tp is None:
  31. return v
  32. tp = _unwrap_optional(tp)
  33. origin = get_origin(tp)
  34. if tp is bool:
  35. return string_to_bool(v)
  36. if tp is int:
  37. return int(v)
  38. if tp is float:
  39. return float(v)
  40. if tp is str:
  41. return str(v)
  42. if origin is list:
  43. if isinstance(v, str):
  44. return [item.strip() for item in v.split(',') if item.strip()]
  45. return list(v)
  46. if tp is dict or origin is dict:
  47. if isinstance(v, str):
  48. import json
  49. return json.loads(v)
  50. return dict(v)
  51. return v
  52. def filter_whitelisted_yaml_config(config_data: Dict[str, Any]) -> Dict[str, Any]:
  53. if not config_data:
  54. return config_data
  55. filtered_data = {}
  56. for key, value in config_data.items():
  57. config_key = key.replace('-', '_')
  58. if config_key in WHITELIST_CONFIG_FIELDS:
  59. filtered_data[config_key] = value
  60. return filtered_data
  61. def is_local_request(request: Request) -> bool:
  62. host = request.client.host
  63. if host in ("127.0.0.1", "::1"):
  64. return True
  65. return False
  66. def apply_registry_override_to_image(
  67. _config: Config, image: str, fallback_registry: str
  68. ) -> str:
  69. """
  70. 1) If the image has an explicit registry, return it as is.
  71. 2) If the image does not have an explicit registry and a system default registry is configured,
  72. prefix the image with the system default registry in config.
  73. 3) If the image does not have an explicit registry and no system default registry is configured,
  74. using docker.io as default if image without "gpustack" prefix.
  75. 4) If the image does not have an explicit registry and no system default registry is configured,
  76. and with "gpustack" prefix, using docker.io as default if docker.io is reachable.
  77. Otherwise, using quay.io.
  78. """
  79. registry_cfg = (_config.system_default_container_registry or "").strip()
  80. parts = image.split("/", 1)
  81. # 1) If the image has an explicit registry, return it as is.
  82. has_explicit = len(parts) >= 2 and (
  83. "." in parts[0] or ":" in parts[0] or parts[0] == "localhost"
  84. )
  85. if has_explicit:
  86. return image
  87. # 2) If the image does not have an explicit registry and a system default registry is configured,
  88. # prefix the image with the system default registry in config.
  89. if registry_cfg:
  90. registry = registry_cfg.rstrip("/")
  91. final = f"{registry}/{image}"
  92. logger.info(
  93. f"Using system default registry '{registry}'; image resolved to: {final}"
  94. )
  95. return final
  96. # 3) no explicit or configured, and not start with "gpustack" using "docker.io" as default.
  97. if not image.startswith("gpustack"):
  98. logger.info(
  99. f"Using Docker Hub for non-gpustack image; image resolved to: {image}"
  100. )
  101. return image
  102. # 4) Otherwise, use fallback registry if configured.
  103. # The fallback registry is Docker Hub or Quay.io depending on reachability.
  104. # If both are not reachable, use docker.io as default.
  105. prefix = fallback_registry + "/" if fallback_registry else ""
  106. return f"{prefix}{image}"