pub fn kopia_lease_identity(lease: &str) -> (String, String)Expand description
The STABLE kopia client identity a maintenance mover assumes for lease
((username, hostname)); the mover sets it with kopia repository set-client so kopia’s designated-owner check compares something stable —
the pod’s own identity is ephemeral (a new hostname every run), which is
why comparing kopia’s recorded owner against it can never work.
The derivation forks on the lease’s own shape (the mover only ever sees the lease STRING, never the CR it came from, so the rule must be derivable from the string alone):
-
Exactly 4
/-separated segments, AND the first is literally"kopiur"—managed_lease’s two cluster-qualified formats (kopiur/{cluster}/{namespace}/{name},kopiur/{cluster}/clusterrepository/{name}) — sanitize each segment INDEPENDENTLY (the same character rule, but with no per-segment cap and without ever crossing a segment boundary) and join with.. Every generated segment is already a lowercase, dot-free RFC 1123 label (the cluster name is validated as such; Kubernetes namespace/resource names are too), so for a generated lease this is verbatimkopiur.{cluster}.{namespace}.{name}— and CRITICALLY, injective: two leases can only produce the same hostname if they were/-split identically, because a-inside one segment can no longer be forged into a fake.boundary the way collapsing everything to-allowed (kopiur/east-prod/db/xandkopiur/east/prod-db/xused to collide). No per-segment cap also means a long cluster/namespace/name no longer collides via truncation — capped defensively only on the TOTAL, at [CLUSTER_HOSTNAME_MAX] (253, the identity-hostname byte cap enforced bycrate::validate::validate_identity_component), which cluster (≤32) + two Kubernetes names +"kopiur"cannot reach in practice.The
"kopiur"-first-segment check matters becausemanaged_leaseis NOT the only source of lease strings:Ownership.owneris a free-form field a user can hand-author, and a hand-authored value that HAPPENS to have 4/-separated segments (e.g.a/b/c/d) is not one of our generated formats at all. Gating the dot-join on the reserved"kopiur"prefix — whichmanaged_leasealways emits and a user has no reason to — guarantees a hand-authored owner’s derivation can never change across an operator upgrade merely because it happens to split into 4 segments; it always falls to the legacy whole-string sanitizer below, exactly as it did pre-M6. Without this gate, such an owner would silently switch from itsa-b-c-didentity toa.b.c.d, and withtakeoverPolicy: Neverthe repository’s maintenance would then yield forever. -
Any other shape (the legacy 3-segment formats, a 4-segment lease NOT
"kopiur"-prefixed, or any other hand-authoredOwnership.owner/alias) — the ORIGINAL whole-string sanitizer: collapse the entire lease through the same character rule and cap at [LEGACY_HOSTNAME_MAX] (63, a DNS label). Byte-identical to every pre-M6 lease this function has ever produced.
use kopiur_api::maintenance::kopia_lease_identity;
// Legacy (3-segment / hand-authored): unchanged.
assert_eq!(
kopia_lease_identity("kopiur/media/nas"),
("kopiur".to_string(), "kopiur-media-nas".to_string())
);
// Cluster-qualified (4-segment, "kopiur"-prefixed): dot-joined, segment-preserving.
assert_eq!(
kopia_lease_identity("kopiur/east/media/nas"),
("kopiur".to_string(), "kopiur.east.media.nas".to_string())
);
// A hand-authored 4-segment owner that is NOT "kopiur"-prefixed: legacy
// sanitization, byte-identical to pre-M6 — never dot-joined.
assert_eq!(
kopia_lease_identity("a/b/c/d"),
("kopiur".to_string(), "a-b-c-d".to_string())
);
// Injective: a '-' inside a segment can no longer masquerade as a boundary.
assert_ne!(
kopia_lease_identity("kopiur/east-prod/db/x").1,
kopia_lease_identity("kopiur/east/prod-db/x").1
);