agent: addresses annotation replaces IPAM allocation
Build flock Image / build (push) Successful in 5m27s
Build flock Image / build (push) Successful in 5m27s
When flock.fritzlab.net/addresses provides a v6 or v4, the IP becomes the pod's primary IP for that family — bound to eth0, default route off it, on-link host route via setHostRoute, and a per-pod /128 or /32 in BGP. IPAM no longer allocates a private IP alongside it. The pod ends up with exactly the operator-supplied addresses on eth0 (plus any extras beyond the first-of-family, which keep the pre-existing layered behavior). This is the fix the original addresses-annotation work missed: bug #1 allocated a private IP next to the public one (so VPN-routed clients could land on the private path on Plex). Promoting addresses-supplied IPs into the IPAM-style routing slot keeps the public IP as the only primary IP visible from outside. Three pieces: - annotations.go: reject pods whose addresses/anycast IP family is disabled (ipv6/ipv4 annotation or NodeConfig default). Both annotation types rely on the family being enabled for return-path routing. - handlers.go: peel first v6 + first v4 from Addresses into res.IP6/IP4; suppress IPAM for those families; skip IPAM call entirely if both families are addresses-supplied. - anycast_linux.go: extend renderBird to advertise any IPAM IP that's outside the node's BGP aggregate as a per-pod /32 or /128. This is what makes 142.202.202.166 reachable when host004's pod CIDR is 172.25.214.0/24 — the addresses-promoted IP isn't covered by the aggregate. Tests: 7 new annotation tests covering the conflict cases (ipv4=false + addresses-v4, NodeConfig default + addresses-v4, etc.) plus 5 unit tests for the splitAddressesPrimary helper. README updated with the addresses-replaces-IPAM behavior, the addresses-vs-anycast comparison, the conflict rule, and a Plex-style example. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -166,9 +166,44 @@ func ParseAnnotations(in map[string]string, defaults FamilyDefaults) (*ParsedAnn
|
||||
out.Addresses = ips
|
||||
}
|
||||
|
||||
// Reject pods that ask for an addresses- or anycast-supplied IP whose
|
||||
// family was disabled (via the pod's ipv6/ipv4 annotation or NodeConfig
|
||||
// default). Both annotation types put the IP on a pod interface and rely
|
||||
// on the family being enabled for return-path routing — addresses needs
|
||||
// the in-pod default v6/v4 route to send replies; anycast on lo needs
|
||||
// the same default route on eth0 for the same reason. Silently accepting
|
||||
// the IP would leave a non-functional pod, so we fail closed at ADD.
|
||||
for _, ip := range out.Addresses {
|
||||
if err := requireFamilyEnabled(ip, out.WantV6, out.WantV4, annAddresses); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, ip := range out.Anycast {
|
||||
if err := requireFamilyEnabled(ip, out.WantV6, out.WantV4, annAnycast); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// requireFamilyEnabled returns an error when ip's family was opted out via
|
||||
// the resolved WantV6/WantV4 booleans (pod annotation > NodeConfig default >
|
||||
// built-in dual-stack). The source string identifies which annotation
|
||||
// supplied the conflicting IP so the operator's error message is specific.
|
||||
func requireFamilyEnabled(ip net.IP, wantV6, wantV4 bool, source string) error {
|
||||
if ip.To4() != nil {
|
||||
if !wantV4 {
|
||||
return fmt.Errorf("annotation %s: contains IPv4 %s but ipv4 is disabled (annotation or NodeConfig default)", source, ip)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !wantV6 {
|
||||
return fmt.Errorf("annotation %s: contains IPv6 %s but ipv6 is disabled (annotation or NodeConfig default)", source, ip)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseBoolAnnotation accepts only "true" or "false" (case-insensitive,
|
||||
// surrounding whitespace tolerated). All other values — including "1", "0",
|
||||
// "yes", "no" — are rejected so operator typos are caught loudly rather
|
||||
|
||||
Reference in New Issue
Block a user