skypilot-nightly 1.0.0.dev20250603__py3-none-any.whl → 1.0.0.dev20250604__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 +3 -3
- sky/adaptors/kubernetes.py +8 -0
- sky/backends/backend_utils.py +1 -0
- sky/backends/cloud_vm_ray_backend.py +8 -4
- sky/{clouds/service_catalog → catalog}/__init__.py +6 -17
- sky/{clouds/service_catalog → catalog}/aws_catalog.py +3 -3
- sky/{clouds/service_catalog → catalog}/azure_catalog.py +2 -2
- sky/{clouds/service_catalog → catalog}/common.py +10 -8
- sky/{clouds/service_catalog → catalog}/cudo_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/data_fetchers/analyze.py +1 -1
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_aws.py +1 -1
- sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_vsphere.py +1 -1
- sky/{clouds/service_catalog → catalog}/do_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/fluidstack_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/gcp_catalog.py +2 -2
- sky/{clouds/service_catalog → catalog}/ibm_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/kubernetes_catalog.py +2 -2
- sky/{clouds/service_catalog → catalog}/lambda_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/nebius_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/oci_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/paperspace_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/runpod_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/scp_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/ssh_catalog.py +3 -3
- sky/{clouds/service_catalog → catalog}/vast_catalog.py +1 -1
- sky/{clouds/service_catalog → catalog}/vsphere_catalog.py +1 -1
- sky/cli.py +7 -6
- sky/client/cli.py +7 -6
- sky/clouds/aws.py +41 -40
- sky/clouds/azure.py +31 -34
- sky/clouds/cloud.py +8 -8
- sky/clouds/cudo.py +26 -26
- sky/clouds/do.py +24 -24
- sky/clouds/fluidstack.py +27 -29
- sky/clouds/gcp.py +42 -42
- sky/clouds/ibm.py +26 -26
- sky/clouds/kubernetes.py +24 -12
- sky/clouds/lambda_cloud.py +28 -30
- sky/clouds/nebius.py +26 -28
- sky/clouds/oci.py +32 -32
- sky/clouds/paperspace.py +24 -26
- sky/clouds/runpod.py +26 -28
- sky/clouds/scp.py +37 -36
- sky/clouds/utils/gcp_utils.py +3 -2
- sky/clouds/vast.py +27 -27
- sky/clouds/vsphere.py +12 -15
- sky/core.py +2 -2
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
- sky/dashboard/out/clusters/[cluster].html +1 -1
- sky/dashboard/out/clusters.html +1 -1
- sky/dashboard/out/config.html +1 -1
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/infra/[context].html +1 -1
- sky/dashboard/out/infra.html +1 -1
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/dashboard/out/users.html +1 -1
- sky/dashboard/out/workspace/new.html +1 -1
- sky/dashboard/out/workspaces/[name].html +1 -1
- sky/dashboard/out/workspaces.html +1 -1
- sky/data/storage_utils.py +5 -2
- sky/global_user_state.py +1 -3
- sky/jobs/server/core.py +1 -1
- sky/optimizer.py +1 -1
- sky/provision/cudo/cudo_machine_type.py +1 -1
- sky/provision/kubernetes/utils.py +35 -22
- sky/provision/vast/utils.py +1 -1
- sky/provision/vsphere/common/vim_utils.py +1 -2
- sky/provision/vsphere/instance.py +1 -1
- sky/provision/vsphere/vsphere_utils.py +7 -11
- sky/resources.py +2 -2
- sky/serve/server/core.py +1 -1
- sky/server/constants.py +1 -1
- sky/server/requests/executor.py +4 -1
- sky/server/requests/payloads.py +16 -0
- sky/server/requests/serializers/decoders.py +1 -1
- sky/server/server.py +3 -3
- sky/server/stream_utils.py +2 -38
- sky/skypilot_config.py +88 -37
- sky/usage/usage_lib.py +4 -3
- sky/utils/accelerator_registry.py +3 -3
- sky/utils/kubernetes/deploy_remote_cluster.py +2 -1
- sky/utils/schemas.py +6 -9
- {skypilot_nightly-1.0.0.dev20250603.dist-info → skypilot_nightly-1.0.0.dev20250604.dist-info}/METADATA +1 -1
- {skypilot_nightly-1.0.0.dev20250603.dist-info → skypilot_nightly-1.0.0.dev20250604.dist-info}/RECORD +102 -102
- /sky/{clouds/service_catalog → catalog}/config.py +0 -0
- /sky/{clouds/service_catalog → catalog}/constants.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/__init__.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_azure.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_cudo.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_fluidstack.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_gcp.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_ibm.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_lambda_cloud.py +0 -0
- /sky/{clouds/service_catalog → catalog}/data_fetchers/fetch_vast.py +0 -0
- /sky/dashboard/out/_next/static/{zTAFq_Iv6_yxQj3fXvJWR → vWwfD3jOky5J5jULHp8JT}/_buildManifest.js +0 -0
- /sky/dashboard/out/_next/static/{zTAFq_Iv6_yxQj3fXvJWR → vWwfD3jOky5J5jULHp8JT}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250603.dist-info → skypilot_nightly-1.0.0.dev20250604.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20250603.dist-info → skypilot_nightly-1.0.0.dev20250604.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250603.dist-info → skypilot_nightly-1.0.0.dev20250604.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250603.dist-info → skypilot_nightly-1.0.0.dev20250604.dist-info}/top_level.txt +0 -0
@@ -1384,7 +1384,7 @@ def check_credentials(context: Optional[str],
|
|
1384
1384
|
# Check if $KUBECONFIG envvar consists of multiple paths. We run this before
|
1385
1385
|
# optional checks.
|
1386
1386
|
try:
|
1387
|
-
_ =
|
1387
|
+
_ = get_kubeconfig_paths()
|
1388
1388
|
except ValueError as e:
|
1389
1389
|
return False, f'{common_utils.format_exception(e, use_bracket=True)}'
|
1390
1390
|
|
@@ -1537,15 +1537,11 @@ def is_kubeconfig_exec_auth(
|
|
1537
1537
|
raise ValueError(f'Kubernetes context {context!r} not found.')
|
1538
1538
|
target_username = context_obj['context']['user']
|
1539
1539
|
|
1540
|
-
#
|
1541
|
-
|
1542
|
-
|
1543
|
-
kubeconfig_path = _get_kubeconfig_path()
|
1544
|
-
|
1545
|
-
# Load the kubeconfig file as a dictionary
|
1546
|
-
with open(kubeconfig_path, 'r', encoding='utf-8') as f:
|
1547
|
-
kubeconfig = yaml.safe_load(f)
|
1540
|
+
# Load the kubeconfig for the context
|
1541
|
+
kubeconfig_text = _get_kubeconfig_text_for_context(context)
|
1542
|
+
kubeconfig = yaml.safe_load(kubeconfig_text)
|
1548
1543
|
|
1544
|
+
# Get the user details
|
1549
1545
|
user_details = kubeconfig['users']
|
1550
1546
|
|
1551
1547
|
# Find user matching the target username
|
@@ -1573,6 +1569,27 @@ def is_kubeconfig_exec_auth(
|
|
1573
1569
|
return False, None
|
1574
1570
|
|
1575
1571
|
|
1572
|
+
def _get_kubeconfig_text_for_context(context: Optional[str] = None) -> str:
|
1573
|
+
"""Get the kubeconfig text for the given context.
|
1574
|
+
|
1575
|
+
The kubeconfig might be multiple files, this function use kubectl to
|
1576
|
+
handle merging automatically.
|
1577
|
+
"""
|
1578
|
+
command = 'kubectl config view --minify'
|
1579
|
+
if context is not None:
|
1580
|
+
command += f' --context={context}'
|
1581
|
+
proc = subprocess.run(command,
|
1582
|
+
shell=True,
|
1583
|
+
check=False,
|
1584
|
+
stdout=subprocess.PIPE,
|
1585
|
+
stderr=subprocess.PIPE)
|
1586
|
+
if proc.returncode != 0:
|
1587
|
+
raise RuntimeError(
|
1588
|
+
f'Failed to get kubeconfig text for context {context}: {proc.stderr.decode("utf-8")}'
|
1589
|
+
)
|
1590
|
+
return proc.stdout.decode('utf-8')
|
1591
|
+
|
1592
|
+
|
1576
1593
|
@annotations.lru_cache(scope='request')
|
1577
1594
|
def get_current_kube_config_context_name() -> Optional[str]:
|
1578
1595
|
"""Get the current kubernetes context from the kubeconfig file
|
@@ -3100,18 +3117,14 @@ def get_gpu_resource_key():
|
|
3100
3117
|
return os.getenv('CUSTOM_GPU_RESOURCE_KEY', default=GPU_RESOURCE_KEY)
|
3101
3118
|
|
3102
3119
|
|
3103
|
-
def
|
3104
|
-
"""Get the path to the kubeconfig
|
3120
|
+
def get_kubeconfig_paths() -> List[str]:
|
3121
|
+
"""Get the path to the kubeconfig files.
|
3105
3122
|
Parses `KUBECONFIG` env var if present, else uses the default path.
|
3106
|
-
Currently, specifying multiple KUBECONFIG paths in the envvar is not
|
3107
|
-
allowed, hence will raise a ValueError.
|
3108
3123
|
"""
|
3109
|
-
|
3110
|
-
|
3111
|
-
|
3112
|
-
|
3113
|
-
|
3114
|
-
|
3115
|
-
|
3116
|
-
f'path(s) are {kubeconfig_path}.')
|
3117
|
-
return kubeconfig_path
|
3124
|
+
# We should always use the latest KUBECONFIG environment variable to
|
3125
|
+
# make sure env var overrides get respected.
|
3126
|
+
paths = os.getenv('KUBECONFIG', kubernetes.DEFAULT_KUBECONFIG_PATH)
|
3127
|
+
expanded = []
|
3128
|
+
for path in paths.split(kubernetes.ENV_KUBECONFIG_PATH_SEPARATOR):
|
3129
|
+
expanded.append(os.path.expanduser(path))
|
3130
|
+
return expanded
|
sky/provision/vast/utils.py
CHANGED
@@ -78,7 +78,7 @@ def launch(name: str, instance_type: str, region: str, disk_size: int,
|
|
78
78
|
amount of memory.
|
79
79
|
|
80
80
|
* Vast instance types are an invention for skypilot. Refer to
|
81
|
-
|
81
|
+
catalog/vast_catalog.py for the current construction
|
82
82
|
of the type.
|
83
83
|
|
84
84
|
"""
|
@@ -8,8 +8,7 @@ from typing import List
|
|
8
8
|
|
9
9
|
from sky import sky_logging
|
10
10
|
from sky.adaptors import vsphere as vsphere_adaptor
|
11
|
-
from sky.
|
12
|
-
get_accelerators_from_csv)
|
11
|
+
from sky.catalog.data_fetchers.fetch_vsphere import get_accelerators_from_csv
|
13
12
|
|
14
13
|
logger = sky_logging.init_logger(__name__)
|
15
14
|
DISPLAY_CONTROLLER_CLASS_ID_PREFIXES = ['03']
|
@@ -6,7 +6,7 @@ from typing import Any, Dict, List, Optional
|
|
6
6
|
from sky import sky_logging
|
7
7
|
from sky.adaptors import common as adaptors_common
|
8
8
|
from sky.adaptors import vsphere as vsphere_adaptor
|
9
|
-
from sky.
|
9
|
+
from sky.catalog.common import get_catalog_path
|
10
10
|
from sky.provision import common
|
11
11
|
from sky.provision.vsphere import vsphere_utils
|
12
12
|
from sky.provision.vsphere.common import custom_script as custom_script_lib
|
@@ -10,18 +10,14 @@ from sky import exceptions
|
|
10
10
|
from sky import sky_logging
|
11
11
|
from sky.adaptors import common as adaptors_common
|
12
12
|
from sky.adaptors import vsphere as vsphere_adaptor
|
13
|
-
from sky.
|
14
|
-
from sky.
|
15
|
-
from sky.
|
16
|
-
|
17
|
-
from sky.
|
18
|
-
|
19
|
-
from sky.clouds.service_catalog.data_fetchers.fetch_vsphere import (
|
20
|
-
initialize_images_csv)
|
21
|
-
from sky.clouds.service_catalog.data_fetchers.fetch_vsphere import (
|
13
|
+
from sky.catalog import vsphere_catalog
|
14
|
+
from sky.catalog.common import get_catalog_path
|
15
|
+
from sky.catalog.data_fetchers.fetch_vsphere import initialize_accelerators_csv
|
16
|
+
from sky.catalog.data_fetchers.fetch_vsphere import initialize_hosts_csv
|
17
|
+
from sky.catalog.data_fetchers.fetch_vsphere import initialize_images_csv
|
18
|
+
from sky.catalog.data_fetchers.fetch_vsphere import (
|
22
19
|
initialize_instance_image_mapping_csv)
|
23
|
-
from sky.
|
24
|
-
initialize_vms_csv)
|
20
|
+
from sky.catalog.data_fetchers.fetch_vsphere import initialize_vms_csv
|
25
21
|
from sky.provision.vsphere.common import vim_utils
|
26
22
|
from sky.provision.vsphere.common.cls_api_client import ClsApiClient
|
27
23
|
from sky.provision.vsphere.common.cls_api_helper import ClsApiHelper
|
sky/resources.py
CHANGED
@@ -7,13 +7,13 @@ from typing import Any, Dict, List, Literal, Optional, Set, Tuple, Union
|
|
7
7
|
import colorama
|
8
8
|
|
9
9
|
import sky
|
10
|
+
from sky import catalog
|
10
11
|
from sky import check as sky_check
|
11
12
|
from sky import clouds
|
12
13
|
from sky import exceptions
|
13
14
|
from sky import sky_logging
|
14
15
|
from sky import skypilot_config
|
15
16
|
from sky.clouds import cloud as sky_cloud
|
16
|
-
from sky.clouds import service_catalog
|
17
17
|
from sky.provision import docker_utils
|
18
18
|
from sky.provision.gcp import constants as gcp_constants
|
19
19
|
from sky.provision.kubernetes import utils as kubernetes_utils
|
@@ -379,7 +379,7 @@ class Resources:
|
|
379
379
|
# if it fails to fetch some account specific catalog information (e.g., AWS
|
380
380
|
# zone mapping). It is fine to use the default catalog as this function is
|
381
381
|
# only for display purposes.
|
382
|
-
@
|
382
|
+
@catalog.fallback_to_default_catalog
|
383
383
|
def __repr__(self) -> str:
|
384
384
|
"""Returns a string representation for display.
|
385
385
|
|
sky/serve/server/core.py
CHANGED
@@ -17,7 +17,7 @@ from sky import sky_logging
|
|
17
17
|
from sky import skypilot_config
|
18
18
|
from sky import task as task_lib
|
19
19
|
from sky.backends import backend_utils
|
20
|
-
from sky.
|
20
|
+
from sky.catalog import common as service_catalog_common
|
21
21
|
from sky.serve import constants as serve_constants
|
22
22
|
from sky.serve import serve_state
|
23
23
|
from sky.serve import serve_utils
|
sky/server/constants.py
CHANGED
@@ -7,7 +7,7 @@ from sky.skylet import constants
|
|
7
7
|
# API server version, whenever there is a change in API server that requires a
|
8
8
|
# restart of the local API server or error out when the client does not match
|
9
9
|
# the server version.
|
10
|
-
API_VERSION = '
|
10
|
+
API_VERSION = '8'
|
11
11
|
|
12
12
|
# Prefix for API request names.
|
13
13
|
REQUEST_NAME_PREFIX = 'sky.'
|
sky/server/requests/executor.py
CHANGED
@@ -239,8 +239,11 @@ def override_request_env_and_config(
|
|
239
239
|
client_command=request_body.entrypoint_command,
|
240
240
|
using_remote_api_server=request_body.using_remote_api_server)
|
241
241
|
try:
|
242
|
+
logger.debug(
|
243
|
+
f'override path: {request_body.override_skypilot_config_path}')
|
242
244
|
with skypilot_config.override_skypilot_config(
|
243
|
-
request_body.override_skypilot_config
|
245
|
+
request_body.override_skypilot_config,
|
246
|
+
request_body.override_skypilot_config_path):
|
244
247
|
yield
|
245
248
|
finally:
|
246
249
|
# We need to call the save_timeline() since atexit will not be
|
sky/server/requests/payloads.py
CHANGED
@@ -82,6 +82,17 @@ def get_override_skypilot_config_from_client() -> Dict[str, Any]:
|
|
82
82
|
return config
|
83
83
|
|
84
84
|
|
85
|
+
def get_override_skypilot_config_path_from_client() -> Optional[str]:
|
86
|
+
"""Returns the override config path from the client."""
|
87
|
+
if annotations.is_on_api_server:
|
88
|
+
return None
|
89
|
+
# Currently, we don't need to check if the client-side config
|
90
|
+
# has been overridden because we only deal with cases where
|
91
|
+
# client has a project-level config/changed config and the
|
92
|
+
# api server has a different config.
|
93
|
+
return skypilot_config.loaded_config_path_serialized()
|
94
|
+
|
95
|
+
|
85
96
|
class RequestBody(pydantic.BaseModel):
|
86
97
|
"""The request body for the SkyPilot API."""
|
87
98
|
env_vars: Dict[str, str] = {}
|
@@ -89,6 +100,7 @@ class RequestBody(pydantic.BaseModel):
|
|
89
100
|
entrypoint_command: str = ''
|
90
101
|
using_remote_api_server: bool = False
|
91
102
|
override_skypilot_config: Optional[Dict[str, Any]] = {}
|
103
|
+
override_skypilot_config_path: Optional[str] = None
|
92
104
|
|
93
105
|
# Allow extra fields in the request body, which is useful for backward
|
94
106
|
# compatibility, i.e., we can add new fields to the request body without
|
@@ -108,6 +120,9 @@ class RequestBody(pydantic.BaseModel):
|
|
108
120
|
data['override_skypilot_config'] = data.get(
|
109
121
|
'override_skypilot_config',
|
110
122
|
get_override_skypilot_config_from_client())
|
123
|
+
data['override_skypilot_config_path'] = data.get(
|
124
|
+
'override_skypilot_config_path',
|
125
|
+
get_override_skypilot_config_path_from_client())
|
111
126
|
super().__init__(**data)
|
112
127
|
|
113
128
|
def to_kwargs(self) -> Dict[str, Any]:
|
@@ -122,6 +137,7 @@ class RequestBody(pydantic.BaseModel):
|
|
122
137
|
kwargs.pop('entrypoint_command')
|
123
138
|
kwargs.pop('using_remote_api_server')
|
124
139
|
kwargs.pop('override_skypilot_config')
|
140
|
+
kwargs.pop('override_skypilot_config_path')
|
125
141
|
return kwargs
|
126
142
|
|
127
143
|
@property
|
@@ -6,7 +6,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
6
6
|
|
7
7
|
from sky import jobs as managed_jobs
|
8
8
|
from sky import models
|
9
|
-
from sky.
|
9
|
+
from sky.catalog import common
|
10
10
|
from sky.data import storage
|
11
11
|
from sky.provision.kubernetes import utils as kubernetes_utils
|
12
12
|
from sky.serve import serve_state
|
sky/server/server.py
CHANGED
@@ -26,6 +26,7 @@ from fastapi.middleware import cors
|
|
26
26
|
import starlette.middleware.base
|
27
27
|
|
28
28
|
import sky
|
29
|
+
from sky import catalog
|
29
30
|
from sky import check as sky_check
|
30
31
|
from sky import clouds
|
31
32
|
from sky import core
|
@@ -34,7 +35,6 @@ from sky import execution
|
|
34
35
|
from sky import global_user_state
|
35
36
|
from sky import models
|
36
37
|
from sky import sky_logging
|
37
|
-
from sky.clouds import service_catalog
|
38
38
|
from sky.data import storage_utils
|
39
39
|
from sky.jobs.server import server as jobs_rest
|
40
40
|
from sky.provision.kubernetes import utils as kubernetes_utils
|
@@ -389,7 +389,7 @@ async def list_accelerators(
|
|
389
389
|
request_id=request.state.request_id,
|
390
390
|
request_name='list_accelerators',
|
391
391
|
request_body=list_accelerator_counts_body,
|
392
|
-
func=
|
392
|
+
func=catalog.list_accelerators,
|
393
393
|
schedule_type=requests_lib.ScheduleType.SHORT,
|
394
394
|
)
|
395
395
|
|
@@ -404,7 +404,7 @@ async def list_accelerator_counts(
|
|
404
404
|
request_id=request.state.request_id,
|
405
405
|
request_name='list_accelerator_counts',
|
406
406
|
request_body=list_accelerator_counts_body,
|
407
|
-
func=
|
407
|
+
func=catalog.list_accelerator_counts,
|
408
408
|
schedule_type=requests_lib.ScheduleType.SHORT,
|
409
409
|
)
|
410
410
|
|
sky/server/stream_utils.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
import asyncio
|
4
4
|
import collections
|
5
5
|
import pathlib
|
6
|
-
from typing import AsyncGenerator, Deque,
|
6
|
+
from typing import AsyncGenerator, Deque, Optional
|
7
7
|
|
8
8
|
import aiofiles
|
9
9
|
import fastapi
|
@@ -15,12 +15,6 @@ from sky.utils import rich_utils
|
|
15
15
|
|
16
16
|
logger = sky_logging.init_logger(__name__)
|
17
17
|
|
18
|
-
# When streaming log lines, buffer the lines in memory and flush them in chunks
|
19
|
-
# to improve log tailing throughput. Buffer size is the max size bytes of each
|
20
|
-
# chunk and the timeout threshold for flushing the buffer to ensure
|
21
|
-
# responsiveness.
|
22
|
-
_BUFFER_SIZE = 8 * 1024 # 8KB
|
23
|
-
_BUFFER_TIMEOUT = 0.02 # 20ms
|
24
18
|
_HEARTBEAT_INTERVAL = 30
|
25
19
|
|
26
20
|
|
@@ -44,20 +38,6 @@ async def log_streamer(request_id: Optional[str],
|
|
44
38
|
follow: bool = True) -> AsyncGenerator[str, None]:
|
45
39
|
"""Streams the logs of a request."""
|
46
40
|
|
47
|
-
# Buffer the lines in memory and flush them in chunks to improve log tailing
|
48
|
-
# throughput.
|
49
|
-
buffer: List[str] = []
|
50
|
-
buffer_bytes = 0
|
51
|
-
last_flush_time = asyncio.get_event_loop().time()
|
52
|
-
|
53
|
-
async def flush_buffer() -> AsyncGenerator[str, None]:
|
54
|
-
nonlocal buffer, buffer_bytes, last_flush_time
|
55
|
-
if buffer:
|
56
|
-
yield ''.join(buffer)
|
57
|
-
buffer.clear()
|
58
|
-
buffer_bytes = 0
|
59
|
-
last_flush_time = asyncio.get_event_loop().time()
|
60
|
-
|
61
41
|
if request_id is not None:
|
62
42
|
status_msg = rich_utils.EncodedStatusMessage(
|
63
43
|
f'[dim]Checking request: {request_id}[/dim]')
|
@@ -119,14 +99,7 @@ async def log_streamer(request_id: Optional[str],
|
|
119
99
|
# while keeps the loop tight to make log stream responsive.
|
120
100
|
await asyncio.sleep(0)
|
121
101
|
line: Optional[bytes] = await f.readline()
|
122
|
-
|
123
|
-
current_time = asyncio.get_event_loop().time()
|
124
102
|
if not line:
|
125
|
-
if buffer and (current_time -
|
126
|
-
last_flush_time) >= _BUFFER_TIMEOUT:
|
127
|
-
async for chunk in flush_buffer():
|
128
|
-
yield chunk
|
129
|
-
|
130
103
|
if request_id is not None:
|
131
104
|
request_task = requests_lib.get_request(request_id)
|
132
105
|
if request_task.status > requests_lib.RequestStatus.RUNNING:
|
@@ -165,16 +138,7 @@ async def log_streamer(request_id: Optional[str],
|
|
165
138
|
# sending invisible characters might be okay.
|
166
139
|
if is_payload:
|
167
140
|
continue
|
168
|
-
|
169
|
-
# Add to buffer
|
170
|
-
buffer.append(line_str)
|
171
|
-
buffer_bytes += len(line_str.encode('utf-8'))
|
172
|
-
|
173
|
-
# Check if we should flush the buffer
|
174
|
-
if (buffer_bytes >= _BUFFER_SIZE or
|
175
|
-
(current_time - last_flush_time) >= _BUFFER_TIMEOUT):
|
176
|
-
async for chunk in flush_buffer():
|
177
|
-
yield chunk
|
141
|
+
yield line_str
|
178
142
|
|
179
143
|
|
180
144
|
def stream_response(
|
sky/skypilot_config.py
CHANGED
@@ -55,7 +55,7 @@ import os
|
|
55
55
|
import tempfile
|
56
56
|
import threading
|
57
57
|
import typing
|
58
|
-
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
58
|
+
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
|
59
59
|
|
60
60
|
import filelock
|
61
61
|
|
@@ -163,11 +163,23 @@ def _set_loaded_config(config: config_utils.Config) -> None:
|
|
163
163
|
_get_config_context().config = config
|
164
164
|
|
165
165
|
|
166
|
-
def _get_loaded_config_path() -> Optional[str]:
|
167
|
-
|
166
|
+
def _get_loaded_config_path() -> List[Optional[str]]:
|
167
|
+
serialized = _get_config_context().config_path
|
168
|
+
if not serialized:
|
169
|
+
return []
|
170
|
+
return json.loads(serialized)
|
171
|
+
|
172
|
+
|
173
|
+
def _set_loaded_config_path(
|
174
|
+
path: Optional[Union[str, List[Optional[str]]]]) -> None:
|
175
|
+
if not path:
|
176
|
+
_get_config_context().config_path = None
|
177
|
+
if isinstance(path, str):
|
178
|
+
path = [path]
|
179
|
+
_get_config_context().config_path = json.dumps(path)
|
168
180
|
|
169
181
|
|
170
|
-
def
|
182
|
+
def _set_loaded_config_path_serialized(path: Optional[str]) -> None:
|
171
183
|
_get_config_context().config_path = path
|
172
184
|
|
173
185
|
|
@@ -184,9 +196,14 @@ def get_user_config_path() -> str:
|
|
184
196
|
return _GLOBAL_CONFIG_PATH
|
185
197
|
|
186
198
|
|
187
|
-
def
|
188
|
-
|
189
|
-
|
199
|
+
def _get_config_from_path(path: Optional[str]) -> config_utils.Config:
|
200
|
+
if path is None:
|
201
|
+
return config_utils.Config()
|
202
|
+
return parse_and_validate_config_file(path)
|
203
|
+
|
204
|
+
|
205
|
+
def _resolve_user_config_path() -> Optional[str]:
|
206
|
+
# find the user config file path, None if not resolved.
|
190
207
|
user_config_path = _get_config_file_path(ENV_VAR_GLOBAL_CONFIG)
|
191
208
|
if user_config_path:
|
192
209
|
logger.debug('using user config file specified by '
|
@@ -203,16 +220,17 @@ def get_user_config() -> config_utils.Config:
|
|
203
220
|
user_config_path = get_user_config_path()
|
204
221
|
logger.debug(f'using default user config file: {user_config_path}')
|
205
222
|
user_config_path = os.path.expanduser(user_config_path)
|
206
|
-
|
207
|
-
# load the user config file
|
208
223
|
if os.path.exists(user_config_path):
|
209
|
-
|
210
|
-
|
211
|
-
user_config = config_utils.Config()
|
212
|
-
return user_config
|
224
|
+
return user_config_path
|
225
|
+
return None
|
213
226
|
|
214
227
|
|
215
|
-
def
|
228
|
+
def get_user_config() -> config_utils.Config:
|
229
|
+
"""Returns the user config."""
|
230
|
+
return _get_config_from_path(_resolve_user_config_path())
|
231
|
+
|
232
|
+
|
233
|
+
def _resolve_project_config_path() -> Optional[str]:
|
216
234
|
# find the project config file
|
217
235
|
project_config_path = _get_config_file_path(ENV_VAR_PROJECT_CONFIG)
|
218
236
|
if project_config_path:
|
@@ -231,17 +249,17 @@ def _get_project_config() -> config_utils.Config:
|
|
231
249
|
f'using default project config file: {_PROJECT_CONFIG_PATH}')
|
232
250
|
project_config_path = _PROJECT_CONFIG_PATH
|
233
251
|
project_config_path = os.path.expanduser(project_config_path)
|
234
|
-
|
235
|
-
# load the project config file
|
236
252
|
if os.path.exists(project_config_path):
|
237
|
-
|
238
|
-
|
239
|
-
project_config = config_utils.Config()
|
240
|
-
return project_config
|
253
|
+
return project_config_path
|
254
|
+
return None
|
241
255
|
|
242
256
|
|
243
|
-
def
|
244
|
-
"""Returns the
|
257
|
+
def _get_project_config() -> config_utils.Config:
|
258
|
+
"""Returns the project config."""
|
259
|
+
return _get_config_from_path(_resolve_project_config_path())
|
260
|
+
|
261
|
+
|
262
|
+
def _resolve_server_config_path() -> Optional[str]:
|
245
263
|
# find the server config file
|
246
264
|
server_config_path = _get_config_file_path(ENV_VAR_GLOBAL_CONFIG)
|
247
265
|
if server_config_path:
|
@@ -259,13 +277,14 @@ def get_server_config() -> config_utils.Config:
|
|
259
277
|
server_config_path = _GLOBAL_CONFIG_PATH
|
260
278
|
logger.debug(f'using default server config file: {server_config_path}')
|
261
279
|
server_config_path = os.path.expanduser(server_config_path)
|
262
|
-
|
263
|
-
# load the server config file
|
264
280
|
if os.path.exists(server_config_path):
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
281
|
+
return server_config_path
|
282
|
+
return None
|
283
|
+
|
284
|
+
|
285
|
+
def get_server_config() -> config_utils.Config:
|
286
|
+
"""Returns the server config."""
|
287
|
+
return _get_config_from_path(_resolve_server_config_path())
|
269
288
|
|
270
289
|
|
271
290
|
def get_nested(keys: Tuple[str, ...],
|
@@ -487,9 +506,11 @@ def _reload_config_from_internal_file(internal_config_path: str) -> None:
|
|
487
506
|
def _reload_config_as_server() -> None:
|
488
507
|
# Reset the global variables, to avoid using stale values.
|
489
508
|
_set_loaded_config(config_utils.Config())
|
509
|
+
_set_loaded_config_path(None)
|
490
510
|
|
491
511
|
overrides: List[config_utils.Config] = []
|
492
|
-
|
512
|
+
server_config_path = _resolve_server_config_path()
|
513
|
+
server_config = _get_config_from_path(server_config_path)
|
493
514
|
if server_config:
|
494
515
|
overrides.append(server_config)
|
495
516
|
|
@@ -503,17 +524,21 @@ def _reload_config_as_server() -> None:
|
|
503
524
|
f'server config: \n'
|
504
525
|
f'{common_utils.dump_yaml_str(dict(overlaid_server_config))}')
|
505
526
|
_set_loaded_config(overlaid_server_config)
|
527
|
+
_set_loaded_config_path(server_config_path)
|
506
528
|
|
507
529
|
|
508
530
|
def _reload_config_as_client() -> None:
|
509
531
|
# Reset the global variables, to avoid using stale values.
|
510
532
|
_set_loaded_config(config_utils.Config())
|
533
|
+
_set_loaded_config_path(None)
|
511
534
|
|
512
535
|
overrides: List[config_utils.Config] = []
|
513
|
-
|
536
|
+
user_config_path = _resolve_user_config_path()
|
537
|
+
user_config = _get_config_from_path(user_config_path)
|
514
538
|
if user_config:
|
515
539
|
overrides.append(user_config)
|
516
|
-
|
540
|
+
project_config_path = _resolve_project_config_path()
|
541
|
+
project_config = _get_config_from_path(project_config_path)
|
517
542
|
if project_config:
|
518
543
|
overrides.append(project_config)
|
519
544
|
|
@@ -527,14 +552,26 @@ def _reload_config_as_client() -> None:
|
|
527
552
|
f'client config (before task and CLI overrides): \n'
|
528
553
|
f'{common_utils.dump_yaml_str(dict(overlaid_client_config))}')
|
529
554
|
_set_loaded_config(overlaid_client_config)
|
555
|
+
_set_loaded_config_path([user_config_path, project_config_path])
|
530
556
|
|
531
557
|
|
532
558
|
def loaded_config_path() -> Optional[str]:
|
533
|
-
"""Returns the path to the loaded config file, or
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
559
|
+
"""Returns the path to the loaded config file, or '<overridden>' if the
|
560
|
+
config is overridden."""
|
561
|
+
path = [p for p in set(_get_loaded_config_path()) if p is not None]
|
562
|
+
if len(path) == 0:
|
563
|
+
return '<overridden>' if _is_config_overridden() else None
|
564
|
+
if len(path) == 1:
|
565
|
+
return path[0]
|
566
|
+
|
567
|
+
header = 'overridden' if _is_config_overridden() else 'merged'
|
568
|
+
path_str = ', '.join(p for p in path if p is not None)
|
569
|
+
return f'<{header} ({path_str})>'
|
570
|
+
|
571
|
+
|
572
|
+
def loaded_config_path_serialized() -> Optional[str]:
|
573
|
+
"""Returns the json serialized config path list"""
|
574
|
+
return _get_config_context().config_path
|
538
575
|
|
539
576
|
|
540
577
|
# Load on import, synchronization is guaranteed by python interpreter.
|
@@ -548,7 +585,9 @@ def loaded() -> bool:
|
|
548
585
|
|
549
586
|
@contextlib.contextmanager
|
550
587
|
def override_skypilot_config(
|
551
|
-
override_configs: Optional[Dict[str, Any]]
|
588
|
+
override_configs: Optional[Dict[str, Any]],
|
589
|
+
override_config_path_serialized: Optional[str] = None
|
590
|
+
) -> Iterator[None]:
|
552
591
|
"""Overrides the user configurations."""
|
553
592
|
# TODO(SKY-1215): allow admin user to extend the disallowed keys or specify
|
554
593
|
# allowed keys.
|
@@ -557,7 +596,13 @@ def override_skypilot_config(
|
|
557
596
|
yield
|
558
597
|
return
|
559
598
|
original_config = _get_loaded_config()
|
599
|
+
original_config_path = loaded_config_path_serialized()
|
560
600
|
override_configs = config_utils.Config(override_configs)
|
601
|
+
if override_config_path_serialized is None:
|
602
|
+
override_config_path = []
|
603
|
+
else:
|
604
|
+
override_config_path = json.loads(override_config_path_serialized)
|
605
|
+
|
561
606
|
disallowed_diff_keys = []
|
562
607
|
for key in constants.SKIPPED_CLIENT_OVERRIDE_KEYS:
|
563
608
|
value = override_configs.pop_nested(key, default_value=None)
|
@@ -602,6 +647,8 @@ def override_skypilot_config(
|
|
602
647
|
skip_none=False)
|
603
648
|
_set_config_overridden(True)
|
604
649
|
_set_loaded_config(config)
|
650
|
+
_set_loaded_config_path(_get_loaded_config_path() +
|
651
|
+
override_config_path)
|
605
652
|
yield
|
606
653
|
except exceptions.InvalidSkyPilotConfigError as e:
|
607
654
|
with ux_utils.print_exception_no_traceback():
|
@@ -616,6 +663,7 @@ def override_skypilot_config(
|
|
616
663
|
finally:
|
617
664
|
_set_loaded_config(original_config)
|
618
665
|
_set_config_overridden(False)
|
666
|
+
_set_loaded_config_path_serialized(original_config_path)
|
619
667
|
|
620
668
|
|
621
669
|
@contextlib.contextmanager
|
@@ -628,6 +676,7 @@ def replace_skypilot_config(new_configs: config_utils.Config) -> Iterator[None]:
|
|
628
676
|
sky_utils.context for more details.
|
629
677
|
"""
|
630
678
|
original_config = _get_loaded_config()
|
679
|
+
original_config_path = loaded_config_path_serialized()
|
631
680
|
original_env_var = os.environ.get(ENV_VAR_SKYPILOT_CONFIG)
|
632
681
|
if new_configs != original_config:
|
633
682
|
# Modify the global config of current process or context
|
@@ -642,9 +691,11 @@ def replace_skypilot_config(new_configs: config_utils.Config) -> Iterator[None]:
|
|
642
691
|
# Note that this code modifies os.environ directly because it
|
643
692
|
# will be hijacked to be context-aware if a context is active.
|
644
693
|
os.environ[ENV_VAR_SKYPILOT_CONFIG] = temp_file.name
|
694
|
+
_set_loaded_config_path(temp_file.name)
|
645
695
|
yield
|
646
696
|
# Restore the original config and env var.
|
647
697
|
_set_loaded_config(original_config)
|
698
|
+
_set_loaded_config_path_serialized(original_config_path)
|
648
699
|
if original_env_var:
|
649
700
|
os.environ[ENV_VAR_SKYPILOT_CONFIG] = original_env_var
|
650
701
|
else:
|
sky/usage/usage_lib.py
CHANGED
@@ -205,8 +205,8 @@ class UsageMessageToReport(MessageToReport):
|
|
205
205
|
logger.debug('Multiple accelerators are not supported: '
|
206
206
|
f'{resources.accelerators}.')
|
207
207
|
self.task_accelerators = list(resources.accelerators.keys())[0]
|
208
|
-
self.task_num_accelerators =
|
209
|
-
self.task_accelerators]
|
208
|
+
self.task_num_accelerators = int(
|
209
|
+
resources.accelerators[self.task_accelerators])
|
210
210
|
else:
|
211
211
|
self.task_accelerators = None
|
212
212
|
self.task_num_accelerators = None
|
@@ -245,7 +245,8 @@ class UsageMessageToReport(MessageToReport):
|
|
245
245
|
logger.debug('Multiple accelerators are not supported: '
|
246
246
|
f'{resources.accelerators}.')
|
247
247
|
self.accelerators = list(resources.accelerators.keys())[0]
|
248
|
-
self.num_accelerators =
|
248
|
+
self.num_accelerators = int(
|
249
|
+
resources.accelerators[self.accelerators])
|
249
250
|
else:
|
250
251
|
self.accelerators = None
|
251
252
|
self.num_accelerators = None
|