skypilot-nightly 1.0.0.dev20240922__py3-none-any.whl → 1.0.0.dev20240924__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 +11 -2
- sky/admin_policy.py +101 -0
- sky/dag.py +17 -13
- sky/exceptions.py +5 -0
- sky/execution.py +13 -3
- sky/jobs/controller.py +2 -0
- sky/jobs/core.py +4 -0
- sky/serve/core.py +6 -0
- sky/skypilot_config.py +64 -37
- sky/templates/jobs-controller.yaml.j2 +3 -1
- sky/templates/sky-serve-controller.yaml.j2 +3 -1
- sky/utils/admin_policy_utils.py +145 -0
- sky/utils/common_utils.py +6 -5
- sky/utils/controller_utils.py +42 -29
- sky/utils/dag_utils.py +6 -3
- sky/utils/schemas.py +8 -0
- {skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/METADATA +1 -1
- {skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/RECORD +22 -20
- {skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/WHEEL +0 -0
- {skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/top_level.txt +0 -0
sky/__init__.py
CHANGED
@@ -5,7 +5,7 @@ from typing import Optional
|
|
5
5
|
import urllib.request
|
6
6
|
|
7
7
|
# Replaced with the current commit when building the wheels.
|
8
|
-
_SKYPILOT_COMMIT_SHA = '
|
8
|
+
_SKYPILOT_COMMIT_SHA = '800f7d6971bd604f266faebb33d044c7d5baca55'
|
9
9
|
|
10
10
|
|
11
11
|
def _get_git_commit():
|
@@ -35,7 +35,7 @@ def _get_git_commit():
|
|
35
35
|
|
36
36
|
|
37
37
|
__commit__ = _get_git_commit()
|
38
|
-
__version__ = '1.0.0.
|
38
|
+
__version__ = '1.0.0.dev20240924'
|
39
39
|
__root_dir__ = os.path.dirname(os.path.abspath(__file__))
|
40
40
|
|
41
41
|
|
@@ -82,6 +82,9 @@ _set_http_proxy_env_vars()
|
|
82
82
|
from sky import backends
|
83
83
|
from sky import benchmark
|
84
84
|
from sky import clouds
|
85
|
+
from sky.admin_policy import AdminPolicy
|
86
|
+
from sky.admin_policy import MutatedUserRequest
|
87
|
+
from sky.admin_policy import UserRequest
|
85
88
|
from sky.clouds.service_catalog import list_accelerators
|
86
89
|
from sky.core import autostop
|
87
90
|
from sky.core import cancel
|
@@ -112,6 +115,7 @@ from sky.optimizer import Optimizer
|
|
112
115
|
from sky.optimizer import OptimizeTarget
|
113
116
|
from sky.resources import Resources
|
114
117
|
from sky.skylet.job_lib import JobStatus
|
118
|
+
from sky.skypilot_config import Config
|
115
119
|
from sky.status_lib import ClusterStatus
|
116
120
|
from sky.task import Task
|
117
121
|
|
@@ -185,4 +189,9 @@ __all__ = [
|
|
185
189
|
# core APIs Storage Management
|
186
190
|
'storage_ls',
|
187
191
|
'storage_delete',
|
192
|
+
# Admin Policy
|
193
|
+
'UserRequest',
|
194
|
+
'MutatedUserRequest',
|
195
|
+
'AdminPolicy',
|
196
|
+
'Config',
|
188
197
|
]
|
sky/admin_policy.py
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
"""Interface for admin-defined policy for user requests."""
|
2
|
+
import abc
|
3
|
+
import dataclasses
|
4
|
+
import typing
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
if typing.TYPE_CHECKING:
|
8
|
+
import sky
|
9
|
+
|
10
|
+
|
11
|
+
@dataclasses.dataclass
|
12
|
+
class RequestOptions:
|
13
|
+
"""Request options for admin policy.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
cluster_name: Name of the cluster to create/reuse. It is None if not
|
17
|
+
specified by the user.
|
18
|
+
idle_minutes_to_autostop: Autostop setting requested by a user. The
|
19
|
+
cluster will be set to autostop after this many minutes of idleness.
|
20
|
+
down: If true, use autodown rather than autostop.
|
21
|
+
dryrun: Is the request a dryrun?
|
22
|
+
"""
|
23
|
+
cluster_name: Optional[str]
|
24
|
+
idle_minutes_to_autostop: Optional[int]
|
25
|
+
down: bool
|
26
|
+
dryrun: bool
|
27
|
+
|
28
|
+
|
29
|
+
@dataclasses.dataclass
|
30
|
+
class UserRequest:
|
31
|
+
"""A user request.
|
32
|
+
|
33
|
+
A "user request" is defined as a `sky launch / exec` command or its API
|
34
|
+
equivalent.
|
35
|
+
|
36
|
+
`sky jobs launch / serve up` involves multiple launch requests, including
|
37
|
+
the launch of controller and clusters for a job (which can have multiple
|
38
|
+
tasks if it is a pipeline) or service replicas. Each launch is a separate
|
39
|
+
request.
|
40
|
+
|
41
|
+
This class wraps the underlying task, the global skypilot config used to run
|
42
|
+
a task, and the request options.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
task: User specified task.
|
46
|
+
skypilot_config: Global skypilot config to be used in this request.
|
47
|
+
request_options: Request options. It is None for jobs and services.
|
48
|
+
"""
|
49
|
+
task: 'sky.Task'
|
50
|
+
skypilot_config: 'sky.Config'
|
51
|
+
request_options: Optional['RequestOptions'] = None
|
52
|
+
|
53
|
+
|
54
|
+
@dataclasses.dataclass
|
55
|
+
class MutatedUserRequest:
|
56
|
+
task: 'sky.Task'
|
57
|
+
skypilot_config: 'sky.Config'
|
58
|
+
|
59
|
+
|
60
|
+
# pylint: disable=line-too-long
|
61
|
+
class AdminPolicy:
|
62
|
+
"""Abstract interface of an admin-defined policy for all user requests.
|
63
|
+
|
64
|
+
Admins can implement a subclass of AdminPolicy with the following signature:
|
65
|
+
|
66
|
+
import sky
|
67
|
+
|
68
|
+
class SkyPilotPolicyV1(sky.AdminPolicy):
|
69
|
+
def validate_and_mutate(user_request: UserRequest) -> MutatedUserRequest:
|
70
|
+
...
|
71
|
+
return MutatedUserRequest(task=..., skypilot_config=...)
|
72
|
+
|
73
|
+
The policy can mutate both task and skypilot_config. Admins then distribute
|
74
|
+
a simple module that contains this implementation, installable in a way
|
75
|
+
that it can be imported by users from the same Python environment where
|
76
|
+
SkyPilot is running.
|
77
|
+
|
78
|
+
Users can register a subclass of AdminPolicy in the SkyPilot config file
|
79
|
+
under the key 'admin_policy', e.g.
|
80
|
+
|
81
|
+
admin_policy: my_package.SkyPilotPolicyV1
|
82
|
+
"""
|
83
|
+
|
84
|
+
@classmethod
|
85
|
+
@abc.abstractmethod
|
86
|
+
def validate_and_mutate(cls,
|
87
|
+
user_request: UserRequest) -> MutatedUserRequest:
|
88
|
+
"""Validates and mutates the user request and returns mutated request.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
user_request: The user request to validate and mutate.
|
92
|
+
UserRequest contains (sky.Task, sky.Config)
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
MutatedUserRequest: The mutated user request.
|
96
|
+
|
97
|
+
Raises:
|
98
|
+
Exception to throw if the user request failed the validation.
|
99
|
+
"""
|
100
|
+
raise NotImplementedError(
|
101
|
+
'Your policy must implement validate_and_mutate')
|
sky/dag.py
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
"""DAGs: user applications to be run."""
|
2
2
|
import pprint
|
3
3
|
import threading
|
4
|
+
import typing
|
4
5
|
from typing import List, Optional
|
5
6
|
|
7
|
+
if typing.TYPE_CHECKING:
|
8
|
+
from sky import task
|
9
|
+
|
6
10
|
|
7
11
|
class Dag:
|
8
12
|
"""Dag: a user application, represented as a DAG of Tasks.
|
@@ -13,37 +17,37 @@ class Dag:
|
|
13
17
|
>>> task = sky.Task(...)
|
14
18
|
"""
|
15
19
|
|
16
|
-
def __init__(self):
|
17
|
-
self.tasks = []
|
20
|
+
def __init__(self) -> None:
|
21
|
+
self.tasks: List['task.Task'] = []
|
18
22
|
import networkx as nx # pylint: disable=import-outside-toplevel
|
19
23
|
|
20
24
|
self.graph = nx.DiGraph()
|
21
|
-
self.name = None
|
25
|
+
self.name: Optional[str] = None
|
22
26
|
|
23
|
-
def add(self, task):
|
27
|
+
def add(self, task: 'task.Task') -> None:
|
24
28
|
self.graph.add_node(task)
|
25
29
|
self.tasks.append(task)
|
26
30
|
|
27
|
-
def remove(self, task):
|
31
|
+
def remove(self, task: 'task.Task') -> None:
|
28
32
|
self.tasks.remove(task)
|
29
33
|
self.graph.remove_node(task)
|
30
34
|
|
31
|
-
def add_edge(self, op1, op2):
|
35
|
+
def add_edge(self, op1: 'task.Task', op2: 'task.Task') -> None:
|
32
36
|
assert op1 in self.graph.nodes
|
33
37
|
assert op2 in self.graph.nodes
|
34
38
|
self.graph.add_edge(op1, op2)
|
35
39
|
|
36
|
-
def __len__(self):
|
40
|
+
def __len__(self) -> int:
|
37
41
|
return len(self.tasks)
|
38
42
|
|
39
|
-
def __enter__(self):
|
43
|
+
def __enter__(self) -> 'Dag':
|
40
44
|
push_dag(self)
|
41
45
|
return self
|
42
46
|
|
43
|
-
def __exit__(self, exc_type, exc_value, traceback):
|
47
|
+
def __exit__(self, exc_type, exc_value, traceback) -> None:
|
44
48
|
pop_dag()
|
45
49
|
|
46
|
-
def __repr__(self):
|
50
|
+
def __repr__(self) -> str:
|
47
51
|
pformat = pprint.pformat(self.tasks)
|
48
52
|
return f'DAG:\n{pformat}'
|
49
53
|
|
@@ -70,15 +74,15 @@ class Dag:
|
|
70
74
|
|
71
75
|
class _DagContext(threading.local):
|
72
76
|
"""A thread-local stack of Dags."""
|
73
|
-
_current_dag = None
|
77
|
+
_current_dag: Optional[Dag] = None
|
74
78
|
_previous_dags: List[Dag] = []
|
75
79
|
|
76
|
-
def push_dag(self, dag):
|
80
|
+
def push_dag(self, dag: Dag):
|
77
81
|
if self._current_dag is not None:
|
78
82
|
self._previous_dags.append(self._current_dag)
|
79
83
|
self._current_dag = dag
|
80
84
|
|
81
|
-
def pop_dag(self):
|
85
|
+
def pop_dag(self) -> Optional[Dag]:
|
82
86
|
old_dag = self._current_dag
|
83
87
|
if self._previous_dags:
|
84
88
|
self._current_dag = self._previous_dags.pop()
|
sky/exceptions.py
CHANGED
@@ -286,3 +286,8 @@ class ServeUserTerminatedError(Exception):
|
|
286
286
|
|
287
287
|
class PortDoesNotExistError(Exception):
|
288
288
|
"""Raised when the port does not exist."""
|
289
|
+
|
290
|
+
|
291
|
+
class UserRequestRejectedByPolicy(Exception):
|
292
|
+
"""Raised when a user request is rejected by an admin policy."""
|
293
|
+
pass
|
sky/execution.py
CHANGED
@@ -9,6 +9,7 @@ from typing import List, Optional, Tuple, Union
|
|
9
9
|
import colorama
|
10
10
|
|
11
11
|
import sky
|
12
|
+
from sky import admin_policy
|
12
13
|
from sky import backends
|
13
14
|
from sky import clouds
|
14
15
|
from sky import global_user_state
|
@@ -16,6 +17,7 @@ from sky import optimizer
|
|
16
17
|
from sky import sky_logging
|
17
18
|
from sky.backends import backend_utils
|
18
19
|
from sky.usage import usage_lib
|
20
|
+
from sky.utils import admin_policy_utils
|
19
21
|
from sky.utils import controller_utils
|
20
22
|
from sky.utils import dag_utils
|
21
23
|
from sky.utils import env_options
|
@@ -158,7 +160,16 @@ def _execute(
|
|
158
160
|
handle: Optional[backends.ResourceHandle]; the handle to the cluster. None
|
159
161
|
if dryrun.
|
160
162
|
"""
|
163
|
+
|
161
164
|
dag = dag_utils.convert_entrypoint_to_dag(entrypoint)
|
165
|
+
dag, _ = admin_policy_utils.apply(
|
166
|
+
dag,
|
167
|
+
request_options=admin_policy.RequestOptions(
|
168
|
+
cluster_name=cluster_name,
|
169
|
+
idle_minutes_to_autostop=idle_minutes_to_autostop,
|
170
|
+
down=down,
|
171
|
+
dryrun=dryrun,
|
172
|
+
))
|
162
173
|
assert len(dag) == 1, f'We support 1 task for now. {dag}'
|
163
174
|
task = dag.tasks[0]
|
164
175
|
|
@@ -170,9 +181,8 @@ def _execute(
|
|
170
181
|
|
171
182
|
cluster_exists = False
|
172
183
|
if cluster_name is not None:
|
173
|
-
|
174
|
-
|
175
|
-
cluster_exists = existing_handle is not None
|
184
|
+
cluster_record = global_user_state.get_cluster_from_name(cluster_name)
|
185
|
+
cluster_exists = cluster_record is not None
|
176
186
|
# TODO(woosuk): If the cluster exists, print a warning that
|
177
187
|
# `cpus` and `memory` are not used as a job scheduling constraint,
|
178
188
|
# unlike `gpus`.
|
sky/jobs/controller.py
CHANGED
@@ -64,6 +64,7 @@ class JobsController:
|
|
64
64
|
if len(self._dag.tasks) <= 1:
|
65
65
|
task_name = self._dag_name
|
66
66
|
else:
|
67
|
+
assert task.name is not None, task
|
67
68
|
task_name = task.name
|
68
69
|
# This is guaranteed by the spot_launch API, where we fill in
|
69
70
|
# the task.name with
|
@@ -447,6 +448,7 @@ def _cleanup(job_id: int, dag_yaml: str):
|
|
447
448
|
# controller, we should keep it in sync with JobsController.__init__()
|
448
449
|
dag, _ = _get_dag_and_name(dag_yaml)
|
449
450
|
for task in dag.tasks:
|
451
|
+
assert task.name is not None, task
|
450
452
|
cluster_name = managed_job_utils.generate_managed_job_cluster_name(
|
451
453
|
task.name, job_id)
|
452
454
|
recovery_strategy.terminate_cluster(cluster_name)
|
sky/jobs/core.py
CHANGED
@@ -18,6 +18,7 @@ from sky.jobs import constants as managed_job_constants
|
|
18
18
|
from sky.jobs import utils as managed_job_utils
|
19
19
|
from sky.skylet import constants as skylet_constants
|
20
20
|
from sky.usage import usage_lib
|
21
|
+
from sky.utils import admin_policy_utils
|
21
22
|
from sky.utils import common_utils
|
22
23
|
from sky.utils import controller_utils
|
23
24
|
from sky.utils import dag_utils
|
@@ -54,6 +55,8 @@ def launch(
|
|
54
55
|
dag_uuid = str(uuid.uuid4().hex[:4])
|
55
56
|
|
56
57
|
dag = dag_utils.convert_entrypoint_to_dag(entrypoint)
|
58
|
+
dag, mutated_user_config = admin_policy_utils.apply(
|
59
|
+
dag, use_mutated_config_in_current_request=False)
|
57
60
|
if not dag.is_chain():
|
58
61
|
with ux_utils.print_exception_no_traceback():
|
59
62
|
raise ValueError('Only single-task or chain DAG is '
|
@@ -103,6 +106,7 @@ def launch(
|
|
103
106
|
**controller_utils.shared_controller_vars_to_fill(
|
104
107
|
controller_utils.Controllers.JOBS_CONTROLLER,
|
105
108
|
remote_user_config_path=remote_user_config_path,
|
109
|
+
local_user_config=mutated_user_config,
|
106
110
|
),
|
107
111
|
}
|
108
112
|
|
sky/serve/core.py
CHANGED
@@ -17,6 +17,7 @@ from sky.serve import serve_state
|
|
17
17
|
from sky.serve import serve_utils
|
18
18
|
from sky.skylet import constants
|
19
19
|
from sky.usage import usage_lib
|
20
|
+
from sky.utils import admin_policy_utils
|
20
21
|
from sky.utils import common_utils
|
21
22
|
from sky.utils import controller_utils
|
22
23
|
from sky.utils import resources_utils
|
@@ -124,6 +125,10 @@ def up(
|
|
124
125
|
|
125
126
|
_validate_service_task(task)
|
126
127
|
|
128
|
+
dag, mutated_user_config = admin_policy_utils.apply(
|
129
|
+
task, use_mutated_config_in_current_request=False)
|
130
|
+
task = dag.tasks[0]
|
131
|
+
|
127
132
|
controller_utils.maybe_translate_local_file_mounts_and_sync_up(task,
|
128
133
|
path='serve')
|
129
134
|
|
@@ -158,6 +163,7 @@ def up(
|
|
158
163
|
**controller_utils.shared_controller_vars_to_fill(
|
159
164
|
controller=controller_utils.Controllers.SKY_SERVE_CONTROLLER,
|
160
165
|
remote_user_config_path=remote_config_yaml_path,
|
166
|
+
local_user_config=mutated_user_config,
|
161
167
|
),
|
162
168
|
}
|
163
169
|
common_utils.fill_template(serve_constants.CONTROLLER_TEMPLATE,
|
sky/skypilot_config.py
CHANGED
@@ -61,6 +61,8 @@ from sky.utils import common_utils
|
|
61
61
|
from sky.utils import schemas
|
62
62
|
from sky.utils import ux_utils
|
63
63
|
|
64
|
+
logger = sky_logging.init_logger(__name__)
|
65
|
+
|
64
66
|
# The config path is discovered in this order:
|
65
67
|
#
|
66
68
|
# (1) (Used internally) If env var {ENV_VAR_SKYPILOT_CONFIG} exists, use its
|
@@ -78,11 +80,57 @@ ENV_VAR_SKYPILOT_CONFIG = 'SKYPILOT_CONFIG'
|
|
78
80
|
# Path to the local config file.
|
79
81
|
CONFIG_PATH = '~/.sky/config.yaml'
|
80
82
|
|
81
|
-
|
83
|
+
|
84
|
+
class Config(Dict[str, Any]):
|
85
|
+
"""SkyPilot config that supports setting/getting values with nested keys."""
|
86
|
+
|
87
|
+
def get_nested(self,
|
88
|
+
keys: Tuple[str, ...],
|
89
|
+
default_value: Any,
|
90
|
+
override_configs: Optional[Dict[str, Any]] = None) -> Any:
|
91
|
+
"""Gets a nested key.
|
92
|
+
|
93
|
+
If any key is not found, or any intermediate key does not point to a
|
94
|
+
dict value, returns 'default_value'.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
keys: A tuple of strings representing the nested keys.
|
98
|
+
default_value: The default value to return if the key is not found.
|
99
|
+
override_configs: A dict of override configs with the same schema as
|
100
|
+
the config file, but only containing the keys to override.
|
101
|
+
|
102
|
+
Returns:
|
103
|
+
The value of the nested key, or 'default_value' if not found.
|
104
|
+
"""
|
105
|
+
config = copy.deepcopy(self)
|
106
|
+
if override_configs is not None:
|
107
|
+
config = _recursive_update(config, override_configs)
|
108
|
+
return _get_nested(config, keys, default_value)
|
109
|
+
|
110
|
+
def set_nested(self, keys: Tuple[str, ...], value: Any) -> None:
|
111
|
+
"""In-place sets a nested key to value.
|
112
|
+
|
113
|
+
Like get_nested(), if any key is not found, this will not raise an
|
114
|
+
error.
|
115
|
+
"""
|
116
|
+
override = {}
|
117
|
+
for i, key in enumerate(reversed(keys)):
|
118
|
+
if i == 0:
|
119
|
+
override = {key: value}
|
120
|
+
else:
|
121
|
+
override = {key: override}
|
122
|
+
_recursive_update(self, override)
|
123
|
+
|
124
|
+
@classmethod
|
125
|
+
def from_dict(cls, config: Optional[Dict[str, Any]]) -> 'Config':
|
126
|
+
if config is None:
|
127
|
+
return cls()
|
128
|
+
return cls(**config)
|
129
|
+
|
82
130
|
|
83
131
|
# The loaded config.
|
84
|
-
_dict
|
85
|
-
_loaded_config_path = None
|
132
|
+
_dict = Config()
|
133
|
+
_loaded_config_path: Optional[str] = None
|
86
134
|
|
87
135
|
|
88
136
|
def _get_nested(configs: Optional[Dict[str, Any]], keys: Iterable[str],
|
@@ -131,17 +179,11 @@ def get_nested(keys: Tuple[str, ...],
|
|
131
179
|
), (f'Override configs must not be provided when keys {keys} is not within '
|
132
180
|
'constants.OVERRIDEABLE_CONFIG_KEYS: '
|
133
181
|
f'{constants.OVERRIDEABLE_CONFIG_KEYS}')
|
134
|
-
|
135
|
-
if _dict is not None:
|
136
|
-
config = copy.deepcopy(_dict)
|
137
|
-
if override_configs is None:
|
138
|
-
override_configs = {}
|
139
|
-
config = _recursive_update(config, override_configs)
|
140
|
-
return _get_nested(config, keys, default_value)
|
182
|
+
return _dict.get_nested(keys, default_value, override_configs)
|
141
183
|
|
142
184
|
|
143
|
-
def _recursive_update(base_config:
|
144
|
-
override_config: Dict[str, Any]) ->
|
185
|
+
def _recursive_update(base_config: Config,
|
186
|
+
override_config: Dict[str, Any]) -> Config:
|
145
187
|
"""Recursively updates base configuration with override configuration"""
|
146
188
|
for key, value in override_config.items():
|
147
189
|
if (isinstance(value, dict) and key in base_config and
|
@@ -157,22 +199,14 @@ def set_nested(keys: Tuple[str, ...], value: Any) -> Dict[str, Any]:
|
|
157
199
|
|
158
200
|
Like get_nested(), if any key is not found, this will not raise an error.
|
159
201
|
"""
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
for i, key in enumerate(reversed(keys)):
|
164
|
-
if i == 0:
|
165
|
-
override = {key: value}
|
166
|
-
else:
|
167
|
-
override = {key: override}
|
168
|
-
return _recursive_update(copy.deepcopy(_dict), override)
|
202
|
+
copied_dict = copy.deepcopy(_dict)
|
203
|
+
copied_dict.set_nested(keys, value)
|
204
|
+
return dict(**copied_dict)
|
169
205
|
|
170
206
|
|
171
|
-
def to_dict() ->
|
207
|
+
def to_dict() -> Config:
|
172
208
|
"""Returns a deep-copied version of the current config."""
|
173
|
-
|
174
|
-
return copy.deepcopy(_dict)
|
175
|
-
return {}
|
209
|
+
return copy.deepcopy(_dict)
|
176
210
|
|
177
211
|
|
178
212
|
def _try_load_config() -> None:
|
@@ -192,13 +226,14 @@ def _try_load_config() -> None:
|
|
192
226
|
config_path = os.path.expanduser(config_path)
|
193
227
|
if os.path.exists(config_path):
|
194
228
|
logger.debug(f'Using config path: {config_path}')
|
195
|
-
_loaded_config_path = config_path
|
196
229
|
try:
|
197
|
-
|
230
|
+
config = common_utils.read_yaml(config_path)
|
231
|
+
_dict = Config.from_dict(config)
|
232
|
+
_loaded_config_path = config_path
|
198
233
|
logger.debug(f'Config loaded:\n{pprint.pformat(_dict)}')
|
199
234
|
except yaml.YAMLError as e:
|
200
235
|
logger.error(f'Error in loading config file ({config_path}):', e)
|
201
|
-
if _dict
|
236
|
+
if _dict:
|
202
237
|
common_utils.validate_schema(
|
203
238
|
_dict,
|
204
239
|
schemas.get_config_schema(),
|
@@ -219,14 +254,6 @@ def loaded_config_path() -> Optional[str]:
|
|
219
254
|
_try_load_config()
|
220
255
|
|
221
256
|
|
222
|
-
def _check_loaded_or_die():
|
223
|
-
"""Checks loaded() is true; otherwise raises RuntimeError."""
|
224
|
-
if _dict is None:
|
225
|
-
raise RuntimeError(
|
226
|
-
f'No user configs loaded. Check {CONFIG_PATH} exists and '
|
227
|
-
'can be loaded.')
|
228
|
-
|
229
|
-
|
230
257
|
def loaded() -> bool:
|
231
258
|
"""Returns if the user configurations are loaded."""
|
232
|
-
return _dict
|
259
|
+
return bool(_dict)
|
@@ -4,7 +4,9 @@ name: {{dag_name}}
|
|
4
4
|
|
5
5
|
file_mounts:
|
6
6
|
{{remote_user_yaml_path}}: {{user_yaml_path}}
|
7
|
-
{
|
7
|
+
{%- if local_user_config_path is not none %}
|
8
|
+
{{remote_user_config_path}}: {{local_user_config_path}}
|
9
|
+
{%- endif %}
|
8
10
|
{%- for remote_catalog_path, local_catalog_path in modified_catalogs.items() %}
|
9
11
|
{{remote_catalog_path}}: {{local_catalog_path}}
|
10
12
|
{%- endfor %}
|
@@ -23,7 +23,9 @@ setup: |
|
|
23
23
|
|
24
24
|
file_mounts:
|
25
25
|
{{remote_task_yaml_path}}: {{local_task_yaml_path}}
|
26
|
-
{
|
26
|
+
{%- if local_user_config_path is not none %}
|
27
|
+
{{remote_user_config_path}}: {{local_user_config_path}}
|
28
|
+
{%- endif %}
|
27
29
|
{%- for remote_catalog_path, local_catalog_path in modified_catalogs.items() %}
|
28
30
|
{{remote_catalog_path}}: {{local_catalog_path}}
|
29
31
|
{%- endfor %}
|
@@ -0,0 +1,145 @@
|
|
1
|
+
"""Admin policy utils."""
|
2
|
+
import copy
|
3
|
+
import importlib
|
4
|
+
import os
|
5
|
+
import tempfile
|
6
|
+
from typing import Optional, Tuple, Union
|
7
|
+
|
8
|
+
import colorama
|
9
|
+
|
10
|
+
from sky import admin_policy
|
11
|
+
from sky import dag as dag_lib
|
12
|
+
from sky import exceptions
|
13
|
+
from sky import sky_logging
|
14
|
+
from sky import skypilot_config
|
15
|
+
from sky import task as task_lib
|
16
|
+
from sky.utils import common_utils
|
17
|
+
from sky.utils import ux_utils
|
18
|
+
|
19
|
+
logger = sky_logging.init_logger(__name__)
|
20
|
+
|
21
|
+
|
22
|
+
def _get_policy_cls(
|
23
|
+
policy: Optional[str]) -> Optional[admin_policy.AdminPolicy]:
|
24
|
+
"""Gets admin-defined policy."""
|
25
|
+
if policy is None:
|
26
|
+
return None
|
27
|
+
try:
|
28
|
+
module_path, class_name = policy.rsplit('.', 1)
|
29
|
+
module = importlib.import_module(module_path)
|
30
|
+
except ImportError as e:
|
31
|
+
with ux_utils.print_exception_no_traceback():
|
32
|
+
raise ImportError(
|
33
|
+
f'Failed to import policy module: {policy}. '
|
34
|
+
'Please check if the module is installed in your Python '
|
35
|
+
'environment.') from e
|
36
|
+
|
37
|
+
try:
|
38
|
+
policy_cls = getattr(module, class_name)
|
39
|
+
except AttributeError as e:
|
40
|
+
with ux_utils.print_exception_no_traceback():
|
41
|
+
raise AttributeError(
|
42
|
+
f'Could not find {class_name} class in module {module_path}. '
|
43
|
+
'Please check with your policy admin for details.') from e
|
44
|
+
|
45
|
+
# Check if the module implements the AdminPolicy interface.
|
46
|
+
if not issubclass(policy_cls, admin_policy.AdminPolicy):
|
47
|
+
with ux_utils.print_exception_no_traceback():
|
48
|
+
raise ValueError(
|
49
|
+
f'Policy class {policy!r} does not implement the AdminPolicy '
|
50
|
+
'interface. Please check with your policy admin for details.')
|
51
|
+
return policy_cls
|
52
|
+
|
53
|
+
|
54
|
+
def apply(
|
55
|
+
entrypoint: Union['dag_lib.Dag', 'task_lib.Task'],
|
56
|
+
use_mutated_config_in_current_request: bool = True,
|
57
|
+
request_options: Optional[admin_policy.RequestOptions] = None,
|
58
|
+
) -> Tuple['dag_lib.Dag', skypilot_config.Config]:
|
59
|
+
"""Applies an admin policy (if registered) to a DAG or a task.
|
60
|
+
|
61
|
+
It mutates a Dag by applying any registered admin policy and also
|
62
|
+
potentially updates (controlled by `use_mutated_config_in_current_request`)
|
63
|
+
the global SkyPilot config if there is any changes made by the policy.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
dag: The dag to be mutated by the policy.
|
67
|
+
use_mutated_config_in_current_request: Whether to use the mutated
|
68
|
+
config in the current request.
|
69
|
+
request_options: Additional options user passed for the current request.
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
- The new copy of dag after applying the policy
|
73
|
+
- The new copy of skypilot config after applying the policy.
|
74
|
+
"""
|
75
|
+
if isinstance(entrypoint, task_lib.Task):
|
76
|
+
dag = dag_lib.Dag()
|
77
|
+
dag.add(entrypoint)
|
78
|
+
else:
|
79
|
+
dag = entrypoint
|
80
|
+
|
81
|
+
policy = skypilot_config.get_nested(('admin_policy',), None)
|
82
|
+
policy_cls = _get_policy_cls(policy)
|
83
|
+
if policy_cls is None:
|
84
|
+
return dag, skypilot_config.to_dict()
|
85
|
+
|
86
|
+
logger.info(f'Applying policy: {policy}')
|
87
|
+
original_config = skypilot_config.to_dict()
|
88
|
+
config = copy.deepcopy(original_config)
|
89
|
+
mutated_dag = dag_lib.Dag()
|
90
|
+
mutated_dag.name = dag.name
|
91
|
+
|
92
|
+
mutated_config = None
|
93
|
+
for task in dag.tasks:
|
94
|
+
user_request = admin_policy.UserRequest(task, config, request_options)
|
95
|
+
try:
|
96
|
+
mutated_user_request = policy_cls.validate_and_mutate(user_request)
|
97
|
+
except Exception as e: # pylint: disable=broad-except
|
98
|
+
with ux_utils.print_exception_no_traceback():
|
99
|
+
raise exceptions.UserRequestRejectedByPolicy(
|
100
|
+
f'{colorama.Fore.RED}User request rejected by policy '
|
101
|
+
f'{policy!r}{colorama.Fore.RESET}: '
|
102
|
+
f'{common_utils.format_exception(e, use_bracket=True)}'
|
103
|
+
) from e
|
104
|
+
if mutated_config is None:
|
105
|
+
mutated_config = mutated_user_request.skypilot_config
|
106
|
+
else:
|
107
|
+
if mutated_config != mutated_user_request.skypilot_config:
|
108
|
+
# In the case of a pipeline of tasks, the mutated config
|
109
|
+
# generated should remain the same for all tasks for now for
|
110
|
+
# simplicity.
|
111
|
+
# TODO(zhwu): We should support per-task mutated config or
|
112
|
+
# allowing overriding required global config in task YAML.
|
113
|
+
with ux_utils.print_exception_no_traceback():
|
114
|
+
raise exceptions.UserRequestRejectedByPolicy(
|
115
|
+
'All tasks must have the same SkyPilot config after '
|
116
|
+
'applying the policy. Please check with your policy '
|
117
|
+
'admin for details.')
|
118
|
+
mutated_dag.add(mutated_user_request.task)
|
119
|
+
assert mutated_config is not None, dag
|
120
|
+
|
121
|
+
# Update the new_dag's graph with the old dag's graph
|
122
|
+
for u, v in dag.graph.edges:
|
123
|
+
u_idx = dag.tasks.index(u)
|
124
|
+
v_idx = dag.tasks.index(v)
|
125
|
+
mutated_dag.graph.add_edge(mutated_dag.tasks[u_idx],
|
126
|
+
mutated_dag.tasks[v_idx])
|
127
|
+
|
128
|
+
if (use_mutated_config_in_current_request and
|
129
|
+
original_config != mutated_config):
|
130
|
+
with tempfile.NamedTemporaryFile(
|
131
|
+
delete=False,
|
132
|
+
mode='w',
|
133
|
+
prefix='policy-mutated-skypilot-config-',
|
134
|
+
suffix='.yaml') as temp_file:
|
135
|
+
|
136
|
+
common_utils.dump_yaml(temp_file.name, dict(**mutated_config))
|
137
|
+
os.environ[skypilot_config.ENV_VAR_SKYPILOT_CONFIG] = temp_file.name
|
138
|
+
logger.debug(f'Updated SkyPilot config: {temp_file.name}')
|
139
|
+
# TODO(zhwu): This is not a clean way to update the SkyPilot config,
|
140
|
+
# because we are resetting the global context for a single DAG,
|
141
|
+
# which is conceptually weird.
|
142
|
+
importlib.reload(skypilot_config)
|
143
|
+
|
144
|
+
logger.debug(f'Mutated user request: {mutated_user_request}')
|
145
|
+
return mutated_dag, mutated_config
|
sky/utils/common_utils.py
CHANGED
@@ -300,7 +300,7 @@ def user_and_hostname_hash() -> str:
|
|
300
300
|
return f'{getpass.getuser()}-{hostname_hash}'
|
301
301
|
|
302
302
|
|
303
|
-
def read_yaml(path) -> Dict[str, Any]:
|
303
|
+
def read_yaml(path: str) -> Dict[str, Any]:
|
304
304
|
with open(path, 'r', encoding='utf-8') as f:
|
305
305
|
config = yaml.safe_load(f)
|
306
306
|
return config
|
@@ -316,12 +316,13 @@ def read_yaml_all(path: str) -> List[Dict[str, Any]]:
|
|
316
316
|
return configs
|
317
317
|
|
318
318
|
|
319
|
-
def dump_yaml(path, config
|
319
|
+
def dump_yaml(path: str, config: Union[List[Dict[str, Any]],
|
320
|
+
Dict[str, Any]]) -> None:
|
320
321
|
with open(path, 'w', encoding='utf-8') as f:
|
321
322
|
f.write(dump_yaml_str(config))
|
322
323
|
|
323
324
|
|
324
|
-
def dump_yaml_str(config):
|
325
|
+
def dump_yaml_str(config: Union[List[Dict[str, Any]], Dict[str, Any]]) -> str:
|
325
326
|
# https://github.com/yaml/pyyaml/issues/127
|
326
327
|
class LineBreakDumper(yaml.SafeDumper):
|
327
328
|
|
@@ -331,9 +332,9 @@ def dump_yaml_str(config):
|
|
331
332
|
super().write_line_break()
|
332
333
|
|
333
334
|
if isinstance(config, list):
|
334
|
-
dump_func = yaml.dump_all
|
335
|
+
dump_func = yaml.dump_all # type: ignore
|
335
336
|
else:
|
336
|
-
dump_func = yaml.dump
|
337
|
+
dump_func = yaml.dump # type: ignore
|
337
338
|
return dump_func(config,
|
338
339
|
Dumper=LineBreakDumper,
|
339
340
|
sort_keys=False,
|
sky/utils/controller_utils.py
CHANGED
@@ -44,8 +44,12 @@ CONTROLLER_RESOURCES_NOT_VALID_MESSAGE = (
|
|
44
44
|
'{controller_type}.controller.resources is a valid resources spec. '
|
45
45
|
'Details:\n {err}')
|
46
46
|
|
47
|
-
# The
|
48
|
-
|
47
|
+
# The suffix for local skypilot config path for a job/service in file mounts
|
48
|
+
# that tells the controller logic to update the config with specific settings,
|
49
|
+
# e.g., removing the ssh_proxy_command when a job/service is launched in a same
|
50
|
+
# cloud as controller.
|
51
|
+
_LOCAL_SKYPILOT_CONFIG_PATH_SUFFIX = (
|
52
|
+
'__skypilot:local_skypilot_config_path.yaml')
|
49
53
|
|
50
54
|
|
51
55
|
@dataclasses.dataclass
|
@@ -350,8 +354,21 @@ def download_and_stream_latest_job_log(
|
|
350
354
|
|
351
355
|
|
352
356
|
def shared_controller_vars_to_fill(
|
353
|
-
controller: Controllers,
|
354
|
-
|
357
|
+
controller: Controllers, remote_user_config_path: str,
|
358
|
+
local_user_config: Dict[str, Any]) -> Dict[str, str]:
|
359
|
+
if not local_user_config:
|
360
|
+
local_user_config_path = None
|
361
|
+
else:
|
362
|
+
# Remove admin_policy from local_user_config so that it is not applied
|
363
|
+
# again on the controller. This is required since admin_policy is not
|
364
|
+
# installed on the controller.
|
365
|
+
local_user_config.pop('admin_policy', None)
|
366
|
+
with tempfile.NamedTemporaryFile(
|
367
|
+
delete=False,
|
368
|
+
suffix=_LOCAL_SKYPILOT_CONFIG_PATH_SUFFIX) as temp_file:
|
369
|
+
common_utils.dump_yaml(temp_file.name, dict(**local_user_config))
|
370
|
+
local_user_config_path = temp_file.name
|
371
|
+
|
355
372
|
vars_to_fill: Dict[str, Any] = {
|
356
373
|
'cloud_dependencies_installation_commands':
|
357
374
|
_get_cloud_dependencies_installation_commands(controller),
|
@@ -360,6 +377,7 @@ def shared_controller_vars_to_fill(
|
|
360
377
|
# accessed.
|
361
378
|
'sky_activate_python_env': constants.ACTIVATE_SKY_REMOTE_PYTHON_ENV,
|
362
379
|
'sky_python_cmd': constants.SKY_PYTHON_CMD,
|
380
|
+
'local_user_config_path': local_user_config_path,
|
363
381
|
}
|
364
382
|
env_vars: Dict[str, str] = {
|
365
383
|
env.value: '1' for env in env_options.Options if env.get()
|
@@ -481,7 +499,8 @@ def get_controller_resources(
|
|
481
499
|
|
482
500
|
|
483
501
|
def _setup_proxy_command_on_controller(
|
484
|
-
controller_launched_cloud: 'clouds.Cloud'
|
502
|
+
controller_launched_cloud: 'clouds.Cloud',
|
503
|
+
user_config: Dict[str, Any]) -> skypilot_config.Config:
|
485
504
|
"""Sets up proxy command on the controller.
|
486
505
|
|
487
506
|
This function should be called on the controller (remote cluster), which
|
@@ -515,21 +534,20 @@ def _setup_proxy_command_on_controller(
|
|
515
534
|
# (or name). It may not be a sufficient check (as it's always
|
516
535
|
# possible that peering is not set up), but it may catch some
|
517
536
|
# obvious errors.
|
537
|
+
config = skypilot_config.Config.from_dict(user_config)
|
518
538
|
proxy_command_key = (str(controller_launched_cloud).lower(),
|
519
539
|
'ssh_proxy_command')
|
520
|
-
ssh_proxy_command =
|
521
|
-
config_dict = skypilot_config.to_dict()
|
540
|
+
ssh_proxy_command = config.get_nested(proxy_command_key, None)
|
522
541
|
if isinstance(ssh_proxy_command, str):
|
523
|
-
|
542
|
+
config.set_nested(proxy_command_key, None)
|
524
543
|
elif isinstance(ssh_proxy_command, dict):
|
525
544
|
# Instead of removing the key, we set the value to empty string
|
526
545
|
# so that the controller will only try the regions specified by
|
527
546
|
# the keys.
|
528
547
|
ssh_proxy_command = {k: None for k in ssh_proxy_command}
|
529
|
-
|
530
|
-
ssh_proxy_command)
|
548
|
+
config.set_nested(proxy_command_key, ssh_proxy_command)
|
531
549
|
|
532
|
-
return
|
550
|
+
return config
|
533
551
|
|
534
552
|
|
535
553
|
def replace_skypilot_config_path_in_file_mounts(
|
@@ -543,25 +561,20 @@ def replace_skypilot_config_path_in_file_mounts(
|
|
543
561
|
if file_mounts is None:
|
544
562
|
return
|
545
563
|
replaced = False
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
if to_replace:
|
558
|
-
file_mounts[remote_path] = f.name
|
559
|
-
replaced = True
|
560
|
-
else:
|
561
|
-
del file_mounts[remote_path]
|
564
|
+
for remote_path, local_path in list(file_mounts.items()):
|
565
|
+
if local_path is None:
|
566
|
+
del file_mounts[remote_path]
|
567
|
+
continue
|
568
|
+
if local_path.endswith(_LOCAL_SKYPILOT_CONFIG_PATH_SUFFIX):
|
569
|
+
with tempfile.NamedTemporaryFile('w', delete=False) as f:
|
570
|
+
user_config = common_utils.read_yaml(local_path)
|
571
|
+
config = _setup_proxy_command_on_controller(cloud, user_config)
|
572
|
+
common_utils.dump_yaml(f.name, dict(**config))
|
573
|
+
file_mounts[remote_path] = f.name
|
574
|
+
replaced = True
|
562
575
|
if replaced:
|
563
|
-
logger.debug(f'Replaced {
|
564
|
-
f'the real path in file mounts: {file_mounts}')
|
576
|
+
logger.debug(f'Replaced {_LOCAL_SKYPILOT_CONFIG_PATH_SUFFIX} '
|
577
|
+
f'with the real path in file mounts: {file_mounts}')
|
565
578
|
|
566
579
|
|
567
580
|
def maybe_translate_local_file_mounts_and_sync_up(task: 'task_lib.Task',
|
sky/utils/dag_utils.py
CHANGED
@@ -36,30 +36,33 @@ The command can then be run as:
|
|
36
36
|
|
37
37
|
|
38
38
|
def convert_entrypoint_to_dag(entrypoint: Any) -> 'dag_lib.Dag':
|
39
|
-
"""
|
39
|
+
"""Converts the entrypoint to a sky.Dag and applies the policy.
|
40
40
|
|
41
41
|
Raises TypeError if 'entrypoint' is not a 'sky.Task' or 'sky.Dag'.
|
42
42
|
"""
|
43
43
|
# Not suppressing stacktrace: when calling this via API user may want to
|
44
44
|
# see their own program in the stacktrace. Our CLI impl would not trigger
|
45
45
|
# these errors.
|
46
|
+
converted_dag: 'dag_lib.Dag'
|
46
47
|
if isinstance(entrypoint, str):
|
47
48
|
with ux_utils.print_exception_no_traceback():
|
48
49
|
raise TypeError(_ENTRYPOINT_STRING_AS_DAG_MESSAGE)
|
49
50
|
elif isinstance(entrypoint, dag_lib.Dag):
|
50
|
-
|
51
|
+
converted_dag = copy.deepcopy(entrypoint)
|
51
52
|
elif isinstance(entrypoint, task_lib.Task):
|
52
53
|
entrypoint = copy.deepcopy(entrypoint)
|
53
54
|
with dag_lib.Dag() as dag:
|
54
55
|
dag.add(entrypoint)
|
55
56
|
dag.name = entrypoint.name
|
56
|
-
|
57
|
+
converted_dag = dag
|
57
58
|
else:
|
58
59
|
with ux_utils.print_exception_no_traceback():
|
59
60
|
raise TypeError(
|
60
61
|
'Expected a sky.Task or sky.Dag but received argument of type: '
|
61
62
|
f'{type(entrypoint)}')
|
62
63
|
|
64
|
+
return converted_dag
|
65
|
+
|
63
66
|
|
64
67
|
def load_chain_dag_from_yaml(
|
65
68
|
path: str,
|
sky/utils/schemas.py
CHANGED
@@ -848,6 +848,13 @@ def get_config_schema():
|
|
848
848
|
},
|
849
849
|
}
|
850
850
|
|
851
|
+
admin_policy_schema = {
|
852
|
+
'type': 'string',
|
853
|
+
# Check regex to be a valid python module path
|
854
|
+
'pattern': (r'^[a-zA-Z_][a-zA-Z0-9_]*'
|
855
|
+
r'(\.[a-zA-Z_][a-zA-Z0-9_]*)+$'),
|
856
|
+
}
|
857
|
+
|
851
858
|
allowed_clouds = {
|
852
859
|
# A list of cloud names that are allowed to be used
|
853
860
|
'type': 'array',
|
@@ -905,6 +912,7 @@ def get_config_schema():
|
|
905
912
|
'spot': controller_resources_schema,
|
906
913
|
'serve': controller_resources_schema,
|
907
914
|
'allowed_clouds': allowed_clouds,
|
915
|
+
'admin_policy': admin_policy_schema,
|
908
916
|
'docker': docker_configs,
|
909
917
|
'nvidia_gpus': gpu_configs,
|
910
918
|
**cloud_configs,
|
{skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/RECORD
RENAMED
@@ -1,17 +1,18 @@
|
|
1
|
-
sky/__init__.py,sha256=
|
1
|
+
sky/__init__.py,sha256=0PGQwE95hQVOiAdvYFyYZVYfolEa40bLrT5YQOijeNc,5854
|
2
|
+
sky/admin_policy.py,sha256=hPo02f_A32gCqhUueF0QYy1fMSSKqRwYEg_9FxScN_s,3248
|
2
3
|
sky/authentication.py,sha256=yvpdkXS9htf-X83DPCiSG3mQ41y0zV1BQ0YgOMgTYBU,20612
|
3
4
|
sky/check.py,sha256=jLMIIJrseaZj1_o5WkbaD9XdyXIlCaT6pyAaIFdhdmA,9079
|
4
5
|
sky/cli.py,sha256=2cOw3lXzRA-uLlEH-eK7N_1VmUpf1LR4Ztu-ZaKu3Is,201673
|
5
6
|
sky/cloud_stores.py,sha256=RjFgmRhUh1Kk__f6g3KxzLp9s7dA0pFK4W1AukEuUaw,21153
|
6
7
|
sky/core.py,sha256=YF_6kwj8Ja171Oycb8L25SZ7V_ylZYovFS_jpnjwGo0,34408
|
7
|
-
sky/dag.py,sha256=
|
8
|
-
sky/exceptions.py,sha256=
|
9
|
-
sky/execution.py,sha256=
|
8
|
+
sky/dag.py,sha256=WLFWr5hfrwjd31uYlNvI-zWUk7tLaT_gzJn4LzbVtkE,2780
|
9
|
+
sky/exceptions.py,sha256=s7j0iCa1Ec0rU1ABb9EAhqn2qFm22bmKQV_ckgRlMGk,8720
|
10
|
+
sky/execution.py,sha256=97yhNh5BKBh2ZJW8GefGID_4KCYU-arnvemSJB9rf6U,25552
|
10
11
|
sky/global_user_state.py,sha256=PywEmUutF97XBgRMClR6IS5_KM8JJC0oA1LsPUZebp0,28681
|
11
12
|
sky/optimizer.py,sha256=YGBhJPlcvylYON7MLrYEMtBOqJLt4LdlguQclVvvl4E,58677
|
12
13
|
sky/resources.py,sha256=_959wcQnoiAYesslN9BPXWABFaQfc_TFXPO_o7SPlxI,67325
|
13
14
|
sky/sky_logging.py,sha256=I59__M9taBjDim15ie0m25Vtn6itLtR9Ao8W9FS36Xs,4253
|
14
|
-
sky/skypilot_config.py,sha256=
|
15
|
+
sky/skypilot_config.py,sha256=E3g65cX3P3dT9b5N0GgFBG6yB0FXwIGpisKoozmJmWU,9094
|
15
16
|
sky/status_lib.py,sha256=J7Jb4_Dz0v2T64ttOdyUgpokvl4S0sBJrMfH7Fvo51A,1457
|
16
17
|
sky/task.py,sha256=KDsTBIxYpkCOPHv3_ei5H3LDMiGHvDeS9_2HeL6yyLA,49766
|
17
18
|
sky/adaptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -94,8 +95,8 @@ sky/data/storage.py,sha256=JYeEWQtCXg9h6CjCaAsINV1MXcvFswuUgf3c0GOZQSs,162523
|
|
94
95
|
sky/data/storage_utils.py,sha256=-s0iQhV8JVx1J2gWtoBFrN04MGv2oVYxo_Hw43R2BSY,6867
|
95
96
|
sky/jobs/__init__.py,sha256=9cqFutVlfjQb7t8hzG-ZlQmMlbmfMirn0KNBxIFnJYQ,1398
|
96
97
|
sky/jobs/constants.py,sha256=YLgcCg_RHSYr_rfsI_4UIdXk78KKKOK29Oem88t5j8I,1350
|
97
|
-
sky/jobs/controller.py,sha256=
|
98
|
-
sky/jobs/core.py,sha256=
|
98
|
+
sky/jobs/controller.py,sha256=k28bbicxtML6p1YxSetk-1nhBHPCubpvLWJsh7TtU9c,26701
|
99
|
+
sky/jobs/core.py,sha256=Q5ExRWnF7yAYWJxwnB9NfAGBVDNqKYBCrWsypiMLCpY,13637
|
99
100
|
sky/jobs/recovery_strategy.py,sha256=G3iFicEajB-l9FefvcqjqPIazb1X8BJ_AgVmD5bDV2w,25556
|
100
101
|
sky/jobs/state.py,sha256=nf6hduj0dSekmP7scW5mKHiYQs6WkdigJpJJbLEBdEw,23041
|
101
102
|
sky/jobs/utils.py,sha256=ZB2dJxtJ4hbCRdxHmy8wrmtXIvvGGE80kk5BQTOQWkQ,35653
|
@@ -171,7 +172,7 @@ sky/serve/__init__.py,sha256=Qg_XPOtQsUxiN-Q3njHZRfzoMcQ_KKU1QthkiTbESDw,1661
|
|
171
172
|
sky/serve/autoscalers.py,sha256=CAdp0vFN_eBx8K3svJToj1hTEc29M5gB2V1BhV9a_lI,30136
|
172
173
|
sky/serve/constants.py,sha256=OansIC7a0Pwat-Y5SF43T9phad_EvyjKO3peZgKFEHk,4367
|
173
174
|
sky/serve/controller.py,sha256=spXd-QXOy0xpyN5VtZ6E3BoHIvE3fOZTysKqS-X880U,7651
|
174
|
-
sky/serve/core.py,sha256=
|
175
|
+
sky/serve/core.py,sha256=cW2SNMPMbGtOcqASHnL__B12BCIErUilGtFw3olQbjk,28947
|
175
176
|
sky/serve/load_balancer.py,sha256=90HWkhhmfrW9kaGVSMxnqzYB_o5PwAOKX0mTAVfQVFM,11548
|
176
177
|
sky/serve/load_balancing_policies.py,sha256=ExdwH_pxPYpJ6CkoTQCOPSa4lzwbq1LFFMKzmIu8ryk,2331
|
177
178
|
sky/serve/replica_managers.py,sha256=c-S8m1Akgy13MjjX3BrqRlUiwbpjbicXEyWASjosPN0,57308
|
@@ -222,7 +223,7 @@ sky/templates/cudo-ray.yml.j2,sha256=SEHVY57iBauCOE2HYJtYVFEKlriAkdwQu_p86a1n_bA
|
|
222
223
|
sky/templates/fluidstack-ray.yml.j2,sha256=t8TCULgiErCZdtFmBZVsA8ZdcqR7ccwsmQhuDFTBEAU,3541
|
223
224
|
sky/templates/gcp-ray.yml.j2,sha256=q2xSWxxYI8MVAq_mA__8FF6PwEqXCAW1SOEOGTt0qPw,9591
|
224
225
|
sky/templates/ibm-ray.yml.j2,sha256=RMBUqPId8i4CnVwcyfK3DbRapF1jFMuGQlY0E0PFbMU,6669
|
225
|
-
sky/templates/jobs-controller.yaml.j2,sha256=
|
226
|
+
sky/templates/jobs-controller.yaml.j2,sha256=Gu3ogFxFYr09VEXP-6zEbrCUOFo1aYxWEjAq7whCrxo,1607
|
226
227
|
sky/templates/kubernetes-ingress.yml.j2,sha256=73iDklVDWBMbItg0IexCa6_ClXPJOxw7PWz3leku4nE,1340
|
227
228
|
sky/templates/kubernetes-loadbalancer.yml.j2,sha256=IxrNYM366N01bbkJEbZ_UPYxUP8wyVEbRNFHRsBuLsw,626
|
228
229
|
sky/templates/kubernetes-port-forward-proxy-command.sh,sha256=HlG7CPBBedCVBlL9qv0erW_eKm6Irj0LFyaAWuJW_lc,3148
|
@@ -234,26 +235,27 @@ sky/templates/oci-ray.yml.j2,sha256=5XfIobW9XuspIpEhI4vFIEcJEFCdtFJqEGfX03zL6DE,
|
|
234
235
|
sky/templates/paperspace-ray.yml.j2,sha256=HQjZNamrB_a4fOMCxQXSVdV5JIHtbGtAE0JzEO8uuVQ,4021
|
235
236
|
sky/templates/runpod-ray.yml.j2,sha256=p3BtYBHzROtNJqnjEo1xCmGSJQfCZYdarWszhDYyl0Q,3697
|
236
237
|
sky/templates/scp-ray.yml.j2,sha256=I9u8Ax-lit-d6UrCC9BVU8avst8w1cwK6TrzZBcz_JM,5608
|
237
|
-
sky/templates/sky-serve-controller.yaml.j2,sha256=
|
238
|
+
sky/templates/sky-serve-controller.yaml.j2,sha256=V1IiYhArv_D_7JzC3sVN4nKlSCCCL1AYtIdxFSa-f4c,1628
|
238
239
|
sky/templates/vsphere-ray.yml.j2,sha256=cOQ-qdpxGA2FHajMMhTJI-SmlYzdPterX4Gsiq-nkb0,3587
|
239
240
|
sky/usage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
240
241
|
sky/usage/constants.py,sha256=8xpg9vhDU9A3eObtpkNFjwa42oCazqGEv4yw_vJSO7U,590
|
241
242
|
sky/usage/usage_lib.py,sha256=uqclBc87_9D_QVWigCjOIfWFoVB6re68C7RnwjzRYvg,17870
|
242
243
|
sky/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
243
244
|
sky/utils/accelerator_registry.py,sha256=BO4iYH5bV80Xyp4EPfO0n1D3LL0FvESCy7xm59Je3_o,3798
|
245
|
+
sky/utils/admin_policy_utils.py,sha256=zFCu1OFIrZRfQNY0JFRO1502WFfdqZhwAU_QgM4fO9U,5943
|
244
246
|
sky/utils/cluster_yaml_utils.py,sha256=1wRRYqI1kI-eFs1pMW4r_FFjHJ0zamq6v2RRI-Gtx5E,849
|
245
247
|
sky/utils/command_runner.py,sha256=SKA90-i4FK7fn9TpkztXY6E3fCBGUjGj-jcrgvb8DAQ,33643
|
246
248
|
sky/utils/command_runner.pyi,sha256=1khh14BhdOpMxvk9Ydnd3OFdas5Nha6dSOzy5xLBUU4,7710
|
247
|
-
sky/utils/common_utils.py,sha256=
|
248
|
-
sky/utils/controller_utils.py,sha256=
|
249
|
-
sky/utils/dag_utils.py,sha256=
|
249
|
+
sky/utils/common_utils.py,sha256=O6PlZTCNhbuXOzjuV2DKw43niWE_qPfYZNGhnMtZzQg,24028
|
250
|
+
sky/utils/controller_utils.py,sha256=VtdjKH9u1kWwUOMzPUxuLpT-XXQ2gCLCLOldB-vdh_8,37483
|
251
|
+
sky/utils/dag_utils.py,sha256=gjGZiJj4_GYsraXX67e6ElvbmOByJcyjSfvVgYZiXvs,5588
|
250
252
|
sky/utils/db_utils.py,sha256=AOvMmBEN9cF4I7CoXihPCtus4mU2VDGjBQSVMMgzKlA,2786
|
251
253
|
sky/utils/env_options.py,sha256=1VXyd3bhiUgGfCpmmTqM9PagRo1ILBH4-pzIxmIeE6E,861
|
252
254
|
sky/utils/kubernetes_enums.py,sha256=imGqHSa8O07zD_6xH1SDMM7dBU5lF5fzFFlQuQy00QM,1384
|
253
255
|
sky/utils/log_utils.py,sha256=W7FYK7xzvbq4V-8R-ihLtz939ryvtABug6O-4DFrjho,8139
|
254
256
|
sky/utils/resources_utils.py,sha256=snByBxgx3Hnjfch2uysdAA3D-OAwrnuzTDHug36s5H4,6515
|
255
257
|
sky/utils/rich_utils.py,sha256=5ZVhzlFx-nhqMXwv00eO9xC4rz7ibDlfD2lmGhZrJEY,1581
|
256
|
-
sky/utils/schemas.py,sha256=
|
258
|
+
sky/utils/schemas.py,sha256=bJan9aOi2gK6jnTzxNkX1kivIUsQ5Mc5SzKiBj3A97g,28597
|
257
259
|
sky/utils/subprocess_utils.py,sha256=zK0L3mvAkvKX1nFFI4IFEmRWX9ytpguhhxOTYUDKoDs,6507
|
258
260
|
sky/utils/timeline.py,sha256=ao_nm0y52ZQILfL7Y92c3pSEFRyPm_ElORC3DrI5BwQ,3936
|
259
261
|
sky/utils/ux_utils.py,sha256=318TRunQCyJpJXonfiJ1SVotNA-6K4F2XgMEYjvWvsk,3264
|
@@ -270,9 +272,9 @@ sky/utils/kubernetes/k8s_gpu_labeler_job.yaml,sha256=KPqp23B-zQ2SZK03jdHeF9fLTog
|
|
270
272
|
sky/utils/kubernetes/k8s_gpu_labeler_setup.yaml,sha256=VLKT2KKimZu1GDg_4AIlIt488oMQvhRZWwsj9vBbPUg,3812
|
271
273
|
sky/utils/kubernetes/rsync_helper.sh,sha256=Ma-N9a271fTfdgP5-8XIQL7KPf8IPUo-uY004PCdUFo,747
|
272
274
|
sky/utils/kubernetes/ssh_jump_lifecycle_manager.py,sha256=RFLJ3k7MR5UN4SKHykQ0lV9SgXumoULpKYIAt1vh-HU,6560
|
273
|
-
skypilot_nightly-1.0.0.
|
274
|
-
skypilot_nightly-1.0.0.
|
275
|
-
skypilot_nightly-1.0.0.
|
276
|
-
skypilot_nightly-1.0.0.
|
277
|
-
skypilot_nightly-1.0.0.
|
278
|
-
skypilot_nightly-1.0.0.
|
275
|
+
skypilot_nightly-1.0.0.dev20240924.dist-info/LICENSE,sha256=emRJAvE7ngL6x0RhQvlns5wJzGI3NEQ_WMjNmd9TZc4,12170
|
276
|
+
skypilot_nightly-1.0.0.dev20240924.dist-info/METADATA,sha256=ctV54hImdfPFY1139WcZs58dlHc2TzsIKt5biVabug8,19011
|
277
|
+
skypilot_nightly-1.0.0.dev20240924.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
278
|
+
skypilot_nightly-1.0.0.dev20240924.dist-info/entry_points.txt,sha256=StA6HYpuHj-Y61L2Ze-hK2IcLWgLZcML5gJu8cs6nU4,36
|
279
|
+
skypilot_nightly-1.0.0.dev20240924.dist-info/top_level.txt,sha256=qA8QuiNNb6Y1OF-pCUtPEr6sLEwy2xJX06Bd_CrtrHY,4
|
280
|
+
skypilot_nightly-1.0.0.dev20240924.dist-info/RECORD,,
|
File without changes
|
{skypilot_nightly-1.0.0.dev20240922.dist-info → skypilot_nightly-1.0.0.dev20240924.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|