mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	extract same code of es and esm to pkg
migrate files: endpointset.go endpointslice_tracker.go endpointslice_tracker_test.go errors.go
This commit is contained in:
		@@ -45,6 +45,7 @@ import (
 | 
				
			|||||||
	endpointslicemetrics "k8s.io/kubernetes/pkg/controller/endpointslice/metrics"
 | 
						endpointslicemetrics "k8s.io/kubernetes/pkg/controller/endpointslice/metrics"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
				
			||||||
	endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
						endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,7 +142,7 @@ func NewController(podInformer coreinformers.PodInformer,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	c.endpointSliceLister = endpointSliceInformer.Lister()
 | 
						c.endpointSliceLister = endpointSliceInformer.Lister()
 | 
				
			||||||
	c.endpointSlicesSynced = endpointSliceInformer.Informer().HasSynced
 | 
						c.endpointSlicesSynced = endpointSliceInformer.Informer().HasSynced
 | 
				
			||||||
	c.endpointSliceTracker = newEndpointSliceTracker()
 | 
						c.endpointSliceTracker = endpointsliceutil.NewEndpointSliceTracker()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.maxEndpointsPerSlice = maxEndpointsPerSlice
 | 
						c.maxEndpointsPerSlice = maxEndpointsPerSlice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -204,7 +205,7 @@ type Controller struct {
 | 
				
			|||||||
	// endpointSliceTracker tracks the list of EndpointSlices and associated
 | 
						// endpointSliceTracker tracks the list of EndpointSlices and associated
 | 
				
			||||||
	// resource versions expected for each Service. It can help determine if a
 | 
						// resource versions expected for each Service. It can help determine if a
 | 
				
			||||||
	// cached EndpointSlice is out of date.
 | 
						// cached EndpointSlice is out of date.
 | 
				
			||||||
	endpointSliceTracker *endpointSliceTracker
 | 
						endpointSliceTracker *endpointsliceutil.EndpointSliceTracker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// nodeLister is able to list/get nodes and is populated by the
 | 
						// nodeLister is able to list/get nodes and is populated by the
 | 
				
			||||||
	// shared informer passed to NewController
 | 
						// shared informer passed to NewController
 | 
				
			||||||
@@ -368,7 +369,7 @@ func (c *Controller) syncService(key string) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.endpointSliceTracker.StaleSlices(service, endpointSlices) {
 | 
						if c.endpointSliceTracker.StaleSlices(service, endpointSlices) {
 | 
				
			||||||
		return &StaleInformerCache{"EndpointSlice informer cache is out of date"}
 | 
							return endpointsliceutil.NewStaleInformerCache("EndpointSlice informer cache is out of date")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We call ComputeEndpointLastChangeTriggerTime here to make sure that the
 | 
						// We call ComputeEndpointLastChangeTriggerTime here to make sure that the
 | 
				
			||||||
@@ -550,7 +551,7 @@ func (c *Controller) checkNodeTopologyDistribution() {
 | 
				
			|||||||
func trackSync(err error) {
 | 
					func trackSync(err error) {
 | 
				
			||||||
	metricLabel := "success"
 | 
						metricLabel := "success"
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if isStaleInformerCacheErr(err) {
 | 
							if endpointsliceutil.IsStaleInformerCacheErr(err) {
 | 
				
			||||||
			metricLabel = "stale"
 | 
								metricLabel = "stale"
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			metricLabel = "error"
 | 
								metricLabel = "error"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
				
			||||||
	endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
						endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	utilpointer "k8s.io/utils/pointer"
 | 
						utilpointer "k8s.io/utils/pointer"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -1522,7 +1523,7 @@ func TestSyncServiceStaleInformer(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			err = esController.syncService(fmt.Sprintf("%s/%s", ns, serviceName))
 | 
								err = esController.syncService(fmt.Sprintf("%s/%s", ns, serviceName))
 | 
				
			||||||
			// Check if we got a StaleInformerCache error
 | 
								// Check if we got a StaleInformerCache error
 | 
				
			||||||
			if isStaleInformerCacheErr(err) != testcase.expectError {
 | 
								if endpointsliceutil.IsStaleInformerCacheErr(err) != testcase.expectError {
 | 
				
			||||||
				t.Fatalf("Expected error because informer cache is outdated")
 | 
									t.Fatalf("Expected error because informer cache is outdated")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/controller/endpointslice/metrics"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslice/metrics"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
				
			||||||
	endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
						endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,7 +46,7 @@ type reconciler struct {
 | 
				
			|||||||
	client               clientset.Interface
 | 
						client               clientset.Interface
 | 
				
			||||||
	nodeLister           corelisters.NodeLister
 | 
						nodeLister           corelisters.NodeLister
 | 
				
			||||||
	maxEndpointsPerSlice int32
 | 
						maxEndpointsPerSlice int32
 | 
				
			||||||
	endpointSliceTracker *endpointSliceTracker
 | 
						endpointSliceTracker *endpointsliceutil.EndpointSliceTracker
 | 
				
			||||||
	metricsCache         *metrics.Cache
 | 
						metricsCache         *metrics.Cache
 | 
				
			||||||
	// topologyCache tracks the distribution of Nodes and endpoints across zones
 | 
						// topologyCache tracks the distribution of Nodes and endpoints across zones
 | 
				
			||||||
	// to enable TopologyAwareHints.
 | 
						// to enable TopologyAwareHints.
 | 
				
			||||||
@@ -148,7 +149,7 @@ func (r *reconciler) reconcileByAddressType(service *corev1.Service, pods []*cor
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Build data structures for desired state.
 | 
						// Build data structures for desired state.
 | 
				
			||||||
	desiredMetaByPortMap := map[endpointutil.PortMapKey]*endpointMeta{}
 | 
						desiredMetaByPortMap := map[endpointutil.PortMapKey]*endpointMeta{}
 | 
				
			||||||
	desiredEndpointsByPortMap := map[endpointutil.PortMapKey]endpointSet{}
 | 
						desiredEndpointsByPortMap := map[endpointutil.PortMapKey]endpointsliceutil.EndpointSet{}
 | 
				
			||||||
	numDesiredEndpoints := 0
 | 
						numDesiredEndpoints := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, pod := range pods {
 | 
						for _, pod := range pods {
 | 
				
			||||||
@@ -160,7 +161,7 @@ func (r *reconciler) reconcileByAddressType(service *corev1.Service, pods []*cor
 | 
				
			|||||||
		endpointPorts := getEndpointPorts(service, pod)
 | 
							endpointPorts := getEndpointPorts(service, pod)
 | 
				
			||||||
		epHash := endpointutil.NewPortMapKey(endpointPorts)
 | 
							epHash := endpointutil.NewPortMapKey(endpointPorts)
 | 
				
			||||||
		if _, ok := desiredEndpointsByPortMap[epHash]; !ok {
 | 
							if _, ok := desiredEndpointsByPortMap[epHash]; !ok {
 | 
				
			||||||
			desiredEndpointsByPortMap[epHash] = endpointSet{}
 | 
								desiredEndpointsByPortMap[epHash] = endpointsliceutil.EndpointSet{}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if _, ok := desiredMetaByPortMap[epHash]; !ok {
 | 
							if _, ok := desiredMetaByPortMap[epHash]; !ok {
 | 
				
			||||||
@@ -355,7 +356,7 @@ func (r *reconciler) finalize(
 | 
				
			|||||||
func (r *reconciler) reconcileByPortMapping(
 | 
					func (r *reconciler) reconcileByPortMapping(
 | 
				
			||||||
	service *corev1.Service,
 | 
						service *corev1.Service,
 | 
				
			||||||
	existingSlices []*discovery.EndpointSlice,
 | 
						existingSlices []*discovery.EndpointSlice,
 | 
				
			||||||
	desiredSet endpointSet,
 | 
						desiredSet endpointsliceutil.EndpointSet,
 | 
				
			||||||
	endpointMeta *endpointMeta,
 | 
						endpointMeta *endpointMeta,
 | 
				
			||||||
) ([]*discovery.EndpointSlice, []*discovery.EndpointSlice, []*discovery.EndpointSlice, int, int) {
 | 
					) ([]*discovery.EndpointSlice, []*discovery.EndpointSlice, []*discovery.EndpointSlice, int, int) {
 | 
				
			||||||
	slicesByName := map[string]*discovery.EndpointSlice{}
 | 
						slicesByName := map[string]*discovery.EndpointSlice{}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,7 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslice/metrics"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslice/metrics"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslice/topologycache"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	utilpointer "k8s.io/utils/pointer"
 | 
						utilpointer "k8s.io/utils/pointer"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -1597,7 +1598,7 @@ func newReconciler(client *fake.Clientset, nodes []*corev1.Node, maxEndpointsPer
 | 
				
			|||||||
		client:               client,
 | 
							client:               client,
 | 
				
			||||||
		nodeLister:           corelisters.NewNodeLister(indexer),
 | 
							nodeLister:           corelisters.NewNodeLister(indexer),
 | 
				
			||||||
		maxEndpointsPerSlice: maxEndpointsPerSlice,
 | 
							maxEndpointsPerSlice: maxEndpointsPerSlice,
 | 
				
			||||||
		endpointSliceTracker: newEndpointSliceTracker(),
 | 
							endpointSliceTracker: endpointsliceutil.NewEndpointSliceTracker(),
 | 
				
			||||||
		metricsCache:         metrics.NewCache(maxEndpointsPerSlice),
 | 
							metricsCache:         metrics.NewCache(maxEndpointsPerSlice),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1670,8 +1671,8 @@ func expectActions(t *testing.T, actions []k8stesting.Action, num int, verb, res
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func expectTrackedGeneration(t *testing.T, tracker *endpointSliceTracker, slice *discovery.EndpointSlice, expectedGeneration int64) {
 | 
					func expectTrackedGeneration(t *testing.T, tracker *endpointsliceutil.EndpointSliceTracker, slice *discovery.EndpointSlice, expectedGeneration int64) {
 | 
				
			||||||
	gfs, ok := tracker.generationsForSliceUnsafe(slice)
 | 
						gfs, ok := tracker.GenerationsForSliceUnsafe(slice)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		t.Fatalf("Expected Service to be tracked for EndpointSlices %s", slice.Name)
 | 
							t.Fatalf("Expected Service to be tracked for EndpointSlices %s", slice.Name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -394,3 +394,16 @@ func hintsEnabled(annotations map[string]string) bool {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return val == "Auto" || val == "auto"
 | 
						return val == "Auto" || val == "auto"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// managedByChanged returns true if one of the provided EndpointSlices is
 | 
				
			||||||
 | 
					// managed by the EndpointSlice controller while the other is not.
 | 
				
			||||||
 | 
					func managedByChanged(endpointSlice1, endpointSlice2 *discovery.EndpointSlice) bool {
 | 
				
			||||||
 | 
						return managedByController(endpointSlice1) != managedByController(endpointSlice2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// managedByController returns true if the controller of the provided
 | 
				
			||||||
 | 
					// EndpointSlices is the EndpointSlice controller.
 | 
				
			||||||
 | 
					func managedByController(endpointSlice *discovery.EndpointSlice) bool {
 | 
				
			||||||
 | 
						managedBy, _ := endpointSlice.Labels[discovery.LabelManagedBy]
 | 
				
			||||||
 | 
						return managedBy == controllerName
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,96 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2020 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 endpointslicemirroring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"sort"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	discovery "k8s.io/api/discovery/v1"
 | 
					 | 
				
			||||||
	endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// endpointHash is used to uniquely identify endpoints. Only including addresses
 | 
					 | 
				
			||||||
// and hostnames as unique identifiers allows us to do more in place updates
 | 
					 | 
				
			||||||
// should attributes such as topology, conditions, or targetRef change.
 | 
					 | 
				
			||||||
type endpointHash string
 | 
					 | 
				
			||||||
type endpointHashObj struct {
 | 
					 | 
				
			||||||
	Addresses []string
 | 
					 | 
				
			||||||
	Hostname  string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func hashEndpoint(endpoint *discovery.Endpoint) endpointHash {
 | 
					 | 
				
			||||||
	sort.Strings(endpoint.Addresses)
 | 
					 | 
				
			||||||
	hashObj := endpointHashObj{Addresses: endpoint.Addresses}
 | 
					 | 
				
			||||||
	if endpoint.Hostname != nil {
 | 
					 | 
				
			||||||
		hashObj.Hostname = *endpoint.Hostname
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return endpointHash(endpointutil.DeepHashObjectToString(hashObj))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// endpointSet provides simple methods for comparing sets of Endpoints.
 | 
					 | 
				
			||||||
type endpointSet map[endpointHash]*discovery.Endpoint
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Insert adds items to the set.
 | 
					 | 
				
			||||||
func (s endpointSet) Insert(items ...*discovery.Endpoint) endpointSet {
 | 
					 | 
				
			||||||
	for _, item := range items {
 | 
					 | 
				
			||||||
		s[hashEndpoint(item)] = item
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return s
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Delete removes all items from the set.
 | 
					 | 
				
			||||||
func (s endpointSet) Delete(items ...*discovery.Endpoint) endpointSet {
 | 
					 | 
				
			||||||
	for _, item := range items {
 | 
					 | 
				
			||||||
		delete(s, hashEndpoint(item))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return s
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Has returns true if and only if item is contained in the set.
 | 
					 | 
				
			||||||
func (s endpointSet) Has(item *discovery.Endpoint) bool {
 | 
					 | 
				
			||||||
	_, contained := s[hashEndpoint(item)]
 | 
					 | 
				
			||||||
	return contained
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Returns an endpoint matching the hash if contained in the set.
 | 
					 | 
				
			||||||
func (s endpointSet) Get(item *discovery.Endpoint) *discovery.Endpoint {
 | 
					 | 
				
			||||||
	return s[hashEndpoint(item)]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UnsortedList returns the slice with contents in random order.
 | 
					 | 
				
			||||||
func (s endpointSet) UnsortedList() []*discovery.Endpoint {
 | 
					 | 
				
			||||||
	endpoints := make([]*discovery.Endpoint, 0, len(s))
 | 
					 | 
				
			||||||
	for _, endpoint := range s {
 | 
					 | 
				
			||||||
		endpoints = append(endpoints, endpoint)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return endpoints
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Returns a single element from the set.
 | 
					 | 
				
			||||||
func (s endpointSet) PopAny() (*discovery.Endpoint, bool) {
 | 
					 | 
				
			||||||
	for _, endpoint := range s {
 | 
					 | 
				
			||||||
		s.Delete(endpoint)
 | 
					 | 
				
			||||||
		return endpoint, true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil, false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Len returns the size of the set.
 | 
					 | 
				
			||||||
func (s endpointSet) Len() int {
 | 
					 | 
				
			||||||
	return len(s)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,204 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2020 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 endpointslicemirroring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	discovery "k8s.io/api/discovery/v1"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	deletionExpected = -1
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// generationsBySlice tracks expected EndpointSlice generations by EndpointSlice
 | 
					 | 
				
			||||||
// uid. A value of deletionExpected (-1) may be used here to indicate that we
 | 
					 | 
				
			||||||
// expect this EndpointSlice to be deleted.
 | 
					 | 
				
			||||||
type generationsBySlice map[types.UID]int64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// endpointSliceTracker tracks EndpointSlices and their associated generation to
 | 
					 | 
				
			||||||
// help determine if a change to an EndpointSlice has been processed by the
 | 
					 | 
				
			||||||
// EndpointSlice controller.
 | 
					 | 
				
			||||||
type endpointSliceTracker struct {
 | 
					 | 
				
			||||||
	// lock protects generationsByService.
 | 
					 | 
				
			||||||
	lock sync.Mutex
 | 
					 | 
				
			||||||
	// generationsByService tracks the generations of EndpointSlices for each
 | 
					 | 
				
			||||||
	// Service.
 | 
					 | 
				
			||||||
	generationsByService map[types.NamespacedName]generationsBySlice
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// newEndpointSliceTracker creates and initializes a new endpointSliceTracker.
 | 
					 | 
				
			||||||
func newEndpointSliceTracker() *endpointSliceTracker {
 | 
					 | 
				
			||||||
	return &endpointSliceTracker{
 | 
					 | 
				
			||||||
		generationsByService: map[types.NamespacedName]generationsBySlice{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Has returns true if the endpointSliceTracker has a generation for the
 | 
					 | 
				
			||||||
// provided EndpointSlice.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) Has(endpointSlice *discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	est.lock.Lock()
 | 
					 | 
				
			||||||
	defer est.lock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, ok = gfs[endpointSlice.UID]
 | 
					 | 
				
			||||||
	return ok
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ShouldSync returns true if this endpointSliceTracker does not have a
 | 
					 | 
				
			||||||
// generation for the provided EndpointSlice or it is greater than the
 | 
					 | 
				
			||||||
// generation of the tracked EndpointSlice.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	est.lock.Lock()
 | 
					 | 
				
			||||||
	defer est.lock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	g, ok := gfs[endpointSlice.UID]
 | 
					 | 
				
			||||||
	return !ok || endpointSlice.Generation > g
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// StaleSlices returns true if any of the following are true:
 | 
					 | 
				
			||||||
// 1. One or more of the provided EndpointSlices have older generations than the
 | 
					 | 
				
			||||||
//    corresponding tracked ones.
 | 
					 | 
				
			||||||
// 2. The tracker is expecting one or more of the provided EndpointSlices to be
 | 
					 | 
				
			||||||
//    deleted.
 | 
					 | 
				
			||||||
// 3. The tracker is tracking EndpointSlices that have not been provided.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices []*discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	est.lock.Lock()
 | 
					 | 
				
			||||||
	defer est.lock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	nn := types.NamespacedName{Name: service.Name, Namespace: service.Namespace}
 | 
					 | 
				
			||||||
	gfs, ok := est.generationsByService[nn]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	providedSlices := map[types.UID]int64{}
 | 
					 | 
				
			||||||
	for _, endpointSlice := range endpointSlices {
 | 
					 | 
				
			||||||
		providedSlices[endpointSlice.UID] = endpointSlice.Generation
 | 
					 | 
				
			||||||
		g, ok := gfs[endpointSlice.UID]
 | 
					 | 
				
			||||||
		if ok && (g == deletionExpected || g > endpointSlice.Generation) {
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for uid, generation := range gfs {
 | 
					 | 
				
			||||||
		if generation == deletionExpected {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		_, ok := providedSlices[uid]
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update adds or updates the generation in this endpointSliceTracker for the
 | 
					 | 
				
			||||||
// provided EndpointSlice.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) Update(endpointSlice *discovery.EndpointSlice) {
 | 
					 | 
				
			||||||
	est.lock.Lock()
 | 
					 | 
				
			||||||
	defer est.lock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		gfs = generationsBySlice{}
 | 
					 | 
				
			||||||
		est.generationsByService[getServiceNN(endpointSlice)] = gfs
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	gfs[endpointSlice.UID] = endpointSlice.Generation
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteService removes the set of generations tracked for the Service.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) DeleteService(namespace, name string) {
 | 
					 | 
				
			||||||
	est.lock.Lock()
 | 
					 | 
				
			||||||
	defer est.lock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	serviceNN := types.NamespacedName{Name: name, Namespace: namespace}
 | 
					 | 
				
			||||||
	delete(est.generationsByService, serviceNN)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ExpectDeletion sets the generation to deletionExpected in this
 | 
					 | 
				
			||||||
// endpointSliceTracker for the provided EndpointSlice.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) ExpectDeletion(endpointSlice *discovery.EndpointSlice) {
 | 
					 | 
				
			||||||
	est.lock.Lock()
 | 
					 | 
				
			||||||
	defer est.lock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		gfs = generationsBySlice{}
 | 
					 | 
				
			||||||
		est.generationsByService[getServiceNN(endpointSlice)] = gfs
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	gfs[endpointSlice.UID] = deletionExpected
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HandleDeletion removes the generation in this endpointSliceTracker for the
 | 
					 | 
				
			||||||
// provided EndpointSlice. This returns true if the tracker expected this
 | 
					 | 
				
			||||||
// EndpointSlice to be deleted and false if not.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) HandleDeletion(endpointSlice *discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	est.lock.Lock()
 | 
					 | 
				
			||||||
	defer est.lock.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ok {
 | 
					 | 
				
			||||||
		g, ok := gfs[endpointSlice.UID]
 | 
					 | 
				
			||||||
		delete(gfs, endpointSlice.UID)
 | 
					 | 
				
			||||||
		if ok && g != deletionExpected {
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// generationsForSliceUnsafe returns the generations for the Service
 | 
					 | 
				
			||||||
// corresponding to the provided EndpointSlice, and a bool to indicate if it
 | 
					 | 
				
			||||||
// exists. A lock must be applied before calling this function.
 | 
					 | 
				
			||||||
func (est *endpointSliceTracker) generationsForSliceUnsafe(endpointSlice *discovery.EndpointSlice) (generationsBySlice, bool) {
 | 
					 | 
				
			||||||
	serviceNN := getServiceNN(endpointSlice)
 | 
					 | 
				
			||||||
	generations, ok := est.generationsByService[serviceNN]
 | 
					 | 
				
			||||||
	return generations, ok
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getServiceNN returns a namespaced name for the Service corresponding to the
 | 
					 | 
				
			||||||
// provided EndpointSlice.
 | 
					 | 
				
			||||||
func getServiceNN(endpointSlice *discovery.EndpointSlice) types.NamespacedName {
 | 
					 | 
				
			||||||
	serviceName, _ := endpointSlice.Labels[discovery.LabelServiceName]
 | 
					 | 
				
			||||||
	return types.NamespacedName{Name: serviceName, Namespace: endpointSlice.Namespace}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// managedByChanged returns true if one of the provided EndpointSlices is
 | 
					 | 
				
			||||||
// managed by the EndpointSlice controller while the other is not.
 | 
					 | 
				
			||||||
func managedByChanged(endpointSlice1, endpointSlice2 *discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	return managedByController(endpointSlice1) != managedByController(endpointSlice2)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// managedByController returns true if the controller of the provided
 | 
					 | 
				
			||||||
// EndpointSlices is the EndpointSlice controller.
 | 
					 | 
				
			||||||
func managedByController(endpointSlice *discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	managedBy, _ := endpointSlice.Labels[discovery.LabelManagedBy]
 | 
					 | 
				
			||||||
	return managedBy == controllerName
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,401 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2020 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 endpointslicemirroring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	discovery "k8s.io/api/discovery/v1"
 | 
					 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestEndpointSliceTrackerUpdate(t *testing.T) {
 | 
					 | 
				
			||||||
	epSlice1 := &discovery.EndpointSlice{
 | 
					 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
			Name:       "example-1",
 | 
					 | 
				
			||||||
			Namespace:  "ns1",
 | 
					 | 
				
			||||||
			UID:        "original",
 | 
					 | 
				
			||||||
			Generation: 1,
 | 
					 | 
				
			||||||
			Labels:     map[string]string{discovery.LabelServiceName: "svc1"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	epSlice1DifferentNS := epSlice1.DeepCopy()
 | 
					 | 
				
			||||||
	epSlice1DifferentNS.Namespace = "ns2"
 | 
					 | 
				
			||||||
	epSlice1DifferentNS.UID = "diff-ns"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	epSlice1DifferentService := epSlice1.DeepCopy()
 | 
					 | 
				
			||||||
	epSlice1DifferentService.Labels[discovery.LabelServiceName] = "svc2"
 | 
					 | 
				
			||||||
	epSlice1DifferentService.UID = "diff-svc"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	epSlice1NewerGen := epSlice1.DeepCopy()
 | 
					 | 
				
			||||||
	epSlice1NewerGen.Generation = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testCases := map[string]struct {
 | 
					 | 
				
			||||||
		updateParam      *discovery.EndpointSlice
 | 
					 | 
				
			||||||
		checksParam      *discovery.EndpointSlice
 | 
					 | 
				
			||||||
		expectHas        bool
 | 
					 | 
				
			||||||
		expectShouldSync bool
 | 
					 | 
				
			||||||
		expectGeneration int64
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		"same slice": {
 | 
					 | 
				
			||||||
			updateParam:      epSlice1,
 | 
					 | 
				
			||||||
			checksParam:      epSlice1,
 | 
					 | 
				
			||||||
			expectHas:        true,
 | 
					 | 
				
			||||||
			expectShouldSync: false,
 | 
					 | 
				
			||||||
			expectGeneration: epSlice1.Generation,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different namespace": {
 | 
					 | 
				
			||||||
			updateParam:      epSlice1,
 | 
					 | 
				
			||||||
			checksParam:      epSlice1DifferentNS,
 | 
					 | 
				
			||||||
			expectHas:        false,
 | 
					 | 
				
			||||||
			expectShouldSync: true,
 | 
					 | 
				
			||||||
			expectGeneration: epSlice1.Generation,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different service": {
 | 
					 | 
				
			||||||
			updateParam:      epSlice1,
 | 
					 | 
				
			||||||
			checksParam:      epSlice1DifferentService,
 | 
					 | 
				
			||||||
			expectHas:        false,
 | 
					 | 
				
			||||||
			expectShouldSync: true,
 | 
					 | 
				
			||||||
			expectGeneration: epSlice1.Generation,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"newer generation": {
 | 
					 | 
				
			||||||
			updateParam:      epSlice1,
 | 
					 | 
				
			||||||
			checksParam:      epSlice1NewerGen,
 | 
					 | 
				
			||||||
			expectHas:        true,
 | 
					 | 
				
			||||||
			expectShouldSync: true,
 | 
					 | 
				
			||||||
			expectGeneration: epSlice1.Generation,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for name, tc := range testCases {
 | 
					 | 
				
			||||||
		t.Run(name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			esTracker := newEndpointSliceTracker()
 | 
					 | 
				
			||||||
			esTracker.Update(tc.updateParam)
 | 
					 | 
				
			||||||
			if esTracker.Has(tc.checksParam) != tc.expectHas {
 | 
					 | 
				
			||||||
				t.Errorf("tc.tracker.Has(%+v) == %t, expected %t", tc.checksParam, esTracker.Has(tc.checksParam), tc.expectHas)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if esTracker.ShouldSync(tc.checksParam) != tc.expectShouldSync {
 | 
					 | 
				
			||||||
				t.Errorf("tc.tracker.ShouldSync(%+v) == %t, expected %t", tc.checksParam, esTracker.ShouldSync(tc.checksParam), tc.expectShouldSync)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			serviceNN := types.NamespacedName{Namespace: epSlice1.Namespace, Name: "svc1"}
 | 
					 | 
				
			||||||
			gfs, ok := esTracker.generationsByService[serviceNN]
 | 
					 | 
				
			||||||
			if !ok {
 | 
					 | 
				
			||||||
				t.Fatalf("expected tracker to have generations for %s Service", serviceNN.Name)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			generation, ok := gfs[epSlice1.UID]
 | 
					 | 
				
			||||||
			if !ok {
 | 
					 | 
				
			||||||
				t.Fatalf("expected tracker to have generation for %s EndpointSlice", epSlice1.Name)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if tc.expectGeneration != generation {
 | 
					 | 
				
			||||||
				t.Fatalf("expected generation to be %d, got %d", tc.expectGeneration, generation)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
 | 
					 | 
				
			||||||
	epSlice1 := &discovery.EndpointSlice{
 | 
					 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
			Name:       "example-1",
 | 
					 | 
				
			||||||
			Namespace:  "ns1",
 | 
					 | 
				
			||||||
			UID:        "original",
 | 
					 | 
				
			||||||
			Generation: 1,
 | 
					 | 
				
			||||||
			Labels:     map[string]string{discovery.LabelServiceName: "svc1"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	epSlice1NewerGen := epSlice1.DeepCopy()
 | 
					 | 
				
			||||||
	epSlice1NewerGen.Generation = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testCases := []struct {
 | 
					 | 
				
			||||||
		name         string
 | 
					 | 
				
			||||||
		tracker      *endpointSliceTracker
 | 
					 | 
				
			||||||
		serviceParam *v1.Service
 | 
					 | 
				
			||||||
		slicesParam  []*discovery.EndpointSlice
 | 
					 | 
				
			||||||
		expectNewer  bool
 | 
					 | 
				
			||||||
	}{{
 | 
					 | 
				
			||||||
		name: "empty tracker",
 | 
					 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
					 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
					 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{},
 | 
					 | 
				
			||||||
		expectNewer:  false,
 | 
					 | 
				
			||||||
	}, {
 | 
					 | 
				
			||||||
		name: "empty slices",
 | 
					 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
					 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
					 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
					 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{},
 | 
					 | 
				
			||||||
		expectNewer:  false,
 | 
					 | 
				
			||||||
	}, {
 | 
					 | 
				
			||||||
		name: "matching slices",
 | 
					 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
					 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
					 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
					 | 
				
			||||||
					epSlice1.UID: epSlice1.Generation,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
					 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{epSlice1},
 | 
					 | 
				
			||||||
		expectNewer:  false,
 | 
					 | 
				
			||||||
	}, {
 | 
					 | 
				
			||||||
		name: "newer slice in tracker",
 | 
					 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
					 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
					 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
					 | 
				
			||||||
					epSlice1.UID: epSlice1NewerGen.Generation,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
					 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{epSlice1},
 | 
					 | 
				
			||||||
		expectNewer:  true,
 | 
					 | 
				
			||||||
	}, {
 | 
					 | 
				
			||||||
		name: "newer slice in params",
 | 
					 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
					 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
					 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
					 | 
				
			||||||
					epSlice1.UID: epSlice1.Generation,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
					 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{epSlice1NewerGen},
 | 
					 | 
				
			||||||
		expectNewer:  false,
 | 
					 | 
				
			||||||
	}, {
 | 
					 | 
				
			||||||
		name: "slice in params is expected to be deleted",
 | 
					 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
					 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
					 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
					 | 
				
			||||||
					epSlice1.UID: deletionExpected,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
					 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{epSlice1},
 | 
					 | 
				
			||||||
		expectNewer:  true,
 | 
					 | 
				
			||||||
	}, {
 | 
					 | 
				
			||||||
		name: "slice in tracker but not in params",
 | 
					 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
					 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
					 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
					 | 
				
			||||||
					epSlice1.UID: epSlice1.Generation,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
					 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{},
 | 
					 | 
				
			||||||
		expectNewer:  true,
 | 
					 | 
				
			||||||
	}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, tc := range testCases {
 | 
					 | 
				
			||||||
		t.Run(tc.name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			actualNewer := tc.tracker.StaleSlices(tc.serviceParam, tc.slicesParam)
 | 
					 | 
				
			||||||
			if actualNewer != tc.expectNewer {
 | 
					 | 
				
			||||||
				t.Errorf("Expected %t, got %t", tc.expectNewer, actualNewer)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
func TestEndpointSliceTrackerDeletion(t *testing.T) {
 | 
					 | 
				
			||||||
	epSlice1 := &discovery.EndpointSlice{
 | 
					 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
			Name:       "example-1",
 | 
					 | 
				
			||||||
			Namespace:  "ns1",
 | 
					 | 
				
			||||||
			UID:        "original",
 | 
					 | 
				
			||||||
			Generation: 1,
 | 
					 | 
				
			||||||
			Labels:     map[string]string{discovery.LabelServiceName: "svc1"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	epSlice1DifferentNS := epSlice1.DeepCopy()
 | 
					 | 
				
			||||||
	epSlice1DifferentNS.Namespace = "ns2"
 | 
					 | 
				
			||||||
	epSlice1DifferentNS.UID = "diff-ns"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	epSlice1DifferentService := epSlice1.DeepCopy()
 | 
					 | 
				
			||||||
	epSlice1DifferentService.Labels[discovery.LabelServiceName] = "svc2"
 | 
					 | 
				
			||||||
	epSlice1DifferentService.UID = "diff-svc"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	epSlice1NewerGen := epSlice1.DeepCopy()
 | 
					 | 
				
			||||||
	epSlice1NewerGen.Generation = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testCases := map[string]struct {
 | 
					 | 
				
			||||||
		expectDeletionParam        *discovery.EndpointSlice
 | 
					 | 
				
			||||||
		checksParam                *discovery.EndpointSlice
 | 
					 | 
				
			||||||
		deleteParam                *discovery.EndpointSlice
 | 
					 | 
				
			||||||
		expectHas                  bool
 | 
					 | 
				
			||||||
		expectShouldSync           bool
 | 
					 | 
				
			||||||
		expectedHandleDeletionResp bool
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		"same slice": {
 | 
					 | 
				
			||||||
			expectDeletionParam:        epSlice1,
 | 
					 | 
				
			||||||
			checksParam:                epSlice1,
 | 
					 | 
				
			||||||
			deleteParam:                epSlice1,
 | 
					 | 
				
			||||||
			expectHas:                  true,
 | 
					 | 
				
			||||||
			expectShouldSync:           true,
 | 
					 | 
				
			||||||
			expectedHandleDeletionResp: true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different namespace": {
 | 
					 | 
				
			||||||
			expectDeletionParam:        epSlice1DifferentNS,
 | 
					 | 
				
			||||||
			checksParam:                epSlice1DifferentNS,
 | 
					 | 
				
			||||||
			deleteParam:                epSlice1DifferentNS,
 | 
					 | 
				
			||||||
			expectHas:                  true,
 | 
					 | 
				
			||||||
			expectShouldSync:           true,
 | 
					 | 
				
			||||||
			expectedHandleDeletionResp: false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different namespace, check original ep slice": {
 | 
					 | 
				
			||||||
			expectDeletionParam:        epSlice1DifferentNS,
 | 
					 | 
				
			||||||
			checksParam:                epSlice1,
 | 
					 | 
				
			||||||
			deleteParam:                epSlice1DifferentNS,
 | 
					 | 
				
			||||||
			expectHas:                  true,
 | 
					 | 
				
			||||||
			expectShouldSync:           false,
 | 
					 | 
				
			||||||
			expectedHandleDeletionResp: false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different service": {
 | 
					 | 
				
			||||||
			expectDeletionParam:        epSlice1DifferentService,
 | 
					 | 
				
			||||||
			checksParam:                epSlice1DifferentService,
 | 
					 | 
				
			||||||
			deleteParam:                epSlice1DifferentService,
 | 
					 | 
				
			||||||
			expectHas:                  true,
 | 
					 | 
				
			||||||
			expectShouldSync:           true,
 | 
					 | 
				
			||||||
			expectedHandleDeletionResp: false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"expectDelete different service, check original ep slice, delete original": {
 | 
					 | 
				
			||||||
			expectDeletionParam:        epSlice1DifferentService,
 | 
					 | 
				
			||||||
			checksParam:                epSlice1,
 | 
					 | 
				
			||||||
			deleteParam:                epSlice1,
 | 
					 | 
				
			||||||
			expectHas:                  true,
 | 
					 | 
				
			||||||
			expectShouldSync:           false,
 | 
					 | 
				
			||||||
			expectedHandleDeletionResp: false,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different generation": {
 | 
					 | 
				
			||||||
			expectDeletionParam:        epSlice1NewerGen,
 | 
					 | 
				
			||||||
			checksParam:                epSlice1NewerGen,
 | 
					 | 
				
			||||||
			deleteParam:                epSlice1NewerGen,
 | 
					 | 
				
			||||||
			expectHas:                  true,
 | 
					 | 
				
			||||||
			expectShouldSync:           true,
 | 
					 | 
				
			||||||
			expectedHandleDeletionResp: true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"expectDelete different generation, check original ep slice, delete original": {
 | 
					 | 
				
			||||||
			expectDeletionParam:        epSlice1NewerGen,
 | 
					 | 
				
			||||||
			checksParam:                epSlice1,
 | 
					 | 
				
			||||||
			deleteParam:                epSlice1,
 | 
					 | 
				
			||||||
			expectHas:                  true,
 | 
					 | 
				
			||||||
			expectShouldSync:           true,
 | 
					 | 
				
			||||||
			expectedHandleDeletionResp: true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for name, tc := range testCases {
 | 
					 | 
				
			||||||
		t.Run(name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			esTracker := newEndpointSliceTracker()
 | 
					 | 
				
			||||||
			esTracker.Update(epSlice1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			esTracker.ExpectDeletion(tc.expectDeletionParam)
 | 
					 | 
				
			||||||
			if esTracker.Has(tc.checksParam) != tc.expectHas {
 | 
					 | 
				
			||||||
				t.Errorf("esTracker.Has(%+v) == %t, expected %t", tc.checksParam, esTracker.Has(tc.checksParam), tc.expectHas)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if esTracker.ShouldSync(tc.checksParam) != tc.expectShouldSync {
 | 
					 | 
				
			||||||
				t.Errorf("esTracker.ShouldSync(%+v) == %t, expected %t", tc.checksParam, esTracker.ShouldSync(tc.checksParam), tc.expectShouldSync)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if esTracker.HandleDeletion(epSlice1) != tc.expectedHandleDeletionResp {
 | 
					 | 
				
			||||||
				t.Errorf("esTracker.ShouldSync(%+v) == %t, expected %t", epSlice1, esTracker.HandleDeletion(epSlice1), tc.expectedHandleDeletionResp)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if esTracker.Has(epSlice1) != false {
 | 
					 | 
				
			||||||
				t.Errorf("esTracker.Has(%+v) == %t, expected false", epSlice1, esTracker.Has(epSlice1))
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestEndpointSliceTrackerDeleteService(t *testing.T) {
 | 
					 | 
				
			||||||
	svcName1, svcNS1 := "svc1", "ns1"
 | 
					 | 
				
			||||||
	svcName2, svcNS2 := "svc2", "ns2"
 | 
					 | 
				
			||||||
	epSlice1 := &discovery.EndpointSlice{
 | 
					 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
			Name:       "example-1",
 | 
					 | 
				
			||||||
			Namespace:  svcNS1,
 | 
					 | 
				
			||||||
			Generation: 1,
 | 
					 | 
				
			||||||
			Labels:     map[string]string{discovery.LabelServiceName: svcName1},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testCases := map[string]struct {
 | 
					 | 
				
			||||||
		updateParam        *discovery.EndpointSlice
 | 
					 | 
				
			||||||
		deleteServiceParam *types.NamespacedName
 | 
					 | 
				
			||||||
		expectHas          bool
 | 
					 | 
				
			||||||
		expectShouldSync   bool
 | 
					 | 
				
			||||||
		expectGeneration   int64
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		"same service": {
 | 
					 | 
				
			||||||
			updateParam:        epSlice1,
 | 
					 | 
				
			||||||
			deleteServiceParam: &types.NamespacedName{Namespace: svcNS1, Name: svcName1},
 | 
					 | 
				
			||||||
			expectHas:          false,
 | 
					 | 
				
			||||||
			expectShouldSync:   true,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different namespace": {
 | 
					 | 
				
			||||||
			updateParam:        epSlice1,
 | 
					 | 
				
			||||||
			deleteServiceParam: &types.NamespacedName{Namespace: svcNS2, Name: svcName1},
 | 
					 | 
				
			||||||
			expectHas:          true,
 | 
					 | 
				
			||||||
			expectShouldSync:   false,
 | 
					 | 
				
			||||||
			expectGeneration:   epSlice1.Generation,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"different service": {
 | 
					 | 
				
			||||||
			updateParam:        epSlice1,
 | 
					 | 
				
			||||||
			deleteServiceParam: &types.NamespacedName{Namespace: svcNS1, Name: svcName2},
 | 
					 | 
				
			||||||
			expectHas:          true,
 | 
					 | 
				
			||||||
			expectShouldSync:   false,
 | 
					 | 
				
			||||||
			expectGeneration:   epSlice1.Generation,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for name, tc := range testCases {
 | 
					 | 
				
			||||||
		t.Run(name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			esTracker := newEndpointSliceTracker()
 | 
					 | 
				
			||||||
			esTracker.Update(tc.updateParam)
 | 
					 | 
				
			||||||
			esTracker.DeleteService(tc.deleteServiceParam.Namespace, tc.deleteServiceParam.Name)
 | 
					 | 
				
			||||||
			if esTracker.Has(tc.updateParam) != tc.expectHas {
 | 
					 | 
				
			||||||
				t.Errorf("tc.tracker.Has(%+v) == %t, expected %t", tc.updateParam, esTracker.Has(tc.updateParam), tc.expectHas)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if esTracker.ShouldSync(tc.updateParam) != tc.expectShouldSync {
 | 
					 | 
				
			||||||
				t.Errorf("tc.tracker.ShouldSync(%+v) == %t, expected %t", tc.updateParam, esTracker.ShouldSync(tc.updateParam), tc.expectShouldSync)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if tc.expectGeneration != 0 {
 | 
					 | 
				
			||||||
				serviceNN := types.NamespacedName{Namespace: epSlice1.Namespace, Name: "svc1"}
 | 
					 | 
				
			||||||
				gfs, ok := esTracker.generationsByService[serviceNN]
 | 
					 | 
				
			||||||
				if !ok {
 | 
					 | 
				
			||||||
					t.Fatalf("expected tracker to have status for %s Service", serviceNN.Name)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				generation, ok := gfs[epSlice1.UID]
 | 
					 | 
				
			||||||
				if !ok {
 | 
					 | 
				
			||||||
					t.Fatalf("expected tracker to have generation for %s EndpointSlice", epSlice1.Name)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if tc.expectGeneration != generation {
 | 
					 | 
				
			||||||
					t.Fatalf("expected generation to be %d, got %d", tc.expectGeneration, generation)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -42,6 +42,7 @@ import (
 | 
				
			|||||||
	"k8s.io/klog/v2"
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslicemirroring/metrics"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslicemirroring/metrics"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -116,7 +117,7 @@ func NewController(endpointsInformer coreinformers.EndpointsInformer,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	c.endpointSliceLister = endpointSliceInformer.Lister()
 | 
						c.endpointSliceLister = endpointSliceInformer.Lister()
 | 
				
			||||||
	c.endpointSlicesSynced = endpointSliceInformer.Informer().HasSynced
 | 
						c.endpointSlicesSynced = endpointSliceInformer.Informer().HasSynced
 | 
				
			||||||
	c.endpointSliceTracker = newEndpointSliceTracker()
 | 
						c.endpointSliceTracker = endpointsliceutil.NewEndpointSliceTracker()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.serviceLister = serviceInformer.Lister()
 | 
						c.serviceLister = serviceInformer.Lister()
 | 
				
			||||||
	c.servicesSynced = serviceInformer.Informer().HasSynced
 | 
						c.servicesSynced = serviceInformer.Informer().HasSynced
 | 
				
			||||||
@@ -169,7 +170,7 @@ type Controller struct {
 | 
				
			|||||||
	// endpointSliceTracker tracks the list of EndpointSlices and associated
 | 
						// endpointSliceTracker tracks the list of EndpointSlices and associated
 | 
				
			||||||
	// resource versions expected for each Endpoints resource. It can help
 | 
						// resource versions expected for each Endpoints resource. It can help
 | 
				
			||||||
	// determine if a cached EndpointSlice is out of date.
 | 
						// determine if a cached EndpointSlice is out of date.
 | 
				
			||||||
	endpointSliceTracker *endpointSliceTracker
 | 
						endpointSliceTracker *endpointsliceutil.EndpointSliceTracker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// serviceLister is able to list/get services and is populated by the shared
 | 
						// serviceLister is able to list/get services and is populated by the shared
 | 
				
			||||||
	// informer passed to NewController.
 | 
						// informer passed to NewController.
 | 
				
			||||||
@@ -317,7 +318,7 @@ func (c *Controller) syncEndpoints(key string) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.endpointSliceTracker.StaleSlices(svc, endpointSlices) {
 | 
						if c.endpointSliceTracker.StaleSlices(svc, endpointSlices) {
 | 
				
			||||||
		return &StaleInformerCache{"EndpointSlice informer cache is out of date"}
 | 
							return endpointsliceutil.NewStaleInformerCache("EndpointSlice informer cache is out of date")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = c.reconciler.reconcile(endpoints, endpointSlices)
 | 
						err = c.reconciler.reconcile(endpoints, endpointSlices)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2021 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 endpointslicemirroring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// StaleInformerCache errors indicate that the informer cache includes out of
 | 
					 | 
				
			||||||
// date resources.
 | 
					 | 
				
			||||||
type StaleInformerCache struct {
 | 
					 | 
				
			||||||
	msg string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (e *StaleInformerCache) Error() string { return e.msg }
 | 
					 | 
				
			||||||
@@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
	"k8s.io/klog/v2"
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslicemirroring/metrics"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslicemirroring/metrics"
 | 
				
			||||||
	endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
						endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reconciler is responsible for transforming current EndpointSlice state into
 | 
					// reconciler is responsible for transforming current EndpointSlice state into
 | 
				
			||||||
@@ -41,7 +42,7 @@ type reconciler struct {
 | 
				
			|||||||
	// endpointSliceTracker tracks the list of EndpointSlices and associated
 | 
						// endpointSliceTracker tracks the list of EndpointSlices and associated
 | 
				
			||||||
	// resource versions expected for each Endpoints resource. It can help
 | 
						// resource versions expected for each Endpoints resource. It can help
 | 
				
			||||||
	// determine if a cached EndpointSlice is out of date.
 | 
						// determine if a cached EndpointSlice is out of date.
 | 
				
			||||||
	endpointSliceTracker *endpointSliceTracker
 | 
						endpointSliceTracker *endpointsliceutil.EndpointSliceTracker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// eventRecorder allows reconciler to record an event if it finds an invalid
 | 
						// eventRecorder allows reconciler to record an event if it finds an invalid
 | 
				
			||||||
	// IP address in an Endpoints resource.
 | 
						// IP address in an Endpoints resource.
 | 
				
			||||||
@@ -173,7 +174,7 @@ func (r *reconciler) reconcile(endpoints *corev1.Endpoints, existingSlices []*di
 | 
				
			|||||||
func (r *reconciler) reconcileByPortMapping(
 | 
					func (r *reconciler) reconcileByPortMapping(
 | 
				
			||||||
	endpoints *corev1.Endpoints,
 | 
						endpoints *corev1.Endpoints,
 | 
				
			||||||
	existingSlices []*discovery.EndpointSlice,
 | 
						existingSlices []*discovery.EndpointSlice,
 | 
				
			||||||
	desiredSet endpointSet,
 | 
						desiredSet endpointsliceutil.EndpointSet,
 | 
				
			||||||
	endpointPorts []discovery.EndpointPort,
 | 
						endpointPorts []discovery.EndpointPort,
 | 
				
			||||||
	addressType discovery.AddressType,
 | 
						addressType discovery.AddressType,
 | 
				
			||||||
) (slicesByAction, totalsByAction) {
 | 
					) (slicesByAction, totalsByAction) {
 | 
				
			||||||
@@ -306,7 +307,7 @@ func endpointSlicesByKey(existingSlices []*discovery.EndpointSlice) map[addrType
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// totalChanges returns the total changes that will be required for an
 | 
					// totalChanges returns the total changes that will be required for an
 | 
				
			||||||
// EndpointSlice to match a desired set of endpoints.
 | 
					// EndpointSlice to match a desired set of endpoints.
 | 
				
			||||||
func totalChanges(existingSlice *discovery.EndpointSlice, desiredSet endpointSet) totalsByAction {
 | 
					func totalChanges(existingSlice *discovery.EndpointSlice, desiredSet endpointsliceutil.EndpointSet) totalsByAction {
 | 
				
			||||||
	totals := totalsByAction{}
 | 
						totals := totalsByAction{}
 | 
				
			||||||
	existingMatches := 0
 | 
						existingMatches := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ package endpointslicemirroring
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
	discovery "k8s.io/api/discovery/v1"
 | 
						discovery "k8s.io/api/discovery/v1"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// slicesByAction includes lists of slices to create, update, or delete.
 | 
					// slicesByAction includes lists of slices to create, update, or delete.
 | 
				
			||||||
@@ -49,7 +50,7 @@ func (t *totalsByAction) add(totals totalsByAction) {
 | 
				
			|||||||
func newDesiredCalc() *desiredCalc {
 | 
					func newDesiredCalc() *desiredCalc {
 | 
				
			||||||
	return &desiredCalc{
 | 
						return &desiredCalc{
 | 
				
			||||||
		portsByKey:          map[addrTypePortMapKey][]discovery.EndpointPort{},
 | 
							portsByKey:          map[addrTypePortMapKey][]discovery.EndpointPort{},
 | 
				
			||||||
		endpointsByKey:      map[addrTypePortMapKey]endpointSet{},
 | 
							endpointsByKey:      map[addrTypePortMapKey]endpointsliceutil.EndpointSet{},
 | 
				
			||||||
		numDesiredEndpoints: 0,
 | 
							numDesiredEndpoints: 0,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -57,7 +58,7 @@ func newDesiredCalc() *desiredCalc {
 | 
				
			|||||||
// desiredCalc helps calculate desired endpoints and ports.
 | 
					// desiredCalc helps calculate desired endpoints and ports.
 | 
				
			||||||
type desiredCalc struct {
 | 
					type desiredCalc struct {
 | 
				
			||||||
	portsByKey          map[addrTypePortMapKey][]discovery.EndpointPort
 | 
						portsByKey          map[addrTypePortMapKey][]discovery.EndpointPort
 | 
				
			||||||
	endpointsByKey      map[addrTypePortMapKey]endpointSet
 | 
						endpointsByKey      map[addrTypePortMapKey]endpointsliceutil.EndpointSet
 | 
				
			||||||
	numDesiredEndpoints int
 | 
						numDesiredEndpoints int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,7 +76,7 @@ func (d *desiredCalc) initPorts(subsetPorts []v1.EndpointPort) multiAddrTypePort
 | 
				
			|||||||
	for _, addrType := range addrTypes {
 | 
						for _, addrType := range addrTypes {
 | 
				
			||||||
		multiKey[addrType] = newAddrTypePortMapKey(endpointPorts, addrType)
 | 
							multiKey[addrType] = newAddrTypePortMapKey(endpointPorts, addrType)
 | 
				
			||||||
		if _, ok := d.endpointsByKey[multiKey[addrType]]; !ok {
 | 
							if _, ok := d.endpointsByKey[multiKey[addrType]]; !ok {
 | 
				
			||||||
			d.endpointsByKey[multiKey[addrType]] = endpointSet{}
 | 
								d.endpointsByKey[multiKey[addrType]] = endpointsliceutil.EndpointSet{}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		d.portsByKey[multiKey[addrType]] = endpointPorts
 | 
							d.portsByKey[multiKey[addrType]] = endpointPorts
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@ import (
 | 
				
			|||||||
	"k8s.io/client-go/tools/record"
 | 
						"k8s.io/client-go/tools/record"
 | 
				
			||||||
	"k8s.io/component-base/metrics/testutil"
 | 
						"k8s.io/component-base/metrics/testutil"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller/endpointslicemirroring/metrics"
 | 
						"k8s.io/kubernetes/pkg/controller/endpointslicemirroring/metrics"
 | 
				
			||||||
 | 
						endpointsliceutil "k8s.io/kubernetes/pkg/controller/util/endpointslice"
 | 
				
			||||||
	utilpointer "k8s.io/utils/pointer"
 | 
						utilpointer "k8s.io/utils/pointer"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -791,7 +792,7 @@ func newReconciler(client *fake.Clientset, maxEndpointsPerSubset int32) *reconci
 | 
				
			|||||||
	return &reconciler{
 | 
						return &reconciler{
 | 
				
			||||||
		client:                client,
 | 
							client:                client,
 | 
				
			||||||
		maxEndpointsPerSubset: maxEndpointsPerSubset,
 | 
							maxEndpointsPerSubset: maxEndpointsPerSubset,
 | 
				
			||||||
		endpointSliceTracker:  newEndpointSliceTracker(),
 | 
							endpointSliceTracker:  endpointsliceutil.NewEndpointSliceTracker(),
 | 
				
			||||||
		metricsCache:          metrics.NewCache(maxEndpointsPerSubset),
 | 
							metricsCache:          metrics.NewCache(maxEndpointsPerSubset),
 | 
				
			||||||
		eventRecorder:         recorder,
 | 
							eventRecorder:         recorder,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -258,3 +258,16 @@ func cloneAndRemoveKeys(a map[string]string, keys ...string) map[string]string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return newMap
 | 
						return newMap
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// managedByChanged returns true if one of the provided EndpointSlices is
 | 
				
			||||||
 | 
					// managed by the EndpointSlice controller while the other is not.
 | 
				
			||||||
 | 
					func managedByChanged(endpointSlice1, endpointSlice2 *discovery.EndpointSlice) bool {
 | 
				
			||||||
 | 
						return managedByController(endpointSlice1) != managedByController(endpointSlice2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// managedByController returns true if the controller of the provided
 | 
				
			||||||
 | 
					// EndpointSlices is the EndpointSlice controller.
 | 
				
			||||||
 | 
					func managedByController(endpointSlice *discovery.EndpointSlice) bool {
 | 
				
			||||||
 | 
						managedBy, _ := endpointSlice.Labels[discovery.LabelManagedBy]
 | 
				
			||||||
 | 
						return managedBy == controllerName
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								pkg/controller/util/endpointslice/OWNERS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pkg/controller/util/endpointslice/OWNERS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					# See the OWNERS docs at https://go.k8s.io/owners
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					approvers:
 | 
				
			||||||
 | 
					- robscott
 | 
				
			||||||
 | 
					- freehan
 | 
				
			||||||
 | 
					- sig-network-approvers
 | 
				
			||||||
 | 
					reviewers:
 | 
				
			||||||
 | 
					- robscott
 | 
				
			||||||
 | 
					- freehan
 | 
				
			||||||
 | 
					- sig-network-reviewers
 | 
				
			||||||
 | 
					labels:
 | 
				
			||||||
 | 
					- sig/network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,11 +42,11 @@ func hashEndpoint(endpoint *discovery.Endpoint) endpointHash {
 | 
				
			|||||||
	return endpointHash(endpointutil.DeepHashObjectToString(hashObj))
 | 
						return endpointHash(endpointutil.DeepHashObjectToString(hashObj))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// endpointSet provides simple methods for comparing sets of Endpoints.
 | 
					// EndpointSet provides simple methods for comparing sets of Endpoints.
 | 
				
			||||||
type endpointSet map[endpointHash]*discovery.Endpoint
 | 
					type EndpointSet map[endpointHash]*discovery.Endpoint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Insert adds items to the set.
 | 
					// Insert adds items to the set.
 | 
				
			||||||
func (s endpointSet) Insert(items ...*discovery.Endpoint) endpointSet {
 | 
					func (s EndpointSet) Insert(items ...*discovery.Endpoint) EndpointSet {
 | 
				
			||||||
	for _, item := range items {
 | 
						for _, item := range items {
 | 
				
			||||||
		s[hashEndpoint(item)] = item
 | 
							s[hashEndpoint(item)] = item
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -54,7 +54,7 @@ func (s endpointSet) Insert(items ...*discovery.Endpoint) endpointSet {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete removes all items from the set.
 | 
					// Delete removes all items from the set.
 | 
				
			||||||
func (s endpointSet) Delete(items ...*discovery.Endpoint) endpointSet {
 | 
					func (s EndpointSet) Delete(items ...*discovery.Endpoint) EndpointSet {
 | 
				
			||||||
	for _, item := range items {
 | 
						for _, item := range items {
 | 
				
			||||||
		delete(s, hashEndpoint(item))
 | 
							delete(s, hashEndpoint(item))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -62,18 +62,18 @@ func (s endpointSet) Delete(items ...*discovery.Endpoint) endpointSet {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Has returns true if and only if item is contained in the set.
 | 
					// Has returns true if and only if item is contained in the set.
 | 
				
			||||||
func (s endpointSet) Has(item *discovery.Endpoint) bool {
 | 
					func (s EndpointSet) Has(item *discovery.Endpoint) bool {
 | 
				
			||||||
	_, contained := s[hashEndpoint(item)]
 | 
						_, contained := s[hashEndpoint(item)]
 | 
				
			||||||
	return contained
 | 
						return contained
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Returns an endpoint matching the hash if contained in the set.
 | 
					// Returns an endpoint matching the hash if contained in the set.
 | 
				
			||||||
func (s endpointSet) Get(item *discovery.Endpoint) *discovery.Endpoint {
 | 
					func (s EndpointSet) Get(item *discovery.Endpoint) *discovery.Endpoint {
 | 
				
			||||||
	return s[hashEndpoint(item)]
 | 
						return s[hashEndpoint(item)]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UnsortedList returns the slice with contents in random order.
 | 
					// UnsortedList returns the slice with contents in random order.
 | 
				
			||||||
func (s endpointSet) UnsortedList() []*discovery.Endpoint {
 | 
					func (s EndpointSet) UnsortedList() []*discovery.Endpoint {
 | 
				
			||||||
	endpoints := make([]*discovery.Endpoint, 0, len(s))
 | 
						endpoints := make([]*discovery.Endpoint, 0, len(s))
 | 
				
			||||||
	for _, endpoint := range s {
 | 
						for _, endpoint := range s {
 | 
				
			||||||
		endpoints = append(endpoints, endpoint)
 | 
							endpoints = append(endpoints, endpoint)
 | 
				
			||||||
@@ -82,7 +82,7 @@ func (s endpointSet) UnsortedList() []*discovery.Endpoint {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Returns a single element from the set.
 | 
					// Returns a single element from the set.
 | 
				
			||||||
func (s endpointSet) PopAny() (*discovery.Endpoint, bool) {
 | 
					func (s EndpointSet) PopAny() (*discovery.Endpoint, bool) {
 | 
				
			||||||
	for _, endpoint := range s {
 | 
						for _, endpoint := range s {
 | 
				
			||||||
		s.Delete(endpoint)
 | 
							s.Delete(endpoint)
 | 
				
			||||||
		return endpoint, true
 | 
							return endpoint, true
 | 
				
			||||||
@@ -91,6 +91,6 @@ func (s endpointSet) PopAny() (*discovery.Endpoint, bool) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Len returns the size of the set.
 | 
					// Len returns the size of the set.
 | 
				
			||||||
func (s endpointSet) Len() int {
 | 
					func (s EndpointSet) Len() int {
 | 
				
			||||||
	return len(s)
 | 
						return len(s)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -28,36 +28,36 @@ const (
 | 
				
			|||||||
	deletionExpected = -1
 | 
						deletionExpected = -1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// generationsBySlice tracks expected EndpointSlice generations by EndpointSlice
 | 
					// GenerationsBySlice tracks expected EndpointSlice generations by EndpointSlice
 | 
				
			||||||
// uid. A value of deletionExpected (-1) may be used here to indicate that we
 | 
					// uid. A value of deletionExpected (-1) may be used here to indicate that we
 | 
				
			||||||
// expect this EndpointSlice to be deleted.
 | 
					// expect this EndpointSlice to be deleted.
 | 
				
			||||||
type generationsBySlice map[types.UID]int64
 | 
					type GenerationsBySlice map[types.UID]int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// endpointSliceTracker tracks EndpointSlices and their associated generation to
 | 
					// EndpointSliceTracker tracks EndpointSlices and their associated generation to
 | 
				
			||||||
// help determine if a change to an EndpointSlice has been processed by the
 | 
					// help determine if a change to an EndpointSlice has been processed by the
 | 
				
			||||||
// EndpointSlice controller.
 | 
					// EndpointSlice controller.
 | 
				
			||||||
type endpointSliceTracker struct {
 | 
					type EndpointSliceTracker struct {
 | 
				
			||||||
	// lock protects generationsByService.
 | 
						// lock protects generationsByService.
 | 
				
			||||||
	lock sync.Mutex
 | 
						lock sync.Mutex
 | 
				
			||||||
	// generationsByService tracks the generations of EndpointSlices for each
 | 
						// generationsByService tracks the generations of EndpointSlices for each
 | 
				
			||||||
	// Service.
 | 
						// Service.
 | 
				
			||||||
	generationsByService map[types.NamespacedName]generationsBySlice
 | 
						generationsByService map[types.NamespacedName]GenerationsBySlice
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// newEndpointSliceTracker creates and initializes a new endpointSliceTracker.
 | 
					// NewEndpointSliceTracker creates and initializes a new endpointSliceTracker.
 | 
				
			||||||
func newEndpointSliceTracker() *endpointSliceTracker {
 | 
					func NewEndpointSliceTracker() *EndpointSliceTracker {
 | 
				
			||||||
	return &endpointSliceTracker{
 | 
						return &EndpointSliceTracker{
 | 
				
			||||||
		generationsByService: map[types.NamespacedName]generationsBySlice{},
 | 
							generationsByService: map[types.NamespacedName]GenerationsBySlice{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Has returns true if the endpointSliceTracker has a generation for the
 | 
					// Has returns true if the endpointSliceTracker has a generation for the
 | 
				
			||||||
// provided EndpointSlice.
 | 
					// provided EndpointSlice.
 | 
				
			||||||
func (est *endpointSliceTracker) Has(endpointSlice *discovery.EndpointSlice) bool {
 | 
					func (est *EndpointSliceTracker) Has(endpointSlice *discovery.EndpointSlice) bool {
 | 
				
			||||||
	est.lock.Lock()
 | 
						est.lock.Lock()
 | 
				
			||||||
	defer est.lock.Unlock()
 | 
						defer est.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
						gfs, ok := est.GenerationsForSliceUnsafe(endpointSlice)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -68,11 +68,11 @@ func (est *endpointSliceTracker) Has(endpointSlice *discovery.EndpointSlice) boo
 | 
				
			|||||||
// ShouldSync returns true if this endpointSliceTracker does not have a
 | 
					// ShouldSync returns true if this endpointSliceTracker does not have a
 | 
				
			||||||
// generation for the provided EndpointSlice or it is greater than the
 | 
					// generation for the provided EndpointSlice or it is greater than the
 | 
				
			||||||
// generation of the tracked EndpointSlice.
 | 
					// generation of the tracked EndpointSlice.
 | 
				
			||||||
func (est *endpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSlice) bool {
 | 
					func (est *EndpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSlice) bool {
 | 
				
			||||||
	est.lock.Lock()
 | 
						est.lock.Lock()
 | 
				
			||||||
	defer est.lock.Unlock()
 | 
						defer est.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
						gfs, ok := est.GenerationsForSliceUnsafe(endpointSlice)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -86,7 +86,7 @@ func (est *endpointSliceTracker) ShouldSync(endpointSlice *discovery.EndpointSli
 | 
				
			|||||||
// 2. The tracker is expecting one or more of the provided EndpointSlices to be
 | 
					// 2. The tracker is expecting one or more of the provided EndpointSlices to be
 | 
				
			||||||
//    deleted.
 | 
					//    deleted.
 | 
				
			||||||
// 3. The tracker is tracking EndpointSlices that have not been provided.
 | 
					// 3. The tracker is tracking EndpointSlices that have not been provided.
 | 
				
			||||||
func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices []*discovery.EndpointSlice) bool {
 | 
					func (est *EndpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices []*discovery.EndpointSlice) bool {
 | 
				
			||||||
	est.lock.Lock()
 | 
						est.lock.Lock()
 | 
				
			||||||
	defer est.lock.Unlock()
 | 
						defer est.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -117,21 +117,21 @@ func (est *endpointSliceTracker) StaleSlices(service *v1.Service, endpointSlices
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Update adds or updates the generation in this endpointSliceTracker for the
 | 
					// Update adds or updates the generation in this endpointSliceTracker for the
 | 
				
			||||||
// provided EndpointSlice.
 | 
					// provided EndpointSlice.
 | 
				
			||||||
func (est *endpointSliceTracker) Update(endpointSlice *discovery.EndpointSlice) {
 | 
					func (est *EndpointSliceTracker) Update(endpointSlice *discovery.EndpointSlice) {
 | 
				
			||||||
	est.lock.Lock()
 | 
						est.lock.Lock()
 | 
				
			||||||
	defer est.lock.Unlock()
 | 
						defer est.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
						gfs, ok := est.GenerationsForSliceUnsafe(endpointSlice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		gfs = generationsBySlice{}
 | 
							gfs = GenerationsBySlice{}
 | 
				
			||||||
		est.generationsByService[getServiceNN(endpointSlice)] = gfs
 | 
							est.generationsByService[getServiceNN(endpointSlice)] = gfs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gfs[endpointSlice.UID] = endpointSlice.Generation
 | 
						gfs[endpointSlice.UID] = endpointSlice.Generation
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteService removes the set of generations tracked for the Service.
 | 
					// DeleteService removes the set of generations tracked for the Service.
 | 
				
			||||||
func (est *endpointSliceTracker) DeleteService(namespace, name string) {
 | 
					func (est *EndpointSliceTracker) DeleteService(namespace, name string) {
 | 
				
			||||||
	est.lock.Lock()
 | 
						est.lock.Lock()
 | 
				
			||||||
	defer est.lock.Unlock()
 | 
						defer est.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,14 +141,14 @@ func (est *endpointSliceTracker) DeleteService(namespace, name string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ExpectDeletion sets the generation to deletionExpected in this
 | 
					// ExpectDeletion sets the generation to deletionExpected in this
 | 
				
			||||||
// endpointSliceTracker for the provided EndpointSlice.
 | 
					// endpointSliceTracker for the provided EndpointSlice.
 | 
				
			||||||
func (est *endpointSliceTracker) ExpectDeletion(endpointSlice *discovery.EndpointSlice) {
 | 
					func (est *EndpointSliceTracker) ExpectDeletion(endpointSlice *discovery.EndpointSlice) {
 | 
				
			||||||
	est.lock.Lock()
 | 
						est.lock.Lock()
 | 
				
			||||||
	defer est.lock.Unlock()
 | 
						defer est.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
						gfs, ok := est.GenerationsForSliceUnsafe(endpointSlice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		gfs = generationsBySlice{}
 | 
							gfs = GenerationsBySlice{}
 | 
				
			||||||
		est.generationsByService[getServiceNN(endpointSlice)] = gfs
 | 
							est.generationsByService[getServiceNN(endpointSlice)] = gfs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gfs[endpointSlice.UID] = deletionExpected
 | 
						gfs[endpointSlice.UID] = deletionExpected
 | 
				
			||||||
@@ -157,11 +157,11 @@ func (est *endpointSliceTracker) ExpectDeletion(endpointSlice *discovery.Endpoin
 | 
				
			|||||||
// HandleDeletion removes the generation in this endpointSliceTracker for the
 | 
					// HandleDeletion removes the generation in this endpointSliceTracker for the
 | 
				
			||||||
// provided EndpointSlice. This returns true if the tracker expected this
 | 
					// provided EndpointSlice. This returns true if the tracker expected this
 | 
				
			||||||
// EndpointSlice to be deleted and false if not.
 | 
					// EndpointSlice to be deleted and false if not.
 | 
				
			||||||
func (est *endpointSliceTracker) HandleDeletion(endpointSlice *discovery.EndpointSlice) bool {
 | 
					func (est *EndpointSliceTracker) HandleDeletion(endpointSlice *discovery.EndpointSlice) bool {
 | 
				
			||||||
	est.lock.Lock()
 | 
						est.lock.Lock()
 | 
				
			||||||
	defer est.lock.Unlock()
 | 
						defer est.lock.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gfs, ok := est.generationsForSliceUnsafe(endpointSlice)
 | 
						gfs, ok := est.GenerationsForSliceUnsafe(endpointSlice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ok {
 | 
						if ok {
 | 
				
			||||||
		g, ok := gfs[endpointSlice.UID]
 | 
							g, ok := gfs[endpointSlice.UID]
 | 
				
			||||||
@@ -174,10 +174,10 @@ func (est *endpointSliceTracker) HandleDeletion(endpointSlice *discovery.Endpoin
 | 
				
			|||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// generationsForSliceUnsafe returns the generations for the Service
 | 
					// GenerationsForSliceUnsafe returns the generations for the Service
 | 
				
			||||||
// corresponding to the provided EndpointSlice, and a bool to indicate if it
 | 
					// corresponding to the provided EndpointSlice, and a bool to indicate if it
 | 
				
			||||||
// exists. A lock must be applied before calling this function.
 | 
					// exists. A lock must be applied before calling this function.
 | 
				
			||||||
func (est *endpointSliceTracker) generationsForSliceUnsafe(endpointSlice *discovery.EndpointSlice) (generationsBySlice, bool) {
 | 
					func (est *EndpointSliceTracker) GenerationsForSliceUnsafe(endpointSlice *discovery.EndpointSlice) (GenerationsBySlice, bool) {
 | 
				
			||||||
	serviceNN := getServiceNN(endpointSlice)
 | 
						serviceNN := getServiceNN(endpointSlice)
 | 
				
			||||||
	generations, ok := est.generationsByService[serviceNN]
 | 
						generations, ok := est.generationsByService[serviceNN]
 | 
				
			||||||
	return generations, ok
 | 
						return generations, ok
 | 
				
			||||||
@@ -189,16 +189,3 @@ func getServiceNN(endpointSlice *discovery.EndpointSlice) types.NamespacedName {
 | 
				
			|||||||
	serviceName, _ := endpointSlice.Labels[discovery.LabelServiceName]
 | 
						serviceName, _ := endpointSlice.Labels[discovery.LabelServiceName]
 | 
				
			||||||
	return types.NamespacedName{Name: serviceName, Namespace: endpointSlice.Namespace}
 | 
						return types.NamespacedName{Name: serviceName, Namespace: endpointSlice.Namespace}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// managedByChanged returns true if one of the provided EndpointSlices is
 | 
					 | 
				
			||||||
// managed by the EndpointSlice controller while the other is not.
 | 
					 | 
				
			||||||
func managedByChanged(endpointSlice1, endpointSlice2 *discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	return managedByController(endpointSlice1) != managedByController(endpointSlice2)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// managedByController returns true if the controller of the provided
 | 
					 | 
				
			||||||
// EndpointSlices is the EndpointSlice controller.
 | 
					 | 
				
			||||||
func managedByController(endpointSlice *discovery.EndpointSlice) bool {
 | 
					 | 
				
			||||||
	managedBy, _ := endpointSlice.Labels[discovery.LabelManagedBy]
 | 
					 | 
				
			||||||
	return managedBy == controllerName
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -86,7 +86,7 @@ func TestEndpointSliceTrackerUpdate(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for name, tc := range testCases {
 | 
						for name, tc := range testCases {
 | 
				
			||||||
		t.Run(name, func(t *testing.T) {
 | 
							t.Run(name, func(t *testing.T) {
 | 
				
			||||||
			esTracker := newEndpointSliceTracker()
 | 
								esTracker := NewEndpointSliceTracker()
 | 
				
			||||||
			esTracker.Update(tc.updateParam)
 | 
								esTracker.Update(tc.updateParam)
 | 
				
			||||||
			if esTracker.Has(tc.checksParam) != tc.expectHas {
 | 
								if esTracker.Has(tc.checksParam) != tc.expectHas {
 | 
				
			||||||
				t.Errorf("tc.tracker.Has(%+v) == %t, expected %t", tc.checksParam, esTracker.Has(tc.checksParam), tc.expectHas)
 | 
									t.Errorf("tc.tracker.Has(%+v) == %t, expected %t", tc.checksParam, esTracker.Has(tc.checksParam), tc.expectHas)
 | 
				
			||||||
@@ -126,22 +126,22 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		name         string
 | 
							name         string
 | 
				
			||||||
		tracker      *endpointSliceTracker
 | 
							tracker      *EndpointSliceTracker
 | 
				
			||||||
		serviceParam *v1.Service
 | 
							serviceParam *v1.Service
 | 
				
			||||||
		slicesParam  []*discovery.EndpointSlice
 | 
							slicesParam  []*discovery.EndpointSlice
 | 
				
			||||||
		expectNewer  bool
 | 
							expectNewer  bool
 | 
				
			||||||
	}{{
 | 
						}{{
 | 
				
			||||||
		name: "empty tracker",
 | 
							name: "empty tracker",
 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
							tracker: &EndpointSliceTracker{
 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{},
 | 
								generationsByService: map[types.NamespacedName]GenerationsBySlice{},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
							serviceParam: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "svc1", Namespace: "ns1"}},
 | 
				
			||||||
		slicesParam:  []*discovery.EndpointSlice{},
 | 
							slicesParam:  []*discovery.EndpointSlice{},
 | 
				
			||||||
		expectNewer:  false,
 | 
							expectNewer:  false,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		name: "empty slices",
 | 
							name: "empty slices",
 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
							tracker: &EndpointSliceTracker{
 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
								generationsByService: map[types.NamespacedName]GenerationsBySlice{
 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {},
 | 
									{Name: "svc1", Namespace: "ns1"}: {},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -150,8 +150,8 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
 | 
				
			|||||||
		expectNewer:  false,
 | 
							expectNewer:  false,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		name: "matching slices",
 | 
							name: "matching slices",
 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
							tracker: &EndpointSliceTracker{
 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
								generationsByService: map[types.NamespacedName]GenerationsBySlice{
 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
									{Name: "svc1", Namespace: "ns1"}: {
 | 
				
			||||||
					epSlice1.UID: epSlice1.Generation,
 | 
										epSlice1.UID: epSlice1.Generation,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -162,8 +162,8 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
 | 
				
			|||||||
		expectNewer:  false,
 | 
							expectNewer:  false,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		name: "newer slice in tracker",
 | 
							name: "newer slice in tracker",
 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
							tracker: &EndpointSliceTracker{
 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
								generationsByService: map[types.NamespacedName]GenerationsBySlice{
 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
									{Name: "svc1", Namespace: "ns1"}: {
 | 
				
			||||||
					epSlice1.UID: epSlice1NewerGen.Generation,
 | 
										epSlice1.UID: epSlice1NewerGen.Generation,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -174,8 +174,8 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
 | 
				
			|||||||
		expectNewer:  true,
 | 
							expectNewer:  true,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		name: "newer slice in params",
 | 
							name: "newer slice in params",
 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
							tracker: &EndpointSliceTracker{
 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
								generationsByService: map[types.NamespacedName]GenerationsBySlice{
 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
									{Name: "svc1", Namespace: "ns1"}: {
 | 
				
			||||||
					epSlice1.UID: epSlice1.Generation,
 | 
										epSlice1.UID: epSlice1.Generation,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -186,8 +186,8 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
 | 
				
			|||||||
		expectNewer:  false,
 | 
							expectNewer:  false,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		name: "slice in params is expected to be deleted",
 | 
							name: "slice in params is expected to be deleted",
 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
							tracker: &EndpointSliceTracker{
 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
								generationsByService: map[types.NamespacedName]GenerationsBySlice{
 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
									{Name: "svc1", Namespace: "ns1"}: {
 | 
				
			||||||
					epSlice1.UID: deletionExpected,
 | 
										epSlice1.UID: deletionExpected,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -198,8 +198,8 @@ func TestEndpointSliceTrackerStaleSlices(t *testing.T) {
 | 
				
			|||||||
		expectNewer:  true,
 | 
							expectNewer:  true,
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		name: "slice in tracker but not in params",
 | 
							name: "slice in tracker but not in params",
 | 
				
			||||||
		tracker: &endpointSliceTracker{
 | 
							tracker: &EndpointSliceTracker{
 | 
				
			||||||
			generationsByService: map[types.NamespacedName]generationsBySlice{
 | 
								generationsByService: map[types.NamespacedName]GenerationsBySlice{
 | 
				
			||||||
				{Name: "svc1", Namespace: "ns1"}: {
 | 
									{Name: "svc1", Namespace: "ns1"}: {
 | 
				
			||||||
					epSlice1.UID: epSlice1.Generation,
 | 
										epSlice1.UID: epSlice1.Generation,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
@@ -309,7 +309,7 @@ func TestEndpointSliceTrackerDeletion(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for name, tc := range testCases {
 | 
						for name, tc := range testCases {
 | 
				
			||||||
		t.Run(name, func(t *testing.T) {
 | 
							t.Run(name, func(t *testing.T) {
 | 
				
			||||||
			esTracker := newEndpointSliceTracker()
 | 
								esTracker := NewEndpointSliceTracker()
 | 
				
			||||||
			esTracker.Update(epSlice1)
 | 
								esTracker.Update(epSlice1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			esTracker.ExpectDeletion(tc.expectDeletionParam)
 | 
								esTracker.ExpectDeletion(tc.expectDeletionParam)
 | 
				
			||||||
@@ -373,7 +373,7 @@ func TestEndpointSliceTrackerDeleteService(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for name, tc := range testCases {
 | 
						for name, tc := range testCases {
 | 
				
			||||||
		t.Run(name, func(t *testing.T) {
 | 
							t.Run(name, func(t *testing.T) {
 | 
				
			||||||
			esTracker := newEndpointSliceTracker()
 | 
								esTracker := NewEndpointSliceTracker()
 | 
				
			||||||
			esTracker.Update(tc.updateParam)
 | 
								esTracker.Update(tc.updateParam)
 | 
				
			||||||
			esTracker.DeleteService(tc.deleteServiceParam.Namespace, tc.deleteServiceParam.Name)
 | 
								esTracker.DeleteService(tc.deleteServiceParam.Namespace, tc.deleteServiceParam.Name)
 | 
				
			||||||
			if esTracker.Has(tc.updateParam) != tc.expectHas {
 | 
								if esTracker.Has(tc.updateParam) != tc.expectHas {
 | 
				
			||||||
@@ -22,9 +22,14 @@ type StaleInformerCache struct {
 | 
				
			|||||||
	msg string
 | 
						msg string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewStaleInformerCache return StaleInformerCache with error mes
 | 
				
			||||||
 | 
					func NewStaleInformerCache(msg string) *StaleInformerCache {
 | 
				
			||||||
 | 
						return &StaleInformerCache{msg}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (e *StaleInformerCache) Error() string { return e.msg }
 | 
					func (e *StaleInformerCache) Error() string { return e.msg }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isStaleInformerCacheErr(err error) bool {
 | 
					func IsStaleInformerCacheErr(err error) bool {
 | 
				
			||||||
	_, ok := err.(*StaleInformerCache)
 | 
						_, ok := err.(*StaleInformerCache)
 | 
				
			||||||
	return ok
 | 
						return ok
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user