skypilot-nightly 1.0.0.dev20250522__py3-none-any.whl → 1.0.0.dev20250524__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 (120) hide show
  1. sky/__init__.py +2 -2
  2. sky/adaptors/kubernetes.py +46 -16
  3. sky/backends/backend_utils.py +62 -45
  4. sky/backends/cloud_vm_ray_backend.py +19 -5
  5. sky/check.py +398 -171
  6. sky/cli.py +302 -98
  7. sky/client/cli.py +302 -98
  8. sky/client/sdk.py +104 -12
  9. sky/clouds/__init__.py +3 -0
  10. sky/clouds/aws.py +4 -2
  11. sky/clouds/azure.py +4 -2
  12. sky/clouds/cloud.py +24 -6
  13. sky/clouds/cudo.py +2 -1
  14. sky/clouds/do.py +2 -1
  15. sky/clouds/fluidstack.py +2 -1
  16. sky/clouds/gcp.py +23 -5
  17. sky/clouds/ibm.py +4 -2
  18. sky/clouds/kubernetes.py +66 -22
  19. sky/clouds/lambda_cloud.py +2 -1
  20. sky/clouds/nebius.py +18 -2
  21. sky/clouds/oci.py +4 -2
  22. sky/clouds/paperspace.py +2 -1
  23. sky/clouds/runpod.py +2 -1
  24. sky/clouds/scp.py +2 -1
  25. sky/clouds/service_catalog/constants.py +1 -1
  26. sky/clouds/service_catalog/ssh_catalog.py +167 -0
  27. sky/clouds/ssh.py +203 -0
  28. sky/clouds/vast.py +2 -1
  29. sky/clouds/vsphere.py +2 -1
  30. sky/core.py +58 -11
  31. sky/dashboard/out/404.html +1 -1
  32. sky/dashboard/out/_next/static/aHej19bZyl4hoHgrzPCn7/_buildManifest.js +1 -0
  33. sky/dashboard/out/_next/static/chunks/480-ee58038f1a4afd5c.js +1 -0
  34. sky/dashboard/out/_next/static/chunks/488-50d843fdb5396d32.js +15 -0
  35. sky/dashboard/out/_next/static/chunks/498-d7722313e5e5b4e6.js +21 -0
  36. sky/dashboard/out/_next/static/chunks/573-f17bd89d9f9118b3.js +66 -0
  37. sky/dashboard/out/_next/static/chunks/578-7a4795009a56430c.js +6 -0
  38. sky/dashboard/out/_next/static/chunks/734-5f5ce8f347b7f417.js +1 -0
  39. sky/dashboard/out/_next/static/chunks/937.f97f83652028e944.js +1 -0
  40. sky/dashboard/out/_next/static/chunks/938-f347f6144075b0c8.js +1 -0
  41. sky/dashboard/out/_next/static/chunks/9f96d65d-5a3e4af68c26849e.js +1 -0
  42. sky/dashboard/out/_next/static/chunks/pages/_app-dec800f9ef1b10f4.js +1 -0
  43. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-37c042a356f8e608.js +1 -0
  44. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-9529d9e882a0e75c.js +16 -0
  45. sky/dashboard/out/_next/static/chunks/pages/clusters-9e6d1ec6e1ac5b29.js +1 -0
  46. sky/dashboard/out/_next/static/chunks/pages/infra-e690d864aa00e2ea.js +1 -0
  47. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-db6558a5ec687011.js +1 -0
  48. sky/dashboard/out/_next/static/chunks/pages/jobs-73d5e0c369d00346.js +16 -0
  49. sky/dashboard/out/_next/static/chunks/pages/users-2d319455c3f1c3e2.js +1 -0
  50. sky/dashboard/out/_next/static/chunks/pages/workspaces-02a7b60f2ead275f.js +1 -0
  51. sky/dashboard/out/_next/static/chunks/webpack-deda68c926e8d0bc.js +1 -0
  52. sky/dashboard/out/_next/static/css/d2cdba64c9202dd7.css +3 -0
  53. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  54. sky/dashboard/out/clusters/[cluster].html +1 -1
  55. sky/dashboard/out/clusters.html +1 -1
  56. sky/dashboard/out/index.html +1 -1
  57. sky/dashboard/out/infra.html +1 -1
  58. sky/dashboard/out/jobs/[job].html +1 -1
  59. sky/dashboard/out/jobs.html +1 -1
  60. sky/dashboard/out/users.html +1 -0
  61. sky/dashboard/out/workspaces.html +1 -0
  62. sky/data/storage.py +1 -1
  63. sky/global_user_state.py +42 -19
  64. sky/jobs/constants.py +1 -1
  65. sky/jobs/server/core.py +72 -56
  66. sky/jobs/state.py +26 -5
  67. sky/jobs/utils.py +65 -13
  68. sky/optimizer.py +29 -7
  69. sky/provision/__init__.py +1 -0
  70. sky/provision/aws/instance.py +17 -1
  71. sky/provision/fluidstack/instance.py +1 -0
  72. sky/provision/kubernetes/instance.py +16 -5
  73. sky/provision/kubernetes/utils.py +37 -19
  74. sky/provision/nebius/instance.py +3 -1
  75. sky/provision/nebius/utils.py +14 -2
  76. sky/provision/ssh/__init__.py +18 -0
  77. sky/resources.py +4 -1
  78. sky/serve/server/core.py +9 -6
  79. sky/server/html/token_page.html +6 -1
  80. sky/server/requests/executor.py +1 -0
  81. sky/server/requests/payloads.py +18 -0
  82. sky/server/server.py +108 -5
  83. sky/setup_files/dependencies.py +1 -0
  84. sky/skylet/constants.py +4 -1
  85. sky/skypilot_config.py +83 -9
  86. sky/templates/nebius-ray.yml.j2 +12 -0
  87. sky/utils/cli_utils/status_utils.py +18 -8
  88. sky/utils/infra_utils.py +21 -1
  89. sky/utils/kubernetes/cleanup-tunnel.sh +62 -0
  90. sky/utils/kubernetes/create_cluster.sh +1 -0
  91. sky/utils/kubernetes/deploy_remote_cluster.py +1440 -0
  92. sky/utils/kubernetes/kubernetes_deploy_utils.py +117 -10
  93. sky/utils/kubernetes/ssh-tunnel.sh +387 -0
  94. sky/utils/log_utils.py +218 -1
  95. sky/utils/schemas.py +75 -0
  96. sky/utils/ux_utils.py +2 -1
  97. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/METADATA +6 -1
  98. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/RECORD +103 -91
  99. sky/dashboard/out/_next/static/CzOVV6JpRQBRt5GhZuhyK/_buildManifest.js +0 -1
  100. sky/dashboard/out/_next/static/chunks/236-1a3a9440417720eb.js +0 -6
  101. sky/dashboard/out/_next/static/chunks/312-c3c8845990db8ffc.js +0 -15
  102. sky/dashboard/out/_next/static/chunks/37-d584022b0da4ac3b.js +0 -6
  103. sky/dashboard/out/_next/static/chunks/393-e1eaa440481337ec.js +0 -1
  104. sky/dashboard/out/_next/static/chunks/480-f28cd152a98997de.js +0 -1
  105. sky/dashboard/out/_next/static/chunks/582-683f4f27b81996dc.js +0 -59
  106. sky/dashboard/out/_next/static/chunks/pages/_app-8cfab319f9fb3ae8.js +0 -1
  107. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-33bc2bec322249b1.js +0 -1
  108. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-e2fc2dd1955e6c36.js +0 -1
  109. sky/dashboard/out/_next/static/chunks/pages/clusters-3a748bd76e5c2984.js +0 -1
  110. sky/dashboard/out/_next/static/chunks/pages/infra-9180cd91cee64b96.js +0 -1
  111. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-70756c2dad850a7e.js +0 -1
  112. sky/dashboard/out/_next/static/chunks/pages/jobs-ecd804b9272f4a7c.js +0 -1
  113. sky/dashboard/out/_next/static/chunks/webpack-830f59b8404e96b8.js +0 -1
  114. sky/dashboard/out/_next/static/css/7e7ce4ff31d3977b.css +0 -3
  115. sky/utils/kubernetes/deploy_remote_cluster.sh +0 -308
  116. /sky/dashboard/out/_next/static/{CzOVV6JpRQBRt5GhZuhyK → aHej19bZyl4hoHgrzPCn7}/_ssgManifest.js +0 -0
  117. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/WHEEL +0 -0
  118. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/entry_points.txt +0 -0
  119. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.dist-info}/licenses/LICENSE +0 -0
  120. {skypilot_nightly-1.0.0.dev20250522.dist-info → skypilot_nightly-1.0.0.dev20250524.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 = '417e7f813d86c03c334e9e12f38718766fc4daa5'
8
+ _SKYPILOT_COMMIT_SHA = 'a5958f14156552e0347f0a7490b028473e7fd593'
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.dev20250522'
38
+ __version__ = '1.0.0.dev20250524'
39
39
  __root_dir__ = os.path.dirname(os.path.abspath(__file__))
40
40
 
41
41
 
@@ -3,8 +3,8 @@ import logging
3
3
  import os
4
4
  from typing import Any, Callable, Optional, Set
5
5
 
6
+ from sky import sky_logging
6
7
  from sky.adaptors import common
7
- from sky.sky_logging import set_logging_level
8
8
  from sky.utils import annotations
9
9
  from sky.utils import common_utils
10
10
  from sky.utils import ux_utils
@@ -26,6 +26,8 @@ DEFAULT_IN_CLUSTER_REGION = 'in-cluster'
26
26
  # set to DEFAULT_IN_CLUSTER_REGION.
27
27
  IN_CLUSTER_CONTEXT_NAME_ENV_VAR = 'SKYPILOT_IN_CLUSTER_CONTEXT_NAME'
28
28
 
29
+ logger = sky_logging.init_logger(__name__)
30
+
29
31
 
30
32
  def _decorate_methods(obj: Any, decorator: Callable, decoration_type: str):
31
33
  for attr_name in dir(obj):
@@ -43,7 +45,7 @@ def _decorate_methods(obj: Any, decorator: Callable, decoration_type: str):
43
45
  return obj
44
46
 
45
47
 
46
- def _api_logging_decorator(logger: str, level: int):
48
+ def _api_logging_decorator(logger_src: str, level: int):
47
49
  """Decorator to set logging level for API calls.
48
50
 
49
51
  This is used to suppress the verbose logging from urllib3 when calls to the
@@ -54,7 +56,9 @@ def _api_logging_decorator(logger: str, level: int):
54
56
 
55
57
  def wrapped(*args, **kwargs):
56
58
  obj = api(*args, **kwargs)
57
- _decorate_methods(obj, set_logging_level(logger, level), 'api_log')
59
+ _decorate_methods(obj,
60
+ sky_logging.set_logging_level(logger_src, level),
61
+ 'api_log')
58
62
  return obj
59
63
 
60
64
  return wrapped
@@ -71,27 +75,53 @@ def _load_config(context: Optional[str] = None):
71
75
  except kubernetes.config.config_exception.ConfigException as e:
72
76
  suffix = common_utils.format_exception(e, use_bracket=True)
73
77
  context_name = '(current-context)' if context is None else context
78
+ is_ssh_node_pool = False
79
+ if context_name.startswith('ssh-'):
80
+ context_name = context_name.lstrip('ssh-')
81
+ is_ssh_node_pool = True
74
82
  # Check if exception was due to no current-context
75
83
  if 'Expected key current-context' in str(e):
76
- err_str = ('Failed to load Kubernetes configuration for '
77
- f'{context_name!r}. '
78
- 'Kubeconfig does not contain any valid context(s).'
79
- f'\n{suffix}\n'
80
- ' If you were running a local Kubernetes '
81
- 'cluster, run `sky local up` to start the cluster.')
84
+ if is_ssh_node_pool:
85
+ context_name = context_name.lstrip('ssh-')
86
+ err_str = ('Failed to load SSH Node Pool configuration for '
87
+ f'{context_name!r}.\n'
88
+ ' Run `sky ssh up --infra {context_name}` to '
89
+ 'set up or repair the cluster.')
90
+ else:
91
+ err_str = (
92
+ 'Failed to load Kubernetes configuration for '
93
+ f'{context_name!r}. '
94
+ 'Kubeconfig does not contain any valid context(s).'
95
+ f'\n{suffix}\n'
96
+ ' If you were running a local Kubernetes '
97
+ 'cluster, run `sky local up` to start the cluster.')
82
98
  else:
83
99
  kubeconfig_path = os.environ.get('KUBECONFIG', '~/.kube/config')
84
- err_str = (
85
- f'Failed to load Kubernetes configuration for '
86
- f'{context_name!r}. Please check if your kubeconfig file '
87
- f'exists at {kubeconfig_path} and is valid.\n{suffix}')
88
- err_str += '\nTo disable Kubernetes for SkyPilot: run `sky check`.'
89
- if context is None: # kubernetes defaults to current-context.
100
+ if is_ssh_node_pool:
101
+ err_str = (
102
+ f'Failed to load SSH Node Pool configuration for '
103
+ f'{context_name!r}. Run `sky ssh up --infra '
104
+ f'{context_name}` to set up or repair the cluster.')
105
+ else:
106
+ err_str = (
107
+ 'Failed to load Kubernetes configuration for '
108
+ f'{context_name!r}. Please check if your kubeconfig '
109
+ f'file exists at {kubeconfig_path} and is valid.'
110
+ f'\n{suffix}\n')
111
+ if is_ssh_node_pool:
112
+ err_str += (f'\nTo disable SSH Node Pool {context_name!r}: '
113
+ 'run `sky check`.')
114
+ else:
90
115
  err_str += (
91
116
  '\nHint: Kubernetes attempted to query the current-context '
92
117
  'set in kubeconfig. Check if the current-context is valid.')
93
118
  with ux_utils.print_exception_no_traceback():
94
- raise ValueError(err_str) from None
119
+ if is_ssh_node_pool:
120
+ # For SSH Node Pool, we don't want to surface k8s errors
121
+ # (e.g., missing context) unless debug flag is set.
122
+ logging.debug(f'Kubernetes error: {suffix}')
123
+ else:
124
+ raise ValueError(err_str) from None
95
125
 
96
126
  if context == in_cluster_context_name() or context is None:
97
127
  try:
@@ -1556,6 +1556,16 @@ def check_owner_identity(cluster_name: str) -> None:
1556
1556
  handle = record['handle']
1557
1557
  if not isinstance(handle, backends.CloudVmRayResourceHandle):
1558
1558
  return
1559
+ active_workspace = skypilot_config.get_active_workspace()
1560
+ cluster_workspace = record.get('workspace',
1561
+ constants.SKYPILOT_DEFAULT_WORKSPACE)
1562
+ if active_workspace != cluster_workspace:
1563
+ with ux_utils.print_exception_no_traceback():
1564
+ raise exceptions.ClusterOwnerIdentityMismatchError(
1565
+ f'{colorama.Fore.YELLOW}'
1566
+ f'The cluster {cluster_name!r} is in workspace '
1567
+ f'{cluster_workspace!r}, but the active workspace is '
1568
+ f'{active_workspace!r}.{colorama.Fore.RESET}')
1559
1569
 
1560
1570
  launched_resources = handle.launched_resources.assert_launchable()
1561
1571
  cloud = launched_resources.cloud
@@ -2152,57 +2162,64 @@ def refresh_cluster_record(
2152
2162
  record = global_user_state.get_cluster_from_name(cluster_name)
2153
2163
  if record is None:
2154
2164
  return None
2155
- check_owner_identity(cluster_name)
2156
-
2157
- if not isinstance(record['handle'], backends.CloudVmRayResourceHandle):
2158
- return record
2159
-
2160
- # The loop logic allows us to notice if the status was updated in the
2161
- # global_user_state by another process and stop trying to get the lock.
2162
- # The core loop logic is adapted from FileLock's implementation.
2163
- lock = filelock.FileLock(CLUSTER_STATUS_LOCK_PATH.format(cluster_name))
2164
- start_time = time.perf_counter()
2165
+ # TODO(zhwu, 05/20): switch to the specific workspace to make sure we are
2166
+ # using the correct cloud credentials.
2167
+ workspace = record.get('workspace', constants.SKYPILOT_DEFAULT_WORKSPACE)
2168
+ with skypilot_config.local_active_workspace_ctx(workspace):
2169
+ check_owner_identity(cluster_name)
2165
2170
 
2166
- # Loop until we have an up-to-date status or until we acquire the lock.
2167
- while True:
2168
- # Check to see if we can return the cached status.
2169
- if not _must_refresh_cluster_status(record, force_refresh_statuses):
2171
+ if not isinstance(record['handle'], backends.CloudVmRayResourceHandle):
2170
2172
  return record
2171
2173
 
2172
- if not acquire_per_cluster_status_lock:
2173
- return _update_cluster_status(cluster_name)
2174
+ # The loop logic allows us to notice if the status was updated in the
2175
+ # global_user_state by another process and stop trying to get the lock.
2176
+ # The core loop logic is adapted from FileLock's implementation.
2177
+ lock = filelock.FileLock(CLUSTER_STATUS_LOCK_PATH.format(cluster_name))
2178
+ start_time = time.perf_counter()
2174
2179
 
2175
- # Try to acquire the lock so we can fetch the status.
2176
- try:
2177
- with lock.acquire(blocking=False):
2178
- # Check the cluster status again, since it could have been
2179
- # updated between our last check and acquiring the lock.
2180
- record = global_user_state.get_cluster_from_name(cluster_name)
2181
- if record is None or not _must_refresh_cluster_status(
2182
- record, force_refresh_statuses):
2183
- return record
2184
- # Update and return the cluster status.
2180
+ # Loop until we have an up-to-date status or until we acquire the lock.
2181
+ while True:
2182
+ # Check to see if we can return the cached status.
2183
+ if not _must_refresh_cluster_status(record, force_refresh_statuses):
2184
+ return record
2185
+
2186
+ if not acquire_per_cluster_status_lock:
2185
2187
  return _update_cluster_status(cluster_name)
2186
- except filelock.Timeout:
2187
- # lock.acquire() will throw a Timeout exception if the lock is not
2188
- # available and we have blocking=False.
2189
- pass
2190
-
2191
- # Logic adapted from FileLock.acquire().
2192
- # If cluster_status_lock_time is <0, we will never hit this. No timeout.
2193
- # Otherwise, if we have timed out, return the cached status. This has
2194
- # the potential to cause correctness issues, but if so it is the
2195
- # caller's responsibility to set the timeout to -1.
2196
- if 0 <= cluster_status_lock_timeout < time.perf_counter() - start_time:
2197
- logger.debug('Refreshing status: Failed get the lock for cluster '
2198
- f'{cluster_name!r}. Using the cached status.')
2199
- return record
2200
- time.sleep(0.05)
2201
2188
 
2202
- # Refresh for next loop iteration.
2203
- record = global_user_state.get_cluster_from_name(cluster_name)
2204
- if record is None:
2205
- return None
2189
+ # Try to acquire the lock so we can fetch the status.
2190
+ try:
2191
+ with lock.acquire(blocking=False):
2192
+ # Check the cluster status again, since it could have been
2193
+ # updated between our last check and acquiring the lock.
2194
+ record = global_user_state.get_cluster_from_name(
2195
+ cluster_name)
2196
+ if record is None or not _must_refresh_cluster_status(
2197
+ record, force_refresh_statuses):
2198
+ return record
2199
+ # Update and return the cluster status.
2200
+ return _update_cluster_status(cluster_name)
2201
+ except filelock.Timeout:
2202
+ # lock.acquire() will throw a Timeout exception if the lock is not
2203
+ # available and we have blocking=False.
2204
+ pass
2205
+
2206
+ # Logic adapted from FileLock.acquire().
2207
+ # If cluster_status_lock_time is <0, we will never hit this. No timeout.
2208
+ # Otherwise, if we have timed out, return the cached status. This has
2209
+ # the potential to cause correctness issues, but if so it is the
2210
+ # caller's responsibility to set the timeout to -1.
2211
+ if 0 <= cluster_status_lock_timeout < time.perf_counter(
2212
+ ) - start_time:
2213
+ logger.debug(
2214
+ 'Refreshing status: Failed get the lock for cluster '
2215
+ f'{cluster_name!r}. Using the cached status.')
2216
+ return record
2217
+ time.sleep(0.05)
2218
+
2219
+ # Refresh for next loop iteration.
2220
+ record = global_user_state.get_cluster_from_name(cluster_name)
2221
+ if record is None:
2222
+ return None
2206
2223
 
2207
2224
 
2208
2225
  @timeline.event
@@ -192,6 +192,7 @@ def _get_cluster_config_template(cloud):
192
192
  clouds.DO: 'do-ray.yml.j2',
193
193
  clouds.RunPod: 'runpod-ray.yml.j2',
194
194
  clouds.Kubernetes: 'kubernetes-ray.yml.j2',
195
+ clouds.SSH: 'kubernetes-ray.yml.j2',
195
196
  clouds.Vsphere: 'vsphere-ray.yml.j2',
196
197
  clouds.Vast: 'vast-ray.yml.j2',
197
198
  clouds.Fluidstack: 'fluidstack-ray.yml.j2',
@@ -1547,11 +1548,13 @@ class RetryingVmProvisioner(object):
1547
1548
  controller_str = ('' if controller is None else
1548
1549
  f' {controller.value.name}')
1549
1550
  if isinstance(to_provision.cloud, clouds.Kubernetes):
1550
- # Omit the region name for Kubernetes.
1551
+ suffix = '.'
1552
+ if region.name.startswith('ssh-'):
1553
+ suffix = f' ({region.name.lstrip("ssh-")})'
1551
1554
  logger.info(
1552
1555
  ux_utils.starting_message(
1553
1556
  f'Launching{controller_str} on '
1554
- f'{to_provision.cloud}.'))
1557
+ f'{to_provision.cloud}{suffix}'))
1555
1558
  else:
1556
1559
  logger.info(
1557
1560
  ux_utils.starting_message(
@@ -1724,8 +1727,17 @@ class RetryingVmProvisioner(object):
1724
1727
  f'{requested_resources}. ')
1725
1728
  elif to_provision.region is not None:
1726
1729
  # For public clouds, provision.region is always set.
1727
- message = ('Failed to acquire resources in all zones in '
1728
- f'{to_provision.region} for {requested_resources}. ')
1730
+ if to_provision.cloud.is_same_cloud(clouds.SSH()):
1731
+ message = ('Failed to acquire resources in SSH Node Pool '
1732
+ f'({to_provision.region.lstrip("ssh-")}) for '
1733
+ f'{requested_resources}. The SSH Node Pool may not '
1734
+ 'have enough resources.')
1735
+ elif to_provision.cloud.is_same_cloud(clouds.Kubernetes()):
1736
+ message = ('Failed to acquire resources in context '
1737
+ f'{to_provision.region} for {requested_resources}. ')
1738
+ else:
1739
+ message = ('Failed to acquire resources in all zones in '
1740
+ f'{to_provision.region} for {requested_resources}. ')
1729
1741
  else:
1730
1742
  message = (f'Failed to acquire resources in {to_provision.cloud} '
1731
1743
  f'for {requested_resources}. ')
@@ -3495,7 +3507,9 @@ class CloudVmRayBackend(backends.Backend['CloudVmRayResourceHandle']):
3495
3507
  # Add the managed job to job queue database.
3496
3508
  managed_job_codegen = managed_jobs.ManagedJobCodeGen()
3497
3509
  managed_job_code = managed_job_codegen.set_pending(
3498
- job_id, managed_job_dag)
3510
+ job_id, managed_job_dag,
3511
+ skypilot_config.get_active_workspace(
3512
+ force_user_workspace=True))
3499
3513
  # Set the managed job to PENDING state to make sure that this
3500
3514
  # managed job appears in the `sky jobs queue`, even if it needs
3501
3515
  # to wait to be submitted.