skypilot-nightly 1.0.0.dev2024053101__py3-none-any.whl → 1.0.0.dev2025022801__py3-none-any.whl

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 (299) hide show
  1. sky/__init__.py +64 -32
  2. sky/adaptors/aws.py +23 -6
  3. sky/adaptors/azure.py +432 -15
  4. sky/adaptors/cloudflare.py +5 -5
  5. sky/adaptors/common.py +19 -9
  6. sky/adaptors/do.py +20 -0
  7. sky/adaptors/gcp.py +3 -2
  8. sky/adaptors/kubernetes.py +122 -88
  9. sky/adaptors/nebius.py +100 -0
  10. sky/adaptors/oci.py +39 -1
  11. sky/adaptors/vast.py +29 -0
  12. sky/admin_policy.py +101 -0
  13. sky/authentication.py +117 -98
  14. sky/backends/backend.py +52 -20
  15. sky/backends/backend_utils.py +669 -557
  16. sky/backends/cloud_vm_ray_backend.py +1099 -808
  17. sky/backends/local_docker_backend.py +14 -8
  18. sky/backends/wheel_utils.py +38 -20
  19. sky/benchmark/benchmark_utils.py +22 -23
  20. sky/check.py +76 -27
  21. sky/cli.py +1586 -1139
  22. sky/client/__init__.py +1 -0
  23. sky/client/cli.py +5683 -0
  24. sky/client/common.py +345 -0
  25. sky/client/sdk.py +1765 -0
  26. sky/cloud_stores.py +283 -19
  27. sky/clouds/__init__.py +7 -2
  28. sky/clouds/aws.py +303 -112
  29. sky/clouds/azure.py +185 -179
  30. sky/clouds/cloud.py +115 -37
  31. sky/clouds/cudo.py +29 -22
  32. sky/clouds/do.py +313 -0
  33. sky/clouds/fluidstack.py +44 -54
  34. sky/clouds/gcp.py +206 -65
  35. sky/clouds/ibm.py +26 -21
  36. sky/clouds/kubernetes.py +345 -91
  37. sky/clouds/lambda_cloud.py +40 -29
  38. sky/clouds/nebius.py +297 -0
  39. sky/clouds/oci.py +129 -90
  40. sky/clouds/paperspace.py +22 -18
  41. sky/clouds/runpod.py +53 -34
  42. sky/clouds/scp.py +28 -24
  43. sky/clouds/service_catalog/__init__.py +19 -13
  44. sky/clouds/service_catalog/aws_catalog.py +29 -12
  45. sky/clouds/service_catalog/azure_catalog.py +33 -6
  46. sky/clouds/service_catalog/common.py +95 -75
  47. sky/clouds/service_catalog/constants.py +3 -3
  48. sky/clouds/service_catalog/cudo_catalog.py +13 -3
  49. sky/clouds/service_catalog/data_fetchers/fetch_aws.py +36 -21
  50. sky/clouds/service_catalog/data_fetchers/fetch_azure.py +31 -4
  51. sky/clouds/service_catalog/data_fetchers/fetch_cudo.py +8 -117
  52. sky/clouds/service_catalog/data_fetchers/fetch_fluidstack.py +197 -44
  53. sky/clouds/service_catalog/data_fetchers/fetch_gcp.py +224 -36
  54. sky/clouds/service_catalog/data_fetchers/fetch_lambda_cloud.py +44 -24
  55. sky/clouds/service_catalog/data_fetchers/fetch_vast.py +147 -0
  56. sky/clouds/service_catalog/data_fetchers/fetch_vsphere.py +1 -1
  57. sky/clouds/service_catalog/do_catalog.py +111 -0
  58. sky/clouds/service_catalog/fluidstack_catalog.py +2 -2
  59. sky/clouds/service_catalog/gcp_catalog.py +16 -2
  60. sky/clouds/service_catalog/ibm_catalog.py +2 -2
  61. sky/clouds/service_catalog/kubernetes_catalog.py +192 -70
  62. sky/clouds/service_catalog/lambda_catalog.py +8 -3
  63. sky/clouds/service_catalog/nebius_catalog.py +116 -0
  64. sky/clouds/service_catalog/oci_catalog.py +31 -4
  65. sky/clouds/service_catalog/paperspace_catalog.py +2 -2
  66. sky/clouds/service_catalog/runpod_catalog.py +2 -2
  67. sky/clouds/service_catalog/scp_catalog.py +2 -2
  68. sky/clouds/service_catalog/vast_catalog.py +104 -0
  69. sky/clouds/service_catalog/vsphere_catalog.py +2 -2
  70. sky/clouds/utils/aws_utils.py +65 -0
  71. sky/clouds/utils/azure_utils.py +91 -0
  72. sky/clouds/utils/gcp_utils.py +5 -9
  73. sky/clouds/utils/oci_utils.py +47 -5
  74. sky/clouds/utils/scp_utils.py +4 -3
  75. sky/clouds/vast.py +280 -0
  76. sky/clouds/vsphere.py +22 -18
  77. sky/core.py +361 -107
  78. sky/dag.py +41 -28
  79. sky/data/data_transfer.py +37 -0
  80. sky/data/data_utils.py +211 -32
  81. sky/data/mounting_utils.py +182 -30
  82. sky/data/storage.py +2118 -270
  83. sky/data/storage_utils.py +126 -5
  84. sky/exceptions.py +179 -8
  85. sky/execution.py +158 -85
  86. sky/global_user_state.py +150 -34
  87. sky/jobs/__init__.py +12 -10
  88. sky/jobs/client/__init__.py +0 -0
  89. sky/jobs/client/sdk.py +302 -0
  90. sky/jobs/constants.py +49 -11
  91. sky/jobs/controller.py +161 -99
  92. sky/jobs/dashboard/dashboard.py +171 -25
  93. sky/jobs/dashboard/templates/index.html +572 -60
  94. sky/jobs/recovery_strategy.py +157 -156
  95. sky/jobs/scheduler.py +307 -0
  96. sky/jobs/server/__init__.py +1 -0
  97. sky/jobs/server/core.py +598 -0
  98. sky/jobs/server/dashboard_utils.py +69 -0
  99. sky/jobs/server/server.py +190 -0
  100. sky/jobs/state.py +627 -122
  101. sky/jobs/utils.py +615 -206
  102. sky/models.py +27 -0
  103. sky/optimizer.py +142 -83
  104. sky/provision/__init__.py +20 -5
  105. sky/provision/aws/config.py +124 -42
  106. sky/provision/aws/instance.py +130 -53
  107. sky/provision/azure/__init__.py +7 -0
  108. sky/{skylet/providers → provision}/azure/azure-config-template.json +19 -7
  109. sky/provision/azure/config.py +220 -0
  110. sky/provision/azure/instance.py +1012 -37
  111. sky/provision/common.py +31 -3
  112. sky/provision/constants.py +25 -0
  113. sky/provision/cudo/__init__.py +2 -1
  114. sky/provision/cudo/cudo_utils.py +112 -0
  115. sky/provision/cudo/cudo_wrapper.py +37 -16
  116. sky/provision/cudo/instance.py +28 -12
  117. sky/provision/do/__init__.py +11 -0
  118. sky/provision/do/config.py +14 -0
  119. sky/provision/do/constants.py +10 -0
  120. sky/provision/do/instance.py +287 -0
  121. sky/provision/do/utils.py +301 -0
  122. sky/provision/docker_utils.py +82 -46
  123. sky/provision/fluidstack/fluidstack_utils.py +57 -125
  124. sky/provision/fluidstack/instance.py +15 -43
  125. sky/provision/gcp/config.py +19 -9
  126. sky/provision/gcp/constants.py +7 -1
  127. sky/provision/gcp/instance.py +55 -34
  128. sky/provision/gcp/instance_utils.py +339 -80
  129. sky/provision/gcp/mig_utils.py +210 -0
  130. sky/provision/instance_setup.py +172 -133
  131. sky/provision/kubernetes/__init__.py +1 -0
  132. sky/provision/kubernetes/config.py +104 -90
  133. sky/provision/kubernetes/constants.py +8 -0
  134. sky/provision/kubernetes/instance.py +680 -325
  135. sky/provision/kubernetes/manifests/smarter-device-manager-daemonset.yaml +3 -0
  136. sky/provision/kubernetes/network.py +54 -20
  137. sky/provision/kubernetes/network_utils.py +70 -21
  138. sky/provision/kubernetes/utils.py +1370 -251
  139. sky/provision/lambda_cloud/__init__.py +11 -0
  140. sky/provision/lambda_cloud/config.py +10 -0
  141. sky/provision/lambda_cloud/instance.py +265 -0
  142. sky/{clouds/utils → provision/lambda_cloud}/lambda_utils.py +24 -23
  143. sky/provision/logging.py +1 -1
  144. sky/provision/nebius/__init__.py +11 -0
  145. sky/provision/nebius/config.py +11 -0
  146. sky/provision/nebius/instance.py +285 -0
  147. sky/provision/nebius/utils.py +318 -0
  148. sky/provision/oci/__init__.py +15 -0
  149. sky/provision/oci/config.py +51 -0
  150. sky/provision/oci/instance.py +436 -0
  151. sky/provision/oci/query_utils.py +681 -0
  152. sky/provision/paperspace/constants.py +6 -0
  153. sky/provision/paperspace/instance.py +4 -3
  154. sky/provision/paperspace/utils.py +2 -0
  155. sky/provision/provisioner.py +207 -130
  156. sky/provision/runpod/__init__.py +1 -0
  157. sky/provision/runpod/api/__init__.py +3 -0
  158. sky/provision/runpod/api/commands.py +119 -0
  159. sky/provision/runpod/api/pods.py +142 -0
  160. sky/provision/runpod/instance.py +64 -8
  161. sky/provision/runpod/utils.py +239 -23
  162. sky/provision/vast/__init__.py +10 -0
  163. sky/provision/vast/config.py +11 -0
  164. sky/provision/vast/instance.py +247 -0
  165. sky/provision/vast/utils.py +162 -0
  166. sky/provision/vsphere/common/vim_utils.py +1 -1
  167. sky/provision/vsphere/instance.py +8 -18
  168. sky/provision/vsphere/vsphere_utils.py +1 -1
  169. sky/resources.py +247 -102
  170. sky/serve/__init__.py +9 -9
  171. sky/serve/autoscalers.py +361 -299
  172. sky/serve/client/__init__.py +0 -0
  173. sky/serve/client/sdk.py +366 -0
  174. sky/serve/constants.py +12 -3
  175. sky/serve/controller.py +106 -36
  176. sky/serve/load_balancer.py +63 -12
  177. sky/serve/load_balancing_policies.py +84 -2
  178. sky/serve/replica_managers.py +42 -34
  179. sky/serve/serve_state.py +62 -32
  180. sky/serve/serve_utils.py +271 -160
  181. sky/serve/server/__init__.py +0 -0
  182. sky/serve/{core.py → server/core.py} +271 -90
  183. sky/serve/server/server.py +112 -0
  184. sky/serve/service.py +52 -16
  185. sky/serve/service_spec.py +95 -32
  186. sky/server/__init__.py +1 -0
  187. sky/server/common.py +430 -0
  188. sky/server/constants.py +21 -0
  189. sky/server/html/log.html +174 -0
  190. sky/server/requests/__init__.py +0 -0
  191. sky/server/requests/executor.py +472 -0
  192. sky/server/requests/payloads.py +487 -0
  193. sky/server/requests/queues/__init__.py +0 -0
  194. sky/server/requests/queues/mp_queue.py +76 -0
  195. sky/server/requests/requests.py +567 -0
  196. sky/server/requests/serializers/__init__.py +0 -0
  197. sky/server/requests/serializers/decoders.py +192 -0
  198. sky/server/requests/serializers/encoders.py +166 -0
  199. sky/server/server.py +1106 -0
  200. sky/server/stream_utils.py +141 -0
  201. sky/setup_files/MANIFEST.in +2 -5
  202. sky/setup_files/dependencies.py +159 -0
  203. sky/setup_files/setup.py +14 -125
  204. sky/sky_logging.py +59 -14
  205. sky/skylet/autostop_lib.py +2 -2
  206. sky/skylet/constants.py +183 -50
  207. sky/skylet/events.py +22 -10
  208. sky/skylet/job_lib.py +403 -258
  209. sky/skylet/log_lib.py +111 -71
  210. sky/skylet/log_lib.pyi +6 -0
  211. sky/skylet/providers/command_runner.py +6 -8
  212. sky/skylet/providers/ibm/node_provider.py +2 -2
  213. sky/skylet/providers/scp/config.py +11 -3
  214. sky/skylet/providers/scp/node_provider.py +8 -8
  215. sky/skylet/skylet.py +3 -1
  216. sky/skylet/subprocess_daemon.py +69 -17
  217. sky/skypilot_config.py +119 -57
  218. sky/task.py +205 -64
  219. sky/templates/aws-ray.yml.j2 +37 -7
  220. sky/templates/azure-ray.yml.j2 +27 -82
  221. sky/templates/cudo-ray.yml.j2 +7 -3
  222. sky/templates/do-ray.yml.j2 +98 -0
  223. sky/templates/fluidstack-ray.yml.j2 +7 -4
  224. sky/templates/gcp-ray.yml.j2 +26 -6
  225. sky/templates/ibm-ray.yml.j2 +3 -2
  226. sky/templates/jobs-controller.yaml.j2 +46 -11
  227. sky/templates/kubernetes-ingress.yml.j2 +7 -0
  228. sky/templates/kubernetes-loadbalancer.yml.j2 +7 -0
  229. sky/templates/{kubernetes-port-forward-proxy-command.sh.j2 → kubernetes-port-forward-proxy-command.sh} +51 -7
  230. sky/templates/kubernetes-ray.yml.j2 +292 -25
  231. sky/templates/lambda-ray.yml.j2 +30 -40
  232. sky/templates/nebius-ray.yml.j2 +79 -0
  233. sky/templates/oci-ray.yml.j2 +18 -57
  234. sky/templates/paperspace-ray.yml.j2 +10 -6
  235. sky/templates/runpod-ray.yml.j2 +26 -4
  236. sky/templates/scp-ray.yml.j2 +3 -2
  237. sky/templates/sky-serve-controller.yaml.j2 +12 -1
  238. sky/templates/skypilot-server-kubernetes-proxy.sh +36 -0
  239. sky/templates/vast-ray.yml.j2 +70 -0
  240. sky/templates/vsphere-ray.yml.j2 +8 -3
  241. sky/templates/websocket_proxy.py +64 -0
  242. sky/usage/constants.py +10 -1
  243. sky/usage/usage_lib.py +130 -37
  244. sky/utils/accelerator_registry.py +35 -51
  245. sky/utils/admin_policy_utils.py +147 -0
  246. sky/utils/annotations.py +51 -0
  247. sky/utils/cli_utils/status_utils.py +81 -23
  248. sky/utils/cluster_utils.py +356 -0
  249. sky/utils/command_runner.py +452 -89
  250. sky/utils/command_runner.pyi +77 -3
  251. sky/utils/common.py +54 -0
  252. sky/utils/common_utils.py +319 -108
  253. sky/utils/config_utils.py +204 -0
  254. sky/utils/control_master_utils.py +48 -0
  255. sky/utils/controller_utils.py +548 -266
  256. sky/utils/dag_utils.py +93 -32
  257. sky/utils/db_utils.py +18 -4
  258. sky/utils/env_options.py +29 -7
  259. sky/utils/kubernetes/create_cluster.sh +8 -60
  260. sky/utils/kubernetes/deploy_remote_cluster.sh +243 -0
  261. sky/utils/kubernetes/exec_kubeconfig_converter.py +73 -0
  262. sky/utils/kubernetes/generate_kubeconfig.sh +336 -0
  263. sky/utils/kubernetes/gpu_labeler.py +4 -4
  264. sky/utils/kubernetes/k8s_gpu_labeler_job.yaml +4 -3
  265. sky/utils/kubernetes/kubernetes_deploy_utils.py +228 -0
  266. sky/utils/kubernetes/rsync_helper.sh +24 -0
  267. sky/utils/kubernetes/ssh_jump_lifecycle_manager.py +1 -1
  268. sky/utils/log_utils.py +240 -33
  269. sky/utils/message_utils.py +81 -0
  270. sky/utils/registry.py +127 -0
  271. sky/utils/resources_utils.py +94 -22
  272. sky/utils/rich_utils.py +247 -18
  273. sky/utils/schemas.py +284 -64
  274. sky/{status_lib.py → utils/status_lib.py} +12 -7
  275. sky/utils/subprocess_utils.py +212 -46
  276. sky/utils/timeline.py +12 -7
  277. sky/utils/ux_utils.py +168 -15
  278. skypilot_nightly-1.0.0.dev2025022801.dist-info/METADATA +363 -0
  279. skypilot_nightly-1.0.0.dev2025022801.dist-info/RECORD +352 -0
  280. {skypilot_nightly-1.0.0.dev2024053101.dist-info → skypilot_nightly-1.0.0.dev2025022801.dist-info}/WHEEL +1 -1
  281. sky/clouds/cloud_registry.py +0 -31
  282. sky/jobs/core.py +0 -330
  283. sky/skylet/providers/azure/__init__.py +0 -2
  284. sky/skylet/providers/azure/azure-vm-template.json +0 -301
  285. sky/skylet/providers/azure/config.py +0 -170
  286. sky/skylet/providers/azure/node_provider.py +0 -466
  287. sky/skylet/providers/lambda_cloud/__init__.py +0 -2
  288. sky/skylet/providers/lambda_cloud/node_provider.py +0 -320
  289. sky/skylet/providers/oci/__init__.py +0 -2
  290. sky/skylet/providers/oci/node_provider.py +0 -488
  291. sky/skylet/providers/oci/query_helper.py +0 -383
  292. sky/skylet/providers/oci/utils.py +0 -21
  293. sky/utils/cluster_yaml_utils.py +0 -24
  294. sky/utils/kubernetes/generate_static_kubeconfig.sh +0 -137
  295. skypilot_nightly-1.0.0.dev2024053101.dist-info/METADATA +0 -315
  296. skypilot_nightly-1.0.0.dev2024053101.dist-info/RECORD +0 -275
  297. {skypilot_nightly-1.0.0.dev2024053101.dist-info → skypilot_nightly-1.0.0.dev2025022801.dist-info}/LICENSE +0 -0
  298. {skypilot_nightly-1.0.0.dev2024053101.dist-info → skypilot_nightly-1.0.0.dev2025022801.dist-info}/entry_points.txt +0 -0
  299. {skypilot_nightly-1.0.0.dev2024053101.dist-info → skypilot_nightly-1.0.0.dev2025022801.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,204 @@
1
+ """Utilities for nested config."""
2
+ import copy
3
+ from typing import Any, Dict, List, Optional, Tuple
4
+
5
+ from sky import sky_logging
6
+
7
+ logger = sky_logging.init_logger(__name__)
8
+
9
+
10
+ class Config(Dict[str, Any]):
11
+ """SkyPilot config that supports setting/getting values with nested keys."""
12
+
13
+ def get_nested(
14
+ self,
15
+ keys: Tuple[str, ...],
16
+ default_value: Any,
17
+ override_configs: Optional[Dict[str, Any]] = None,
18
+ allowed_override_keys: Optional[List[Tuple[str, ...]]] = None,
19
+ disallowed_override_keys: Optional[List[Tuple[str,
20
+ ...]]] = None) -> Any:
21
+ """Gets a nested key.
22
+
23
+ If any key is not found, or any intermediate key does not point to a
24
+ dict value, returns 'default_value'.
25
+
26
+ Args:
27
+ keys: A tuple of strings representing the nested keys.
28
+ default_value: The default value to return if the key is not found.
29
+ override_configs: A dict of override configs with the same schema as
30
+ the config file, but only containing the keys to override.
31
+ allowed_override_keys: A list of keys that are allowed to be
32
+ overridden.
33
+ disallowed_override_keys: A list of keys that are disallowed to be
34
+ overridden.
35
+
36
+ Returns:
37
+ The value of the nested key, or 'default_value' if not found.
38
+ """
39
+ config = copy.deepcopy(self)
40
+ if override_configs is not None:
41
+ config = _recursive_update(config, override_configs,
42
+ allowed_override_keys,
43
+ disallowed_override_keys)
44
+ return _get_nested(config, keys, default_value, pop=False)
45
+
46
+ def set_nested(self, keys: Tuple[str, ...], value: Any) -> None:
47
+ """In-place sets a nested key to value.
48
+
49
+ Like get_nested(), if any key is not found, this will not raise an
50
+ error.
51
+ """
52
+ override = {}
53
+ for i, key in enumerate(reversed(keys)):
54
+ if i == 0:
55
+ override = {key: value}
56
+ else:
57
+ override = {key: override}
58
+ _recursive_update(self, override)
59
+
60
+ def pop_nested(self, keys: Tuple[str, ...], default_value: Any) -> Any:
61
+ """Pops a nested key."""
62
+ return _get_nested(self, keys, default_value, pop=True)
63
+
64
+ @classmethod
65
+ def from_dict(cls, config: Optional[Dict[str, Any]]) -> 'Config':
66
+ if config is None:
67
+ return cls()
68
+ return cls(**config)
69
+
70
+
71
+ def _check_allowed_and_disallowed_override_keys(
72
+ key: str,
73
+ allowed_override_keys: Optional[List[Tuple[str, ...]]] = None,
74
+ disallowed_override_keys: Optional[List[Tuple[str, ...]]] = None
75
+ ) -> Tuple[Optional[List[Tuple[str, ...]]], Optional[List[Tuple[str, ...]]]]:
76
+ allowed_keys_with_matched_prefix: Optional[List[Tuple[str, ...]]] = []
77
+ disallowed_keys_with_matched_prefix: Optional[List[Tuple[str, ...]]] = []
78
+ if allowed_override_keys is not None:
79
+ for nested_key in allowed_override_keys:
80
+ if key == nested_key[0]:
81
+ if len(nested_key) == 1:
82
+ # Allowed key is fully matched, no need to check further.
83
+ allowed_keys_with_matched_prefix = None
84
+ break
85
+ assert allowed_keys_with_matched_prefix is not None
86
+ allowed_keys_with_matched_prefix.append(nested_key[1:])
87
+ if (allowed_keys_with_matched_prefix is not None and
88
+ not allowed_keys_with_matched_prefix):
89
+ raise ValueError(f'Key {key} is not in allowed override keys: '
90
+ f'{allowed_override_keys}')
91
+ else:
92
+ allowed_keys_with_matched_prefix = None
93
+
94
+ if disallowed_override_keys is not None:
95
+ for nested_key in disallowed_override_keys:
96
+ if key == nested_key[0]:
97
+ if len(nested_key) == 1:
98
+ raise ValueError(
99
+ f'Key {key} is in disallowed override keys: '
100
+ f'{disallowed_override_keys}')
101
+ assert disallowed_keys_with_matched_prefix is not None
102
+ disallowed_keys_with_matched_prefix.append(nested_key[1:])
103
+ else:
104
+ disallowed_keys_with_matched_prefix = None
105
+ return allowed_keys_with_matched_prefix, disallowed_keys_with_matched_prefix
106
+
107
+
108
+ def _recursive_update(
109
+ base_config: Config,
110
+ override_config: Dict[str, Any],
111
+ allowed_override_keys: Optional[List[Tuple[str, ...]]] = None,
112
+ disallowed_override_keys: Optional[List[Tuple[str,
113
+ ...]]] = None) -> Config:
114
+ """Recursively updates base configuration with override configuration"""
115
+ for key, value in override_config.items():
116
+ (next_allowed_override_keys, next_disallowed_override_keys
117
+ ) = _check_allowed_and_disallowed_override_keys(
118
+ key, allowed_override_keys, disallowed_override_keys)
119
+ if key == 'kubernetes' and key in base_config:
120
+ merge_k8s_configs(base_config[key], value,
121
+ next_allowed_override_keys,
122
+ next_disallowed_override_keys)
123
+ elif (isinstance(value, dict) and key in base_config and
124
+ isinstance(base_config[key], dict)):
125
+ _recursive_update(base_config[key], value,
126
+ next_allowed_override_keys,
127
+ next_disallowed_override_keys)
128
+ else:
129
+ base_config[key] = value
130
+ return base_config
131
+
132
+
133
+ def _get_nested(configs: Optional[Dict[str, Any]],
134
+ keys: Tuple[str, ...],
135
+ default_value: Any,
136
+ pop: bool = False) -> Any:
137
+ if configs is None:
138
+ return default_value
139
+ curr = configs
140
+ for i, key in enumerate(keys):
141
+ if isinstance(curr, dict) and key in curr:
142
+ value = curr[key]
143
+ if i == len(keys) - 1:
144
+ if pop:
145
+ curr.pop(key, default_value)
146
+ curr = value
147
+ else:
148
+ return default_value
149
+ logger.debug(f'User config: {".".join(keys)} -> {curr}')
150
+ return curr
151
+
152
+
153
+ def merge_k8s_configs(
154
+ base_config: Dict[Any, Any],
155
+ override_config: Dict[Any, Any],
156
+ allowed_override_keys: Optional[List[Tuple[str, ...]]] = None,
157
+ disallowed_override_keys: Optional[List[Tuple[str,
158
+ ...]]] = None) -> None:
159
+ """Merge two configs into the base_config.
160
+
161
+ Updates nested dictionaries instead of replacing them.
162
+ If a list is encountered, it will be appended to the base_config list.
163
+
164
+ An exception is when the key is 'containers', in which case the
165
+ first container in the list will be fetched and merge_dict will be
166
+ called on it with the first container in the base_config list.
167
+ """
168
+ for key, value in override_config.items():
169
+ (next_allowed_override_keys, next_disallowed_override_keys
170
+ ) = _check_allowed_and_disallowed_override_keys(
171
+ key, allowed_override_keys, disallowed_override_keys)
172
+ if isinstance(value, dict) and key in base_config:
173
+ merge_k8s_configs(base_config[key], value,
174
+ next_allowed_override_keys,
175
+ next_disallowed_override_keys)
176
+ elif isinstance(value, list) and key in base_config:
177
+ assert isinstance(base_config[key], list), \
178
+ f'Expected {key} to be a list, found {base_config[key]}'
179
+ if key in ['containers', 'imagePullSecrets']:
180
+ # If the key is 'containers' or 'imagePullSecrets, we take the
181
+ # first and only container/secret in the list and merge it, as
182
+ # we only support one container per pod.
183
+ assert len(value) == 1, \
184
+ f'Expected only one container, found {value}'
185
+ merge_k8s_configs(base_config[key][0], value[0],
186
+ next_allowed_override_keys,
187
+ next_disallowed_override_keys)
188
+ elif key in ['volumes', 'volumeMounts']:
189
+ # If the key is 'volumes' or 'volumeMounts', we search for
190
+ # item with the same name and merge it.
191
+ for new_volume in value:
192
+ new_volume_name = new_volume.get('name')
193
+ if new_volume_name is not None:
194
+ destination_volume = next(
195
+ (v for v in base_config[key]
196
+ if v.get('name') == new_volume_name), None)
197
+ if destination_volume is not None:
198
+ merge_k8s_configs(destination_volume, new_volume)
199
+ else:
200
+ base_config[key].append(new_volume)
201
+ else:
202
+ base_config[key].extend(value)
203
+ else:
204
+ base_config[key] = value
@@ -0,0 +1,48 @@
1
+ """Utils to check if the ssh control master should be disabled."""
2
+
3
+ from sky import sky_logging
4
+ from sky.utils import annotations
5
+ from sky.utils import subprocess_utils
6
+
7
+ logger = sky_logging.init_logger(__name__)
8
+
9
+
10
+ def is_tmp_9p_filesystem() -> bool:
11
+ """Check if the /tmp filesystem is 9p.
12
+
13
+ Returns:
14
+ bool: True if the /tmp filesystem is 9p, False otherwise.
15
+ """
16
+
17
+ result = subprocess_utils.run(['df', '-T', '/tmp'],
18
+ capture_output=True,
19
+ text=True,
20
+ shell=None,
21
+ check=False,
22
+ executable=None)
23
+
24
+ if result.returncode != 0:
25
+ return False
26
+
27
+ filesystem_infos = result.stdout.strip().split('\n')
28
+ if len(filesystem_infos) < 2:
29
+ return False
30
+ filesystem_types = filesystem_infos[1].split()
31
+ if len(filesystem_types) < 2:
32
+ return False
33
+ return filesystem_types[1].lower() == '9p'
34
+
35
+
36
+ @annotations.lru_cache(scope='global')
37
+ def should_disable_control_master() -> bool:
38
+ """Whether disable ssh control master based on file system.
39
+
40
+ Returns:
41
+ bool: True if the ssh control master should be disabled,
42
+ False otherwise.
43
+ """
44
+ if is_tmp_9p_filesystem():
45
+ return True
46
+ # there may be additional criteria to disable ssh control master
47
+ # in the future. They should be checked here
48
+ return False