Distinct cluster names can map to the same name on cloud and adopt each other's resources
Summary
Cluster names that SkyPilot accepts as distinct can map to the same name on cloud. When they do, launching the second one adopts the first one's instances/pods instead of creating its own, so two rows in sky status silently share the same cloud resources.
Why the names collide
make_cluster_name_on_cloud() (sky/utils/common_utils.py) lowercases the display name and replaces . and _ with -:
cluster_name_on_cloud = re.sub(r'[._]', '-', display_name).lower()The validator that runs at launch (check_cluster_name_is_valid(), regex CLUSTER_NAME_VALID_REGEX = '[a-zA-Z]([-_.a-zA-Z0-9]*[a-zA-Z0-9])?' in sky/skylet/constants.py) accepts uppercase letters, . and _, and does not reject a name because another cluster already maps to the same name on cloud.
make_cluster_name_on_cloud() does append a hash of the display name, which would keep these apart — but only when the name is long enough to need truncating. Below each cloud's limit (Kubernetes 42, GCP 35, Azure 42, AWS 248) the name is passed through unhashed, so the collision is the normal case rather than a corner case.
Repro
from sky.utils import common_utils
for name in ['My-Cluster', 'my_cluster', 'my.cluster', 'my-cluster']:
common_utils.check_cluster_name_is_valid(name) # all four are accepted
print(name, '->', common_utils.make_cluster_name_on_cloud(name, max_length=42))My-Cluster -> my-cluster-<user hash>
my_cluster -> my-cluster-<user hash>
my.cluster -> my-cluster-<user hash>
my-cluster -> my-cluster-<user hash>The user hash is the same in all four (same user), so all four are one name on cloud. Concretely: sky launch -c my-cluster followed by sky launch -c my_cluster.
What goes wrong then
Provisioners identify a cluster's existing instances by the name on cloud, not by the display name.
Kubernetes (sky/provision/kubernetes/instance.py, run_instances): pods are matched with the label selector ray-cluster-name: <cluster_name_on_cloud> (ray_tag_filter + filter_pods). With that selector the second launch
- deletes the first cluster's pods that are in
Failed/Succeededphase ("Clean up pods in Failed/Succeeded phase from previous runs"), and - counts the first cluster's
Pending/Runningpods as its own, creating onlyconfig.count - len(running_pods)new ones.
AWS (sky/provision/aws/instance.py, run_instances) does the same thing with the EC2 filter tag:ray-cluster-name = <cluster_name_on_cloud>: existing instances are adopted, stopped ones are resumed, and to_start_count is reduced accordingly. GCP filters on the same tag.
So the second cluster gets its own row in the state DB pointing at the first cluster's resources. sky down on either one takes the resources away from the other, which is left pointing at instances that no longer exist.
Expected
Two clusters that sky status shows as separate should never end up sharing resources. Launching a name that collides with a live cluster should fail with a clear message rather than silently adopt it.
Proposed fix
At launch, once cluster_name_on_cloud has been computed, check whether another existing cluster of the same user already maps to that name on cloud, and refuse the launch naming the other cluster (e.g. "cluster name 'my_cluster' maps to 'my-cluster-' on the cloud, which is already used by cluster 'my-cluster'"). A warning would at least make it visible, but a hard error seems right: names that differ only by case or by ./_ vs - are far more often a typo than a deliberate second cluster.
Always appending the display-name hash would also fix it, but it would rename every existing cluster on the cloud, so it is not backward compatible.
Verified on master at 6cf931fe5.
-Claude
Source: skypilot-org/skypilot