@@ -59,11 +59,17 @@ 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+ bdr := ctrl .NewControllerManagedBy (mgr ).
6773 For (& apiv2.ApisixRoute {}).
6874 WithEventFilter (
6975 predicate .Or (
@@ -81,10 +87,21 @@ func (r *ApisixRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
8187 ).
8288 Watches (& v1alpha1.GatewayProxy {},
8389 handler .EnqueueRequestsFromMapFunc (r .listApisixRouteForGatewayProxy ),
84- ).
85- Watches (& discoveryv1.EndpointSlice {},
90+ )
91+
92+ // Conditionally watch EndpointSlice or Endpoints based on cluster API support
93+ if r .supportsEndpointSlice {
94+ bdr = bdr .Watches (& discoveryv1.EndpointSlice {},
8695 handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForService ),
87- ).
96+ )
97+ } else {
98+ r .Log .Info ("EndpointSlice API not available, falling back to Endpoints API for service discovery" )
99+ bdr = bdr .Watches (& corev1.Endpoints {},
100+ handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForEndpoints ),
101+ )
102+ }
103+
104+ return bdr .
88105 Watches (& corev1.Secret {},
89106 handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForSecret ),
90107 ).
@@ -341,23 +358,46 @@ func (r *ApisixRouteReconciler) validateBackends(ctx context.Context, tc *provid
341358 }
342359 tc .Services [serviceNN ] = & service
343360
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 ),
361+ // Conditionally collect EndpointSlice or Endpoints based on cluster API support
362+ if r .supportsEndpointSlice {
363+ var endpoints discoveryv1.EndpointSliceList
364+ if err := r .List (ctx , & endpoints ,
365+ client .InNamespace (service .Namespace ),
366+ client.MatchingLabels {
367+ discoveryv1 .LabelServiceName : service .Name ,
368+ },
369+ ); err != nil {
370+ return types.ReasonError {
371+ Reason : string (apiv2 .ConditionReasonInvalidSpec ),
372+ Message : fmt .Sprintf ("failed to list endpoint slices: %v" , err ),
373+ }
354374 }
355- }
356375
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 )
376+ // backend.subset specifies a subset of upstream nodes.
377+ // It specifies that the target pod's label should be a superset of the subset labels of the ApisixUpstream of the serviceName
378+ subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
379+ tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , endpoints .Items , subsetLabels )
380+ } else {
381+ // Fallback to Endpoints API for Kubernetes 1.18 compatibility
382+ var ep corev1.Endpoints
383+ if err := r .Get (ctx , serviceNN , & ep ); err != nil {
384+ if client .IgnoreNotFound (err ) != nil {
385+ return types.ReasonError {
386+ Reason : string (apiv2 .ConditionReasonInvalidSpec ),
387+ Message : fmt .Sprintf ("failed to get endpoints: %v" , err ),
388+ }
389+ }
390+ // If endpoints not found, create empty EndpointSlice list
391+ tc .EndpointSlices [serviceNN ] = []discoveryv1.EndpointSlice {}
392+ } else {
393+ // Convert Endpoints to EndpointSlice format for internal consistency
394+ convertedEndpointSlices := pkgutils .ConvertEndpointsToEndpointSlice (& ep )
395+
396+ // Apply subset filtering to converted EndpointSlices
397+ subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
398+ tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , convertedEndpointSlices , subsetLabels )
399+ }
400+ }
361401 }
362402
363403 return nil
@@ -443,6 +483,32 @@ func (r *ApisixRouteReconciler) listApisixRoutesForService(ctx context.Context,
443483 return pkgutils .DedupComparable (requests )
444484}
445485
486+ // listApisixRoutesForEndpoints handles Endpoints objects and converts them to ApisixRoute reconcile requests.
487+ // This function provides backward compatibility for Kubernetes 1.18 clusters that don't support EndpointSlice.
488+ func (r * ApisixRouteReconciler ) listApisixRoutesForEndpoints (ctx context.Context , obj client.Object ) []reconcile.Request {
489+ endpoint , ok := obj .(* corev1.Endpoints )
490+ if ! ok {
491+ return nil
492+ }
493+
494+ var (
495+ namespace = endpoint .GetNamespace ()
496+ serviceName = endpoint .GetName () // For Endpoints, the name is the service name
497+ arList apiv2.ApisixRouteList
498+ )
499+ if err := r .List (ctx , & arList , client.MatchingFields {
500+ indexer .ServiceIndexRef : indexer .GenIndexKey (namespace , serviceName ),
501+ }); err != nil {
502+ r .Log .Error (err , "failed to list apisixroutes by service" , "service" , serviceName )
503+ return nil
504+ }
505+ requests := make ([]reconcile.Request , 0 , len (arList .Items ))
506+ for _ , ar := range arList .Items {
507+ requests = append (requests , reconcile.Request {NamespacedName : utils .NamespacedName (& ar )})
508+ }
509+ return pkgutils .DedupComparable (requests )
510+ }
511+
446512func (r * ApisixRouteReconciler ) listApisixRoutesForSecret (ctx context.Context , obj client.Object ) []reconcile.Request {
447513 secret , ok := obj .(* corev1.Secret )
448514 if ! ok {
0 commit comments