Repository: skypilot-org/skypilot
Pull request: [k8s] Show enabled contexts in `sky check`
Closes #4570.

Some examples:

### WIthout `allowed_contexts` set

<img width="987" alt="image" src="https://github.com/user-attachments/assets/6cc24c42-4b1d-4a96-a5f0-fed21a082d14" />

### With `allowed_contexts` set

<img width="989" alt="image" src="https://github.com/user-attachments/assets/7dc5464d-462c-424d-a1f7-c67e74c36260" />


### Running in-cluster auth

<img width="1487" alt="image" src="https://github.com/user-attachments/assets/77f73947-6109-425a-9db6-a8284bf40d52" />


### Running in-cluster auth without `SKYPILOT_IN_CLUSTER_CONTEXT_NAME` set:
<img width="1492" alt="image" src="https://github.com/user-attachments/assets/b5c33e92-f5df-433c-a2b6-8afedb39f6f0" />


Problem statement:
[k8s] Show enabled contexts (allowed_contexts) after `sky check`
At the end of `sky check` we should show the context(s) that will be used for the Kubernetes cluster. Useful for users to know which ones are enabled and disabled, instead of having to guess when specifying with `--regions`.

```
🎉 Enabled clouds 🎉
  ✔ AWS
  ✔ Kubernetes: context1, context2 
# Or as sub bullet points
```

Hints from issue discussion:

Commit message: Show context in check

Patch to review:
diff --git a/sky/check.py b/sky/check.py
index 1ab92cb1af6..d2877fd9c0e 100644
--- a/sky/check.py
+++ b/sky/check.py
@@ -155,7 +155,8 @@ def get_all_clouds():
         # Pretty print for UX.
         if not quiet:
             enabled_clouds_str = '\n  :heavy_check_mark: '.join(
-                [''] + sorted(all_enabled_clouds))
+                [''] +
+                [_format_enabled_cloud(c) for c in sorted(all_enabled_clouds)])
             rich.print('\n[green]:tada: Enabled clouds :tada:'
                        f'{enabled_clouds_str}[/green]')
 
@@ -222,3 +223,32 @@ def get_cloud_credential_file_mounts(
         r2_credential_mounts = cloudflare.get_credential_file_mounts()
         file_mounts.update(r2_credential_mounts)
     return file_mounts
+
+
+def _format_enabled_cloud(cloud_name: str) -> str:
+    if cloud_name == repr(sky_clouds.Kubernetes()):
+        # Get enabled contexts for Kubernetes
+        contexts = sky_clouds.Kubernetes.existing_allowed_contexts()
+        if not contexts:
+            return cloud_name
+
+        # Check if allowed_contexts is explicitly set in config
+        allowed_contexts = skypilot_config.get_nested(
+            ('kubernetes', 'allowed_contexts'), None)
+
+        # Format the context info with consistent styling
+        if allowed_contexts is not None:
+            contexts_formatted = []
+            for i, context in enumerate(contexts):
+                # TODO: We should use ux_utils.INDENT_SYMBOL and
+                # INDENT_LAST_SYMBOL but, they are formatted for colorama, while
+                # here we are using rich. We should migrate this file to
+                # use colorama as we do in the rest of the codebase.
+                symbol = ('└── ' if i == len(contexts) - 1 else '├── ')
+                contexts_formatted.append(f'\n        {symbol}{context}')
+            context_info = f'Allowed contexts:{"".join(contexts_formatted)}'
+        else:
+            context_info = f'Active context: {contexts[0]}'
+
+        return f'{cloud_name}[/green][dim]\n    └── {context_info}[/dim][green]'
+    return cloud_name
diff --git a/sky/clouds/kubernetes.py b/sky/clouds/kubernetes.py
index f9242bd77aa..3bab7687f48 100644
--- a/sky/clouds/kubernetes.py
+++ b/sky/clouds/kubernetes.py
@@ -131,7 +131,7 @@ def _log_skipped_contexts_once(cls, skipped_contexts: Tuple[str,
                 'Ignoring these contexts.')
 
     @classmethod
-    def _existing_allowed_contexts(cls) -> List[str]:
+    def existing_allowed_contexts(cls) -> List[str]:
         """Get existing allowed contexts.
 
         If None is returned in the list, it means that we are running in a pod
@@ -175,7 +175,7 @@ def regions_with_offering(cls, instance_type: Optional[str],
                               use_spot: bool, region: Optional[str],
                               zone: Optional[str]) -> List[clouds.Region]:
         del accelerators, zone, use_spot  # unused
-        existing_contexts = cls._existing_allowed_contexts()
+        existing_contexts = cls.existing_allowed_contexts()
 
         regions = []
         for context in existing_contexts:
@@ -591,7 +591,7 @@ def _make(instance_list):
     def check_credentials(cls) -> Tuple[bool, Optional[str]]:
         # Test using python API
         try:
-            existing_allowed_contexts = cls._existing_allowed_contexts()
+            existing_allowed_contexts = cls.existing_allowed_contexts()
         except ImportError as e:
             return (False,
                     f'{common_utils.format_exception(e, use_bracket=True)}')


Code review comments:
diff_hunk: @@ -222,3 +223,32 @@ def get_cloud_credential_file_mounts(
         r2_credential_mounts = cloudflare.get_credential_file_mounts()
         file_mounts.update(r2_credential_mounts)
     return file_mounts
+
+
+def _format_enabled_cloud(cloud_name: str) -> str:
+    if cloud_name == repr(sky_clouds.Kubernetes()):
+        # Get enabled contexts for Kubernetes
+        contexts = sky_clouds.Kubernetes.existing_allowed_contexts()
path: sky/check.py
text: @user1:
minor: renaming this to `existing_contexts` ?