skypilot-nightly 1.0.0.dev20241108__py3-none-any.whl → 1.0.0.dev20241109__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 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 = '7bea46813b47a46ce7b00501413cac637ee400a3'
8
+ _SKYPILOT_COMMIT_SHA = '42c79e1d0a5e018e275705ada53957573f9a0181'
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.dev20241108'
38
+ __version__ = '1.0.0.dev20241109'
39
39
  __root_dir__ = os.path.dirname(os.path.abspath(__file__))
40
40
 
41
41
 
@@ -3711,7 +3711,8 @@ class CloudVmRayBackend(backends.Backend['CloudVmRayResourceHandle']):
3711
3711
  handle: CloudVmRayResourceHandle,
3712
3712
  job_id: Optional[int],
3713
3713
  managed_job_id: Optional[int] = None,
3714
- follow: bool = True) -> int:
3714
+ follow: bool = True,
3715
+ tail: int = 0) -> int:
3715
3716
  """Tail the logs of a job.
3716
3717
 
3717
3718
  Args:
@@ -3719,10 +3720,13 @@ class CloudVmRayBackend(backends.Backend['CloudVmRayResourceHandle']):
3719
3720
  job_id: The job ID to tail the logs of.
3720
3721
  managed_job_id: The managed job ID for display purpose only.
3721
3722
  follow: Whether to follow the logs.
3723
+ tail: The number of lines to display from the end of the
3724
+ log file. If 0, print all lines.
3722
3725
  """
3723
3726
  code = job_lib.JobLibCodeGen.tail_logs(job_id,
3724
3727
  managed_job_id=managed_job_id,
3725
- follow=follow)
3728
+ follow=follow,
3729
+ tail=tail)
3726
3730
  if job_id is None and managed_job_id is None:
3727
3731
  logger.info(
3728
3732
  'Job ID not provided. Streaming the logs of the latest job.')
@@ -129,7 +129,11 @@ def _build_sky_wheel() -> pathlib.Path:
129
129
 
130
130
  wheel_dir = WHEEL_DIR / hash_of_latest_wheel
131
131
  wheel_dir.mkdir(parents=True, exist_ok=True)
132
- shutil.move(str(wheel_path), wheel_dir)
132
+ # shutil.move will fail when the file already exists and is being
133
+ # moved across filesystems.
134
+ if not os.path.exists(
135
+ os.path.join(wheel_dir, os.path.basename(wheel_path))):
136
+ shutil.move(str(wheel_path), wheel_dir)
133
137
  return wheel_dir / wheel_path.name
134
138
 
135
139
 
sky/cli.py CHANGED
@@ -46,6 +46,7 @@ from rich import progress as rich_progress
46
46
  import yaml
47
47
 
48
48
  import sky
49
+ from sky import admin_policy
49
50
  from sky import backends
50
51
  from sky import check as sky_check
51
52
  from sky import clouds as sky_clouds
@@ -67,6 +68,7 @@ from sky.skylet import constants
67
68
  from sky.skylet import job_lib
68
69
  from sky.skylet import log_lib
69
70
  from sky.usage import usage_lib
71
+ from sky.utils import admin_policy_utils
70
72
  from sky.utils import common_utils
71
73
  from sky.utils import controller_utils
72
74
  from sky.utils import dag_utils
@@ -582,6 +584,15 @@ def _launch_with_confirm(
582
584
  with ux_utils.print_exception_no_traceback():
583
585
  raise RuntimeError(f'{colorama.Fore.YELLOW}{e}'
584
586
  f'{colorama.Style.RESET_ALL}') from e
587
+ dag, _ = admin_policy_utils.apply(
588
+ dag,
589
+ request_options=admin_policy.RequestOptions(
590
+ cluster_name=cluster,
591
+ idle_minutes_to_autostop=idle_minutes_to_autostop,
592
+ down=down,
593
+ dryrun=dryrun,
594
+ ),
595
+ )
585
596
  dag = sky.optimize(dag)
586
597
  task = dag.tasks[0]
587
598
 
@@ -2011,6 +2022,12 @@ def queue(clusters: List[str], skip_finished: bool, all_users: bool):
2011
2022
  help=('Follow the logs of a job. '
2012
2023
  'If --no-follow is specified, print the log so far and exit. '
2013
2024
  '[default: --follow]'))
