skypilot-nightly 1.0.0.dev20250623__py3-none-any.whl → 1.0.0.dev20250625__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. sky/__init__.py +2 -2
  2. sky/admin_policy.py +16 -5
  3. sky/backends/__init__.py +2 -1
  4. sky/backends/backend_utils.py +38 -11
  5. sky/backends/cloud_vm_ray_backend.py +52 -18
  6. sky/client/cli/command.py +264 -25
  7. sky/client/sdk.py +119 -85
  8. sky/clouds/aws.py +10 -7
  9. sky/clouds/azure.py +10 -7
  10. sky/clouds/cloud.py +2 -0
  11. sky/clouds/cudo.py +2 -0
  12. sky/clouds/do.py +10 -7
  13. sky/clouds/fluidstack.py +2 -0
  14. sky/clouds/gcp.py +10 -7
  15. sky/clouds/hyperbolic.py +10 -7
  16. sky/clouds/ibm.py +2 -0
  17. sky/clouds/kubernetes.py +27 -9
  18. sky/clouds/lambda_cloud.py +10 -7
  19. sky/clouds/nebius.py +10 -7
  20. sky/clouds/oci.py +10 -7
  21. sky/clouds/paperspace.py +10 -7
  22. sky/clouds/runpod.py +10 -7
  23. sky/clouds/scp.py +10 -7
  24. sky/clouds/vast.py +10 -7
  25. sky/clouds/vsphere.py +2 -0
  26. sky/core.py +89 -15
  27. sky/dag.py +14 -0
  28. sky/dashboard/out/404.html +1 -1
  29. sky/dashboard/out/_next/static/ZWdSYkqVe3WjnFR8ocqoG/_buildManifest.js +1 -0
  30. sky/dashboard/out/_next/static/chunks/230-d6e363362017ff3a.js +1 -0
  31. sky/dashboard/out/_next/static/chunks/310.2671028c20e892c7.js +16 -0
  32. sky/dashboard/out/_next/static/chunks/37-1f1e94f5a561202a.js +6 -0
  33. sky/dashboard/out/_next/static/chunks/42.bc85e5b1a4debf22.js +6 -0
  34. sky/dashboard/out/_next/static/chunks/470-92dd1614396389be.js +1 -0
  35. sky/dashboard/out/_next/static/chunks/{513.211357a2914a34b2.js → 513.309df9e18a9ff005.js} +1 -1
  36. sky/dashboard/out/_next/static/chunks/544.110e53813fb98e2e.js +1 -0
  37. sky/dashboard/out/_next/static/chunks/645.961f08e39b8ce447.js +1 -0
  38. sky/dashboard/out/_next/static/chunks/66-66ae330df2d3c1c7.js +1 -0
  39. sky/dashboard/out/_next/static/chunks/682.00e56a220dd26fe1.js +6 -0
  40. sky/dashboard/out/_next/static/chunks/697.6460bf72e760addd.js +20 -0
  41. sky/dashboard/out/_next/static/chunks/856-cdf66268ec878d0c.js +1 -0
  42. sky/dashboard/out/_next/static/chunks/938-068520cc11738deb.js +1 -0
  43. sky/dashboard/out/_next/static/chunks/969-d3a0b53f728d280a.js +1 -0
  44. sky/dashboard/out/_next/static/chunks/989-db34c16ad7ea6155.js +1 -0
  45. sky/dashboard/out/_next/static/chunks/pages/{_app-c416e87d5c2715cf.js → _app-0ef7418d1a3822f3.js} +1 -1
  46. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-aff040d7bc5d0086.js +6 -0
  47. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-32ce4f49f2261f55.js +6 -0
  48. sky/dashboard/out/_next/static/chunks/pages/clusters-4aa031d1f42723d8.js +1 -0
  49. sky/dashboard/out/_next/static/chunks/pages/config-3102d02a188f04b3.js +1 -0
  50. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-6f1e02e31eecb5ce.js +1 -0
  51. sky/dashboard/out/_next/static/chunks/pages/infra-fd5dc8a91bd9169a.js +1 -0
  52. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-e4b23128db0774cd.js +16 -0
  53. sky/dashboard/out/_next/static/chunks/pages/jobs-26da173e20af16e4.js +1 -0
  54. sky/dashboard/out/_next/static/chunks/pages/users-ce29e7420385563d.js +1 -0
  55. sky/dashboard/out/_next/static/chunks/pages/volumes-476b670ef33d1ecd.js +1 -0
  56. sky/dashboard/out/_next/static/chunks/pages/workspace/new-09ae0f6f972aa871.js +1 -0
  57. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-c4ff1ec05e2f3daf.js → [name]-0b4c662a25e4747a.js} +1 -1
  58. sky/dashboard/out/_next/static/chunks/pages/workspaces-862b120406461b10.js +1 -0
  59. sky/dashboard/out/_next/static/chunks/webpack-6133dc1e928bd0b5.js +1 -0
  60. sky/dashboard/out/_next/static/css/b23cb0257bf96c51.css +3 -0
  61. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  62. sky/dashboard/out/clusters/[cluster].html +1 -1
  63. sky/dashboard/out/clusters.html +1 -1
  64. sky/dashboard/out/config.html +1 -1
  65. sky/dashboard/out/index.html +1 -1
  66. sky/dashboard/out/infra/[context].html +1 -1
  67. sky/dashboard/out/infra.html +1 -1
  68. sky/dashboard/out/jobs/[job].html +1 -1
  69. sky/dashboard/out/jobs.html +1 -1
  70. sky/dashboard/out/users.html +1 -1
  71. sky/dashboard/out/volumes.html +1 -0
  72. sky/dashboard/out/workspace/new.html +1 -1
  73. sky/dashboard/out/workspaces/[name].html +1 -1
  74. sky/dashboard/out/workspaces.html +1 -1
  75. sky/data/storage_utils.py +2 -4
  76. sky/exceptions.py +26 -0
  77. sky/execution.py +5 -0
  78. sky/global_user_state.py +263 -20
  79. sky/jobs/client/sdk.py +13 -12
  80. sky/jobs/controller.py +5 -1
  81. sky/jobs/scheduler.py +4 -3
  82. sky/jobs/server/core.py +121 -51
  83. sky/jobs/state.py +15 -0
  84. sky/jobs/utils.py +114 -8
  85. sky/models.py +16 -0
  86. sky/provision/__init__.py +26 -0
  87. sky/provision/kubernetes/__init__.py +3 -0
  88. sky/provision/kubernetes/instance.py +38 -77
  89. sky/provision/kubernetes/utils.py +52 -2
  90. sky/provision/kubernetes/volume.py +147 -0
  91. sky/resources.py +20 -76
  92. sky/serve/client/sdk.py +13 -13
  93. sky/serve/server/core.py +5 -1
  94. sky/server/common.py +40 -5
  95. sky/server/constants.py +5 -1
  96. sky/server/metrics.py +105 -0
  97. sky/server/requests/executor.py +30 -14
  98. sky/server/requests/payloads.py +22 -3
  99. sky/server/requests/requests.py +59 -2
  100. sky/server/rest.py +152 -0
  101. sky/server/server.py +70 -19
  102. sky/server/state.py +20 -0
  103. sky/server/stream_utils.py +8 -3
  104. sky/server/uvicorn.py +153 -13
  105. sky/setup_files/dependencies.py +2 -0
  106. sky/skylet/constants.py +19 -14
  107. sky/task.py +141 -43
  108. sky/templates/jobs-controller.yaml.j2 +12 -1
  109. sky/templates/kubernetes-ray.yml.j2 +31 -2
  110. sky/users/permission.py +2 -0
  111. sky/utils/admin_policy_utils.py +5 -1
  112. sky/utils/cli_utils/status_utils.py +25 -17
  113. sky/utils/command_runner.py +118 -12
  114. sky/utils/command_runner.pyi +57 -0
  115. sky/utils/common_utils.py +9 -1
  116. sky/utils/context.py +3 -1
  117. sky/utils/controller_utils.py +1 -2
  118. sky/utils/resources_utils.py +66 -0
  119. sky/utils/rich_utils.py +6 -0
  120. sky/utils/schemas.py +180 -38
  121. sky/utils/status_lib.py +10 -0
  122. sky/utils/validator.py +11 -1
  123. sky/volumes/__init__.py +0 -0
  124. sky/volumes/client/__init__.py +0 -0
  125. sky/volumes/client/sdk.py +64 -0
  126. sky/volumes/server/__init__.py +0 -0
  127. sky/volumes/server/core.py +199 -0
  128. sky/volumes/server/server.py +85 -0
  129. sky/volumes/utils.py +158 -0
  130. sky/volumes/volume.py +198 -0
  131. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250625.dist-info}/METADATA +2 -1
  132. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250625.dist-info}/RECORD +139 -123
  133. sky/dashboard/out/_next/static/F4kiZ6Zh72jA6HzZ3ncFo/_buildManifest.js +0 -1
  134. sky/dashboard/out/_next/static/chunks/350.9e123a4551f68b0d.js +0 -1
  135. sky/dashboard/out/_next/static/chunks/37-3a4d77ad62932eaf.js +0 -6
  136. sky/dashboard/out/_next/static/chunks/42.d39e24467181b06b.js +0 -6
  137. sky/dashboard/out/_next/static/chunks/470-4d1a5dbe58a8a2b9.js +0 -1
  138. sky/dashboard/out/_next/static/chunks/641.c8e452bc5070a630.js +0 -1
  139. sky/dashboard/out/_next/static/chunks/682.4dd5dc116f740b5f.js +0 -6
  140. sky/dashboard/out/_next/static/chunks/760-a89d354797ce7af5.js +0 -1
  141. sky/dashboard/out/_next/static/chunks/856-c2c39c0912285e54.js +0 -1
  142. sky/dashboard/out/_next/static/chunks/901-b424d293275e1fd7.js +0 -1
  143. sky/dashboard/out/_next/static/chunks/938-1493ac755eadeb35.js +0 -1
  144. sky/dashboard/out/_next/static/chunks/969-20d54a9d998dc102.js +0 -1
  145. sky/dashboard/out/_next/static/chunks/984.ae8c08791d274ca0.js +0 -50
  146. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-89216c616dbaa9c5.js +0 -6
  147. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-36bc0962129f72df.js +0 -6
  148. sky/dashboard/out/_next/static/chunks/pages/clusters-82a651dbad53ec6e.js +0 -1
  149. sky/dashboard/out/_next/static/chunks/pages/config-497a35a7ed49734a.js +0 -1
  150. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-d2910be98e9227cb.js +0 -1
  151. sky/dashboard/out/_next/static/chunks/pages/infra-780860bcc1103945.js +0 -1
  152. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-cf490d1fa38f3740.js +0 -16
  153. sky/dashboard/out/_next/static/chunks/pages/jobs-336ab80e270ce2ce.js +0 -1
  154. sky/dashboard/out/_next/static/chunks/pages/users-928edf039219e47b.js +0 -1
  155. sky/dashboard/out/_next/static/chunks/pages/workspace/new-31aa8bdcb7592635.js +0 -1
  156. sky/dashboard/out/_next/static/chunks/pages/workspaces-82e6601baa5dd280.js +0 -1
  157. sky/dashboard/out/_next/static/chunks/webpack-0263b00d6a10e64a.js +0 -1
  158. sky/dashboard/out/_next/static/css/6c12ecc3bd2239b6.css +0 -3
  159. /sky/dashboard/out/_next/static/{F4kiZ6Zh72jA6HzZ3ncFo → ZWdSYkqVe3WjnFR8ocqoG}/_ssgManifest.js +0 -0
  160. /sky/dashboard/out/_next/static/chunks/{843-b3040e493f6e7947.js → 843-07d25a7e64462fd8.js} +0 -0
  161. /sky/dashboard/out/_next/static/chunks/{973-db3c97c2bfbceb65.js → 973-5b5019ba333e8d62.js} +0 -0
  162. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250625.dist-info}/WHEEL +0 -0
  163. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250625.dist-info}/entry_points.txt +0 -0
  164. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250625.dist-info}/licenses/LICENSE +0 -0
  165. {skypilot_nightly-1.0.0.dev20250623.dist-info → skypilot_nightly-1.0.0.dev20250625.dist-info}/top_level.txt +0 -0
