|
3 | 3 | from typing import Optional |
4 | 4 |
|
5 | 5 |
|
| 6 | +# Cap cluster names to 41 characters to stay withing the limit of |
| 7 | +# the strictest provider (dstack). |
| 8 | +# This means capping the basename portion to 28 characters (use 25 to be safe) |
| 9 | +# to leave room for a ``-job-<short_id>`` suffix (13 chars). |
| 10 | +_MAX_BASENAME_LENGTH = 25 |
| 11 | + |
| 12 | + |
6 | 13 | def sanitize_cluster_basename(base_name: Optional[str]) -> str: |
7 | | - """Return a filesystem-safe cluster base name.""" |
| 14 | + """Return a cluster base name that is safe across compute providers. |
| 15 | +
|
| 16 | + Some providers have restrictions on resource names. |
| 17 | + Sanitize generated name so the result is: |
| 18 | + - lowercased |
| 19 | + - underscores are replaced with hyphens |
| 20 | + - name is guaranteed to start with a letter |
| 21 | + - no longer than 41 characters |
| 22 | + """ |
8 | 23 | if not base_name: |
9 | 24 | return "remote-template" |
10 | | - normalized = "".join(ch if ch.isalnum() or ch in ("-", "_") else "-" for ch in base_name.strip()) |
11 | | - normalized = normalized.strip("-_") |
12 | | - return normalized or "remote-template" |
| 25 | + lowered = base_name.strip().lower() |
| 26 | + normalized = "".join(ch if (ch.isalnum() and ch.isascii()) or ch == "-" else "-" for ch in lowered) |
| 27 | + normalized = normalized.strip("-") |
| 28 | + if not normalized: |
| 29 | + return "remote-template" |
| 30 | + if not normalized[0].isalpha(): |
| 31 | + normalized = f"t-{normalized}" |
| 32 | + if len(normalized) > _MAX_BASENAME_LENGTH: |
| 33 | + normalized = normalized[:_MAX_BASENAME_LENGTH].rstrip("-") |
| 34 | + return normalized |
0 commit comments