-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial ambient dual stack support #51606
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,6 +193,17 @@ func (a *index) constructService(svc *v1.Service, w *Waypoint) *workloadapi.Serv | |
Mode: workloadapi.LoadBalancing_STRICT, | ||
} | ||
} | ||
ipFamily := workloadapi.IPFamilies_AUTOMATIC | ||
if len(svc.Spec.IPFamilies) == 2 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems potentially a little brittle, can users do silly things like put v1.IPv4Protocol in the list twice ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No the validation is pretty strict. We could make it more robust anyways though |
||
ipFamily = workloadapi.IPFamilies_DUAL | ||
} else if len(svc.Spec.IPFamilies) == 1 { | ||
family := svc.Spec.IPFamilies[0] | ||
if family == v1.IPv4Protocol { | ||
ipFamily = workloadapi.IPFamilies_IPV4_ONLY | ||
} else { | ||
ipFamily = workloadapi.IPFamilies_IPV6_ONLY | ||
} | ||
} | ||
// TODO this is only checking one controller - we may be missing service vips for instances in another cluster | ||
return &workloadapi.Service{ | ||
Name: svc.Name, | ||
|
@@ -202,13 +213,20 @@ func (a *index) constructService(svc *v1.Service, w *Waypoint) *workloadapi.Serv | |
Ports: ports, | ||
Waypoint: waypointAddress, | ||
LoadBalancing: lb, | ||
IpFamilies: ipFamily, | ||
} | ||
} | ||
|
||
func getVIPs(svc *v1.Service) []string { | ||
res := []string{} | ||
if svc.Spec.ClusterIP != "" && svc.Spec.ClusterIP != v1.ClusterIPNone { | ||
res = append(res, svc.Spec.ClusterIP) | ||
cips := svc.Spec.ClusterIPs | ||
if len(cips) == 0 { | ||
cips = []string{svc.Spec.ClusterIP} | ||
} | ||
for _, cip := range cips { | ||
if cip != "" && cip != v1.ClusterIPNone { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add ut for this function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #51665 adds some basic ones, will make it easier to add new ones. if we can merge that it will help and Ill update this PR with a test |
||
res = append(res, cip) | ||
} | ||
} | ||
return res | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -155,7 +155,20 @@ func (a *index) podWorkloadBuilder( | |
if (!IsPodRunning(p) && !IsPodPending(p)) || p.Spec.HostNetwork { | ||
return nil | ||
} | ||
podIP, err := netip.ParseAddr(p.Status.PodIP) | ||
k8sPodIPs := p.Status.PodIPs | ||
if len(k8sPodIPs) == 0 && p.Status.PodIP != "" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question, when Status.PodIPs can be empty? I guess we donot need to check it since have checked Running status before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is mostly defense in depth against weird Pods, shouldn't really happen. In particular our tests often just set podIp and not PodIps. But any Pod can be arbitrarily modified by user, so we cannot assume it's valid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also IIRC the k8s docs say that if PodIPs is non-empty, the first entry in the list is PodIP - they don't declare any guarantees that PodIPs will be non-empty. I think this is mostly for back compat with old K8S versions though. So yeah better to check. |
||
k8sPodIPs = []v1.PodIP{{IP: p.Status.PodIP}} | ||
} | ||
if len(k8sPodIPs) == 0 { | ||
return nil | ||
} | ||
podIPs, err := slices.MapErr(k8sPodIPs, func(e v1.PodIP) ([]byte, error) { | ||
n, err := netip.ParseAddr(e.IP) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return n.AsSlice(), nil | ||
}) | ||
if err != nil { | ||
// Is this possible? Probably not in typical case, but anyone could put garbage there. | ||
return nil | ||
|
@@ -174,6 +187,7 @@ func (a *index) podWorkloadBuilder( | |
status = workloadapi.WorkloadStatus_UNHEALTHY | ||
} | ||
a.networkUpdateTrigger.MarkDependant(ctx) // Mark we depend on out of band a.Network | ||
// We only check the network of the first IP. This should be fine; it is not supported for a single pod to span multiple networks | ||
network := a.Network(p.Status.PodIP, p.Labels).String() | ||
|
||
var appTunnel *workloadapi.ApplicationTunnel | ||
|
@@ -198,7 +212,7 @@ func (a *index) podWorkloadBuilder( | |
Namespace: p.Namespace, | ||
Network: network, | ||
ClusterId: string(a.ClusterID), | ||
Addresses: [][]byte{podIP.AsSlice()}, | ||
Addresses: podIPs, | ||
ServiceAccount: p.Spec.ServiceAccountName, | ||
Waypoint: a.getWaypointAddress(targetWaypoint), | ||
Node: p.Spec.NodeName, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QQ - are we emulating the k8s
ipfamily
oripfamilypolicy
API here, or both?It seems like mostly this is just emulating
ipfamilypolicy
semantics? Do we plan to hardcode the behavior, or additionally exposeipfamily
style APIs for the relevant resources we have in Istio, to match k8s?"dualstack" is just two problems AFAICT:
ipfamilypolicy
), I think you said we will probably always assign both if V6 enabled.ipfamily
ordering semantics)The only bit we really care about as A Problem is the ordering of that IP list for non-K8S/Istio-only resources, right? Doesn't matter whether that list contains 1 V4 and 1 V6, 2 V4s, or 5 V6s, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ipFamilyPolicy. This is actually mostly about joining two resources (Service and Pod), and which IPs you should join. Since k8s already pre-filters the IPs (and assigns the IPs), its not like we need to do "which IPs do we assign" , etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would we need to do that for stuff like SEs eventually, though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We really only need this for headless services selecting pods, which there is no SE equivalent. So I don't think we really do