skypilot-nightly 1.0.0.dev20250417__py3-none-any.whl → 1.0.0.dev20250422__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.
- sky/__init__.py +2 -2
- sky/adaptors/aws.py +2 -13
- sky/backends/backend_utils.py +28 -0
- sky/backends/wheel_utils.py +9 -0
- sky/cli.py +93 -24
- sky/client/cli.py +93 -24
- sky/client/common.py +10 -3
- sky/client/sdk.py +6 -3
- sky/clouds/aws.py +5 -5
- sky/clouds/service_catalog/data_fetchers/fetch_gcp.py +9 -9
- sky/dashboard/out/404.html +1 -0
- sky/dashboard/out/_next/static/2GsKhI8XKYj9B2969iIDf/_buildManifest.js +1 -0
- sky/dashboard/out/_next/static/2GsKhI8XKYj9B2969iIDf/_ssgManifest.js +1 -0
- sky/dashboard/out/_next/static/chunks/236-d437cf66e68a6f64.js +6 -0
- sky/dashboard/out/_next/static/chunks/312-c3c8845990db8ffc.js +15 -0
- sky/dashboard/out/_next/static/chunks/37-72fdc8f71d6e4784.js +6 -0
- sky/dashboard/out/_next/static/chunks/678-206dddca808e6d16.js +59 -0
- sky/dashboard/out/_next/static/chunks/845-2ea1cc63ba1f4067.js +1 -0
- sky/dashboard/out/_next/static/chunks/979-7cd0778078b9cfad.js +1 -0
- sky/dashboard/out/_next/static/chunks/fd9d1056-2821b0f0cabcd8bd.js +1 -0
- sky/dashboard/out/_next/static/chunks/framework-87d061ee6ed71b28.js +33 -0
- sky/dashboard/out/_next/static/chunks/main-app-241eb28595532291.js +1 -0
- sky/dashboard/out/_next/static/chunks/main-e0e2335212e72357.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/_app-3001e84c61acddfb.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/_error-1be831200e60c5c0.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-b09f7fbf6d5d74f6.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-b57ec043f09c5813.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/clusters-a93b93e10b8b074e.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/index-f9f039532ca8cbc4.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-ef2e0e91a9222cac.js +1 -0
- sky/dashboard/out/_next/static/chunks/pages/jobs-a75029b67aab6a2e.js +1 -0
- sky/dashboard/out/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js +1 -0
- sky/dashboard/out/_next/static/chunks/webpack-830f59b8404e96b8.js +1 -0
- sky/dashboard/out/_next/static/css/f3538cd90cfca88c.css +3 -0
- sky/dashboard/out/clusters/[cluster]/[job].html +1 -0
- sky/dashboard/out/clusters/[cluster].html +1 -0
- sky/dashboard/out/clusters.html +1 -0
- sky/dashboard/out/favicon.ico +0 -0
- sky/dashboard/out/index.html +1 -0
- sky/dashboard/out/jobs/[job].html +1 -0
- sky/dashboard/out/jobs.html +1 -0
- sky/dashboard/out/skypilot.svg +15 -0
- sky/dashboard/out/videos/cursor-small.mp4 +0 -0
- sky/data/data_transfer.py +2 -1
- sky/data/storage.py +24 -14
- sky/optimizer.py +7 -9
- sky/provision/provisioner.py +2 -1
- sky/provision/runpod/utils.py +32 -6
- sky/resources.py +11 -2
- sky/serve/__init__.py +2 -0
- sky/serve/autoscalers.py +6 -2
- sky/serve/client/sdk.py +61 -0
- sky/serve/replica_managers.py +6 -8
- sky/serve/serve_utils.py +33 -1
- sky/serve/server/core.py +187 -5
- sky/serve/server/server.py +28 -0
- sky/server/common.py +19 -1
- sky/server/constants.py +6 -0
- sky/server/requests/executor.py +4 -0
- sky/server/requests/payloads.py +27 -15
- sky/server/server.py +43 -0
- sky/setup_files/MANIFEST.in +1 -0
- sky/sky_logging.py +10 -0
- sky/skypilot_config.py +58 -37
- sky/templates/kubernetes-ray.yml.j2 +6 -2
- sky/utils/config_utils.py +0 -1
- sky/utils/controller_utils.py +0 -1
- {skypilot_nightly-1.0.0.dev20250417.dist-info → skypilot_nightly-1.0.0.dev20250422.dist-info}/METADATA +1 -1
- {skypilot_nightly-1.0.0.dev20250417.dist-info → skypilot_nightly-1.0.0.dev20250422.dist-info}/RECORD +73 -40
- {skypilot_nightly-1.0.0.dev20250417.dist-info → skypilot_nightly-1.0.0.dev20250422.dist-info}/WHEEL +1 -1
- {skypilot_nightly-1.0.0.dev20250417.dist-info → skypilot_nightly-1.0.0.dev20250422.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250417.dist-info → skypilot_nightly-1.0.0.dev20250422.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250417.dist-info → skypilot_nightly-1.0.0.dev20250422.dist-info}/top_level.txt +0 -0
sky/server/requests/payloads.py
CHANGED
@@ -6,7 +6,6 @@ with the backend functions. The benefit of having the default values in the
|
|
6
6
|
payloads is that a user can find the default values in the Restful API docs.
|
7
7
|
"""
|
8
8
|
import getpass
|
9
|
-
import json
|
10
9
|
import os
|
11
10
|
import typing
|
12
11
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
@@ -32,6 +31,19 @@ else:
|
|
32
31
|
|
33
32
|
logger = sky_logging.init_logger(__name__)
|
34
33
|
|
34
|
+
# These non-skypilot environment variables will be updated from the local
|
35
|
+
# environment on each request when running a local API server.
|
36
|
+
# We should avoid adding variables here, but we should include credential-
|
37
|
+
# related variables.
|
38
|
+
EXTERNAL_LOCAL_ENV_VARS = [
|
39
|
+
# Allow overriding the AWS authentication.
|
40
|
+
'AWS_PROFILE',
|
41
|
+
'AWS_ACCESS_KEY_ID',
|
42
|
+
'AWS_SECRET_ACCESS_KEY',
|
43
|
+
# Allow overriding the GCP authentication.
|
44
|
+
'GOOGLE_APPLICATION_CREDENTIALS',
|
45
|
+
]
|
46
|
+
|
35
47
|
|
36
48
|
@annotations.lru_cache(scope='global')
|
37
49
|
def request_body_env_vars() -> dict:
|
@@ -39,6 +51,8 @@ def request_body_env_vars() -> dict:
|
|
39
51
|
for env_var in os.environ:
|
40
52
|
if env_var.startswith(constants.SKYPILOT_ENV_VAR_PREFIX):
|
41
53
|
env_vars[env_var] = os.environ[env_var]
|
54
|
+
if common.is_api_server_local() and env_var in EXTERNAL_LOCAL_ENV_VARS:
|
55
|
+
env_vars[env_var] = os.environ[env_var]
|
42
56
|
env_vars[constants.USER_ID_ENV_VAR] = common_utils.get_user_hash()
|
43
57
|
env_vars[constants.USER_ENV_VAR] = os.getenv(constants.USER_ENV_VAR,
|
44
58
|
getpass.getuser())
|
@@ -47,7 +61,7 @@ def request_body_env_vars() -> dict:
|
|
47
61
|
# Remove the path to config file, as the config content is included in the
|
48
62
|
# request body and will be merged with the config on the server side.
|
49
63
|
env_vars.pop(skypilot_config.ENV_VAR_SKYPILOT_CONFIG, None)
|
50
|
-
env_vars.pop(skypilot_config.
|
64
|
+
env_vars.pop(skypilot_config.ENV_VAR_GLOBAL_CONFIG, None)
|
51
65
|
env_vars.pop(skypilot_config.ENV_VAR_PROJECT_CONFIG, None)
|
52
66
|
return env_vars
|
53
67
|
|
@@ -56,20 +70,9 @@ def get_override_skypilot_config_from_client() -> Dict[str, Any]:
|
|
56
70
|
"""Returns the override configs from the client."""
|
57
71
|
config = skypilot_config.to_dict()
|
58
72
|
# Remove the API server config, as we should not specify the SkyPilot
|
59
|
-
# server endpoint on the server side. This avoids the warning
|
73
|
+
# server endpoint on the server side. This avoids the warning at
|
74
|
+
# server-side.
|
60
75
|
config.pop_nested(('api_server',), default_value=None)
|
61
|
-
ignored_key_values = {}
|
62
|
-
for nested_key in constants.SKIPPED_CLIENT_OVERRIDE_KEYS:
|
63
|
-
value = config.pop_nested(nested_key, default_value=None)
|
64
|
-
if value is not None:
|
65
|
-
ignored_key_values['.'.join(nested_key)] = value
|
66
|
-
if ignored_key_values:
|
67
|
-
logger.debug(f'The following keys ({json.dumps(ignored_key_values)}) '
|
68
|
-
'are specified in the client SkyPilot config at '
|
69
|
-
f'{skypilot_config.loaded_config_path()!r}. '
|
70
|
-
'This will be ignored. If you want to specify it, '
|
71
|
-
'please modify it on server side or contact your '
|
72
|
-
'administrator.')
|
73
76
|
return config
|
74
77
|
|
75
78
|
|
@@ -420,6 +423,15 @@ class ServeLogsBody(RequestBody):
|
|
420
423
|
follow: bool = True
|
421
424
|
|
422
425
|
|
426
|
+
class ServeDownloadLogsBody(RequestBody):
|
427
|
+
"""The request body for the serve download logs endpoint."""
|
428
|
+
service_name: str
|
429
|
+
local_dir: str
|
430
|
+
targets: Optional[Union[str, serve.ServiceComponent,
|
431
|
+
List[Union[str, serve.ServiceComponent]]]]
|
432
|
+
replica_ids: Optional[List[int]] = None
|
433
|
+
|
434
|
+
|
423
435
|
class ServeStatusBody(RequestBody):
|
424
436
|
"""The request body for the serve status endpoint."""
|
425
437
|
service_names: Optional[Union[str, List[str]]]
|
sky/server/server.py
CHANGED
@@ -150,7 +150,21 @@ async def lifespan(app: fastapi.FastAPI): # pylint: disable=redefined-outer-nam
|
|
150
150
|
# Shutdown: Add any cleanup code here if needed
|
151
151
|
|
152
152
|
|
153
|
+
# Add a new middleware class to handle /internal/dashboard prefix
|
154
|
+
class InternalDashboardPrefixMiddleware(
|
155
|
+
starlette.middleware.base.BaseHTTPMiddleware):
|
156
|
+
"""Middleware to handle /internal/dashboard prefix in requests."""
|
157
|
+
|
158
|
+
async def dispatch(self, request: fastapi.Request, call_next):
|
159
|
+
path = request.url.path
|
160
|
+
if path.startswith('/internal/dashboard/'):
|
161
|
+
# Remove /internal/dashboard prefix and update request scope
|
162
|
+
request.scope['path'] = path.replace('/internal/dashboard/', '/', 1)
|
163
|
+
return await call_next(request)
|
164
|
+
|
165
|
+
|
153
166
|
app = fastapi.FastAPI(prefix='/api/v1', debug=True, lifespan=lifespan)
|
167
|
+
app.add_middleware(InternalDashboardPrefixMiddleware)
|
154
168
|
app.add_middleware(
|
155
169
|
cors.CORSMiddleware,
|
156
170
|
# TODO(zhwu): in production deployment, we should restrict the allowed
|
@@ -1101,6 +1115,35 @@ async def complete_storage_name(incomplete: str,) -> List[str]:
|
|
1101
1115
|
return global_user_state.get_storage_names_start_with(incomplete)
|
1102
1116
|
|
1103
1117
|
|
1118
|
+
# Add a route to serve static files
|
1119
|
+
@app.get('/{full_path:path}')
|
1120
|
+
async def serve_static_or_dashboard(full_path: str):
|
1121
|
+
"""Serves static files for any unmatched routes.
|
1122
|
+
|
1123
|
+
Handles the /dashboard prefix from Next.js configuration.
|
1124
|
+
"""
|
1125
|
+
# Check if the path starts with 'dashboard/' and remove it if it does
|
1126
|
+
if full_path.startswith('dashboard/'):
|
1127
|
+
full_path = full_path[len('dashboard/'):]
|
1128
|
+
|
1129
|
+
# Try to serve the file directly from the out directory first
|
1130
|
+
file_path = os.path.join(server_constants.DASHBOARD_DIR, full_path)
|
1131
|
+
if os.path.isfile(file_path):
|
1132
|
+
return fastapi.responses.FileResponse(file_path)
|
1133
|
+
|
1134
|
+
# If file not found, serve the index.html for client-side routing.
|
1135
|
+
# For example, the non-matched arbitrary route (/ or /test) from
|
1136
|
+
# client will be redirected to the index.html.
|
1137
|
+
index_path = os.path.join(server_constants.DASHBOARD_DIR, 'index.html')
|
1138
|
+
try:
|
1139
|
+
with open(index_path, 'r', encoding='utf-8') as f:
|
1140
|
+
content = f.read()
|
1141
|
+
return fastapi.responses.HTMLResponse(content=content)
|
1142
|
+
except Exception as e:
|
1143
|
+
logger.error(f'Error serving dashboard: {e}')
|
1144
|
+
raise fastapi.HTTPException(status_code=500, detail=str(e))
|
1145
|
+
|
1146
|
+
|
1104
1147
|
if __name__ == '__main__':
|
1105
1148
|
import uvicorn
|
1106
1149
|
|
sky/setup_files/MANIFEST.in
CHANGED
sky/sky_logging.py
CHANGED
@@ -18,6 +18,12 @@ _FORMAT = '%(levelname).1s %(asctime)s %(filename)s:%(lineno)d] %(message)s'
|
|
18
18
|
_DATE_FORMAT = '%m-%d %H:%M:%S'
|
19
19
|
_SENSITIVE_LOGGER = ['sky.provisioner', 'sky.optimizer']
|
20
20
|
|
21
|
+
DEBUG = logging.DEBUG
|
22
|
+
INFO = logging.INFO
|
23
|
+
WARNING = logging.WARNING
|
24
|
+
ERROR = logging.ERROR
|
25
|
+
CRITICAL = logging.CRITICAL
|
26
|
+
|
21
27
|
|
22
28
|
def _show_logging_prefix():
|
23
29
|
return env_options.Options.SHOW_DEBUG_INFO.get(
|
@@ -127,6 +133,10 @@ def set_logging_level(logger: str, level: int):
|
|
127
133
|
logger.setLevel(original_level)
|
128
134
|
|
129
135
|
|
136
|
+
def logging_enabled(logger: logging.Logger, level: int) -> bool:
|
137
|
+
return logger.level <= level
|
138
|
+
|
139
|
+
|
130
140
|
@contextlib.contextmanager
|
131
141
|
def silent():
|
132
142
|
"""Make all sky_logging.print() and logger.{info, warning...} silent.
|
sky/skypilot_config.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""Immutable user configurations (EXPERIMENTAL).
|
2
2
|
|
3
|
-
On module import, we attempt to parse the config located at
|
3
|
+
On module import, we attempt to parse the config located at _GLOBAL_CONFIG_PATH
|
4
4
|
(default: ~/.sky/config.yaml). Caller can then use
|
5
5
|
|
6
6
|
>> skypilot_config.loaded()
|
@@ -50,8 +50,8 @@ then:
|
|
50
50
|
"""
|
51
51
|
import contextlib
|
52
52
|
import copy
|
53
|
+
import json
|
53
54
|
import os
|
54
|
-
import pprint
|
55
55
|
import threading
|
56
56
|
import typing
|
57
57
|
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
@@ -80,8 +80,8 @@ logger = sky_logging.init_logger(__name__)
|
|
80
80
|
# path as the config file. Do not use any other config files.
|
81
81
|
# This behavior is subject to change and should not be relied on by users.
|
82
82
|
# Else,
|
83
|
-
# (1) If env var {
|
84
|
-
# config file. Else, use the default path {
|
83
|
+
# (1) If env var {ENV_VAR_GLOBAL_CONFIG} exists, use its path as the user
|
84
|
+
# config file. Else, use the default path {_GLOBAL_CONFIG_PATH}.
|
85
85
|
# (2) If env var {ENV_VAR_PROJECT_CONFIG} exists, use its path as the project
|
86
86
|
# config file. Else, use the default path {_PROJECT_CONFIG_PATH}.
|
87
87
|
# (3) Override any config keys in (1) with the ones in (2).
|
@@ -97,21 +97,16 @@ logger = sky_logging.init_logger(__name__)
|
|
97
97
|
# use the same config file.
|
98
98
|
ENV_VAR_SKYPILOT_CONFIG = f'{constants.SKYPILOT_ENV_VAR_PREFIX}CONFIG'
|
99
99
|
|
100
|
-
#
|
101
|
-
#
|
102
|
-
|
100
|
+
# Environment variables for setting non-default server and user
|
101
|
+
# config files.
|
102
|
+
ENV_VAR_GLOBAL_CONFIG = f'{constants.SKYPILOT_ENV_VAR_PREFIX}GLOBAL_CONFIG'
|
103
|
+
# Environment variables for setting non-default project config files.
|
103
104
|
ENV_VAR_PROJECT_CONFIG = f'{constants.SKYPILOT_ENV_VAR_PREFIX}PROJECT_CONFIG'
|
104
105
|
|
105
|
-
# (Used by server) Environment variable for setting the server config file.
|
106
|
-
ENV_VAR_SERVER_CONFIG = f'{constants.SKYPILOT_ENV_VAR_PREFIX}SERVER_CONFIG'
|
107
|
-
|
108
106
|
# Path to the client config files.
|
109
|
-
|
107
|
+
_GLOBAL_CONFIG_PATH = '~/.sky/config.yaml'
|
110
108
|
_PROJECT_CONFIG_PATH = '.sky.yaml'
|
111
109
|
|
112
|
-
# Path to the server config file.
|
113
|
-
_SERVER_CONFIG_PATH = _USER_CONFIG_PATH
|
114
|
-
|
115
110
|
# The loaded config.
|
116
111
|
_dict = config_utils.Config()
|
117
112
|
_loaded_config_path: Optional[str] = None
|
@@ -121,24 +116,24 @@ _reload_config_lock = threading.Lock()
|
|
121
116
|
|
122
117
|
def get_user_config_path() -> str:
|
123
118
|
"""Returns the path to the user config file."""
|
124
|
-
return
|
119
|
+
return _GLOBAL_CONFIG_PATH
|
125
120
|
|
126
121
|
|
127
122
|
def get_user_config() -> config_utils.Config:
|
128
123
|
"""Returns the user config."""
|
129
124
|
# find the user config file
|
130
|
-
user_config_path = _get_config_file_path(
|
125
|
+
user_config_path = _get_config_file_path(ENV_VAR_GLOBAL_CONFIG)
|
131
126
|
if user_config_path:
|
132
127
|
logger.debug('using user config file specified by '
|
133
|
-
f'{
|
128
|
+
f'{ENV_VAR_GLOBAL_CONFIG}: {user_config_path}')
|
134
129
|
user_config_path = os.path.expanduser(user_config_path)
|
135
130
|
if not os.path.exists(user_config_path):
|
136
131
|
with ux_utils.print_exception_no_traceback():
|
137
132
|
raise FileNotFoundError(
|
138
133
|
'Config file specified by env var '
|
139
|
-
f'{
|
134
|
+
f'{ENV_VAR_GLOBAL_CONFIG} ({user_config_path!r}) '
|
140
135
|
'does not exist. Please double check the path or unset the '
|
141
|
-
f'env var: unset {
|
136
|
+
f'env var: unset {ENV_VAR_GLOBAL_CONFIG}')
|
142
137
|
else:
|
143
138
|
user_config_path = get_user_config_path()
|
144
139
|
logger.debug(f'using default user config file: {user_config_path}')
|
@@ -185,20 +180,20 @@ def _get_project_config() -> config_utils.Config:
|
|
185
180
|
def get_server_config() -> config_utils.Config:
|
186
181
|
"""Returns the server config."""
|
187
182
|
# find the server config file
|
188
|
-
server_config_path = _get_config_file_path(
|
183
|
+
server_config_path = _get_config_file_path(ENV_VAR_GLOBAL_CONFIG)
|
189
184
|
if server_config_path:
|
190
185
|
logger.debug('using server config file specified by '
|
191
|
-
f'{
|
186
|
+
f'{ENV_VAR_GLOBAL_CONFIG}: {server_config_path}')
|
192
187
|
server_config_path = os.path.expanduser(server_config_path)
|
193
188
|
if not os.path.exists(server_config_path):
|
194
189
|
with ux_utils.print_exception_no_traceback():
|
195
190
|
raise FileNotFoundError(
|
196
191
|
'Config file specified by env var '
|
197
|
-
f'{
|
192
|
+
f'{ENV_VAR_GLOBAL_CONFIG} ({server_config_path!r}) '
|
198
193
|
'does not exist. Please double check the path or unset the '
|
199
|
-
f'env var: unset {
|
194
|
+
f'env var: unset {ENV_VAR_GLOBAL_CONFIG}')
|
200
195
|
else:
|
201
|
-
server_config_path =
|
196
|
+
server_config_path = _GLOBAL_CONFIG_PATH
|
202
197
|
logger.debug(f'using default server config file: {server_config_path}')
|
203
198
|
server_config_path = os.path.expanduser(server_config_path)
|
204
199
|
|
@@ -314,8 +309,9 @@ def _parse_config_file(config_path: str) -> config_utils.Config:
|
|
314
309
|
try:
|
315
310
|
config_dict = common_utils.read_yaml(config_path)
|
316
311
|
config = config_utils.Config.from_dict(config_dict)
|
317
|
-
logger.
|
318
|
-
f'Config loaded from {config_path}:\n
|
312
|
+
if sky_logging.logging_enabled(logger, sky_logging.DEBUG):
|
313
|
+
logger.debug(f'Config loaded from {config_path}:\n'
|
314
|
+
f'{common_utils.dump_yaml_str(dict(config))}')
|
319
315
|
except yaml.YAMLError as e:
|
320
316
|
logger.error(f'Error in loading config file ({config_path}):', e)
|
321
317
|
if config:
|
@@ -359,7 +355,10 @@ def _reload_config_as_server() -> None:
|
|
359
355
|
for override in overrides:
|
360
356
|
overlaid_server_config = overlay_skypilot_config(
|
361
357
|
original_config=overlaid_server_config, override_configs=override)
|
362
|
-
|
358
|
+
if sky_logging.logging_enabled(logger, sky_logging.DEBUG):
|
359
|
+
logger.debug(
|
360
|
+
f'server config: \n'
|
361
|
+
f'{common_utils.dump_yaml_str(dict(overlaid_server_config))}')
|
363
362
|
_dict = overlaid_server_config
|
364
363
|
|
365
364
|
|
@@ -381,7 +380,10 @@ def _reload_config_as_client() -> None:
|
|
381
380
|
for override in overrides:
|
382
381
|
overlaid_client_config = overlay_skypilot_config(
|
383
382
|
original_config=overlaid_client_config, override_configs=override)
|
384
|
-
|
383
|
+
if sky_logging.logging_enabled(logger, sky_logging.DEBUG):
|
384
|
+
logger.debug(
|
385
|
+
f'client config (before task and CLI overrides): \n'
|
386
|
+
f'{common_utils.dump_yaml_str(dict(overlaid_client_config))}')
|
385
387
|
_dict = overlaid_client_config
|
386
388
|
|
387
389
|
|
@@ -414,10 +416,26 @@ def override_skypilot_config(
|
|
414
416
|
yield
|
415
417
|
return
|
416
418
|
original_config = _dict
|
419
|
+
override_configs = config_utils.Config(override_configs)
|
420
|
+
disallowed_diff_keys = []
|
421
|
+
for key in constants.SKIPPED_CLIENT_OVERRIDE_KEYS:
|
422
|
+
value = override_configs.pop_nested(key, default_value=None)
|
423
|
+
if (value is not None and
|
424
|
+
value != original_config.get_nested(key, default_value=None)):
|
425
|
+
disallowed_diff_keys.append('.'.join(key))
|
426
|
+
# Only warn if there is a diff in disallowed override keys, as the client
|
427
|
+
# use the same config file when connecting to a local server.
|
428
|
+
if disallowed_diff_keys:
|
429
|
+
logger.warning(
|
430
|
+
f'The following keys ({json.dumps(disallowed_diff_keys)}) have '
|
431
|
+
'different values in the client SkyPilot config with the server '
|
432
|
+
'and will be ignored. Remove these keys to disable this warning. '
|
433
|
+
'If you want to specify it, please modify it on server side or '
|
434
|
+
'contact your administrator.')
|
417
435
|
config = _dict.get_nested(
|
418
436
|
keys=tuple(),
|
419
437
|
default_value=None,
|
420
|
-
override_configs=override_configs,
|
438
|
+
override_configs=dict(override_configs),
|
421
439
|
allowed_override_keys=None,
|
422
440
|
disallowed_override_keys=constants.SKIPPED_CLIENT_OVERRIDE_KEYS)
|
423
441
|
try:
|
@@ -439,14 +457,14 @@ def override_skypilot_config(
|
|
439
457
|
'=== SkyPilot config on API server ===\n'
|
440
458
|
f'{common_utils.dump_yaml_str(dict(original_config))}\n'
|
441
459
|
'=== Your local SkyPilot config ===\n'
|
442
|
-
f'{common_utils.dump_yaml_str(override_configs)}\n'
|
460
|
+
f'{common_utils.dump_yaml_str(dict(override_configs))}\n'
|
443
461
|
f'Details: {e}') from e
|
444
462
|
finally:
|
445
463
|
_dict = original_config
|
446
464
|
_config_overridden = False
|
447
465
|
|
448
466
|
|
449
|
-
def _compose_cli_config(cli_config: Optional[str]
|
467
|
+
def _compose_cli_config(cli_config: Optional[List[str]]) -> config_utils.Config:
|
450
468
|
"""Composes the skypilot CLI config.
|
451
469
|
CLI config can either be:
|
452
470
|
- A path to a config file
|
@@ -457,18 +475,19 @@ def _compose_cli_config(cli_config: Optional[str],) -> config_utils.Config:
|
|
457
475
|
return config_utils.Config()
|
458
476
|
|
459
477
|
config_source = 'CLI'
|
460
|
-
maybe_config_path = os.path.expanduser(cli_config)
|
461
478
|
try:
|
479
|
+
maybe_config_path = os.path.expanduser(cli_config[0])
|
462
480
|
if os.path.isfile(maybe_config_path):
|
481
|
+
if len(cli_config) != 1:
|
482
|
+
raise ValueError(
|
483
|
+
'Cannot use multiple --config flags with a config file.')
|
463
484
|
config_source = maybe_config_path
|
464
485
|
# cli_config is a path to a config file
|
465
486
|
parsed_config = OmegaConf.to_object(
|
466
487
|
OmegaConf.load(maybe_config_path))
|
467
488
|
else: # cli_config is a comma-separated list of key-value pairs
|
468
|
-
variables: List[str] = []
|
469
|
-
variables = cli_config.split(',')
|
470
489
|
parsed_config = OmegaConf.to_object(
|
471
|
-
OmegaConf.from_dotlist(
|
490
|
+
OmegaConf.from_dotlist(cli_config))
|
472
491
|
_validate_config(parsed_config, config_source)
|
473
492
|
except ValueError as e:
|
474
493
|
raise ValueError(f'Invalid config override: {cli_config}. '
|
@@ -479,7 +498,7 @@ def _compose_cli_config(cli_config: Optional[str],) -> config_utils.Config:
|
|
479
498
|
return parsed_config
|
480
499
|
|
481
500
|
|
482
|
-
def apply_cli_config(cli_config: Optional[str]) -> Dict[str, Any]:
|
501
|
+
def apply_cli_config(cli_config: Optional[List[str]]) -> Dict[str, Any]:
|
483
502
|
"""Applies the CLI provided config.
|
484
503
|
SAFETY:
|
485
504
|
This function directly modifies the global _dict variable.
|
@@ -491,7 +510,9 @@ def apply_cli_config(cli_config: Optional[str]) -> Dict[str, Any]:
|
|
491
510
|
"""
|
492
511
|
global _dict
|
493
512
|
parsed_config = _compose_cli_config(cli_config)
|
494
|
-
|
513
|
+
if sky_logging.logging_enabled(logger, sky_logging.DEBUG):
|
514
|
+
logger.debug(f'applying following CLI overrides: \n'
|
515
|
+
f'{common_utils.dump_yaml_str(dict(parsed_config))}')
|
495
516
|
_dict = overlay_skypilot_config(original_config=_dict,
|
496
517
|
override_configs=parsed_config)
|
497
518
|
return parsed_config
|
@@ -384,8 +384,12 @@ available_node_types:
|
|
384
384
|
set +e
|
385
385
|
|
386
386
|
if [ ! -z "$MISSING_PACKAGES" ]; then
|
387
|
-
|
388
|
-
|
387
|
+
# Install missing packages individually to avoid failure installation breaks the whole install process,
|
388
|
+
# e.g. fuse3 is not available on some distributions.
|
389
|
+
echo "Installing missing packages individually: $MISSING_PACKAGES";
|
390
|
+
for pkg in $MISSING_PACKAGES; do
|
391
|
+
DEBIAN_FRONTEND=noninteractive $(prefix_cmd) apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" $pkg || echo "Optional package $pkg installation failed, skip.";
|
392
|
+
done
|
389
393
|
fi;
|
390
394
|
|
391
395
|
{% if k8s_fuse_device_required %}
|
sky/utils/config_utils.py
CHANGED
sky/utils/controller_utils.py
CHANGED