Skip to content

Commit 261bfc9

Browse files
AlinsRanCopilot
andcommitted
chore: fix CI, generate CRD, add L4 route translator tests
- Generate config/crd/bases/apisix.apache.org_l4routepolicies.yaml via make manifests - Regenerate config/rbac/role.yaml with l4routepolicies RBAC entries - Regenerate api/v2/zz_generated.deepcopy.go via make generate - Fix pre-existing go vet error in apisixconsumer_test.go: AuthParameter is a pointer field - Fix import ordering in indexer.go (goimports-reviser) - Fix prealloc lint warnings in tcproute/udproute/tlsroute controllers - Add translator-level tests: TestTranslateTCPRouteWithL4RoutePolicy, TestTranslateUDPRouteWithL4RoutePolicy, TestTranslateTLSRouteWithL4RoutePolicy Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 242ce9c commit 261bfc9

9 files changed

Lines changed: 712 additions & 19 deletions

File tree

api/v2/zz_generated.deepcopy.go

Lines changed: 15 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 427 additions & 0 deletions
Large diffs are not rendered by default.

config/rbac/role.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ rules:
3636
- consumers
3737
- gatewayproxies
3838
- httproutepolicies
39+
- l4routepolicies
3940
- pluginconfigs
4041
verbs:
4142
- get
@@ -53,6 +54,7 @@ rules:
5354
- backendtrafficpolicies/status
5455
- consumers/status
5556
- httproutepolicies/status
57+
- l4routepolicies/status
5658
verbs:
5759
- get
5860
- update

