@@ -64,16 +64,22 @@ type ApisixRouteReconciler struct {
6464 Readier readiness.ReadinessManager
6565
6666 ICGVK schema.GroupVersionKind
67+ // supportsEndpointSlice indicates whether the cluster supports EndpointSlice API
68+ supportsEndpointSlice bool
6769}
6870
6971// SetupWithManager sets up the controller with the Manager.
7072func (r * ApisixRouteReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
71- return ctrl .NewControllerManagedBy (mgr ).
73+ // Check and store EndpointSlice API support
74+ r .supportsEndpointSlice = pkgutils .HasAPIResource (mgr , & discoveryv1.EndpointSlice {})
75+
76+ bdr := ctrl .NewControllerManagedBy (mgr ).
7277 For (& apiv2.ApisixRoute {}).
7378 WithEventFilter (
7479 predicate .Or (
7580 predicate.GenerationChangedPredicate {},
7681 predicate.AnnotationChangedPredicate {},
82+ predicate .NewPredicateFuncs (TypePredicate [* corev1.Endpoints ]()),
7783 predicate .NewPredicateFuncs (TypePredicate [* corev1.Secret ]()),
7884 ),
7985 ).
@@ -86,10 +92,21 @@ func (r *ApisixRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
8692 ).
8793 Watches (& v1alpha1.GatewayProxy {},
8894 handler .EnqueueRequestsFromMapFunc (r .listApisixRouteForGatewayProxy ),
89- ).
90- Watches (& discoveryv1.EndpointSlice {},
95+ )
96+
97+ // Conditionally watch EndpointSlice or Endpoints based on cluster API support
98+ if r .supportsEndpointSlice {
99+ bdr = bdr .Watches (& discoveryv1.EndpointSlice {},
91100 handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForService ),
92- ).
101+ )
102+ } else {
103+ r .Log .Info ("EndpointSlice API not available, falling back to Endpoints API for service discovery" )
104+ bdr = bdr .Watches (& corev1.Endpoints {},
105+ handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForEndpoints ),
106+ )
107+ }
108+
109+ return bdr .
93110 Watches (& corev1.Secret {},
94111 handler .EnqueueRequestsFromMapFunc (r .listApisixRoutesForSecret ),
95112 ).
@@ -348,23 +365,46 @@ func (r *ApisixRouteReconciler) validateBackends(ctx context.Context, tc *provid
348365 }
349366 tc .Services [serviceNN ] = & service
350367
351- var endpoints discoveryv1.EndpointSliceList
352- if err := r .List (ctx , & endpoints ,
353- client .InNamespace (service .Namespace ),
354- client.MatchingLabels {
355- discoveryv1 .LabelServiceName : service .Name ,
356- },
357- ); err != nil {
358- return types.ReasonError {
359- Reason : string (apiv2 .ConditionReasonInvalidSpec ),
360- Message : fmt .Sprintf ("failed to list endpoint slices: %v" , err ),
368+ // Conditionally collect EndpointSlice or Endpoints based on cluster API support
369+ if r .supportsEndpointSlice {
370+ var endpoints discoveryv1.EndpointSliceList
371+ if err := r .List (ctx , & endpoints ,
372+ client .InNamespace (service .Namespace ),
373+ client.MatchingLabels {
374+ discoveryv1 .LabelServiceName : service .Name ,
375+ },
376+ ); err != nil {
377+ return types.ReasonError {
378+ Reason : string (apiv2 .ConditionReasonInvalidSpec ),
379+ Message : fmt .Sprintf ("failed to list endpoint slices: %v" , err ),
380+ }
361381 }
362- }
363382
364- // backend.subset specifies a subset of upstream nodes.
365- // It specifies that the target pod's label should be a superset of the subset labels of the ApisixUpstream of the serviceName
366- subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
367- tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , endpoints .Items , subsetLabels )
383+ // backend.subset specifies a subset of upstream nodes.
384+ // It specifies that the target pod's label should be a superset of the subset labels of the ApisixUpstream of the serviceName
385+ subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
386+ tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , endpoints .Items , subsetLabels )
387+ } else {
388+ // Fallback to Endpoints API for Kubernetes 1.18 compatibility
389+ var ep corev1.Endpoints
390+ if err := r .Get (ctx , serviceNN , & ep ); err != nil {
391+ if client .IgnoreNotFound (err ) != nil {
392+ return types.ReasonError {
393+ Reason : string (apiv2 .ConditionReasonInvalidSpec ),
394+ Message : fmt .Sprintf ("failed to get endpoints: %v" , err ),
395+ }
396+ }
397+ // If endpoints not found, create empty EndpointSlice list
398+ tc .EndpointSlices [serviceNN ] = []discoveryv1.EndpointSlice {}
399+ } else {
400+ // Convert Endpoints to EndpointSlice format for internal consistency
401+ convertedEndpointSlices := pkgutils .ConvertEndpointsToEndpointSlice (& ep )
402+
403+ // Apply subset filtering to converted EndpointSlices
404+ subsetLabels := r .getSubsetLabels (tc , serviceNN , backend )
405+ tc .EndpointSlices [serviceNN ] = r .filterEndpointSlicesBySubsetLabels (ctx , convertedEndpointSlices , subsetLabels )
406+ }
407+ }
368408 }
369409
370410 return nil
@@ -450,6 +490,32 @@ func (r *ApisixRouteReconciler) listApisixRoutesForService(ctx context.Context,
450490 return pkgutils .DedupComparable (requests )
451491}
452492
493+ // listApisixRoutesForEndpoints handles Endpoints objects and converts them to ApisixRoute reconcile requests.
494+ // This function provides backward compatibility for Kubernetes 1.18 clusters that don't support EndpointSlice.
495+ func (r * ApisixRouteReconciler ) listApisixRoutesForEndpoints (ctx context.Context , obj client.Object ) []reconcile.Request {
496+ endpoint , ok := obj .(* corev1.Endpoints )
497+ if ! ok {
498+ return nil
499+ }
500+
501+ var (
502+ namespace = endpoint .GetNamespace ()
503+ serviceName = endpoint .GetName () // For Endpoints, the name is the service name
504+ arList apiv2.ApisixRouteList
505+ )
506+ if err := r .List (ctx , & arList , client.MatchingFields {
507+ indexer .ServiceIndexRef : indexer .GenIndexKey (namespace , serviceName ),
508+ }); err != nil {
509+ r .Log .Error (err , "failed to list apisixroutes by service" , "service" , serviceName )
510+ return nil
511+ }
512+ requests := make ([]reconcile.Request , 0 , len (arList .Items ))
513+ for _ , ar := range arList .Items {
514+ requests = append (requests , reconcile.Request {NamespacedName : utils .NamespacedName (& ar )})
515+ }
516+ return pkgutils .DedupComparable (requests )
517+ }
518+
453519func (r * ApisixRouteReconciler ) listApisixRoutesForSecret (ctx context.Context , obj client.Object ) []reconcile.Request {
454520 secret , ok := obj .(* corev1.Secret )
455521 if ! ok {
0 commit comments