Conversation
A claim may name a family instead of a class, and NSO files every primary interface address that way. The resolved class was written to the IPAllocation but never to the claim, so the object never recorded what it allocated under — and the default for a family can be repointed, making the answer unrecoverable. The claim is persisted after resolution, so spec.className is now defaulted from the resolved class the same way a PersistentVolumeClaim is defaulted to the cluster's StorageClass. It is already immutable, so it cannot drift. spec.ipFamily selects a default class; it was also silently ignored whenever a claim named one, so a claim could ask for IPv4, get IPv6, and see no error. A family that disagrees with the class is now refused. Agreement stays legal: the fabric-identity controller sets both fields on every claim it files. Prefix-length validation assumed IPv4 when the claim stated no family, so a /64 from an IPv6 class was rejected with "must be between 1 and 32" — the common shape under the class model. Validation runs before the class is resolved and cannot read it, so it now applies only the bound that holds for every family; EffectivePrefixLength already applies the family-specific one against the class.
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #128.
Problem
A claim may name a family instead of a class, and NSO files every primary interface address that way —
NetworkInterfaceClaim.spec.ipFamiliesdefaults to{IPv6}, so it is the default path, not an edge case. Three things follow from that:The resolved class is never recorded. It reaches the IPAllocation but not the claim, so the object does not say what it allocated under. The default for a family can be repointed, so the answer is not recoverable afterwards — and
kubectl get ipclaimsshows<none>for a claim that is very much bound to a class.spec.ipFamilyis silently ignored when a class is named.ResolveClassreturnsLoadClass(className)without looking at it, so a claim asking for IPv4 from an IPv6 class allocates IPv6 and reports no error.Prefix-length validation assumes IPv4. It reads the claim's own family and defaults to 32, so a claim naming an IPv6 class and asking for
prefixLength: 64is rejected with "must be between 1 and 32" — exactly the shape the class model encourages.Change
spec.classNameis defaulted from the resolved class, inCreate, before the claim is persisted — the same way a PersistentVolumeClaim is defaulted to the cluster's StorageClass. It is already immutable, so what is written is what the claim allocated under, permanently. The conventional defaulting hook cannot do this:PrepareForCreatehas no transaction and cannot find the default class.A family that disagrees with the resolved class is refused, under a new
FamilyMismatchreason. Agreement stays legal — the fabric-identity controller sets both fields on all 18 of its claims in production, consistently.Validation now applies only the bound that holds for every family (1–128).
EffectivePrefixLengthalready applies the family-specific one against the resolved class, so the check was redundant as well as wrong.Left out
spec.ipFamilystays. It is the input the defaulting needs, and removing it would break primary addressing for every workload. No API types change and no migration: existing claims keep whatever they were created with, and new ones name their class.