internal/adc/translator/apisixconsumer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestTranslateApisixConsumer_UsesMetadataLabelsWithoutOverwritingControllerL
4949
},
5050
},
5151
Spec: apiv2.ApisixConsumerSpec{
52-
AuthParameter: apiv2.ApisixConsumerAuthParameter{
52+
AuthParameter: &apiv2.ApisixConsumerAuthParameter{
5353
BasicAuth: &apiv2.ApisixConsumerBasicAuth{
5454
Value: &apiv2.ApisixConsumerBasicAuthValue{
5555
Username: "demo",
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package translator
19+
20+
import (
21+
"context"
22+
"testing"
23+
24+
"github.com/go-logr/logr"
25+
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
k8stypes "k8s.io/apimachinery/pkg/types"
29+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
30+
31+
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
32+
"github.com/apache/apisix-ingress-controller/internal/provider"
33+
)
34+
35+
func TestTranslateTCPRouteWithL4RoutePolicy(t *testing.T) {
36+
tests := []struct {
37+
name string
38+
policy *v1alpha1.L4RoutePolicy
39+
wantPlugins []string
40+
wantNoPlugins bool
41+
}{
42+
{
43+
name: "attaches plugins from matching L4RoutePolicy",
44+
policy: makeL4RoutePolicy("default", "tcp-policy", "TCPRoute", "my-tcp", []v1alpha1.Plugin{
45+
{Name: "limit-conn", Config: mustJSON(map[string]any{"conn": 100})},
46+
{Name: "ip-restriction", Config: mustJSON(map[string]any{"whitelist": []string{"10.0.0.0/8"}})},
47+
}),
48+
wantPlugins: []string{"limit-conn", "ip-restriction"},
49+
},
50+
{
51+
name: "does not attach plugins from policy targeting different route kind",
52+
policy: makeL4RoutePolicy("default", "udp-policy", "UDPRoute", "my-tcp", []v1alpha1.Plugin{
53+
{Name: "limit-conn", Config: mustJSON(map[string]any{"conn": 100})},
54+
}),
55+
wantNoPlugins: true,
56+
},
57+
{
58+
name: "does not attach plugins from policy targeting different route name",
59+
policy: makeL4RoutePolicy("default", "tcp-policy", "TCPRoute", "other-tcp", []v1alpha1.Plugin{
60+
{Name: "limit-conn", Config: mustJSON(map[string]any{"conn": 100})},
61+
}),
62+
wantNoPlugins: true,
63+
},
64+
{
65+
name: "succeeds with no policy in context",
66+
policy: nil,
67+
wantNoPlugins: true,
68+
},
69+
}
70+
71+
for _, tt := range tests {
72+
t.Run(tt.name, func(t *testing.T) {
73+
translator := NewTranslator(logr.Discard())
74+
tctx := provider.NewDefaultTranslateContext(context.Background())
75+
76+
if tt.policy != nil {
77+
key := k8stypes.NamespacedName{Namespace: tt.policy.Namespace, Name: tt.policy.Name}
78+
tctx.L4RoutePolicies[key] = tt.policy
79+
}
80+
81+
route := &gatewayv1alpha2.TCPRoute{
82+
ObjectMeta: metav1.ObjectMeta{
83+
Name: "my-tcp",
84+
Namespace: "default",
85+
},
86+
Spec: gatewayv1alpha2.TCPRouteSpec{
87+
Rules: []gatewayv1alpha2.TCPRouteRule{
88+
{BackendRefs: []gatewayv1alpha2.BackendRef{}},
89+
},
90+
},
91+
}
92+
93+
result, err := translator.TranslateTCPRoute(tctx, route)
94+
require.NoError(t, err)
95+
require.Len(t, result.Services, 1)
96+
97+
plugins := result.Services[0].Plugins
98+
if tt.wantNoPlugins {
99+
assert.Empty(t, plugins)
100+
} else {
101+
for _, name := range tt.wantPlugins {
102+
assert.Contains(t, plugins, name, "expected plugin %q to be attached", name)
103+
}
104+
}
105+
})
106+
}
107+
}
108+
109+
func TestTranslateUDPRouteWithL4RoutePolicy(t *testing.T) {
110+
tests := []struct {
111+
name string
112+
policy *v1alpha1.L4RoutePolicy
113+
wantPlugins []string
114+
wantNoPlugins bool
115+
}{
116+
{
117+
name: "attaches plugins from matching L4RoutePolicy",
118+
policy: makeL4RoutePolicy("default", "udp-policy", "UDPRoute", "my-udp", []v1alpha1.Plugin{
119+
{Name: "limit-conn", Config: mustJSON(map[string]any{"conn": 50})},
120+
}),
121+
wantPlugins: []string{"limit-conn"},
122+
},
123+
{
124+
name: "does not attach plugins from policy targeting TCPRoute",
125+
policy: makeL4RoutePolicy("default", "tcp-policy", "TCPRoute", "my-udp", []v1alpha1.Plugin{
126+
{Name: "limit-conn", Config: mustJSON(map[string]any{"conn": 50})},
127+
}),
128+
wantNoPlugins: true,
129+
},
130+
{
131+
name: "succeeds with no policy in context",
132+
policy: nil,
133+
wantNoPlugins: true,
134+
},
135+
}
136+
137+
for _, tt := range tests {
138+
t.Run(tt.name, func(t *testing.T) {
139+
translator := NewTranslator(logr.Discard())
140+
tctx := provider.NewDefaultTranslateContext(context.Background())
141+
142+
if tt.policy != nil {
143+
key := k8stypes.NamespacedName{Namespace: tt.policy.Namespace, Name: tt.policy.Name}
144+
tctx.L4RoutePolicies[key] = tt.policy
145+
}
146+
147+
route := &gatewayv1alpha2.UDPRoute{
148+
ObjectMeta: metav1.ObjectMeta{
149+
Name: "my-udp",
150+
Namespace: "default",
151+
},
152+
Spec: gatewayv1alpha2.UDPRouteSpec{
153+
Rules: []gatewayv1alpha2.UDPRouteRule{
154+
{BackendRefs: []gatewayv1alpha2.BackendRef{}},
155+
},
156+
},
157+
}
158+
159+
result, err := translator.TranslateUDPRoute(tctx, route)
160+
require.NoError(t, err)
161+
require.Len(t, result.Services, 1)
162+
163+
plugins := result.Services[0].Plugins
164+
if tt.wantNoPlugins {
165+
assert.Empty(t, plugins)
166+
} else {
167+
for _, name := range tt.wantPlugins {
168+
assert.Contains(t, plugins, name, "expected plugin %q to be attached", name)
169+
}
170+
}
171+
})
172+
}
173+
}
174+
175+
func TestTranslateTLSRouteWithL4RoutePolicy(t *testing.T) {
176+
tests := []struct {
177+
name string
178+
policy *v1alpha1.L4RoutePolicy
179+
hostnames []string
180+
wantPlugins []string
181+
wantNoPlugins bool
182+
}{
183+
{
184+
name: "attaches plugins from matching L4RoutePolicy",
185+
policy: makeL4RoutePolicy("default", "tls-policy", "TLSRoute", "my-tls", []v1alpha1.Plugin{
186+
{Name: "ip-restriction", Config: mustJSON(map[string]any{"whitelist": []string{"192.168.0.0/16"}})},
187+
}),
188+
hostnames: []string{"example.com"},
189+
wantPlugins: []string{"ip-restriction"},
190+
},
191+
{
192+
name: "plugins attached once per rule even with multiple SNI hostnames",
193+
policy: makeL4RoutePolicy("default", "tls-policy", "TLSRoute", "my-tls", []v1alpha1.Plugin{
194+
{Name: "limit-conn", Config: mustJSON(map[string]any{"conn": 20})},
195+
}),
196+
hostnames: []string{"foo.example.com", "bar.example.com"},
197+
wantPlugins: []string{"limit-conn"},
198+
},
199+
{
200+
name: "does not attach plugins from policy targeting TCPRoute",
201+
policy: makeL4RoutePolicy("default", "tcp-policy", "TCPRoute", "my-tls", []v1alpha1.Plugin{
202+
{Name: "limit-conn", Config: mustJSON(map[string]any{"conn": 20})},
203+
}),
204+
hostnames: []string{"example.com"},
205+
wantNoPlugins: true,
206+
},
207+
{
208+
name: "succeeds with no policy in context",
209+
policy: nil,
210+
hostnames: []string{"example.com"},
211+
wantNoPlugins: true,
212+
},
213+
}
214+
215+
for _, tt := range tests {
216+
t.Run(tt.name, func(t *testing.T) {
217+
translator := NewTranslator(logr.Discard())
218+
tctx := provider.NewDefaultTranslateContext(context.Background())
219+
220+
if tt.policy != nil {
221+
key := k8stypes.NamespacedName{Namespace: tt.policy.Namespace, Name: tt.policy.Name}
222+
tctx.L4RoutePolicies[key] = tt.policy
223+
}
224+
225+
hostnames := make([]gatewayv1alpha2.Hostname, 0, len(tt.hostnames))
226+
for _, h := range tt.hostnames {
227+
hostnames = append(hostnames, gatewayv1alpha2.Hostname(h))
228+
}
229+
230+
route := &gatewayv1alpha2.TLSRoute{
231+
ObjectMeta: metav1.ObjectMeta{
232+
Name: "my-tls",
233+
Namespace: "default",
234+
},
235+
Spec: gatewayv1alpha2.TLSRouteSpec{
236+
Hostnames: hostnames,
237+
Rules: []gatewayv1alpha2.TLSRouteRule{
238+
{BackendRefs: []gatewayv1alpha2.BackendRef{}},
239+
},
240+
},
241+
}
242+
243+
result, err := translator.TranslateTLSRoute(tctx, route)
244+
require.NoError(t, err)
245+
require.Len(t, result.Services, 1)
246+
247+
// Verify stream routes are created per SNI hostname
248+
if len(tt.hostnames) > 0 {
249+
assert.Len(t, result.Services[0].StreamRoutes, len(tt.hostnames))
250+
}
251+
252+
plugins := result.Services[0].Plugins
253+
if tt.wantNoPlugins {
254+
assert.Empty(t, plugins)
255+
} else {
256+
for _, name := range tt.wantPlugins {
257+
assert.Contains(t, plugins, name, "expected plugin %q to be attached", name)
258+
}
259+
// Plugins are on the service, not duplicated per stream route
260+
assert.Len(t, result.Services, 1, "plugins should be on service level, not duplicated")
261+
}
262+
})
263+
}
264+
}

internal/controller/indexer/indexer.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,6 @@ func L4RoutePolicyIndexFunc(rawObj client.Object) []string {
911911
return keys
912912
}
913913

914-
915914
func IngressClassParametersRefIndexFunc(rawObj client.Object) []string {
916915
ingressClass := rawObj.(*networkingv1.IngressClass)
917916
// check if the IngressClass references this gateway proxy

internal/controller/tcproute_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ func (r *TCPRouteReconciler) listTCPRoutesForL4RoutePolicy(ctx context.Context,
515515
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to L4RoutePolicy")
516516
return nil
517517
}
518-
var requests []reconcile.Request
518+
requests := make([]reconcile.Request, 0, len(policy.Spec.TargetRefs))
519519
seen := make(map[k8stypes.NamespacedName]struct{})
520520
for _, ref := range policy.Spec.TargetRefs {
521521
if string(ref.Kind) != KindTCPRoute {

internal/controller/tlsroute_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ func (r *TLSRouteReconciler) listTLSRoutesForL4RoutePolicy(ctx context.Context,
515515
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to L4RoutePolicy")
516516
return nil
517517
}
518-
var requests []reconcile.Request
518+
requests := make([]reconcile.Request, 0, len(policy.Spec.TargetRefs))
519519
seen := make(map[k8stypes.NamespacedName]struct{})
520520
for _, ref := range policy.Spec.TargetRefs {
521521
if string(ref.Kind) != types.KindTLSRoute {

internal/controller/udproute_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ func (r *UDPRouteReconciler) listUDPRoutesForL4RoutePolicy(ctx context.Context,
515515
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to L4RoutePolicy")
516516
return nil
517517
}
518-
var requests []reconcile.Request
518+
requests := make([]reconcile.Request, 0, len(policy.Spec.TargetRefs))
519519
seen := make(map[k8stypes.NamespacedName]struct{})
520520
for _, ref := range policy.Spec.TargetRefs {
521521
if string(ref.Kind) != KindUDPRoute {

0 commit comments

Comments
 (0)