sky/client/cli/command.py CHANGED
@@ -81,6 +81,8 @@ from sky.utils import subprocess_utils
81
81
  from sky.utils import timeline
82
82
  from sky.utils import ux_utils
83
83
  from sky.utils.cli_utils import status_utils
84
+ from sky.volumes import utils as volumes_utils
85
+ from sky.volumes.client import sdk as volumes_sdk
84
86
 
85
87
  if typing.TYPE_CHECKING:
86
88
  import types
@@ -202,13 +204,14 @@ def _get_cluster_records_and_set_ssh_config(
202
204
 
203
205
 
204
206
  def _get_glob_matches(candidate_names: List[str],
205
- glob_patterns: List[str]) -> List[str]:
207
+ glob_patterns: List[str],
208
+ resource_type: str = 'Storage') -> List[str]:
206
209
  """Returns a list of names that match the glob pattern."""
207
210
  glob_storages = []
208
211
  for glob_pattern in glob_patterns:
209
212
  glob_storage = fnmatch.filter(candidate_names, glob_pattern)
210
213
  if not glob_storage:
211
- click.echo(f'Storage {glob_pattern} not found.')
214
+ click.echo(f'{resource_type} {glob_pattern} not found.')
212
215
  glob_storages.extend(glob_storage)
213
216
  return list(set(glob_storages))
