| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | Copyright 2017 The Kubernetes Authors. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | You may obtain a copy of the License at | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | See the License for the specific language governing permissions and | 
					
						
							|  |  |  | limitations under the License. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package proxy | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	"net" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-11-06 04:38:50 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-13 18:57:52 +08:00
										 |  |  | 	"k8s.io/client-go/tools/events" | 
					
						
							| 
									
										
										
										
											2020-04-18 03:25:06 +08:00
										 |  |  | 	"k8s.io/klog/v2" | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-14 20:00:07 +08:00
										 |  |  | 	v1 "k8s.io/api/core/v1" | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 	discovery "k8s.io/api/discovery/v1" | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/types" | 
					
						
							|  |  |  | 	"k8s.io/apimachinery/pkg/util/sets" | 
					
						
							| 
									
										
										
										
											2019-02-14 01:48:45 +08:00
										 |  |  | 	"k8s.io/kubernetes/pkg/proxy/metrics" | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	utilproxy "k8s.io/kubernetes/pkg/proxy/util" | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 06:59:15 +08:00
										 |  |  | var supportedEndpointSliceAddressTypes = sets.NewString( | 
					
						
							|  |  |  | 	string(discovery.AddressTypeIPv4), | 
					
						
							|  |  |  | 	string(discovery.AddressTypeIPv6), | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | // BaseEndpointInfo contains base information that defines an endpoint.
 | 
					
						
							|  |  |  | // This could be used directly by proxier while processing endpoints,
 | 
					
						
							|  |  |  | // or can be used for constructing a more specific EndpointInfo struct
 | 
					
						
							|  |  |  | // defined by the proxier if needed.
 | 
					
						
							|  |  |  | type BaseEndpointInfo struct { | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	Endpoint string // TODO: should be an endpointString type
 | 
					
						
							|  |  |  | 	// IsLocal indicates whether the endpoint is running in same host as kube-proxy.
 | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 	IsLocal bool | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-06 04:06:07 +08:00
										 |  |  | 	// ZoneHints represent the zone hints for the endpoint. This is based on
 | 
					
						
							|  |  |  | 	// endpoint.hints.forZones[*].name in the EndpointSlice API.
 | 
					
						
							|  |  |  | 	ZoneHints sets.String | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 	// Ready indicates whether this endpoint is ready and NOT terminating.
 | 
					
						
							|  |  |  | 	// For pods, this is true if a pod has a ready status and a nil deletion timestamp.
 | 
					
						
							|  |  |  | 	// This is only set when watching EndpointSlices. If using Endpoints, this is always
 | 
					
						
							|  |  |  | 	// true since only ready endpoints are read from Endpoints.
 | 
					
						
							|  |  |  | 	// TODO: Ready can be inferred from Serving and Terminating below when enabled by default.
 | 
					
						
							|  |  |  | 	Ready bool | 
					
						
							|  |  |  | 	// Serving indiciates whether this endpoint is ready regardless of its terminating state.
 | 
					
						
							|  |  |  | 	// For pods this is true if it has a ready status regardless of its deletion timestamp.
 | 
					
						
							|  |  |  | 	// This is only set when watching EndpointSlices. If using Endpoints, this is always
 | 
					
						
							|  |  |  | 	// true since only ready endpoints are read from Endpoints.
 | 
					
						
							|  |  |  | 	Serving bool | 
					
						
							|  |  |  | 	// Terminating indicates whether this endpoint is terminating.
 | 
					
						
							|  |  |  | 	// For pods this is true if it has a non-nil deletion timestamp.
 | 
					
						
							|  |  |  | 	// This is only set when watching EndpointSlices. If using Endpoints, this is always
 | 
					
						
							|  |  |  | 	// false since terminating endpoints are always excluded from Endpoints.
 | 
					
						
							|  |  |  | 	Terminating bool | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// NodeName is the name of the node this endpoint belongs to
 | 
					
						
							|  |  |  | 	NodeName string | 
					
						
							|  |  |  | 	// Zone is the name of the zone this endpoint belongs to
 | 
					
						
							|  |  |  | 	Zone string | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | var _ Endpoint = &BaseEndpointInfo{} | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // String is part of proxy.Endpoint interface.
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | func (info *BaseEndpointInfo) String() string { | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	return info.Endpoint | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | // GetIsLocal is part of proxy.Endpoint interface.
 | 
					
						
							|  |  |  | func (info *BaseEndpointInfo) GetIsLocal() bool { | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	return info.IsLocal | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | // IsReady returns true if an endpoint is ready and not terminating.
 | 
					
						
							|  |  |  | func (info *BaseEndpointInfo) IsReady() bool { | 
					
						
							|  |  |  | 	return info.Ready | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsServing returns true if an endpoint is ready, regardless of if the
 | 
					
						
							|  |  |  | // endpoint is terminating.
 | 
					
						
							|  |  |  | func (info *BaseEndpointInfo) IsServing() bool { | 
					
						
							|  |  |  | 	return info.Serving | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsTerminating retruns true if an endpoint is terminating. For pods,
 | 
					
						
							|  |  |  | // that is any pod with a deletion timestamp.
 | 
					
						
							|  |  |  | func (info *BaseEndpointInfo) IsTerminating() bool { | 
					
						
							|  |  |  | 	return info.Terminating | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-06 04:06:07 +08:00
										 |  |  | // GetZoneHints returns the zone hint for the endpoint.
 | 
					
						
							|  |  |  | func (info *BaseEndpointInfo) GetZoneHints() sets.String { | 
					
						
							|  |  |  | 	return info.ZoneHints | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | // IP returns just the IP part of the endpoint, it's a part of proxy.Endpoint interface.
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | func (info *BaseEndpointInfo) IP() string { | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	return utilproxy.IPPart(info.Endpoint) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Port returns just the Port part of the endpoint.
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | func (info *BaseEndpointInfo) Port() (int, error) { | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	return utilproxy.PortPart(info.Endpoint) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Equal is part of proxy.Endpoint interface.
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | func (info *BaseEndpointInfo) Equal(other Endpoint) bool { | 
					
						
							| 
									
										
										
										
											2021-11-05 07:37:45 +08:00
										 |  |  | 	return info.String() == other.String() && | 
					
						
							|  |  |  | 		info.GetIsLocal() == other.GetIsLocal() && | 
					
						
							|  |  |  | 		info.IsReady() == other.IsReady() | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | // GetNodeName returns the NodeName for this endpoint.
 | 
					
						
							|  |  |  | func (info *BaseEndpointInfo) GetNodeName() string { | 
					
						
							|  |  |  | 	return info.NodeName | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetZone returns the Zone for this endpoint.
 | 
					
						
							|  |  |  | func (info *BaseEndpointInfo) GetZone() string { | 
					
						
							|  |  |  | 	return info.Zone | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newBaseEndpointInfo(IP, nodeName, zone string, port int, isLocal bool, | 
					
						
							| 
									
										
										
										
											2021-03-06 04:06:07 +08:00
										 |  |  | 	ready, serving, terminating bool, zoneHints sets.String) *BaseEndpointInfo { | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | 	return &BaseEndpointInfo{ | 
					
						
							| 
									
										
										
										
											2020-11-09 23:24:43 +08:00
										 |  |  | 		Endpoint:    net.JoinHostPort(IP, strconv.Itoa(port)), | 
					
						
							|  |  |  | 		IsLocal:     isLocal, | 
					
						
							|  |  |  | 		Ready:       ready, | 
					
						
							|  |  |  | 		Serving:     serving, | 
					
						
							|  |  |  | 		Terminating: terminating, | 
					
						
							| 
									
										
										
										
											2021-03-06 04:06:07 +08:00
										 |  |  | 		ZoneHints:   zoneHints, | 
					
						
							| 
									
										
										
										
											2021-06-29 10:16:55 +08:00
										 |  |  | 		NodeName:    nodeName, | 
					
						
							|  |  |  | 		Zone:        zone, | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-10 06:10:57 +08:00
										 |  |  | type makeEndpointFunc func(info *BaseEndpointInfo, svcPortName *ServicePortName) Endpoint | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-30 05:29:46 +08:00
										 |  |  | // This handler is invoked by the apply function on every change. This function should not modify the
 | 
					
						
							|  |  |  | // EndpointsMap's but just use the changes for any Proxier specific cleanup.
 | 
					
						
							|  |  |  | type processEndpointsMapChangeFunc func(oldEndpointsMap, newEndpointsMap EndpointsMap) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | // EndpointChangeTracker carries state about uncommitted changes to an arbitrary number of
 | 
					
						
							|  |  |  | // Endpoints, keyed by their namespace and name.
 | 
					
						
							|  |  |  | type EndpointChangeTracker struct { | 
					
						
							| 
									
										
										
										
											2022-12-31 10:30:37 +08:00
										 |  |  | 	// lock protects lastChangeTriggerTimes
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	lock sync.Mutex | 
					
						
							| 
									
										
										
										
											2022-12-31 10:30:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-30 05:29:46 +08:00
										 |  |  | 	processEndpointsMapChange processEndpointsMapChangeFunc | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	// endpointSliceCache holds a simplified version of endpoint slices.
 | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	endpointSliceCache *EndpointSliceCache | 
					
						
							| 
									
										
										
										
											2018-11-06 04:38:50 +08:00
										 |  |  | 	// Map from the Endpoints namespaced-name to the times of the triggers that caused the endpoints
 | 
					
						
							|  |  |  | 	// object to change. Used to calculate the network-programming-latency.
 | 
					
						
							|  |  |  | 	lastChangeTriggerTimes map[types.NamespacedName][]time.Time | 
					
						
							| 
									
										
										
										
											2021-04-08 01:09:59 +08:00
										 |  |  | 	// record the time when the endpointChangeTracker was created so we can ignore the endpoints
 | 
					
						
							|  |  |  | 	// that were generated before, because we can't estimate the network-programming-latency on those.
 | 
					
						
							|  |  |  | 	// This is specially problematic on restarts, because we process all the endpoints that may have been
 | 
					
						
							|  |  |  | 	// created hours or days before.
 | 
					
						
							|  |  |  | 	trackerStartTime time.Time | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewEndpointChangeTracker initializes an EndpointsChangeMap
 | 
					
						
							| 
									
										
										
										
											2021-07-03 06:49:19 +08:00
										 |  |  | func NewEndpointChangeTracker(hostname string, makeEndpointInfo makeEndpointFunc, ipFamily v1.IPFamily, recorder events.EventRecorder, processEndpointsMapChange processEndpointsMapChangeFunc) *EndpointChangeTracker { | 
					
						
							|  |  |  | 	return &EndpointChangeTracker{ | 
					
						
							| 
									
										
										
										
											2020-06-30 05:29:46 +08:00
										 |  |  | 		lastChangeTriggerTimes:    make(map[types.NamespacedName][]time.Time), | 
					
						
							| 
									
										
										
										
											2021-04-08 01:09:59 +08:00
										 |  |  | 		trackerStartTime:          time.Now(), | 
					
						
							| 
									
										
										
										
											2020-06-30 05:29:46 +08:00
										 |  |  | 		processEndpointsMapChange: processEndpointsMapChange, | 
					
						
							| 
									
										
										
										
											2021-07-03 06:49:19 +08:00
										 |  |  | 		endpointSliceCache:        NewEndpointSliceCache(hostname, ipFamily, recorder, makeEndpointInfo), | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | // EndpointSliceUpdate updates given service's endpoints change map based on the <previous, current> endpoints pair.
 | 
					
						
							|  |  |  | // It returns true if items changed, otherwise return false. Will add/update/delete items of EndpointsChangeMap.
 | 
					
						
							|  |  |  | // If removeSlice is true, slice will be removed, otherwise it will be added or updated.
 | 
					
						
							|  |  |  | func (ect *EndpointChangeTracker) EndpointSliceUpdate(endpointSlice *discovery.EndpointSlice, removeSlice bool) bool { | 
					
						
							| 
									
										
										
										
											2019-11-14 06:59:15 +08:00
										 |  |  | 	if !supportedEndpointSliceAddressTypes.Has(string(endpointSlice.AddressType)) { | 
					
						
							| 
									
										
										
										
											2021-10-15 00:47:17 +08:00
										 |  |  | 		klog.V(4).InfoS("EndpointSlice address type not supported by kube-proxy", "addressType", endpointSlice.AddressType) | 
					
						
							| 
									
										
										
										
											2019-11-14 06:59:15 +08:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	// This should never happen
 | 
					
						
							|  |  |  | 	if endpointSlice == nil { | 
					
						
							| 
									
										
										
										
											2021-10-15 00:47:17 +08:00
										 |  |  | 		klog.ErrorS(nil, "Nil endpointSlice passed to EndpointSliceUpdate") | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 		return false | 
					
						
							| 
									
										
										
										
											2019-08-14 20:00:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 15:12:15 +08:00
										 |  |  | 	namespacedName, _, err := endpointSliceCacheKeys(endpointSlice) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-10-15 00:47:17 +08:00
										 |  |  | 		klog.InfoS("Error getting endpoint slice cache keys", "err", err) | 
					
						
							| 
									
										
										
										
											2019-08-30 15:12:15 +08:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	metrics.EndpointChangesTotal.Inc() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ect.lock.Lock() | 
					
						
							|  |  |  | 	defer ect.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	changeNeeded := ect.endpointSliceCache.updatePending(endpointSlice, removeSlice) | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	if changeNeeded { | 
					
						
							|  |  |  | 		metrics.EndpointChangesPending.Inc() | 
					
						
							| 
									
										
										
										
											2020-10-07 21:02:20 +08:00
										 |  |  | 		// In case of Endpoints deletion, the LastChangeTriggerTime annotation is
 | 
					
						
							|  |  |  | 		// by-definition coming from the time of last update, which is not what
 | 
					
						
							|  |  |  | 		// we want to measure. So we simply ignore it in this cases.
 | 
					
						
							|  |  |  | 		// TODO(wojtek-t, robscott): Address the problem for EndpointSlice deletion
 | 
					
						
							|  |  |  | 		// when other EndpointSlice for that service still exist.
 | 
					
						
							| 
									
										
										
										
											2022-12-31 09:32:53 +08:00
										 |  |  | 		if removeSlice { | 
					
						
							|  |  |  | 			delete(ect.lastChangeTriggerTimes, namespacedName) | 
					
						
							|  |  |  | 		} else if t := getLastChangeTriggerTime(endpointSlice.Annotations); !t.IsZero() && t.After(ect.trackerStartTime) { | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 			ect.lastChangeTriggerTimes[namespacedName] = | 
					
						
							|  |  |  | 				append(ect.lastChangeTriggerTimes[namespacedName], t) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	return changeNeeded | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-06 20:32:30 +08:00
										 |  |  | // PendingChanges returns a set whose keys are the names of the services whose endpoints
 | 
					
						
							|  |  |  | // have changed since the last time ect was used to update an EndpointsMap. (You must call
 | 
					
						
							|  |  |  | // this _before_ calling em.Update(ect).)
 | 
					
						
							|  |  |  | func (ect *EndpointChangeTracker) PendingChanges() sets.String { | 
					
						
							| 
									
										
										
										
											2022-12-31 10:30:37 +08:00
										 |  |  | 	return ect.endpointSliceCache.pendingChanges() | 
					
						
							| 
									
										
										
										
											2022-04-06 20:32:30 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | // checkoutChanges returns a list of pending endpointsChanges and marks them as
 | 
					
						
							|  |  |  | // applied.
 | 
					
						
							|  |  |  | func (ect *EndpointChangeTracker) checkoutChanges() []*endpointsChange { | 
					
						
							|  |  |  | 	metrics.EndpointChangesPending.Set(0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-31 10:30:37 +08:00
										 |  |  | 	return ect.endpointSliceCache.checkoutChanges() | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | // checkoutTriggerTimes applies the locally cached trigger times to a map of
 | 
					
						
							|  |  |  | // trigger times that have been passed in and empties the local cache.
 | 
					
						
							|  |  |  | func (ect *EndpointChangeTracker) checkoutTriggerTimes(lastChangeTriggerTimes *map[types.NamespacedName][]time.Time) { | 
					
						
							|  |  |  | 	ect.lock.Lock() | 
					
						
							|  |  |  | 	defer ect.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k, v := range ect.lastChangeTriggerTimes { | 
					
						
							|  |  |  | 		prev, ok := (*lastChangeTriggerTimes)[k] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			(*lastChangeTriggerTimes)[k] = v | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			(*lastChangeTriggerTimes)[k] = append(prev, v...) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ect.lastChangeTriggerTimes = make(map[types.NamespacedName][]time.Time) | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getLastChangeTriggerTime returns the time.Time value of the
 | 
					
						
							|  |  |  | // EndpointsLastChangeTriggerTime annotation stored in the given endpoints
 | 
					
						
							|  |  |  | // object or the "zero" time if the annotation wasn't set or was set
 | 
					
						
							|  |  |  | // incorrectly.
 | 
					
						
							|  |  |  | func getLastChangeTriggerTime(annotations map[string]string) time.Time { | 
					
						
							|  |  |  | 	// TODO(#81360): ignore case when Endpoint is deleted.
 | 
					
						
							|  |  |  | 	if _, ok := annotations[v1.EndpointsLastChangeTriggerTime]; !ok { | 
					
						
							|  |  |  | 		// It's possible that the Endpoints object won't have the
 | 
					
						
							|  |  |  | 		// EndpointsLastChangeTriggerTime annotation set. In that case return
 | 
					
						
							|  |  |  | 		// the 'zero value', which is ignored in the upstream code.
 | 
					
						
							| 
									
										
										
										
											2019-02-13 16:11:59 +08:00
										 |  |  | 		return time.Time{} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | 	val, err := time.Parse(time.RFC3339Nano, annotations[v1.EndpointsLastChangeTriggerTime]) | 
					
						
							| 
									
										
										
										
											2018-11-06 04:38:50 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-10-15 00:47:17 +08:00
										 |  |  | 		klog.ErrorS(err, "Error while parsing EndpointsLastChangeTriggerTimeAnnotation", | 
					
						
							|  |  |  | 			"value", annotations[v1.EndpointsLastChangeTriggerTime]) | 
					
						
							| 
									
										
										
										
											2018-11-06 04:38:50 +08:00
										 |  |  | 		// In case of error val = time.Zero, which is ignored in the upstream code.
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return val | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 08:17:16 +08:00
										 |  |  | // endpointsChange contains all changes to endpoints that happened since proxy
 | 
					
						
							|  |  |  | // rules were synced.  For a single object, changes are accumulated, i.e.
 | 
					
						
							|  |  |  | // previous is state from before applying the changes, current is state after
 | 
					
						
							|  |  |  | // applying the changes.
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | type endpointsChange struct { | 
					
						
							|  |  |  | 	previous EndpointsMap | 
					
						
							|  |  |  | 	current  EndpointsMap | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UpdateEndpointMapResult is the updated results after applying endpoints changes.
 | 
					
						
							|  |  |  | type UpdateEndpointMapResult struct { | 
					
						
							|  |  |  | 	// StaleEndpoints identifies if an endpoints service pair is stale.
 | 
					
						
							|  |  |  | 	StaleEndpoints []ServiceEndpoint | 
					
						
							|  |  |  | 	// StaleServiceNames identifies if a service is stale.
 | 
					
						
							|  |  |  | 	StaleServiceNames []ServicePortName | 
					
						
							| 
									
										
										
										
											2018-11-06 04:38:50 +08:00
										 |  |  | 	// List of the trigger times for all endpoints objects that changed. It's used to export the
 | 
					
						
							|  |  |  | 	// network programming latency.
 | 
					
						
							| 
									
										
										
										
											2019-08-14 20:01:55 +08:00
										 |  |  | 	// NOTE(oxddr): this can be simplified to []time.Time if memory consumption becomes an issue.
 | 
					
						
							|  |  |  | 	LastChangeTriggerTimes map[types.NamespacedName][]time.Time | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 03:44:37 +08:00
										 |  |  | // Update updates endpointsMap base on the given changes.
 | 
					
						
							| 
									
										
										
										
											2019-04-30 03:28:47 +08:00
										 |  |  | func (em EndpointsMap) Update(changes *EndpointChangeTracker) (result UpdateEndpointMapResult) { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	result.StaleEndpoints = make([]ServiceEndpoint, 0) | 
					
						
							|  |  |  | 	result.StaleServiceNames = make([]ServicePortName, 0) | 
					
						
							| 
									
										
										
										
											2019-08-14 20:01:55 +08:00
										 |  |  | 	result.LastChangeTriggerTimes = make(map[types.NamespacedName][]time.Time) | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-30 03:28:47 +08:00
										 |  |  | 	em.apply( | 
					
						
							| 
									
										
										
										
											2018-11-06 04:38:50 +08:00
										 |  |  | 		changes, &result.StaleEndpoints, &result.StaleServiceNames, &result.LastChangeTriggerTimes) | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	return result | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 05:32:55 +08:00
										 |  |  | // EndpointsMap maps a service name to a list of all its Endpoints.
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | type EndpointsMap map[ServicePortName][]Endpoint | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // apply the changes to EndpointsMap and updates stale endpoints and service-endpoints pair. The `staleEndpoints` argument
 | 
					
						
							|  |  |  | // is passed in to store the stale udp endpoints and `staleServiceNames` argument is passed in to store the stale udp service.
 | 
					
						
							|  |  |  | // The changes map is cleared after applying them.
 | 
					
						
							| 
									
										
										
										
											2018-11-06 04:38:50 +08:00
										 |  |  | // In addition it returns (via argument) and resets the lastChangeTriggerTimes for all endpoints
 | 
					
						
							|  |  |  | // that were changed and will result in syncing the proxy rules.
 | 
					
						
							| 
									
										
										
										
											2020-06-30 05:29:46 +08:00
										 |  |  | // apply triggers processEndpointsMapChange on every change.
 | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | func (em EndpointsMap) apply(ect *EndpointChangeTracker, staleEndpoints *[]ServiceEndpoint, | 
					
						
							| 
									
										
										
										
											2019-08-14 20:01:55 +08:00
										 |  |  | 	staleServiceNames *[]ServicePortName, lastChangeTriggerTimes *map[types.NamespacedName][]time.Time) { | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	if ect == nil { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	changes := ect.checkoutChanges() | 
					
						
							|  |  |  | 	for _, change := range changes { | 
					
						
							| 
									
										
										
										
											2020-06-30 05:29:46 +08:00
										 |  |  | 		if ect.processEndpointsMapChange != nil { | 
					
						
							|  |  |  | 			ect.processEndpointsMapChange(change.previous, change.current) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-05 02:41:15 +08:00
										 |  |  | 		em.unmerge(change.previous) | 
					
						
							|  |  |  | 		em.merge(change.current) | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 		detectStaleConnections(change.previous, change.current, staleEndpoints, staleServiceNames) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-20 03:58:18 +08:00
										 |  |  | 	ect.checkoutTriggerTimes(lastChangeTriggerTimes) | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Merge ensures that the current EndpointsMap contains all <service, endpoints> pairs from the EndpointsMap passed in.
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:41:15 +08:00
										 |  |  | func (em EndpointsMap) merge(other EndpointsMap) { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	for svcPortName := range other { | 
					
						
							|  |  |  | 		em[svcPortName] = other[svcPortName] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Unmerge removes the <service, endpoints> pairs from the current EndpointsMap which are contained in the EndpointsMap passed in.
 | 
					
						
							| 
									
										
										
										
											2019-04-05 02:41:15 +08:00
										 |  |  | func (em EndpointsMap) unmerge(other EndpointsMap) { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	for svcPortName := range other { | 
					
						
							|  |  |  | 		delete(em, svcPortName) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 23:25:22 +08:00
										 |  |  | // getLocalEndpointIPs returns endpoints IPs if given endpoint is local - local means the endpoint is running in same host as kube-proxy.
 | 
					
						
							| 
									
										
										
										
											2020-11-10 03:12:27 +08:00
										 |  |  | func (em EndpointsMap) getLocalReadyEndpointIPs() map[types.NamespacedName]sets.String { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	localIPs := make(map[types.NamespacedName]sets.String) | 
					
						
							| 
									
										
										
										
											2019-04-05 02:53:11 +08:00
										 |  |  | 	for svcPortName, epList := range em { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 		for _, ep := range epList { | 
					
						
							| 
									
										
										
										
											2020-11-10 03:12:27 +08:00
										 |  |  | 			// Only add ready endpoints for health checking. Terminating endpoints may still serve traffic
 | 
					
						
							|  |  |  | 			// but the health check signal should fail if there are only terminating endpoints on a node.
 | 
					
						
							|  |  |  | 			if !ep.IsReady() { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-17 11:09:33 +08:00
										 |  |  | 			if ep.GetIsLocal() { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 				nsn := svcPortName.NamespacedName | 
					
						
							|  |  |  | 				if localIPs[nsn] == nil { | 
					
						
							|  |  |  | 					localIPs[nsn] = sets.NewString() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				localIPs[nsn].Insert(ep.IP()) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return localIPs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 23:25:22 +08:00
										 |  |  | // LocalReadyEndpoints returns a map of Service names to the number of local ready
 | 
					
						
							|  |  |  | // endpoints for that service.
 | 
					
						
							|  |  |  | func (em EndpointsMap) LocalReadyEndpoints() map[types.NamespacedName]int { | 
					
						
							|  |  |  | 	// TODO: If this will appear to be computationally expensive, consider
 | 
					
						
							|  |  |  | 	// computing this incrementally similarly to endpointsMap.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// (Note that we need to call getLocalEndpointIPs first to squash the data by IP,
 | 
					
						
							|  |  |  | 	// because the EndpointsMap is sorted by IP+port, not just IP, and we want to
 | 
					
						
							|  |  |  | 	// consider a Service pointing to 10.0.0.1:80 and 10.0.0.1:443 to have 1 endpoint,
 | 
					
						
							|  |  |  | 	// not 2.)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	eps := make(map[types.NamespacedName]int) | 
					
						
							|  |  |  | 	localIPs := em.getLocalReadyEndpointIPs() | 
					
						
							|  |  |  | 	for nsn, ips := range localIPs { | 
					
						
							|  |  |  | 		eps[nsn] = len(ips) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return eps | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | // detectStaleConnections modifies <staleEndpoints> and <staleServices> with detected stale connections. <staleServiceNames>
 | 
					
						
							|  |  |  | // is used to store stale udp service in order to clear udp conntrack later.
 | 
					
						
							|  |  |  | func detectStaleConnections(oldEndpointsMap, newEndpointsMap EndpointsMap, staleEndpoints *[]ServiceEndpoint, staleServiceNames *[]ServicePortName) { | 
					
						
							| 
									
										
										
										
											2021-11-05 07:37:45 +08:00
										 |  |  | 	// Detect stale endpoints: an endpoint can have stale conntrack entries if it was receiving traffic
 | 
					
						
							|  |  |  | 	// and then goes unready or changes its IP address.
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	for svcPortName, epList := range oldEndpointsMap { | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 		if svcPortName.Protocol != v1.ProtocolUDP { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 		for _, ep := range epList { | 
					
						
							| 
									
										
										
										
											2021-11-05 07:37:45 +08:00
										 |  |  | 			// if the old endpoint wasn't ready is not possible to have stale entries
 | 
					
						
							|  |  |  | 			// since there was no traffic sent to it.
 | 
					
						
							|  |  |  | 			if !ep.IsReady() { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 			stale := true | 
					
						
							| 
									
										
										
										
											2021-11-05 07:37:45 +08:00
										 |  |  | 			// Check if the endpoint has changed, including if it went from ready to not ready.
 | 
					
						
							|  |  |  | 			// If it did change stale entries for the old endpoint has to be cleared.
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 			for i := range newEndpointsMap[svcPortName] { | 
					
						
							|  |  |  | 				if newEndpointsMap[svcPortName][i].Equal(ep) { | 
					
						
							|  |  |  | 					stale = false | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if stale { | 
					
						
							| 
									
										
										
										
											2021-10-15 00:47:17 +08:00
										 |  |  | 				klog.V(4).InfoS("Stale endpoint", "portName", svcPortName, "endpoint", ep) | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 				*staleEndpoints = append(*staleEndpoints, ServiceEndpoint{Endpoint: ep.String(), ServicePortName: svcPortName}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-05 07:37:45 +08:00
										 |  |  | 	// Detect stale services
 | 
					
						
							|  |  |  | 	// For udp service, if its backend changes from 0 to non-0 ready endpoints.
 | 
					
						
							|  |  |  | 	// There may exist a conntrack entry that could blackhole traffic to the service.
 | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 	for svcPortName, epList := range newEndpointsMap { | 
					
						
							| 
									
										
										
										
											2019-09-25 08:41:50 +08:00
										 |  |  | 		if svcPortName.Protocol != v1.ProtocolUDP { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-05 07:37:45 +08:00
										 |  |  | 		epReady := 0 | 
					
						
							|  |  |  | 		for _, ep := range epList { | 
					
						
							|  |  |  | 			if ep.IsReady() { | 
					
						
							|  |  |  | 				epReady++ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		oldEpReady := 0 | 
					
						
							|  |  |  | 		for _, ep := range oldEndpointsMap[svcPortName] { | 
					
						
							|  |  |  | 			if ep.IsReady() { | 
					
						
							|  |  |  | 				oldEpReady++ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if epReady > 0 && oldEpReady == 0 { | 
					
						
							| 
									
										
										
										
											2017-12-10 15:11:35 +08:00
										 |  |  | 			*staleServiceNames = append(*staleServiceNames, svcPortName) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |