@@ -18,7 +18,9 @@ package proxy
18
18
19
19
import (
20
20
v1 "k8s.io/api/core/v1"
21
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
21
22
"k8s.io/klog/v2"
23
+ "k8s.io/kubernetes/pkg/features"
22
24
)
23
25
24
26
// CategorizeEndpoints returns:
@@ -39,16 +41,18 @@ import (
39
41
// "Usable endpoints" means Ready endpoints by default, but will fall back to
40
42
// Serving-Terminating endpoints (independently for Cluster and Local) if no Ready
41
43
// endpoints are available.
42
- func CategorizeEndpoints (endpoints []Endpoint , svcInfo ServicePort , nodeLabels map [string ]string ) (clusterEndpoints , localEndpoints , allReachableEndpoints []Endpoint , hasAnyEndpoints bool ) {
43
- var useTopology , useServingTerminatingEndpoints bool
44
+ func CategorizeEndpoints (endpoints []Endpoint , svcInfo ServicePort , nodeName string , nodeLabels map [string ]string ) (clusterEndpoints , localEndpoints , allReachableEndpoints []Endpoint , hasAnyEndpoints bool ) {
45
+ var topologyMode string
46
+ var useServingTerminatingEndpoints bool
44
47
45
48
if svcInfo .UsesClusterEndpoints () {
46
- useTopology = canUseTopology (endpoints , nodeLabels )
49
+ zone := nodeLabels [v1 .LabelTopologyZone ]
50
+ topologyMode = topologyModeFromHints (svcInfo , endpoints , nodeName , zone )
47
51
clusterEndpoints = filterEndpoints (endpoints , func (ep Endpoint ) bool {
48
52
if ! ep .IsReady () {
49
53
return false
50
54
}
51
- if useTopology && ! availableForTopology (ep , nodeLabels ) {
55
+ if ! availableForTopology (ep , topologyMode , nodeName , zone ) {
52
56
return false
53
57
}
54
58
return true
@@ -114,9 +118,9 @@ func CategorizeEndpoints(endpoints []Endpoint, svcInfo ServicePort, nodeLabels m
114
118
return
115
119
}
116
120
117
- if ! useTopology && ! useServingTerminatingEndpoints {
121
+ if topologyMode == "" && ! useServingTerminatingEndpoints {
118
122
// !useServingTerminatingEndpoints means that localEndpoints contains only
119
- // Ready endpoints. !useTopology means that clusterEndpoints contains *every*
123
+ // Ready endpoints. topologyMode=="" means that clusterEndpoints contains *every*
120
124
// Ready endpoint. So clusterEndpoints must be a superset of localEndpoints.
121
125
allReachableEndpoints = clusterEndpoints
122
126
return
@@ -140,48 +144,77 @@ func CategorizeEndpoints(endpoints []Endpoint, svcInfo ServicePort, nodeLabels m
140
144
return
141
145
}
142
146
143
- // canUseTopology returns true if all of the following are true:
144
- // - The node's labels include "topology.kubernetes.io/zone".
145
- // - All of the endpoints for this Service have a topology hint.
146
- // - At least one endpoint for this Service is hinted for this node's zone.
147
- func canUseTopology (endpoints []Endpoint , nodeLabels map [string ]string ) bool {
148
- zone , foundZone := nodeLabels [v1 .LabelTopologyZone ]
147
+ // topologyModeFromHints returns a topology mode ("", "PreferSameZone", or
148
+ // "PreferSameNode") based on the Endpoint hints:
149
+ // - If the PreferSameTrafficDistribution feature gate is enabled, and every ready
150
+ // endpoint has a node hint, and at least one endpoint is hinted for this node, then
151
+ // it returns "PreferSameNode".
152
+ // - Otherwise, if every ready endpoint has a zone hint, and at least one endpoint is
153
+ // hinted for this node's zone, then it returns "PreferSameZone".
154
+ // - Otherwise it returns "" (meaning, no topology / default traffic distribution).
155
+ func topologyModeFromHints (svcInfo ServicePort , endpoints []Endpoint , nodeName , zone string ) string {
156
+ hasEndpointForNode := false
157
+ allEndpointsHaveNodeHints := true
149
158
hasEndpointForZone := false
159
+ allEndpointsHaveZoneHints := true
150
160
for _ , endpoint := range endpoints {
151
161
if ! endpoint .IsReady () {
152
162
continue
153
163
}
154
164
155
- // If any of the endpoints do not have zone hints, we bail out
156
- if endpoint .ZoneHints ().Len () == 0 {
157
- klog .V (7 ).InfoS ("Skipping topology aware endpoint filtering since one or more endpoints is missing a zone hint" , "endpoint" , endpoint )
158
- return false
159
- }
160
-
161
- // If we've made it this far, we have endpoints with hints set. Now we check if there is a
162
- // zone label, if there isn't one we log a warning and bail out
163
- if ! foundZone || zone == "" {
164
- klog .V (2 ).InfoS ("Skipping topology aware endpoint filtering since node is missing label" , "label" , v1 .LabelTopologyZone )
165
- return false
165
+ if endpoint .NodeHints ().Len () == 0 {
166
+ allEndpointsHaveNodeHints = false
167
+ } else if endpoint .NodeHints ().Has (nodeName ) {
168
+ hasEndpointForNode = true
166
169
}
167
170
168
- if endpoint .ZoneHints ().Has (zone ) {
171
+ if endpoint .ZoneHints ().Len () == 0 {
172
+ allEndpointsHaveZoneHints = false
173
+ } else if endpoint .ZoneHints ().Has (zone ) {
169
174
hasEndpointForZone = true
170
175
}
171
176
}
172
177
173
- if ! hasEndpointForZone {
174
- klog .V (7 ).InfoS ("Skipping topology aware endpoint filtering since no hints were provided for zone" , "zone" , zone )
175
- return false
178
+ if utilfeature .DefaultFeatureGate .Enabled (features .PreferSameTrafficDistribution ) {
179
+ if allEndpointsHaveNodeHints {
180
+ if hasEndpointForNode {
181
+ return v1 .ServiceTrafficDistributionPreferSameNode
182
+ }
183
+ klog .V (2 ).InfoS ("Ignoring same-node topology hints for service since no hints were provided for node" , "service" , svcInfo , "node" , nodeName )
184
+ } else {
185
+ klog .V (7 ).InfoS ("Ignoring same-node topology hints for service since one or more endpoints is missing a node hint" , "service" , svcInfo )
186
+ }
176
187
}
177
- return true
188
+ if allEndpointsHaveZoneHints {
189
+ if hasEndpointForZone {
190
+ return v1 .ServiceTrafficDistributionPreferSameZone
191
+ }
192
+ if zone == "" {
193
+ klog .V (2 ).InfoS ("Ignoring same-zone topology hints for service since node is missing label" , "service" , svcInfo , "label" , v1 .LabelTopologyZone )
194
+ } else {
195
+ klog .V (2 ).InfoS ("Ignoring same-zone topology hints for service since no hints were provided for zone" , "service" , svcInfo , "zone" , zone )
196
+ }
197
+ } else {
198
+ klog .V (7 ).InfoS ("Ignoring same-zone topology hints for service since one or more endpoints is missing a zone hint" , "service" , svcInfo .String ())
199
+ }
200
+
201
+ return ""
178
202
}
179
203
180
- // availableForTopology checks if this endpoint is available for use on this node, given
181
- // topology constraints. (It assumes that canUseTopology() returned true.)
182
- func availableForTopology (endpoint Endpoint , nodeLabels map [string ]string ) bool {
183
- zone := nodeLabels [v1 .LabelTopologyZone ]
184
- return endpoint .ZoneHints ().Has (zone )
204
+ // availableForTopology checks if this endpoint is available for use on this node when
205
+ // using the given topologyMode. (Note that there's no fallback here; the fallback happens
206
+ // when deciding which mode to use, not when applying that decision.)
207
+ func availableForTopology (endpoint Endpoint , topologyMode , nodeName , zone string ) bool {
208
+ switch topologyMode {
209
+ case "" :
210
+ return true
211
+ case v1 .ServiceTrafficDistributionPreferSameNode :
212
+ return endpoint .NodeHints ().Has (nodeName )
213
+ case v1 .ServiceTrafficDistributionPreferSameZone :
214
+ return endpoint .ZoneHints ().Has (zone )
215
+ default :
216
+ return false
217
+ }
185
218
}
186
219
187
220
// filterEndpoints filters endpoints according to predicate
0 commit comments