Skip to content

Commit 93583e9

Browse files
committed
feat: enhance subset support for ApisixUpstream and update validations, RBAC, and e2e tests
1 parent 12f72c0 commit 93583e9

File tree

9 files changed

+75
-21
lines changed

9 files changed

+75
-21
lines changed

api/v2/apisixupstream_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
)
2020

2121
// ApisixUpstreamSpec describes the specification of ApisixUpstream.
22-
// +kubebuilder:validation:XValidation:rule="has(self.externalNodes)!=has(self.discovery)"
22+
// +kubebuilder:validation:XValidation:rule="has(self.subsets) || (has(self.externalNodes)!=has(self.discovery))"
2323
type ApisixUpstreamSpec struct {
2424
// IngressClassName is the name of an IngressClass cluster resource.
2525
// controller implementations use this field to know whether they should be

config/crd/bases/apisix.apache.org_apisixupstreams.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ spec:
562562
type: string
563563
type: object
564564
x-kubernetes-validations:
565-
- rule: has(self.externalNodes)!=has(self.discovery)
565+
- rule: has(self.subsets) || (has(self.externalNodes)!=has(self.discovery))
566566
status:
567567
description: ApisixStatus is the status report for Apisix ingress Resources
568568
properties:

config/rbac/role.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rules:
1515
- ""
1616
resources:
1717
- namespaces
18+
- pods
1819
- secrets
1920
- services
2021
verbs:

internal/controller/apisixroute_controller.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,9 +350,8 @@ func (r *ApisixRouteReconciler) validateBackends(ctx context.Context, tc *provid
350350
if err != nil {
351351
return err
352352
}
353-
endpoints.Items = r.filterEndpointSlicesBySubsetLabels(ctx, endpoints.Items, subsetLabels)
354353

355-
tc.EndpointSlices[serviceNN] = endpoints.Items
354+
tc.EndpointSlices[serviceNN] = r.filterEndpointSlicesBySubsetLabels(ctx, endpoints.Items, subsetLabels)
356355
}
357356

358357
return nil
@@ -714,7 +713,7 @@ func (r *ApisixRouteReconciler) getSubsetLabels(ctx context.Context, ar *apiv2.A
714713
return nil, err
715714
}
716715

717-
// try tro get the subset labels from the ApisixUpstream subsets
716+
// try to get the subset labels from the ApisixUpstream subsets
718717
for _, subset := range au.Spec.Subsets {
719718
if backend.Subset == subset.Name {
720719
return subset.Labels, nil

internal/controller/indexer/indexer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,11 @@ func ApisixRouteSecretIndexFunc(cli client.Client) func(client.Object) []string
527527
func ApisixRouteApisixUpstreamIndexFunc(obj client.Object) (keys []string) {
528528
ar := obj.(*apiv2.ApisixRoute)
529529
for _, rule := range ar.Spec.HTTP {
530+
for _, backend := range rule.Backends {
531+
if backend.Subset != "" && backend.ServiceName != "" {
532+
keys = append(keys, GenIndexKey(ar.GetNamespace(), backend.ServiceName))
533+
}
534+
}
530535
for _, upstream := range rule.Upstreams {
531536
if upstream.Name != "" {
532537
keys = append(keys, GenIndexKey(ar.GetNamespace(), upstream.Name))

internal/manager/controllers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update;patch;delete
3030
// +kubebuilder:rbac:groups="discovery.k8s.io",resources=endpointslices,verbs=get;list;watch
3131
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch
32+
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
3233
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
3334
// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch
3435

test/e2e/apisix/route.go

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,67 @@ spec:
291291
s.NewAPISIXClient().GET("/get").Expect().Header("X-Upstream-IP").IsEqual(clusterIP)
292292
})
293293

294-
PIt("Test ApisixRoute subset", func() {
295-
// route.Spec.HTTP[].Backends[].Subset depends on ApisixUpstream.
296-
// ApisixUpstream is not implemented yet.
297-
// So the case is pending for now
294+
It("Test ApisixRoute subset", func() {
295+
const apisixRouteSpec = `
296+
apiVersion: apisix.apache.org/v2
297+
kind: ApisixRoute
298+
metadata:
299+
name: default
300+
spec:
301+
ingressClassName: apisix
302+
http:
303+
- name: rule0
304+
match:
305+
hosts:
306+
- httpbin
307+
paths:
308+
- /*
309+
backends:
310+
- serviceName: httpbin-service-e2e-test
311+
servicePort: 80
312+
subset: test-subset
313+
`
314+
const apisixUpstreamSpec0 = `
315+
apiVersion: apisix.apache.org/v2
316+
kind: ApisixUpstream
317+
metadata:
318+
name: httpbin-service-e2e-test
319+
spec:
320+
ingressClassName: apisix
321+
subsets:
322+
- name: test-subset
323+
labels:
324+
unknown-key: unknown-value
325+
`
326+
const apisixUpstreamSpec1 = `
327+
apiVersion: apisix.apache.org/v2
328+
kind: ApisixUpstream
329+
metadata:
330+
name: httpbin-service-e2e-test
331+
spec:
332+
ingressClassName: apisix
333+
subsets:
334+
- name: test-subset
335+
labels:
336+
app: httpbin-deployment-e2e-test
337+
`
338+
request := func() int {
339+
return s.NewAPISIXClient().GET("/get").WithHost("httpbin").Expect().Raw().StatusCode
340+
}
341+
By("apply ApisixRoute")
342+
var apisixRoute apiv2.ApisixRoute
343+
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apisixRoute, apisixRouteSpec)
344+
Eventually(request).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK))
345+
346+
// no pod matches the subset label "unknown-key: unknown-value" so there will be no node in the upstream,
347+
// to request the route will get http.StatusServiceUnavailable
348+
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-service-e2e-test"}, new(apiv2.ApisixUpstream), apisixUpstreamSpec0)
349+
Eventually(request).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusServiceUnavailable))
350+
351+
// the pod matches the subset label "app: httpbin-deployment-e2e-test",
352+
// to request the route will be OK
353+
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-service-e2e-test"}, new(apiv2.ApisixUpstream), apisixUpstreamSpec1)
354+
Eventually(request).WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK))
298355
})
299356
})
300357

test/e2e/apisix/upstream.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,12 @@ spec:
5353
`
5454
err = s.CreateResourceFromString(apisixUpstreamSpec0)
5555
Expect(err).Should(HaveOccurred())
56-
Expect(err.Error()).Should(ContainSubstring("failed rule: has(self.externalNodes)!=has(self.discovery)"))
56+
Expect(err.Error()).Should(ContainSubstring("has(self.externalNodes)!=has(self.discovery)"))
5757

5858
err = s.CreateResourceFromString(apisixUpstreamSpec1)
5959
Expect(err).Should(HaveOccurred())
60-
Expect(err.Error()).Should(ContainSubstring("failed rule: has(self.externalNodes)!=has(self.discovery)"))
60+
Expect(err.Error()).Should(ContainSubstring("has(self.externalNodes)!=has(self.discovery)"))
6161

6262
})
63-
64-
It("", func() {})
65-
66-
It("", func() {})
67-
68-
It("", func() {})
69-
70-
It("", func() {})
71-
72-
It("", func() {})
7363
})
7464
})

test/e2e/framework/manifests/ingress.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ rules:
8282
- ""
8383
resources:
8484
- services
85+
- pods
8586
verbs:
8687
- get
8788
- list

0 commit comments

Comments
 (0)