@@ -59,19 +59,29 @@ type ApisixRouteReconciler struct {
5959 Provider provider.Provider
6060 Updater status.Updater
6161 Readier readiness.ReadinessManager
62+
63+ // supportsEndpointSlice indicates whether the cluster supports EndpointSlice API
64+ supportsEndpointSlice bool
6265}
6366
6467// SetupWithManager sets up the controller with the Manager.
6568func (r * ApisixRouteReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
66- return ctrl .NewControllerManagedBy (mgr ).
69+ // Check and store EndpointSlice API support
70+ r .supportsEndpointSlice = pkgutils .HasAPIResource (mgr , & discoveryv1.EndpointSlice {})
71+
72+ eventFilters := []predicate.Predicate {
73+ predicate.GenerationChangedPredicate {},
74+ predicate.AnnotationChangedPredicate {},
75+ predicate .NewPredicateFuncs (TypePredicate [* corev1.Secret ]()),
76+ }
77+
78+ if ! r .supportsEndpointSlice {
79+ eventFilters = append (eventFilters , predicate .NewPredicateFuncs (TypePredicate [* corev1.Endpoints ]()))
80+ }
81+
82+ bdr := ctrl .NewControllerManagedBy (mgr ).
6783 For (& apiv2.ApisixRoute {}).
68- WithEventFilter (
69- predicate .Or (
70- predicate.GenerationChangedPredicate {},
71- predicate.AnnotationChangedPredicate {},
72- predicate .NewPredicateFuncs (TypePredicate [* corev1.Secret ]()),
73- ),
74- ).
84+ WithEventFilter (predicate .Or (eventFilters ... )).
7585 Watches (
7686 & networkingv1.IngressClass {},
7787 handler .EnqueueRequestsFromMapFunc (r .listApisixRouteForIngressClass ),
@@ -81,10 +91,15 @@ func (r *ApisixRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
8191 ).
8292 Watches (& v1alpha1.GatewayProxy {},
8393 handler .EnqueueRequestsFromMapFunc (r .listApisixRouteForGatewayProxy ),
84- ).
85- Watches (& discoveryv1.EndpointSlice {},
86- handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForService ),
87- ).
94+ )
95+
96+ // Conditionally watch EndpointSlice or Endpoints based on cluster API support
97+ bdr = watchEndpointSliceOrEndpoints (bdr , r .supportsEndpointSlice ,
98+ r .listApisixRoutesForService ,
99+ r .listApisixRoutesForEndpoints ,
100+ r .Log )
101+
102+ return bdr .
88103 Watches (& corev1.Secret {},
89104 handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForSecret ),
90105 ).
@@ -341,23 +356,46 @@ func (r *ApisixRouteReconciler) validateBackends(ctx context.Context, tc *provid
341356 }
342357 tc .Services [serviceNN ] = & service
343358
344- var endpoints discoveryv1.EndpointSliceList
345- if err := r .List (ctx , & endpoints ,
346- client .InNamespace (service .Namespace ),
347- client.MatchingLabels {
348- discoveryv1 .LabelServiceName : service .Name ,
349- },
350- ); err != nil {
351- return types.ReasonError {
352- Reason : string (apiv2 .ConditionReasonInvalidSpec ),
353- Message : fmt .Sprintf ("failed to list endpoint slices: %v" , err ),
359+ // Conditionally collect EndpointSlice or Endpoints based on cluster API support
360+ if r .supportsEndpointSlice {
361+ var endpoints discoveryv1.EndpointSliceList
362+ if err := r .List (ctx , & endpoints ,
363+ client .InNamespace (service .Namespace ),
364+ client.MatchingLabels {
365+ discoveryv1 .LabelServiceName : service .Name ,
366+ },
367+ ); err != nil {
368+ return types.ReasonError {
369+ Reason : string (apiv2 .ConditionReasonInvalidSpec ),
370+ Message : fmt .Sprintf ("failed to list endpoint slices: %v" , err ),
371+ }
354372 }
355- }
356373
357- // backend.subset specifies a subset of upstream nodes.
358- // It specifies that the target pod's label should be a superset of the subset labels of the ApisixUpstream of the serviceName
359- subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
360- tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , endpoints .Items , subsetLabels )
374+ // backend.subset specifies a subset of upstream nodes.
375+ // It specifies that the target pod's label should be a superset of the subset labels of the ApisixUpstream of the serviceName
376+ subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
377+ tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , endpoints .Items , subsetLabels )
378+ } else {
379+ // Fallback to Endpoints API for Kubernetes 1.18 compatibility
380+ var ep corev1.Endpoints
381+ if err := r .Get (ctx , serviceNN , & ep ); err != nil {
382+ if client .IgnoreNotFound (err ) != nil {
383+ return types.ReasonError {
384+ Reason : string (apiv2 .ConditionReasonInvalidSpec ),
385+ Message : fmt .Sprintf ("failed to get endpoints: %v" , err ),
386+ }
387+ }
388+ // If endpoints not found, create empty EndpointSlice list
389+ tc .EndpointSlices [serviceNN ] = []discoveryv1.EndpointSlice {}
390+ } else {
391+ // Convert Endpoints to EndpointSlice format for internal consistency
392+ convertedEndpointSlices := pkgutils .ConvertEndpointsToEndpointSlice (& ep )
393+
394+ // Apply subset filtering to converted EndpointSlices
395+ subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
396+ tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , convertedEndpointSlices , subsetLabels )
397+ }
398+ }
361399 }
362400
363401 return nil
@@ -443,6 +481,32 @@ func (r *ApisixRouteReconciler) listApisixRoutesForService(ctx context.Context,
443481 return pkgutils .DedupComparable (requests )
444482}
445483
484+ // listApisixRoutesForEndpoints handles Endpoints objects and converts them to ApisixRoute reconcile requests.
485+ // This function provides backward compatibility for Kubernetes 1.18 clusters that don't support EndpointSlice.
486+ func (r * ApisixRouteReconciler ) listApisixRoutesForEndpoints (ctx context.Context , obj client.Object ) []reconcile.Request {
487+ endpoint , ok := obj .(* corev1.Endpoints )
488+ if ! ok {
489+ return nil
490+ }
491+
492+ var (
493+ namespace = endpoint .GetNamespace ()
494+ serviceName = endpoint .GetName () // For Endpoints, the name is the service name
495+ arList apiv2.ApisixRouteList
496+ )
497+ if err := r .List (ctx , & arList , client.MatchingFields {
498+ indexer .ServiceIndexRef : indexer .GenIndexKey (namespace , serviceName ),
499+ }); err != nil {
500+ r .Log .Error (err , "failed to list apisixroutes by service" , "service" , serviceName )
501+ return nil
502+ }
503+ requests := make ([]reconcile.Request , 0 , len (arList .Items ))
504+ for _ , ar := range arList .Items {
505+ requests = append (requests , reconcile.Request {NamespacedName : utils .NamespacedName (& ar )})
506+ }
507+ return pkgutils .DedupComparable (requests )
508+ }
509+
446510func (r * ApisixRouteReconciler ) listApisixRoutesForSecret (ctx context.Context , obj client.Object ) []reconcile.Request {
447511 secret , ok := obj .(* corev1.Secret )
448512 if ! ok {
0 commit comments