cmd/k8s-operator: stabilize StaticEndpoints order in ProxyGroup reconciles (#19755)
findStaticEndpoints built its return slice by iterating nodes.Items in the order returned by r.List, which is not guaranteed to be stable across calls. When the resulting set of addresses already matched the existing config Secret, the slice could still permute between reconciles, making the marshalled config Secret differ byte-for-byte. That tripped the DeepEqual check on the config Secret, which rewrote the Secret, which fired a watch event, which re-enqueued the ProxyGroup, looping forever. Detect this case and return the existing currAddrs slice unchanged when the resulting set is the same, preserving the "use the currently used IPs first" intent without spurious writes. Fixes #19700 Signed-off-by: Jason Dillingham <jasonmdillingham@gmail.com>
This commit is contained in:
@@ -1104,9 +1104,35 @@ func (r *ProxyGroupReconciler) findStaticEndpoints(ctx context.Context, existing
|
||||
return nil, &FindStaticEndpointErr{msg: fmt.Sprintf("failed to find any `status.addresses` of type %q on nodes using configured Selectors on `spec.staticEndpoints.nodePort.selectors` for ProxyClass %q", corev1.NodeExternalIP, proxyClass.Name)}
|
||||
}
|
||||
|
||||
// If we ended up selecting the same set of addresses already in use, keep
|
||||
// the existing order. nodes.Items from r.List is not guaranteed to be in
|
||||
// a stable order across calls, so without this the slice can permute on
|
||||
// each reconcile, making the marshalled config Secret differ byte-for-byte
|
||||
// even though nothing has effectively changed. That trips the DeepEqual
|
||||
// check on the config Secret, which writes the Secret, which fires a
|
||||
// watch event, which re-enqueues the ProxyGroup, and so on.
|
||||
if len(currAddrs) > 0 && sameAddrPortSet(endpoints, currAddrs) {
|
||||
return currAddrs, nil
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// sameAddrPortSet reports whether a and b contain the same AddrPorts,
|
||||
// ignoring order. Both slices are assumed to be free of duplicates, which
|
||||
// holds for callers in this package.
|
||||
func sameAddrPortSet(a, b []netip.AddrPort) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for _, x := range a {
|
||||
if !slices.Contains(b, x) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getStaticEndpointAddress(a *corev1.NodeAddress, port uint16) *netip.AddrPort {
|
||||
addr, err := netip.ParseAddr(a.Address)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user