Skip to content

Commit 5c464f3

Browse files
committed
Merge remote-tracking branch 'origin/release-v2-dev' into fix/list_matching
2 parents 4db8719 + 33d3cac commit 5c464f3

File tree

13 files changed

+1070
-108
lines changed

13 files changed

+1070
-108
lines changed

config/rbac/role.yaml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,7 @@ rules:
77
- apiGroups:
88
- ""
99
resources:
10-
- events
11-
verbs:
12-
- create
13-
- patch
14-
- apiGroups:
15-
- ""
16-
resources:
10+
- endpoints
1711
- namespaces
1812
- pods
1913
- secrets
@@ -22,6 +16,13 @@ rules:
2216
- get
2317
- list
2418
- watch
19+
- apiGroups:
20+
- ""
21+
resources:
22+
- events
23+
verbs:
24+
- create
25+
- patch
2526
- apiGroups:
2627
- apisix.apache.org
2728
resources:

internal/controller/apisixroute_controller.go

Lines changed: 91 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
6568
func (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+
446510
func (r *ApisixRouteReconciler) listApisixRoutesForSecret(ctx context.Context, obj client.Object) []reconcile.Request {
447511
secret, ok := obj.(*corev1.Secret)
448512
if !ok {

internal/controller/gatewayproxy_controller.go

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/apache/apisix-ingress-controller/internal/controller/indexer"
3939
"github.com/apache/apisix-ingress-controller/internal/provider"
4040
"github.com/apache/apisix-ingress-controller/internal/utils"
41+
pkgutils "github.com/apache/apisix-ingress-controller/pkg/utils"
4142
)
4243

4344
// GatewayProxyController reconciles a GatewayProxy object.
@@ -47,23 +48,38 @@ type GatewayProxyController struct {
4748
Scheme *runtime.Scheme
4849
Log logr.Logger
4950
Provider provider.Provider
51+
52+
// supportsEndpointSlice indicates whether the cluster supports EndpointSlice API
53+
supportsEndpointSlice bool
5054
}
5155

5256
func (r *GatewayProxyController) SetupWithManager(mrg ctrl.Manager) error {
53-
return ctrl.NewControllerManagedBy(mrg).
57+
// Check and store EndpointSlice API support
58+
r.supportsEndpointSlice = pkgutils.HasAPIResource(mrg, &discoveryv1.EndpointSlice{})
59+
60+
eventFilters := []predicate.Predicate{
61+
predicate.GenerationChangedPredicate{},
62+
predicate.NewPredicateFuncs(TypePredicate[*corev1.Secret]()),
63+
}
64+
65+
if !r.supportsEndpointSlice {
66+
eventFilters = append(eventFilters, predicate.NewPredicateFuncs(TypePredicate[*corev1.Endpoints]()))
67+
}
68+
69+
bdr := ctrl.NewControllerManagedBy(mrg).
5470
For(&v1alpha1.GatewayProxy{}).
55-
WithEventFilter(
56-
predicate.Or(
57-
predicate.GenerationChangedPredicate{},
58-
predicate.NewPredicateFuncs(TypePredicate[*corev1.Secret]()),
59-
),
60-
).
71+
WithEventFilter(predicate.Or(eventFilters...)).
6172
Watches(&corev1.Service{},
6273
handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForProviderService),
63-
).
64-
Watches(&discoveryv1.EndpointSlice{},
65-
handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForProviderEndpointSlice),
66-
).
74+
)
75+
76+
// Conditionally watch EndpointSlice or Endpoints based on cluster API support
77+
bdr = watchEndpointSliceOrEndpoints(bdr, r.supportsEndpointSlice,
78+
r.listGatewayProxiesForProviderEndpointSlice,
79+
r.listGatewayProxiesForProviderEndpoints,
80+
r.Log)
81+
82+
return bdr.
6783
Watches(&corev1.Secret{},
6884
handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForSecret),
6985
).
@@ -93,10 +109,10 @@ func (r *GatewayProxyController) Reconcile(ctx context.Context, req ctrl.Request
93109
if providerService == nil {
94110
tctx.EndpointSlices[req.NamespacedName] = nil
95111
} else {
96-
if err := addProviderEndpointsToTranslateContext(tctx, r.Client, types.NamespacedName{
112+
if err := addProviderEndpointsToTranslateContextWithEndpointSliceSupport(tctx, r.Client, types.NamespacedName{
97113
Namespace: gp.Namespace,
98114
Name: providerService.Name,
99-
}); err != nil {
115+
}, r.supportsEndpointSlice); err != nil {
100116
return reconcile.Result{}, err
101117
}
102118
}
@@ -174,6 +190,20 @@ func (r *GatewayProxyController) listGatewayProxiesForProviderEndpointSlice(ctx
174190
})
175191
}
176192

193+
// listGatewayProxiesForProviderEndpoints handles Endpoints objects and converts them to GatewayProxy reconcile requests.
194+
// This function provides backward compatibility for Kubernetes 1.18 clusters that don't support EndpointSlice.
195+
func (r *GatewayProxyController) listGatewayProxiesForProviderEndpoints(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
196+
endpoint, ok := obj.(*corev1.Endpoints)
197+
if !ok {
198+
r.Log.Error(errors.New("unexpected object type"), "failed to convert object to Endpoints")
199+
return nil
200+
}
201+
202+
return ListRequests(ctx, r.Client, r.Log, &v1alpha1.GatewayProxyList{}, client.MatchingFields{
203+
indexer.ServiceIndexRef: indexer.GenIndexKey(endpoint.GetNamespace(), endpoint.GetName()),
204+
})
205+
}
206+
177207
func (r *GatewayProxyController) listGatewayProxiesForSecret(ctx context.Context, object client.Object) []reconcile.Request {
178208
secret, ok := object.(*corev1.Secret)
179209
if !ok {

0 commit comments

Comments
 (0)