2025
+ @click.option(
2026
+ '--tail',
2027
+ default=0,
2028
+ type=int,
2029
+ help=('The number of lines to display from the end of the log file. '
2030
+ 'Default is 0, which means print all lines.'))
2014
2031
  @click.argument('cluster',
2015
2032
  required=True,
2016
2033
  type=str,
@@ -2024,6 +2041,7 @@ def logs(
2024
2041
  sync_down: bool,
2025
2042
  status: bool, # pylint: disable=redefined-outer-name
2026
2043
  follow: bool,
2044
+ tail: int,
2027
2045
  ):
2028
2046
  # NOTE(dev): Keep the docstring consistent between the Python API and CLI.
2029
2047
  """Tail the log of a job.
@@ -2090,7 +2108,7 @@ def logs(
2090
2108
  click.secho(f'Job {id_str}not found', fg='red')
2091
2109
  sys.exit(1)
2092
2110
 
2093
- core.tail_logs(cluster, job_id, follow)
2111
+ core.tail_logs(cluster, job_id, follow, tail)
2094
2112
 
2095
2113
 
2096
2114
  @cli.command()
@@ -3667,6 +3685,8 @@ def jobs_launch(
3667
3685
 
3668
3686
  click.secho(f'Managed job {dag.name!r} will be launched on (estimated):',
3669
3687
  fg='cyan')
3688
+ dag, _ = admin_policy_utils.apply(
3689
+ dag, use_mutated_config_in_current_request=False)
3670
3690
  dag = sky.optimize(dag)
3671
3691
 
3672
3692
  if not yes:
@@ -4145,6 +4165,8 @@ def serve_up(
4145
4165
  fg='cyan')
4146
4166
  with sky.Dag() as dag:
4147
4167
  dag.add(task)
4168
+ dag, _ = admin_policy_utils.apply(
4169
+ dag, use_mutated_config_in_current_request=False)
4148
4170
  sky.optimize(dag)
4149
4171
 
4150
4172
  if not yes:
@@ -4261,6 +4283,8 @@ def serve_update(
4261
4283
  fg='cyan')
4262
4284
  with sky.Dag() as dag:
4263
4285
  dag.add(task)
4286
+ dag, _ = admin_policy_utils.apply(
4287
+ dag, use_mutated_config_in_current_request=False)
4264
4288
  sky.optimize(dag)
4265
4289
 
4266
4290
  if not yes:
sky/core.py CHANGED
@@ -742,7 +742,8 @@ def cancel(
742
742
  @usage_lib.entrypoint
743
743
  def tail_logs(cluster_name: str,
744
744
  job_id: Optional[int],
745
- follow: bool = True) -> None:
745
+ follow: bool = True,
746
+ tail: int = 0) -> None:
746
747
  # NOTE(dev): Keep the docstring consistent between the Python API and CLI.
747
748
  """Tail the logs of a job.
748
749
 
@@ -775,7 +776,7 @@ def tail_logs(cluster_name: str,
775
776
  f'{colorama.Style.RESET_ALL}')
776
777
 
777
778
  usage_lib.record_cluster_name_for_current_operation(cluster_name)
778
- backend.tail_logs(handle, job_id, follow=follow)
779
+ backend.tail_logs(handle, job_id, follow=follow, tail=tail)
779
780
 
780
781
 
781
782
  @usage_lib.entrypoint
sky/dag.py CHANGED
@@ -23,6 +23,7 @@ class Dag:
23
23
 
24
24
  self.graph = nx.DiGraph()
25
25
  self.name: Optional[str] = None
26
+ self.policy_applied: bool = False
26
27
 
27
28
  def add(self, task: 'task.Task') -> None:
28
29
  self.graph.add_node(task)
@@ -276,23 +276,11 @@ def get_mounting_command(
276
276
  script = get_mounting_script(mount_path, mount_cmd, install_cmd,
277
277
  version_check_cmd)
278
278
 
279
- # TODO(romilb): Get direct bash script to work like so:
280
- # command = f'bash <<-\EOL' \
281
- # f'{script}' \
282
- # 'EOL'
283
-
284
- # TODO(romilb): This heredoc should have EOF after script, but it
285
- # fails with sky's ssh pipeline. Instead, we don't use EOF and use )
286
- # as the end of heredoc. This raises a warning (here-document delimited
287
- # by end-of-file) that can be safely ignored.
288
-
289
279
  # While these commands are run sequentially for each storage object,
290
280
  # we add random int to be on the safer side and avoid collisions.
291
281
  script_path = f'~/.sky/mount_{random.randint(0, 1000000)}.sh'
292
- first_line = r'(cat <<-\EOF > {}'.format(script_path)
293
- command = (f'{first_line}'
294
- f'{script}'
295
- f') && chmod +x {script_path}'
296
- f' && bash {script_path}'
297
- f' && rm {script_path}')
282
+ command = (f'echo {shlex.quote(script)} > {script_path} && '
283
+ f'chmod +x {script_path} && '
284
+ f'bash {script_path} && '
285
+ f'rm {script_path}')
298
286
  return command
sky/exceptions.py CHANGED
@@ -3,6 +3,8 @@ import enum
3
3
  import typing
4
4
  from typing import List, Optional, Sequence
5
5
 
6
+ from sky.utils import env_options
7
+
6
8
  if typing.TYPE_CHECKING:
7
9
  from sky import status_lib
8
10
  from sky.backends import backend
@@ -104,7 +106,8 @@ class CommandError(Exception):
104
106
  if not command:
105
107
  message = error_msg
106
108
  else:
107
- if len(command) > 100:
109
+ if (len(command) > 100 and
110
+ not env_options.Options.SHOW_DEBUG_INFO.get()):
108
111
  # Chunck the command to avoid overflow.
109
112
  command = command[:100] + '...'
110
113
  message = (f'Command {command} failed with return code '
sky/execution.py CHANGED
@@ -160,14 +160,16 @@ def _execute(
160
160
  """
161
161
 
162
162
  dag = dag_utils.convert_entrypoint_to_dag(entrypoint)
163
- dag, _ = admin_policy_utils.apply(
164
- dag,
165
- request_options=admin_policy.RequestOptions(
166
- cluster_name=cluster_name,
167
- idle_minutes_to_autostop=idle_minutes_to_autostop,
168
- down=down,
169
- dryrun=dryrun,
170
- ))
163
+ if not dag.policy_applied:
164
+ dag, _ = admin_policy_utils.apply(
165
+ dag,
166
+ request_options=admin_policy.RequestOptions(
167
+ cluster_name=cluster_name,
168
+ idle_minutes_to_autostop=idle_minutes_to_autostop,
169
+ down=down,
170
+ dryrun=dryrun,
171
+ ),
172
+ )
171
173
  assert len(dag) == 1, f'We support 1 task for now. {dag}'
172
174
  task = dag.tasks[0]
173
175
 
sky/jobs/core.py CHANGED
@@ -59,8 +59,10 @@ def launch(
59
59
  """
60
60
  entrypoint = task
61
61
  dag_uuid = str(uuid.uuid4().hex[:4])
62
-
63
62
  dag = dag_utils.convert_entrypoint_to_dag(entrypoint)
63
+ # Always apply the policy again here, even though it might have been applied
64
+ # in the CLI. This is to ensure that we apply the policy to the final DAG
65
+ # and get the mutated config.
64
66
  dag, mutated_user_config = admin_policy_utils.apply(
65
67
  dag, use_mutated_config_in_current_request=False)
66
68
  if not dag.is_chain():
@@ -42,8 +42,9 @@ def _skypilot_log_error_and_exit_for_failover(error: str) -> None:
42
42
  Mainly used for handling VPC/subnet errors before nodes are launched.
43
43
  """
44
44
  # NOTE: keep. The backend looks for this to know no nodes are launched.
45
- prefix = 'SKYPILOT_ERROR_NO_NODES_LAUNCHED: '
46
- raise RuntimeError(prefix + error)
45
+ full_error = f'SKYPILOT_ERROR_NO_NODES_LAUNCHED: {error}'
46
+ logger.error(full_error)
47
+ raise RuntimeError(full_error)
47
48
 
48
49
 
49
50
  def bootstrap_instances(
@@ -222,10 +223,27 @@ def _configure_iam_role(iam) -> Dict[str, Any]:
222
223
 
223
224
 
224
225
  @functools.lru_cache(maxsize=128) # Keep bounded.
225
- def _get_route_tables(ec2, vpc_id: Optional[str], main: bool) -> List[Any]:
226
+ def _get_route_tables(ec2, vpc_id: Optional[str], region: str,
227
+ main: bool) -> List[Any]:
228
+ """Get route tables associated with a VPC and region
229
+
230
+ Args:
231
+ ec2: ec2 resource object
232
+ vpc_id: vpc_id is optional, if not provided, all route tables in the
233
+ region will be returned
234
+ region: region is mandatory to allow the lru cache
235
+ to return the corect results
236
+ main: if True, only main route tables will be returned otherwise
237
+ only non-main route tables will be returned
238
+
239
+ Returns:
240
+ A list of route tables associated with the options VPC and region
241
+ """
226
242
  filters = [{'Name': 'association.main', 'Values': [str(main).lower()]}]
227
243
  if vpc_id is not None:
228
244
  filters.append({'Name': 'vpc-id', 'Values': [vpc_id]})
245
+ logger.debug(
246
+ f'Getting route tables with filters: {filters} in region: {region}')
229
247
  return ec2.meta.client.describe_route_tables(Filters=filters).get(
230
248
  'RouteTables', [])
231
249
 
@@ -238,7 +256,8 @@ def _is_subnet_public(ec2, subnet_id, vpc_id: Optional[str]) -> bool:
238
256
  https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html
239
257
  """
240
258
  # Get the route tables associated with the subnet
241
- all_route_tables = _get_route_tables(ec2, vpc_id, main=False)
259
+ region = ec2.meta.client.meta.region_name
260
+ all_route_tables = _get_route_tables(ec2, vpc_id, region, main=False)
242
261
  route_tables = [
243
262
  rt for rt in all_route_tables
244
263
  # An RT can be associated with multiple subnets, i.e.,
@@ -267,7 +286,8 @@ def _is_subnet_public(ec2, subnet_id, vpc_id: Optional[str]) -> bool:
267
286
  # subnets. Since the associations are implicit, the filter above won't find
268
287
  # any. Check there exists a main route table with routes pointing to an IGW.
269
288
  logger.debug('Checking main route table')
270
- main_route_tables = _get_route_tables(ec2, vpc_id, main=True)
289
+ region = ec2.meta.client.meta.region_name
290
+ main_route_tables = _get_route_tables(ec2, vpc_id, region, main=True)
271
291
  return _has_igw_route(main_route_tables)
272
292
 
273
293
 
sky/serve/core.py CHANGED
@@ -124,7 +124,9 @@ def up(
124
124
  f'{constants.CLUSTER_NAME_VALID_REGEX}')
125
125
 
126
126
  _validate_service_task(task)
127
-
127
+ # Always apply the policy again here, even though it might have been applied
128
+ # in the CLI. This is to ensure that we apply the policy to the final DAG
129
+ # and get the mutated config.
128
130
  dag, mutated_user_config = admin_policy_utils.apply(
129
131
  task, use_mutated_config_in_current_request=False)
130
132
  task = dag.tasks[0]
@@ -319,6 +321,14 @@ def update(
319
321
  service_name: Name of the service.
320
322
  """
321
323
  _validate_service_task(task)
324
+ # Always apply the policy again here, even though it might have been applied
325
+ # in the CLI. This is to ensure that we apply the policy to the final DAG
326
+ # and get the mutated config.
327
+ # TODO(cblmemo,zhwu): If a user sets a new skypilot_config, the update
328
+ # will not apply the config.
329
+ dag, _ = admin_policy_utils.apply(
330
+ task, use_mutated_config_in_current_request=False)
331
+ task = dag.tasks[0]
322
332
  handle = backend_utils.is_controller_accessible(
323
333
  controller=controller_utils.Controllers.SKY_SERVE_CONTROLLER,
324
334
  stopped_message=
sky/skylet/constants.py CHANGED
@@ -79,7 +79,7 @@ SKYLET_VERSION = '8'
79
79
  # The version of the lib files that skylet/jobs use. Whenever there is an API
80
80
  # change for the job_lib or log_lib, we need to bump this version, so that the
81
81
  # user can be notified to update their SkyPilot version on the remote cluster.
82
- SKYLET_LIB_VERSION = 1
82
+ SKYLET_LIB_VERSION = 2
83
83
  SKYLET_VERSION_FILE = '~/.sky/skylet_version'
84
84
 
85
85
  # `sky jobs dashboard`-related
sky/skylet/job_lib.py CHANGED
@@ -29,6 +29,7 @@ if typing.TYPE_CHECKING:
29
29
 
30
30
  logger = sky_logging.init_logger(__name__)
31
31
 
32
+ _LINUX_NEW_LINE = '\n'
32
33
  _JOB_STATUS_LOCK = '~/.sky/locks/.job_{}.lock'
33
34
 
34
35
 
@@ -602,6 +603,7 @@ def update_job_status(job_ids: List[int],
602
603
  # the pending table until appearing in ray jobs. For jobs
603
604
  # submitted outside of the grace period, we will consider the
604
605
  # ray job status.
606
+
605
607
  if not (pending_job['submit'] > 0 and pending_job['submit'] <
606
608
  ray_job_query_time - _PENDING_SUBMIT_GRACE_PERIOD):
607
609
  # Reset the job status to PENDING even though it may not
@@ -903,14 +905,19 @@ class JobLibCodeGen:
903
905
  def tail_logs(cls,
904
906
  job_id: Optional[int],
905
907
  managed_job_id: Optional[int],
906
- follow: bool = True) -> str:
908
+ follow: bool = True,
909
+ tail: int = 0) -> str:
907
910
  # pylint: disable=line-too-long
911
+
908
912
  code = [
913
+ # We use != instead of is not because 1 is not None will print a warning:
914
+ # <stdin>:1: SyntaxWarning: "is not" with a literal. Did you mean "!="?
909
915
  f'job_id = {job_id} if {job_id} != None else job_lib.get_latest_job_id()',
910
916
  'run_timestamp = job_lib.get_run_timestamp(job_id)',
911
917
  f'log_dir = None if run_timestamp is None else os.path.join({constants.SKY_LOGS_DIRECTORY!r}, run_timestamp)',
912
- f'log_lib.tail_logs(job_id=job_id, log_dir=log_dir, '
913
- f'managed_job_id={managed_job_id!r}, follow={follow})',
918
+ f'tail_log_kwargs = {{"job_id": job_id, "log_dir": log_dir, "managed_job_id": {managed_job_id!r}, "follow": {follow}}}',
919
+ f'{_LINUX_NEW_LINE}if getattr(constants, "SKYLET_LIB_VERSION", 1) > 1: tail_log_kwargs["tail"] = {tail}',
920
+ f'{_LINUX_NEW_LINE}log_lib.tail_logs(**tail_log_kwargs)',
914
921
  ]
915
922
  return cls._build(code)
916
923
 
sky/skylet/log_lib.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  This is a remote utility module that provides logging functionality.
4
4
  """
5
+ import collections
5
6
  import copy
6
7
  import io
7
8
  import multiprocessing.pool
@@ -12,7 +13,8 @@ import sys
12
13
  import tempfile
13
14
  import textwrap
14
15
  import time
15
- from typing import Dict, Iterator, List, Optional, Tuple, Union
16
+ from typing import (Deque, Dict, Iterable, Iterator, List, Optional, TextIO,
17
+ Tuple, Union)
16
18
 
17
19
  import colorama
18
20
 
@@ -26,6 +28,9 @@ from sky.utils import ux_utils
26
28
  _SKY_LOG_WAITING_GAP_SECONDS = 1
27
29
  _SKY_LOG_WAITING_MAX_RETRY = 5
28
30
  _SKY_LOG_TAILING_GAP_SECONDS = 0.2
31
+ # Peek the head of the lines to check if we need to start
32
+ # streaming when tail > 0.
33
+ PEEK_HEAD_LINES_FOR_START_STREAM = 20
29
34
 
30
35
  logger = sky_logging.init_logger(__name__)
31
36
 
@@ -330,6 +335,7 @@ def run_bash_command_with_log(bash_command: str,
330
335
 
331
336
  def _follow_job_logs(file,
332
337
  job_id: int,
338
+ start_streaming: bool,
333
339
  start_streaming_at: str = '') -> Iterator[str]:
334
340
  """Yield each line from a file as they are written.
335
341
 
@@ -338,7 +344,6 @@ def _follow_job_logs(file,
338
344
  # No need to lock the status here, as the while loop can handle
339
345
  # the older status.
340
346
  status = job_lib.get_status_no_lock(job_id)
341
- start_streaming = False
342
347
  wait_last_logs = True
343
348
  while True:
344
349
  tmp = file.readline()
@@ -378,10 +383,45 @@ def _follow_job_logs(file,
378
383
  status = job_lib.get_status_no_lock(job_id)
379
384
 
380
385
 
386
+ def _peek_head_lines(log_file: TextIO) -> List[str]:
387
+ """Peek the head of the file."""
388
+ lines = [
389
+ log_file.readline() for _ in range(PEEK_HEAD_LINES_FOR_START_STREAM)
390
+ ]
391
+ # Reset the file pointer to the beginning
392
+ log_file.seek(0, os.SEEK_SET)
393
+ return [line for line in lines if line]
394
+
395
+
396
+ def _should_stream_the_whole_tail_lines(head_lines_of_log_file: List[str],
397
+ tail_lines: Deque[str],
398
+ start_stream_at: str) -> bool:
399
+ """Check if the entire tail lines should be streamed."""
400
+ # See comment:
401
+ # https://github.com/skypilot-org/skypilot/pull/4241#discussion_r1833611567
402
+ # for more details.
403
+ # Case 1: If start_stream_at is found at the head of the tail lines,
404
+ # we should not stream the whole tail lines.
405
+ for index, line in enumerate(tail_lines):
406
+ if index >= PEEK_HEAD_LINES_FOR_START_STREAM:
407
+ break
408
+ if start_stream_at in line:
409
+ return False
410
+ # Case 2: If start_stream_at is found at the head of log file, but not at
411
+ # the tail lines, we need to stream the whole tail lines.
412
+ for line in head_lines_of_log_file:
413
+ if start_stream_at in line:
414
+ return True
415
+ # Case 3: If start_stream_at is not at the head, and not found at the tail
416
+ # lines, we should not stream the whole tail lines.
417
+ return False
418
+
419
+
381
420
  def tail_logs(job_id: Optional[int],
382
421
  log_dir: Optional[str],
383
422
  managed_job_id: Optional[int] = None,
384
- follow: bool = True) -> None:
423
+ follow: bool = True,
424
+ tail: int = 0) -> None:
385
425
  """Tail the logs of a job.
386
426
 
387
427
  Args:
@@ -390,6 +430,8 @@ def tail_logs(job_id: Optional[int],
390
430
  managed_job_id: The managed job id (for logging info only to avoid
391
431
  confusion).
392
432
  follow: Whether to follow the logs or print the logs so far and exit.
433
+ tail: The number of lines to display from the end of the log file,
434
+ if 0, print all lines.
393
435
  """
394
436
  if job_id is None:
395
437
  # This only happens when job_lib.get_latest_job_id() returns None,
@@ -430,6 +472,8 @@ def tail_logs(job_id: Optional[int],
430
472
  status = job_lib.update_job_status([job_id], silent=True)[0]
431
473
 
432
474
  start_stream_at = 'Waiting for task resources on '
475
+ # Explicitly declare the type to avoid mypy warning.
476
+ lines: Iterable[str] = []
433
477
  if follow and status in [
434
478
  job_lib.JobStatus.SETTING_UP,
435
479
  job_lib.JobStatus.PENDING,
@@ -440,18 +484,43 @@ def tail_logs(job_id: Optional[int],
440
484
  with open(log_path, 'r', newline='', encoding='utf-8') as log_file:
441
485
  # Using `_follow` instead of `tail -f` to streaming the whole
442
486
  # log and creating a new process for tail.
487
+ start_streaming = False
488
+ if tail > 0:
489
+ head_lines_of_log_file = _peek_head_lines(log_file)
490
+ lines = collections.deque(log_file, maxlen=tail)
491
+ start_streaming = _should_stream_the_whole_tail_lines(
492
+ head_lines_of_log_file, lines, start_stream_at)
493
+ for line in lines:
494
+ if start_stream_at in line:
495
+ start_streaming = True
496
+ if start_streaming:
497
+ print(line, end='')
498
+ # Flush the last n lines
499
+ print(end='', flush=True)
500
+ # Now, the cursor is at the end of the last lines
501
+ # if tail > 0
443
502
  for line in _follow_job_logs(log_file,
444
503
  job_id=job_id,
504
+ start_streaming=start_streaming,
445
505
  start_streaming_at=start_stream_at):
446
506
  print(line, end='', flush=True)
447
507
  else:
448
508
  try:
449
- start_stream = False
450
- with open(log_path, 'r', encoding='utf-8') as f:
451
- for line in f.readlines():
509
+ start_streaming = False
510
+ with open(log_path, 'r', encoding='utf-8') as log_file:
511
+ if tail > 0:
512
+ # If tail > 0, we need to read the last n lines.
513
+ # We use double ended queue to rotate the last n lines.
514
+ head_lines_of_log_file = _peek_head_lines(log_file)
515
+ lines = collections.deque(log_file, maxlen=tail)
516
+ start_streaming = _should_stream_the_whole_tail_lines(
517
+ head_lines_of_log_file, lines, start_stream_at)
518
+ else:
519
+ lines = log_file
520
+ for line in lines:
452
521
  if start_stream_at in line:
453
- start_stream = True
454
- if start_stream:
522
+ start_streaming = True
523
+ if start_streaming:
455
524
  print(line, end='', flush=True)
456
525
  except FileNotFoundError:
457
526
  print(f'{colorama.Fore.RED}ERROR: Logs for job {job_id} (status:'
@@ -324,6 +324,8 @@ available_node_types:
324
324
  command: ["/bin/bash", "-c", "--"]
325
325
  args:
326
326
  - |
327
+ function mylsof { p=$(for pid in /proc/{0..9}*; do i=$(basename "$pid"); for file in "$pid"/fd/*; do link=$(readlink -e "$file"); if [ "$link" = "$1" ]; then echo "$i"; fi; done; done); echo "$p"; };
328
+
327
329
  # Tails file and checks every 5 sec for
328
330
  # open file handlers with write access
329
331
  # closes if none exist
@@ -333,7 +335,7 @@ available_node_types:
333
335
  while kill -0 $TAIL_PID 2> /dev/null; do
334
336
  # only two PIDs should be accessing the file
335
337
  # the log appender and log tailer
336
- if [ $(lsof -w $file | wc -l) -lt 3 ]; then
338
+ if [ $(mylsof $file | wc -l) -lt 2 ]; then
337
339
  kill $TAIL_PID
338
340
  break
339
341
  fi
@@ -142,4 +142,5 @@ def apply(
142
142
  importlib.reload(skypilot_config)
143
143
 
144
144
  logger.debug(f'Mutated user request: {mutated_user_request}')
145
+ mutated_dag.policy_applied = True
145
146
  return mutated_dag, mutated_config
@@ -11,6 +11,7 @@ from sky import sky_logging
11
11
  from sky.skylet import constants
12
12
  from sky.skylet import log_lib
13
13
  from sky.utils import common_utils
14
+ from sky.utils import control_master_utils
14
15
  from sky.utils import subprocess_utils
15
16
  from sky.utils import timeline
16
17
 
@@ -104,13 +105,22 @@ def ssh_options_list(
104
105
  }
105
106
  # SSH Control will have a severe delay when using docker_ssh_proxy_command.
106
107
  # TODO(tian): Investigate why.
108
+ #
109
+ # We disable ControlMaster when ssh_proxy_command is used, because the
110
+ # master connection will be idle although the connection might be shared
111
+ # by other ssh commands that is not idle. In that case, user's custom proxy
112
+ # command may drop the connection due to idle timeout, since it will only
113
+ # see the idle master connection. It is an issue even with the
114
+ # ServerAliveInterval set, since the keepalive message may not be recognized
115
+ # by the custom proxy command, such as AWS SSM Session Manager.
116
+ #
107
117
  # We also do not use ControlMaster when we use `kubectl port-forward`
108
118
  # to access Kubernetes pods over SSH+Proxycommand. This is because the
109
119
  # process running ProxyCommand is kept running as long as the ssh session
110
120
  # is running and the ControlMaster keeps the session, which results in
111
121
  # 'ControlPersist' number of seconds delay per ssh commands ran.
112
122
  if (ssh_control_name is not None and docker_ssh_proxy_command is None and
113
- not disable_control_master):
123
+ ssh_proxy_command is None and not disable_control_master):
114
124
  arg_dict.update({
115
125
  # Control path: important optimization as we do multiple ssh in one
116
126
  # sky.launch().
@@ -459,7 +469,9 @@ class SSHCommandRunner(CommandRunner):
459
469
  None if ssh_control_name is None else hashlib.md5(
460
470
  ssh_control_name.encode()).hexdigest()[:_HASH_MAX_LENGTH])
461
471
  self._ssh_proxy_command = ssh_proxy_command
462
- self.disable_control_master = disable_control_master
472
+ self.disable_control_master = (
473
+ disable_control_master or
474
+ control_master_utils.should_disable_control_master())
463
475
  if docker_user is not None:
464
476
  assert port is None or port == 22, (
465
477
  f'port must be None or 22 for docker_user, got {port}.')
@@ -0,0 +1,49 @@
1
+ """Utils to check if the ssh control master should be disabled."""
2
+
3
+ import functools
4
+
5
+ from sky import sky_logging
6
+ from sky.utils import subprocess_utils
7
+
8
+ logger = sky_logging.init_logger(__name__)
9
+
10
+
11
+ def is_tmp_9p_filesystem() -> bool:
12
+ """Check if the /tmp filesystem is 9p.
13
+
14
+ Returns:
15
+ bool: True if the /tmp filesystem is 9p, False otherwise.
16
+ """
17
+
18
+ result = subprocess_utils.run(['df', '-T', '/tmp'],
19
+ capture_output=True,
20
+ text=True,
21
+ shell=None,
22
+ check=False,
23
+ executable=None)
24
+
25
+ if result.returncode != 0:
26
+ return False
27
+
28
+ filesystem_infos = result.stdout.strip().split('\n')
29
+ if len(filesystem_infos) < 2:
30
+ return False
31
+ filesystem_types = filesystem_infos[1].split()
32
+ if len(filesystem_types) < 2:
33
+ return False
34
+ return filesystem_types[1].lower() == '9p'
35
+
36
+
37
+ @functools.lru_cache
38
+ def should_disable_control_master() -> bool:
39
+ """Whether disable ssh control master based on file system.
40
+
41
+ Returns:
42
+ bool: True if the ssh control master should be disabled,
43
+ False otherwise.
44
+ """
45
+ if is_tmp_9p_filesystem():
46
+ return True
47
+ # there may be additional criteria to disable ssh control master
48
+ # in the future. They should be checked here
49
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: skypilot-nightly
3
- Version: 1.0.0.dev20241108
3
+ Version: 1.0.0.dev20241109
4
4
  Summary: SkyPilot: An intercloud broker for the clouds
5
5
  Author: SkyPilot Team
6
6
  License: Apache 2.0
@@ -1,13 +1,13 @@
1
- sky/__init__.py,sha256=3bl83kLcrLpOZrRBB4ZOcINvEGjYSmx_kP8Aypn8fbc,5882
1
+ sky/__init__.py,sha256=vuaxCFFtQHJTriSEGG_wKshl6nmhDcnt70q66x1rkvA,5882
2
2
  sky/admin_policy.py,sha256=hPo02f_A32gCqhUueF0QYy1fMSSKqRwYEg_9FxScN_s,3248
3
3
  sky/authentication.py,sha256=pAdCT60OxxiXI9KXDyP2lQ9u9vMc6aMtq5Xi2h_hbdw,20984
4
4
  sky/check.py,sha256=D3Y3saIFAYVvPxuBHnVgJEO0fUVDxgjwuMBaO-D778k,9472
5
- sky/cli.py,sha256=STcQ0jaLicXahQOCruebuRrRa94KouQPF_P_EVP1CjI,211212
5
+ sky/cli.py,sha256=jEjXs5Z0u263eJIsTHoKyG9oOY6giqw19s2di9kEv1s,212088
6
6
  sky/cloud_stores.py,sha256=RjFgmRhUh1Kk__f6g3KxzLp9s7dA0pFK4W1AukEuUaw,21153
7
- sky/core.py,sha256=DW9OGE2kS2CmsvQ1grrpRnNFS3woMGWSHu5GE99e-I4,38190
8
- sky/dag.py,sha256=WLFWr5hfrwjd31uYlNvI-zWUk7tLaT_gzJn4LzbVtkE,2780
9
- sky/exceptions.py,sha256=KBIEJHgrw6OMBL8H65o-Gk6qYQEV1SR9gBwMjnMnxxg,8858
10
- sky/execution.py,sha256=HF76sz-gCEZPGkuL48jJaLOTqjuHg0KysgKaPw-hn84,25997
7
+ sky/core.py,sha256=0-4W_DKJZgbwXuzNZKQ2R_qJxqxbqqNfyi0U0PQBKvQ,38230
8
+ sky/dag.py,sha256=O9g8NnO8L1SGUEDyqW9W341AH4Wvd3nJs54niR-pkrk,2822
9
+ sky/exceptions.py,sha256=E3C2Ejcc8RUDAUQn7ar_Jr97C_AxD2rKKMmJOfLJ9d0,8965
10
+ sky/execution.py,sha256=TwcorzFxR_0m8uazPdeKltU3g3ikgUSqqzcSBrHp7K4,26070
11
11
  sky/global_user_state.py,sha256=PywEmUutF97XBgRMClR6IS5_KM8JJC0oA1LsPUZebp0,28681
12
12
  sky/optimizer.py,sha256=tXGrFpc6xNtKH34qjBAMd4jTuWcDZTPnGFwEtuCQFmk,59702
13
13
  sky/resources.py,sha256=Zt8mCCmdvZ5ZCqY-l3KXlx_lkUesAopRtaEcEsrRFZo,68465
@@ -31,10 +31,10 @@ sky/adaptors/vsphere.py,sha256=zJP9SeObEoLrpgHW2VHvZE48EhgVf8GfAEIwBeaDMfM,2129
31
31
  sky/backends/__init__.py,sha256=UDjwbUgpTRApbPJnNfR786GadUuwgRk3vsWoVu5RB_c,536
32
32
  sky/backends/backend.py,sha256=wwfbrxPhjMPs6PSyy3tAHI8WJhl-xhgzWBsAZjmJJ6g,6249
33
33
  sky/backends/backend_utils.py,sha256=2myfryj1zG9xxPaX6XYYJruxAOGNGbpsy2ckT4A77sE,121813
34
- sky/backends/cloud_vm_ray_backend.py,sha256=yxsyqzA_jubsWhpFUmeTowxUPUj20M6jo9kkBI1Tbw4,232913
34
+ sky/backends/cloud_vm_ray_backend.py,sha256=6Ew9Ej92KGlumlCnyDcGSEbHInj7g2Shqwx4oxRkWVQ,233122
35
35
  sky/backends/docker_utils.py,sha256=Hyw1YY20EyghhEbYx6O2FIMDcGkNzBzV9TM7LFynei8,8358
36
36
  sky/backends/local_docker_backend.py,sha256=0JL5m0YUgOmOL4aWEUe4tmt89dsxjk4_WXkPwgEKEis,16801
37
- sky/backends/wheel_utils.py,sha256=3QS4T_Ydvo4DbYhogtyADyNBEf04I6jUCL71M285shQ,7963
37
+ sky/backends/wheel_utils.py,sha256=CUVOwlBtQjOMv-RSDGx2jMQ0M1D0w9ZPm0TDafJwBDI,8180
38
38
  sky/backends/monkey_patches/monkey_patch_ray_up.py,sha256=76-y2fCaE3JINj8lEwHT1eirYzCbpD8O1ySsysuGu8o,3450
39
39
  sky/benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  sky/benchmark/benchmark_state.py,sha256=X8CXmuU9KgsDRhKedhFgjeRMUFWtQsjFs1qECvPG2yg,8723
@@ -90,13 +90,13 @@ sky/clouds/utils/scp_utils.py,sha256=RUp7NwyhKygOoVOwvdAOGdoQNSJjryOG6WSExCf-yas
90
90
  sky/data/__init__.py,sha256=Nhaf1NURisXpZuwWANa2IuCyppIuc720FRwqSE2oEwY,184
91
91
  sky/data/data_transfer.py,sha256=MBmjey9_p2L3IKNKTi8um09SlZe32n4wK3CkVnlTVvo,7346
92
92
  sky/data/data_utils.py,sha256=-P5GsDH_m4slrCz4vHdgiFezIys8ufzvhEKePJwfjFc,28597
93
- sky/data/mounting_utils.py,sha256=44YkYIIgArEkyvxCtfmXXumybrU8bmn1TfLXWv_eldI,11480
93
+ sky/data/mounting_utils.py,sha256=HwBGg1NmX-2IJZV_6h2r1U3ajTGOyfmA3MqboA7znqU,11004
94
94
  sky/data/storage.py,sha256=OQ_kznF-P50Jq0feO5FBqm97QGhfbsZ2dX-Ar3sVWr4,163903
95
95
  sky/data/storage_utils.py,sha256=cM3kxlffYE7PnJySDu8huyUsMX_JYsf9uer8r5OYsjo,9556
96
96
  sky/jobs/__init__.py,sha256=yucibSB_ZimtJMvOhMxn6ZqwBIYNfcwmc6pSXtCqmNQ,1483
97
97
  sky/jobs/constants.py,sha256=YLgcCg_RHSYr_rfsI_4UIdXk78KKKOK29Oem88t5j8I,1350
98
98
  sky/jobs/controller.py,sha256=sirpi730_GfKfPZeZ2PvCXnJWger0r6AyLSOx2sLd6A,27368
99
- sky/jobs/core.py,sha256=w7PancHi8_-afLKZQ3HHMD1sEDoepm1vEMxyDlXdo64,17155
99
+ sky/jobs/core.py,sha256=Lk_zKizc9a7O-8WHhh4-VXBS5kT0jRpwmNNA7S4ueIo,17347
100
100
  sky/jobs/recovery_strategy.py,sha256=O_DouAfWx8FNdQxXsr2msMwlKCIodS99cW6V4Lf1vMo,27219
101
101
  sky/jobs/state.py,sha256=DE02bCZc9bPbbuayb3Zml553mb4pEV7Z8t1pt8IGbYM,25252
102
102
  sky/jobs/utils.py,sha256=Ff3TttIEdVeM1_kOVkviqIDjeVfBPIXVE8i-yP1VDM8,37976
@@ -112,7 +112,7 @@ sky/provision/logging.py,sha256=yZWgejrFBhhRjAtvFu5N5bRXIMK5TuwNjp1vKQqz2pw,2103
112
112
  sky/provision/metadata_utils.py,sha256=LrxeV4wD2QPzNdXV_npj8q-pr35FatxBBjF_jSbpOT0,4013
113
113
  sky/provision/provisioner.py,sha256=mTvtBjS-Xz64LJcyeHx_-wdM8Gin8D49YRaV_TADaz4,25334
114
114
  sky/provision/aws/__init__.py,sha256=mxq8PeWJqUtalDozTNpbtENErRZ1ktEs8uf2aG9UUgU,731
115
- sky/provision/aws/config.py,sha256=ApEh63RR_KyCp9nPXX35z6jBREoulJPQ5st8K9Jlclo,23385
115
+ sky/provision/aws/config.py,sha256=dbwulPxXGIJjKJddv85PbtlXOjwLemaD65j3DISNsK0,24214
116
116
  sky/provision/aws/instance.py,sha256=eCslJ2XfJo_pkQMnKFQqhGnUIRvwKiT12oxBY5-klss,40750
117
117
  sky/provision/aws/utils.py,sha256=m49pS-SHGW7Au3bhDeTPsL8N5iRzbwOXzyEWRCc1Vho,3238
118
118
  sky/provision/azure/__init__.py,sha256=87cgk1_Ws7n9rqaDDPv-HpfrkVeSQMdFQnhnXwyx9g4,548
@@ -175,7 +175,7 @@ sky/serve/__init__.py,sha256=gFZt7W3UPMi4qvYe2xgkHg1VxbR1WGavKyWLBUD3mpg,1731
175
175
  sky/serve/autoscalers.py,sha256=khY1oZ22PRaUQNsLCoNKH178X_NiJw0LSLOKr7_LNgY,30275
176
176
  sky/serve/constants.py,sha256=7MflfgTHO9gDSux93U4BmNeEMWXxZB4q7I54KUwgp-s,4651
177
177
  sky/serve/controller.py,sha256=R5iIEGEEFtbm_6MvSGelYZP-vSmW0cSFuy64OexUc4g,11719
178
- sky/serve/core.py,sha256=jwrgglvtFqbD9Y4pzXmuso5hKc0OQcTWJ-AkvypiQII,30986
178
+ sky/serve/core.py,sha256=hszs95BwtC4wIJujGNokvFC46VjojgRz1BbYOIIPh6k,31601
179
179
  sky/serve/load_balancer.py,sha256=aUfDsgUT_fYrchCwJCeunMPXmAkwJAY58BEu-IN2FaA,11571
180
180
  sky/serve/load_balancing_policies.py,sha256=ExdwH_pxPYpJ6CkoTQCOPSa4lzwbq1LFFMKzmIu8ryk,2331
181
181
  sky/serve/replica_managers.py,sha256=1xYDK9Te5wFEF5hUK0gyNIUib0MY-HScLHUBDlTSl-k,57774
@@ -190,10 +190,10 @@ sky/skylet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
190
190
  sky/skylet/attempt_skylet.py,sha256=GZ6ITjjA0m-da3IxXXfoHR6n4pjp3X3TOXUqVvSrV0k,2136
191
191
  sky/skylet/autostop_lib.py,sha256=JPDHmByuhoNYXSUHl-OnyeJUkOFWn7gDM1FrS7Kr3E8,4478
192
192
  sky/skylet/configs.py,sha256=UtnpmEL0F9hH6PSjhsps7xgjGZ6qzPOfW1p2yj9tSng,1887
193
- sky/skylet/constants.py,sha256=TL-O0ZoxA1ZeNvKXzzA_UyIMXsma7flbsDZ1N_o9dKg,14468
193
+ sky/skylet/constants.py,sha256=w05Enrg9RhGp99P1WDYMKK_ki0M-e0bS8Wr-VZR0Vn8,14468
194
194
  sky/skylet/events.py,sha256=A09E7LmmwzcGrSG0n8K7d3EZ1ZJr1mmmzoGyhnArYJA,12303
195
- sky/skylet/job_lib.py,sha256=-SCbpJRiWMSwvhDjUwfwnvBap7Y5B3ol1l_PDPra3XI,36860
196
- sky/skylet/log_lib.py,sha256=Jyj3h2yMBlheFX53AabXEiPaKyCbu06hLEhay5_ZRN0,18734
195
+ sky/skylet/job_lib.py,sha256=FD1n9vE0daOEUKSH3lnccfBh7Vs81R8s4ILZyKu2o7M,37275
196
+ sky/skylet/log_lib.py,sha256=BmhAgcLvlin3szhj33IH0kbdCALacVisF2x61BQpZdY,21888
197
197
  sky/skylet/log_lib.pyi,sha256=AHMkW2DGK2erFovb3ToZWxRiYaATlzkxKb5J9pkgF2Y,4295
198
198
  sky/skylet/skylet.py,sha256=U9plr5hmhD9-Nyy0LMCymlE8DWtRXTFXQvfbFsS746Y,1153
199
199
  sky/skylet/subprocess_daemon.py,sha256=IJwGAzOdERrhWJS7VYKAUByNsLyIkKkB0w5nk06okG8,2818
@@ -228,7 +228,7 @@ sky/templates/jobs-controller.yaml.j2,sha256=Gu3ogFxFYr09VEXP-6zEbrCUOFo1aYxWEjA
228
228
  sky/templates/kubernetes-ingress.yml.j2,sha256=73iDklVDWBMbItg0IexCa6_ClXPJOxw7PWz3leku4nE,1340
229
229
  sky/templates/kubernetes-loadbalancer.yml.j2,sha256=IxrNYM366N01bbkJEbZ_UPYxUP8wyVEbRNFHRsBuLsw,626
230
230
  sky/templates/kubernetes-port-forward-proxy-command.sh,sha256=HlG7CPBBedCVBlL9qv0erW_eKm6Irj0LFyaAWuJW_lc,3148
231
- sky/templates/kubernetes-ray.yml.j2,sha256=Wq9luXc6-t141uyHbtOy1IDmLMM0PBbePTZfZEtAKw0,18160
231
+ sky/templates/kubernetes-ray.yml.j2,sha256=dsWlkX-0b1igeZI4c0u0Jzia5I_9gezCiewR6pX1LlY,18374
232
232
  sky/templates/kubernetes-ssh-jump.yml.j2,sha256=k5W5sOIMppU7dDkJMwPlqsUcb92y7L5_TVG3hkgMy8M,2747
233
233
  sky/templates/lambda-ray.yml.j2,sha256=HyvO_tX2vxwSsc4IFVSqGuIbjLMk0bevP9bcxb8ZQII,4498
234
234
  sky/templates/local-ray.yml.j2,sha256=FNHeyHF6nW9nU9QLIZceUWfvrFTTcO51KqhTnYCEFaA,1185
@@ -243,11 +243,12 @@ sky/usage/constants.py,sha256=8xpg9vhDU9A3eObtpkNFjwa42oCazqGEv4yw_vJSO7U,590
243
243
  sky/usage/usage_lib.py,sha256=mxsbwUMEQjesUOIv4Yne-Ze7rVxSQYr3_wBXruifGRA,17898
244
244
  sky/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
245
245
  sky/utils/accelerator_registry.py,sha256=BO4iYH5bV80Xyp4EPfO0n1D3LL0FvESCy7xm59Je3_o,3798
246
- sky/utils/admin_policy_utils.py,sha256=zFCu1OFIrZRfQNY0JFRO1502WFfdqZhwAU_QgM4fO9U,5943
246
+ sky/utils/admin_policy_utils.py,sha256=_Vt_jTTYCXmMdryj0vrrumFPewa93qHnzUqBDXjAhRU,5981
247
247
  sky/utils/cluster_yaml_utils.py,sha256=1wRRYqI1kI-eFs1pMW4r_FFjHJ0zamq6v2RRI-Gtx5E,849
248
- sky/utils/command_runner.py,sha256=seU7uX9CrxiC8WOWBKHW94m67-V6DYghqRXhYdUIdQI,35756
248
+ sky/utils/command_runner.py,sha256=GHTZxoJQ3V8WVSRAaOA4JpRTxtCtuq36H9U8kOfWUwc,36450
249
249
  sky/utils/command_runner.pyi,sha256=mJOzCgcYZAfHwnY_6Wf1YwlTEJGb9ihzc2f0rE0Kw98,7751
250
250
  sky/utils/common_utils.py,sha256=Qy25LuIoTT0qg391EWyT9i5D6fwk1S4OdFwRpCTZ9Vk,24657
251
+ sky/utils/control_master_utils.py,sha256=90hnxiAUP20gbJ9e3MERh7rb04ZO_I3LsljNjR26H5I,1416
251
252
  sky/utils/controller_utils.py,sha256=wF4_y1PCsLAWoo3XEtECwkNYTN6hO3vn_cxGxgQYcd8,43268
252
253
  sky/utils/dag_utils.py,sha256=pVX3lGDDcYTcGoH_1jEWzl9767Y4mwlIEYIzoyHO6gM,6105
253
254
  sky/utils/db_utils.py,sha256=AOvMmBEN9cF4I7CoXihPCtus4mU2VDGjBQSVMMgzKlA,2786
@@ -274,9 +275,9 @@ sky/utils/kubernetes/k8s_gpu_labeler_job.yaml,sha256=k0TBoQ4zgf79-sVkixKSGYFHQ7Z
274
275
  sky/utils/kubernetes/k8s_gpu_labeler_setup.yaml,sha256=VLKT2KKimZu1GDg_4AIlIt488oMQvhRZWwsj9vBbPUg,3812
275
276
  sky/utils/kubernetes/rsync_helper.sh,sha256=hyYDaYSNxYaNvzUQBzC8AidB7nDeojizjkzc_CTxycY,1077
276
277
  sky/utils/kubernetes/ssh_jump_lifecycle_manager.py,sha256=RFLJ3k7MR5UN4SKHykQ0lV9SgXumoULpKYIAt1vh-HU,6560
277
- skypilot_nightly-1.0.0.dev20241108.dist-info/LICENSE,sha256=emRJAvE7ngL6x0RhQvlns5wJzGI3NEQ_WMjNmd9TZc4,12170
278
- skypilot_nightly-1.0.0.dev20241108.dist-info/METADATA,sha256=5DHqRTobJ2Irrs9uV-6ixI4dUDQFGvFRVnp3KCDT9pc,19708
279
- skypilot_nightly-1.0.0.dev20241108.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
280
- skypilot_nightly-1.0.0.dev20241108.dist-info/entry_points.txt,sha256=StA6HYpuHj-Y61L2Ze-hK2IcLWgLZcML5gJu8cs6nU4,36
281
- skypilot_nightly-1.0.0.dev20241108.dist-info/top_level.txt,sha256=qA8QuiNNb6Y1OF-pCUtPEr6sLEwy2xJX06Bd_CrtrHY,4
282
- skypilot_nightly-1.0.0.dev20241108.dist-info/RECORD,,
278
+ skypilot_nightly-1.0.0.dev20241109.dist-info/LICENSE,sha256=emRJAvE7ngL6x0RhQvlns5wJzGI3NEQ_WMjNmd9TZc4,12170
279
+ skypilot_nightly-1.0.0.dev20241109.dist-info/METADATA,sha256=YM8C71GXOj5CoHQlj5yNYhL8UkZ75DL-qMMTPXCOmXY,19708
280
+ skypilot_nightly-1.0.0.dev20241109.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
281
+ skypilot_nightly-1.0.0.dev20241109.dist-info/entry_points.txt,sha256=StA6HYpuHj-Y61L2Ze-hK2IcLWgLZcML5gJu8cs6nU4,36
282
+ skypilot_nightly-1.0.0.dev20241109.dist-info/top_level.txt,sha256=qA8QuiNNb6Y1OF-pCUtPEr6sLEwy2xJX06Bd_CrtrHY,4
283
+ skypilot_nightly-1.0.0.dev20241109.dist-info/RECORD,,