Repository: skypilot-org/skypilot
Pull request: Fix Logging for `sky launch` on new machine
Fixes #1381.

## Tests

- `sky launch` on a manually provisioned AWS machine (with no credentials).

```
ubuntu@ip-172-31-37-168:~/skypilot/sky$ sky launch
Defaulting to local cloud as there are no enabled public clouds in `sky check`.
Error: Specify -c [local_cluster] to launch on a local cluster.
See `sky status` for local cluster name(s).
```

Problem statement:
UX: New machine that hasn't run `sky check` raises Local errors on first `sky launch`
To repro:

1. Create a new VM manually in console
2. Run `pip install skypilot`
3. Run `sky launch`

This will show the following:
```
...
I 11-06 15:02:41 optimizer.py:685] Considered resources (1 node):
I 11-06 15:02:41 optimizer.py:713] ---------------------------------------------------------------
I 11-06 15:02:41 optimizer.py:713]  CLOUD   INSTANCE   vCPUs   ACCELERATORS   COST ($)   CHOSEN
I 11-06 15:02:41 optimizer.py:713] ---------------------------------------------------------------
I 11-06 15:02:41 optimizer.py:713]  Local   on-prem    -       -              0.00          ✔
I 11-06 15:02:41 optimizer.py:713] ---------------------------------------------------------------
I 11-06 15:02:41 optimizer.py:713]
Launching a new cluster 'sky-6be7-ec2-user'. Proceed? [Y/n]: No existing clusters.

Traceback (most recent call last):
  File "/home/ec2-user/.local/bin/sky", line 8, in <module>
    sys.exit(cli())
  File "/home/ec2-user/.local/lib/python3.7/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/utils/common_utils.py", line 179, in _record
    return f(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/cli.py", line 916, in invoke
    return super().invoke(ctx)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/ec2-user/.local/lib/python3.7/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/utils/common_utils.py", line 200, in _record
    return f(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/cli.py", line 1088, in launch
    is_local_cloud=onprem_utils.check_if_local_cloud(cluster))
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/cli.py", line 658, in _launch_with_confirm
    no_setup=no_setup,
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/utils/common_utils.py", line 200, in _record
    return f(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/utils/common_utils.py", line 200, in _record
    return f(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/execution.py", line 302, in launch
    no_setup=no_setup,
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/execution.py", line 196, in _execute
    retry_until_up=retry_until_up)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/utils/common_utils.py", line 200, in _record
    return f(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/utils/common_utils.py", line 179, in _record
    return f(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/backends/backend.py", line 50, in provision
    cluster_name, retry_until_up)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/backends/cloud_vm_ray_backend.py", line 1669, in _provision
    task, to_provision, cluster_name)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/utils/common_utils.py", line 200, in _record
    return f(*args, **kwargs)
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/backends/cloud_vm_ray_backend.py", line 2698, in _check_existing_cluster
    cluster_name)['auth']['ssh_user']
  File "/home/ec2-user/.local/lib/python3.7/site-packages/sky/backends/onprem_utils.py", line 154, in get_local_cluster_config_or_error
    raise ValueError(f'Cluster config {local_file} not found.')
ValueError: Cluster config /home/ec2-user/.sky/local/sky-6be7-ec2-user.yml not found.
```

cc @michaelzhiluo

Hints from issue discussion:

Commit message: ok

Patch to review:
diff --git a/sky/backends/onprem_utils.py b/sky/backends/onprem_utils.py
index 7cb28c624b2..b112fc845ef 100644
--- a/sky/backends/onprem_utils.py
+++ b/sky/backends/onprem_utils.py
@@ -11,6 +11,8 @@
 import rich.console as rich_console
 import yaml
 
+from sky import check
+from sky import clouds
 from sky import global_user_state
 from sky import sky_logging
 from sky.backends import backend_utils
@@ -467,6 +469,13 @@ def save_distributable_yaml(cluster_config: Dict[str, Dict[str, Any]]) -> None:
     common_utils.dump_yaml(abs_yaml_path, cluster_config)
 
 
+# Checks if there is public cloud enabled.
+def _is_public_cloud_disabled():
+    enabled_clouds = global_user_state.get_enabled_clouds()
+    return len(enabled_clouds) == 1 and isinstance(enabled_clouds[0],
+                                                   clouds.Local)
+
+
 # Currently, programmatic API doesn't check this.
 def check_local_cloud_args(cloud: Optional[str] = None,
                            cluster_name: Optional[str] = None,
@@ -492,6 +501,11 @@ def check_local_cloud_args(cloud: Optional[str] = None,
                 '`cloud: local` or no cloud in YAML or CLI args.')
         return True
     else:
+        if _is_public_cloud_disabled():
+            check.check(quiet=True)
+            if _is_public_cloud_disabled():
+                raise click.UsageError('Cloud access is not set up. '
+                                       'Run: `sky check`')
         if cloud == 'local' or yaml_cloud == 'local':
             if cluster_name is not None:
                 raise click.UsageError(
diff --git a/sky/optimizer.py b/sky/optimizer.py
index 34a910c05bf..e7cc509c18f 100644
--- a/sky/optimizer.py
+++ b/sky/optimizer.py
@@ -53,6 +53,12 @@ def _create_table(field_names: List[str]) -> prettytable.PrettyTable:
     return log_utils.create_table(field_names, **table_kwargs)
 
 
+# Checks if there is public cloud enabled.
+def _is_public_cloud_disabled(enabled_clouds: List[clouds.Cloud]):
+    return len(enabled_clouds) == 1 and isinstance(enabled_clouds[0],
+                                                   clouds.Local)
+
+
 class Optimizer:
     """Optimizer: assigns best resources to user tasks."""
 
@@ -826,7 +832,7 @@ def _fill_in_launchable_resources(
 ) -> Tuple[Dict[resources_lib.Resources, List[resources_lib.Resources]],
            _PerCloudCandidates]:
     enabled_clouds = global_user_state.get_enabled_clouds()
-    if len(enabled_clouds) == 0 and try_fix_with_sky_check:
+    if _is_public_cloud_disabled(enabled_clouds) and try_fix_with_sky_check:
         check.check(quiet=True)
         return _fill_in_launchable_resources(task, blocked_launchable_resources,
                                              False)


Code review comments:
diff_hunk: @@ -467,6 +469,13 @@ def save_distributable_yaml(cluster_config: Dict[str, Dict[str, Any]]) -> None:
     common_utils.dump_yaml(abs_yaml_path, cluster_config)
 
 
+# Checks if there is public cloud enabled.
+def _is_public_cloud_disabled():
+    enabled_clouds = global_user_state.get_enabled_clouds()
+    return len(enabled_clouds) == 1 and isinstance(enabled_clouds[0],
+                                                   clouds.Local)
+
path: sky/backends/onprem_utils.py
text: @user1:
We are duplicating this function in multiple files. How about we move this function into `global_user_state`?