214
217
 
@@ -290,6 +293,19 @@ def _complete_storage_name(ctx: click.Context, param: click.Parameter,
290
293
  return response.json()
291
294
 
292
295
 
296
+ def _complete_volume_name(ctx: click.Context, param: click.Parameter,
297
+ incomplete: str) -> List[str]:
298
+ """Handle shell completion for volume names."""
299
+ del ctx, param # Unused.
300
+ response = requests_lib.get(
301
+ f'{server_common.get_server_url()}'
302
+ f'/api/completion/volume_name?incomplete={incomplete}',
303
+ timeout=2.0,
304
+ )
305
+ response.raise_for_status()
306
+ return response.json()
307
+
308
+
293
309
  def _complete_file_name(ctx: click.Context, param: click.Parameter,
294
310
  incomplete: str) -> List[str]:
295
311
  """Handle shell completion for file names.
@@ -531,8 +547,9 @@ def _parse_override_params(
531
547
  return override_params
532
548
 
533
549
 
534
- def _check_yaml(entrypoint: str) -> Tuple[bool, Optional[Dict[str, Any]]]:
535
- """Checks if entrypoint is a readable YAML file.
550
+ def _check_yaml_only(
551
+ entrypoint: str) -> Tuple[bool, Optional[Dict[str, Any]], bool, str]:
552
+ """Checks if entrypoint is a readable YAML file without confirmation.
536
553
 
537
554
  Args:
538
555
  entrypoint: Path to a YAML file.
@@ -580,6 +597,17 @@ def _check_yaml(entrypoint: str) -> Tuple[bool, Optional[Dict[str, Any]]]:
580
597
  invalid_reason = ('yaml.safe_load() failed. Please check if the'
581
598
  ' path is correct.')
582
599
  is_yaml = False
600
+ return is_yaml, result, yaml_file_provided, invalid_reason
601
+
602
+
603
+ def _check_yaml(entrypoint: str) -> Tuple[bool, Optional[Dict[str, Any]]]:
604
+ """Checks if entrypoint is a readable YAML file.
605
+
606
+ Args:
607
+ entrypoint: Path to a YAML file.
608
+ """
609
+ is_yaml, result, yaml_file_provided, invalid_reason = _check_yaml_only(
610
+ entrypoint)
583
611
  if not is_yaml:
584
612
  if yaml_file_provided:
585
613
  click.confirm(
@@ -632,7 +660,6 @@ def _make_task_or_dag_from_entrypoint_with_overrides(
632
660
  field_to_ignore: Optional[List[str]] = None,
633
661
  # job launch specific
634
662
  job_recovery: Optional[str] = None,
635
- priority: Optional[int] = None,
636
663
  config_override: Optional[Dict[str, Any]] = None,
637
664
  ) -> Union[sky.Task, sky.Dag]:
638
665
  """Creates a task or a dag from an entrypoint with overrides.
@@ -714,9 +741,6 @@ def _make_task_or_dag_from_entrypoint_with_overrides(
714
741
  task.num_nodes = num_nodes
715
742
  if name is not None:
716
743
  task.name = name
717
- # job launch specific.
718
- if priority is not None:
719
- task.set_job_priority(priority)
720
744
  return task
721
745
 
722
746
 
@@ -1243,7 +1267,7 @@ def _handle_jobs_queue_request(
1243
1267
  try:
1244
1268
  if not is_called_by_user:
1245
1269
  usage_lib.messages.usage.set_internal()
1246
- managed_jobs_ = sdk.get(request_id)
1270
+ managed_jobs_ = sdk.stream_and_get(request_id)
1247
1271
  num_in_progress_jobs = len(set(job['job_id'] for job in managed_jobs_))
1248
1272
  except exceptions.ClusterNotUpError as e:
1249
1273
  controller_status = e.cluster_status
@@ -1800,8 +1824,13 @@ def status(verbose: bool, refresh: bool, ip: bool, endpoints: bool,
1800
1824
  @cli.command()
1801
1825
  @flags.config_option(expose_value=False)
1802
1826
  @flags.all_option('Show all cluster information.')
1827
+ @click.option('--days',
1828
+ default=30,
1829
+ type=int,
1830
+ help='Show clusters from the last N days. Default is 30 days. '
1831
+ 'If set to 0, show all clusters.')
1803
1832
  @usage_lib.entrypoint
1804
- def cost_report(all: bool): # pylint: disable=redefined-builtin
1833
+ def cost_report(all: bool, days: int): # pylint: disable=redefined-builtin
1805
1834
  # NOTE(dev): Keep the docstring consistent between the Python API and CLI.
1806
1835
  """Show estimated costs for launched clusters.
1807
1836
 
@@ -1820,13 +1849,22 @@ def cost_report(all: bool): # pylint: disable=redefined-builtin
1820
1849
 
1821
1850
  - Clusters that were terminated/stopped on the cloud console.
1822
1851
  """
1823
- cluster_records = sdk.get(sdk.cost_report())
1852
+ days_to_query: Optional[int] = days
1853
+ if days == 0:
1854
+ days_to_query = None
1855
+ cluster_records = sdk.get(sdk.cost_report(days=days_to_query))
1824
1856
 
1825
1857
  normal_cluster_records = []
1826
1858
  controllers = dict()
1827
1859
  for cluster_record in cluster_records:
1828
1860
  cluster_name = cluster_record['name']
1829
- controller = controller_utils.Controllers.from_name(cluster_name)
1861
+ try:
1862
+ controller = controller_utils.Controllers.from_name(cluster_name)
1863
+ except AssertionError:
1864
+ # There could be some old controller clusters from previous
1865
+ # versions that we should not show in the cost report.
1866
+ logger.debug(f'Cluster {cluster_name} is not a controller cluster.')
1867
+ continue
1830
1868
  if controller is not None:
1831
1869
  controller_name = controller.value.name
1832
1870
  # to display most recent entry for each controller cluster
@@ -1839,10 +1877,14 @@ def cost_report(all: bool): # pylint: disable=redefined-builtin
1839
1877
  total_cost = status_utils.get_total_cost_of_displayed_records(
1840
1878
  normal_cluster_records, all)
1841
1879
 
1842
- status_utils.show_cost_report_table(normal_cluster_records, all)
1880
+ status_utils.show_cost_report_table(normal_cluster_records,
1881
+ all,
1882
+ days=days_to_query)
1843
1883
  for controller_name, cluster_record in controllers.items():
1844
- status_utils.show_cost_report_table(
1845
- [cluster_record], all, controller_name=controller_name.capitalize())
1884
+ status_utils.show_cost_report_table([cluster_record],
1885
+ all,
1886
+ controller_name=controller_name,
1887
+ days=days_to_query)
1846
1888
  total_cost += cluster_record['total_cost']
1847
1889
 
1848
1890
  click.echo(f'\n{colorama.Style.BRIGHT}'
@@ -2643,6 +2685,23 @@ def _hint_or_raise_for_down_jobs_controller(controller_name: str,
2643
2685
  # the controller being STOPPED or being firstly launched, i.e.,
2644
2686
  # there is no in-prgress managed jobs.
2645
2687
  managed_jobs_ = []
2688
+ except exceptions.InconsistentConsolidationModeError:
2689
+ # If this error is raised, it means the user switched to the
2690
+ # consolidation mode but the previous controller cluster is still
2691
+ # running. We should allow the user to tear down the controller
2692
+ # cluster in this case.
2693
+ with skypilot_config.override_skypilot_config(
2694
+ {'jobs': {
2695
+ 'controller': {
2696
+ 'consolidation_mode': False
2697
+ }
2698
+ }}):
2699
+ # Check again with the consolidation mode disabled. This is to
2700
+ # make sure there is no in-progress managed jobs.
2701
+ request_id = managed_jobs.queue(refresh=False,
2702
+ skip_finished=True,
2703
+ all_users=True)
2704
+ managed_jobs_ = sdk.stream_and_get(request_id)
2646
2705
 
2647
2706
  msg = (f'{colorama.Fore.YELLOW}WARNING: Tearing down the managed '
2648
2707
  'jobs controller. Please be aware of the following:'
@@ -3736,6 +3795,196 @@ def storage_delete(names: List[str], all: bool, yes: bool, async_call: bool): #
3736
3795
  f'{colorama.Style.RESET_ALL}')
3737
3796
 
3738
3797
 
3798
+ @cli.group(cls=_NaturalOrderGroup)
3799
+ def volumes():
3800
+ """SkyPilot Volumes CLI."""
3801
+ pass
3802
+
3803
+
3804
+ @volumes.command('apply', cls=_DocumentedCodeCommand)
3805
+ @flags.config_option(expose_value=False)
3806
+ @click.argument('entrypoint',
3807
+ required=False,
3808
+ type=str,
3809
+ nargs=-1,
3810
+ **_get_shell_complete_args(_complete_file_name))
3811
+ @click.option('--name',
3812
+ '-n',
3813
+ required=False,
3814
+ type=str,
3815
+ help='Volume name. Override the name defined in the YAML.')
3816
+ @click.option('--infra',
3817
+ required=False,
3818
+ type=str,
3819
+ help='Infra. Format: k8s, k8s/context-name. '
3820
+ 'Override the infra defined in the YAML.')
3821
+ @click.option(
3822
+ '--type',
3823
+ required=False,
3824
+ type=str,
3825
+ help='Volume type. Format: pvc. Override the type defined in the YAML.')
3826
+ @click.option('--size',
3827
+ required=False,
3828
+ type=str,
3829
+ help='Volume size. Override the size defined in the YAML.')
3830
+ @click.option('--yes',
3831
+ '-y',
3832
+ is_flag=True,
3833
+ default=False,
3834
+ required=False,
3835
+ help='Skip confirmation prompt.')
3836
+ @_add_click_options(flags.COMMON_OPTIONS)
3837
+ @usage_lib.entrypoint
3838
+ def volumes_apply(
3839
+ entrypoint: Optional[Tuple[str, ...]],
3840
+ name: Optional[str],
3841
+ infra: Optional[str],
3842
+ type: Optional[str], # pylint: disable=redefined-builtin
3843
+ size: Optional[str],
3844
+ yes: bool,
3845
+ async_call: bool):
3846
+ """Apply a volume.
3847
+
3848
+ Examples:
3849
+
3850
+ .. code-block:: bash
3851
+
3852
+ # Apply a volume from a YAML file.
3853
+ sky volumes apply volume.yaml
3854
+ \b
3855
+ # Apply a volume from a command.
3856
+ sky volumes apply --name pvc1 --infra k8s --type pvc --size 100Gi
3857
+ """
3858
+ # pylint: disable=import-outside-toplevel
3859
+ from sky.volumes import volume as volume_lib
3860
+
3861
+ volume_config_dict: Dict[str, Any] = {}
3862
+ if entrypoint is not None and len(entrypoint) > 0:
3863
+ entrypoint_str = ' '.join(entrypoint)
3864
+ is_yaml, yaml_config, yaml_file_provided, invalid_reason = (
3865
+ _check_yaml_only(entrypoint_str))
3866
+ if not is_yaml:
3867
+ if yaml_file_provided:
3868
+ raise click.BadParameter(f'{entrypoint_str!r} looks like a '
3869
+ f'yaml path but {invalid_reason}')
3870
+ else:
3871
+ raise click.BadParameter(
3872
+ f'{entrypoint_str!r} needs to be a YAML file')
3873
+ if yaml_config is not None:
3874
+ volume_config_dict = yaml_config.copy()
3875
+
3876
+ # Create Volume instance
3877
+ volume = volume_lib.Volume.from_dict(volume_config_dict)
3878
+
3879
+ # Normalize the volume config with CLI options
3880
+ volume.normalize_config(name=name, infra=infra, type=type, size=size)
3881
+
3882
+ logger.debug(f'Volume config: {volume.to_dict()}')
3883
+
3884
+ if not yes:
3885
+ click.confirm(f'Proceed to create volume {volume.name!r}?',
3886
+ default=True,
3887
+ abort=True,
3888
+ show_default=True)
3889
+
3890
+ # Call SDK to create volume
3891
+ try:
3892
+ request_id = volumes_sdk.apply(volume)
3893
+ _async_call_or_wait(request_id, async_call, 'sky.volumes.apply')
3894
+ except RuntimeError as e:
3895
+ logger.error(f'{colorama.Fore.RED}Error applying volume: '
3896
+ f'{common_utils.format_exception(e, use_bracket=True)}'
3897
+ f'{colorama.Style.RESET_ALL}')
3898
+
3899
+
3900
+ @volumes.command('ls', cls=_DocumentedCodeCommand)
3901
+ @flags.config_option(expose_value=False)
3902
+ @click.option('--verbose',
3903
+ '-v',
3904
+ default=False,
3905
+ is_flag=True,
3906
+ required=False,
3907
+ help='Show all information in full.')
3908
+ @usage_lib.entrypoint
3909
+ def volumes_ls(verbose: bool):
3910
+ """List volumes managed by SkyPilot."""
3911
+ request_id = volumes_sdk.ls()
3912
+ all_volumes = sdk.stream_and_get(request_id)
3913
+ volume_table = volumes_utils.format_volume_table(all_volumes,
3914
+ show_all=verbose)
3915
+ click.echo(volume_table)
3916
+
3917
+
3918
+ @volumes.command('delete', cls=_DocumentedCodeCommand)
3919
+ @flags.config_option(expose_value=False)
3920
+ @click.argument('names',
3921
+ required=False,
3922
+ type=str,
3923
+ nargs=-1,
3924
+ **_get_shell_complete_args(_complete_volume_name))
3925
+ @click.option('--all',
3926
+ '-a',
3927
+ default=False,
3928
+ is_flag=True,
3929
+ required=False,
3930
+ help='Delete all volumes.')
3931
+ @click.option('--yes',
3932
+ '-y',
3933
+ default=False,
3934
+ is_flag=True,
3935
+ required=False,
3936
+ help='Skip confirmation prompt.')
3937
+ @_add_click_options(flags.COMMON_OPTIONS)
3938
+ @usage_lib.entrypoint
3939
+ def volumes_delete(names: List[str], all: bool, yes: bool, async_call: bool): # pylint: disable=redefined-builtin
3940
+ """Delete volumes.
3941
+
3942
+ Examples:
3943
+
3944
+ .. code-block:: bash
3945
+
3946
+ # Delete two volumes.
3947
+ sky volumes delete pvc1 pvc2
3948
+ \b
3949
+ # Delete all volumes matching glob pattern 'pvc*'.
3950
+ sky volumes delete "pvc*"
3951
+ \b
3952
+ # Delete all volumes.
3953
+ sky volumes delete -a
3954
+ """
3955
+ if sum([bool(names), all]) != 1:
3956
+ raise click.UsageError('Either --all or a name must be specified.')
3957
+ all_volumes = sdk.get(volumes_sdk.ls())
3958
+ if all:
3959
+ if not all_volumes:
3960
+ click.echo('No volumes to delete.')
3961
+ return
3962
+ names = [volume['name'] for volume in all_volumes]
3963
+ else:
3964
+ existing_volume_names = [volume['name'] for volume in all_volumes]
3965
+ names = _get_glob_matches(existing_volume_names,
3966
+ names,
3967
+ resource_type='Volume')
3968
+ if names:
3969
+ if not yes:
3970
+ volume_names = ', '.join(names)
3971
+ volume_str = 'volumes' if len(names) > 1 else 'volume'
3972
+ click.confirm(
3973
+ f'Deleting {len(names)} {volume_str}: '
3974
+ f'{volume_names}. Proceed?',
3975
+ default=True,
3976
+ abort=True,
3977
+ show_default=True)
3978
+
3979
+ try:
3980
+ _async_call_or_wait(volumes_sdk.delete(names), async_call,
3981
+ 'sky.volumes.delete')
3982
+ except Exception as e: # pylint: disable=broad-except
3983
+ logger.error(f'{colorama.Fore.RED}Error deleting volumes {names}: '
3984
+ f'{common_utils.format_exception(e, use_bracket=True)}'
3985
+ f'{colorama.Style.RESET_ALL}')
3986
+
3987
+
3739
3988
  @cli.group(cls=_NaturalOrderGroup)
3740
3989
  def jobs():
3741
3990
  """Managed Jobs CLI (jobs with auto-recovery)."""
@@ -3762,14 +4011,6 @@ def jobs():
3762
4011
  default=None,
3763
4012
  type=str,
3764
4013
  help='Recovery strategy to use for managed jobs.')
3765
- @click.option('--priority',
3766
- type=click.IntRange(constants.MIN_PRIORITY,
3767
- constants.MAX_PRIORITY),
3768
- default=None,
3769
- show_default=True,
3770
- help=f'Job priority from ({constants.MIN_PRIORITY} '
3771
- f'to {constants.MAX_PRIORITY}). '
3772
- f'Default: {constants.DEFAULT_PRIORITY}.')
3773
4014
  @click.option(
3774
4015
  '--detach-run',
3775
4016
  '-d',
@@ -3804,7 +4045,6 @@ def jobs_launch(
3804
4045
  disk_tier: Optional[str],
3805
4046
  network_tier: Optional[str],
3806
4047
  ports: Tuple[str],
3807
- priority: Optional[int],
3808
4048
  detach_run: bool,
3809
4049
  yes: bool,
3810
4050
  async_call: bool,
@@ -3853,7 +4093,6 @@ def jobs_launch(
3853
4093
  network_tier=network_tier,
3854
4094
  ports=ports,
3855
4095
  job_recovery=job_recovery,
3856
- priority=priority,
3857
4096
  config_override=config_override,
3858
4097
  )
3859
4098