Skip to content

Commit bff47bf

Browse files
committed
Bootstrap TPP reconciler tests - more to come.
1 parent 1cd4d2e commit bff47bf

3 files changed

Lines changed: 296 additions & 33 deletions

File tree

internal/controller/gateway_controller_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,12 @@ func TestEnsureHostnamesClaimed(t *testing.T) {
733733
}
734734
}
735735

736-
func newGateway(testConfig config.NetworkServicesOperator, namespace, name string, opts ...func(*gatewayv1.Gateway)) *gatewayv1.Gateway {
736+
func newGateway(
737+
testConfig config.NetworkServicesOperator,
738+
namespace,
739+
name string,
740+
opts ...func(*gatewayv1.Gateway),
741+
) *gatewayv1.Gateway {
737742
gw := &gatewayv1.Gateway{
738743
ObjectMeta: metav1.ObjectMeta{
739744
Namespace: namespace,

internal/controller/trafficprotectionpolicy_controller.go

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -83,27 +83,7 @@ func (r *TrafficProtectionPolicyReconciler) Reconcile(ctx context.Context, req N
8383
originalTrafficProtectionPolicies[client.ObjectKeyFromObject(&trafficProtectionPolicyList.Items[i]).String()] = trafficProtectionPolicyList.Items[i]
8484
}
8585

86-
trafficProtectionPolicies := make([]*policyContext, 0, len(trafficProtectionPolicyList.Items))
87-
for i, tpp := range trafficProtectionPolicyList.Items {
88-
if dt := tpp.DeletionTimestamp; dt != nil {
89-
continue
90-
}
91-
trafficProtectionPolicies = append(trafficProtectionPolicies, &policyContext{
92-
TrafficProtectionPolicy: trafficProtectionPolicyList.Items[i].DeepCopy(),
93-
})
94-
}
95-
96-
// Sort TrafficProtectionPolicies by creation timestamp, then namespace/name
97-
// Precedence aligns with Envoy Gateway's policy sorting.
98-
sort.Slice(trafficProtectionPolicies, func(i, j int) bool {
99-
if trafficProtectionPolicies[i].CreationTimestamp.Equal(&(trafficProtectionPolicies[j].CreationTimestamp)) {
100-
if trafficProtectionPolicies[i].Namespace != trafficProtectionPolicies[j].Namespace {
101-
return trafficProtectionPolicies[i].Namespace < trafficProtectionPolicies[j].Namespace
102-
}
103-
return trafficProtectionPolicies[i].Name < trafficProtectionPolicies[j].Name
104-
}
105-
return trafficProtectionPolicies[i].CreationTimestamp.Before(&(trafficProtectionPolicies[j].CreationTimestamp))
106-
})
86+
trafficProtectionPolicies := r.getTrafficProtectionPolicyContexts(trafficProtectionPolicyList.Items)
10787

10888
var upstreamGateways gatewayv1.GatewayList
10989
if err := cl.GetClient().List(ctx, &upstreamGateways, client.InNamespace(req.Namespace)); err != nil {
@@ -115,7 +95,7 @@ func (r *TrafficProtectionPolicyReconciler) Reconcile(ctx context.Context, req N
11595
return ctrl.Result{}, err
11696
}
11797

118-
attachments := r.collectTrafficProtectionPolicyAttachments(ctx, trafficProtectionPolicies, upstreamGateways, upstreamHTTPRoutes)
98+
attachments := r.collectTrafficProtectionPolicyAttachments(ctx, trafficProtectionPolicies, upstreamGateways.Items, upstreamHTTPRoutes.Items)
11999
desiredPolicies, err := r.getDesiredEnvoyPatchPolicies(downstreamNamespaceName, attachments)
120100
if err != nil {
121101
return ctrl.Result{}, err
@@ -168,6 +148,34 @@ func (r *TrafficProtectionPolicyReconciler) Reconcile(ctx context.Context, req N
168148
return ctrl.Result{}, nil
169149
}
170150

151+
func (r *TrafficProtectionPolicyReconciler) getTrafficProtectionPolicyContexts(
152+
policies []networkingv1alpha.TrafficProtectionPolicy,
153+
) []*policyContext {
154+
trafficProtectionPolicies := make([]*policyContext, 0, len(policies))
155+
for i, tpp := range policies {
156+
if dt := tpp.DeletionTimestamp; dt != nil {
157+
continue
158+
}
159+
trafficProtectionPolicies = append(trafficProtectionPolicies, &policyContext{
160+
TrafficProtectionPolicy: policies[i].DeepCopy(),
161+
})
162+
}
163+
164+
// Sort TrafficProtectionPolicies by creation timestamp, then namespace/name
165+
// Precedence aligns with Envoy Gateway's policy sorting.
166+
sort.Slice(trafficProtectionPolicies, func(i, j int) bool {
167+
if trafficProtectionPolicies[i].CreationTimestamp.Equal(&(trafficProtectionPolicies[j].CreationTimestamp)) {
168+
if trafficProtectionPolicies[i].Namespace != trafficProtectionPolicies[j].Namespace {
169+
return trafficProtectionPolicies[i].Namespace < trafficProtectionPolicies[j].Namespace
170+
}
171+
return trafficProtectionPolicies[i].Name < trafficProtectionPolicies[j].Name
172+
}
173+
return trafficProtectionPolicies[i].CreationTimestamp.Before(&(trafficProtectionPolicies[j].CreationTimestamp))
174+
})
175+
176+
return trafficProtectionPolicies
177+
}
178+
171179
func (r *TrafficProtectionPolicyReconciler) updateTPPAncestorsStatus(
172180
ctx context.Context,
173181
upstreamClient client.Client,
@@ -319,32 +327,32 @@ type policyRouteTargetContext struct {
319327
func (r *TrafficProtectionPolicyReconciler) collectTrafficProtectionPolicyAttachments(
320328
ctx context.Context,
321329
trafficProtectionPolicies []*policyContext,
322-
upstreamGateways gatewayv1.GatewayList,
323-
upstreamHTTPRoutes gatewayv1.HTTPRouteList,
330+
upstreamGateways []gatewayv1.Gateway,
331+
upstreamHTTPRoutes []gatewayv1.HTTPRoute,
324332
) []policyAttachment {
325333
logger := log.FromContext(ctx)
326334

327335
logger.Info(
328336
"processing traffic protection policies",
329337
"totalPolicies", len(trafficProtectionPolicies),
330-
"totalGateways", len(upstreamGateways.Items),
331-
"totalRoutes", len(upstreamHTTPRoutes.Items),
338+
"totalGateways", len(upstreamGateways),
339+
"totalRoutes", len(upstreamHTTPRoutes),
332340
)
333341

334-
routeMapSize := len(upstreamHTTPRoutes.Items)
335-
gatewayMapSize := len(upstreamGateways.Items)
342+
routeMapSize := len(upstreamHTTPRoutes)
343+
gatewayMapSize := len(upstreamGateways)
336344

337345
routeMap := make(map[client.ObjectKey]*policyRouteTargetContext, routeMapSize)
338-
for i, route := range upstreamHTTPRoutes.Items {
346+
for i, route := range upstreamHTTPRoutes {
339347
routeMap[client.ObjectKeyFromObject(&route)] = &policyRouteTargetContext{
340-
HTTPRoute: &upstreamHTTPRoutes.Items[i],
348+
HTTPRoute: &upstreamHTTPRoutes[i],
341349
}
342350
}
343351

344352
gatewayMap := make(map[client.ObjectKey]*policyGatewayTargetContext, gatewayMapSize)
345-
for i, gw := range upstreamGateways.Items {
353+
for i, gw := range upstreamGateways {
346354
gatewayMap[client.ObjectKeyFromObject(&gw)] = &policyGatewayTargetContext{
347-
Gateway: &upstreamGateways.Items[i],
355+
Gateway: &upstreamGateways[i],
348356
}
349357
}
350358

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package controller
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
"k8s.io/apimachinery/pkg/util/uuid"
9+
"k8s.io/utils/ptr"
10+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
11+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
12+
13+
networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha"
14+
"go.datum.net/network-services-operator/internal/config"
15+
gatewayutil "go.datum.net/network-services-operator/internal/util/gateway"
16+
)
17+
18+
func TestCollectTrafficProtectionPolicyAttachments(t *testing.T) {
19+
20+
operatorConfig := config.NetworkServicesOperator{
21+
Gateway: config.GatewayConfig{
22+
TargetDomain: "example.com",
23+
ListenerTLSOptions: map[gatewayv1.AnnotationKey]gatewayv1.AnnotationValue{
24+
gatewayv1.AnnotationKey("gateway.networking.datumapis.com/certificate-issuer"): gatewayv1.AnnotationValue("test"),
25+
},
26+
},
27+
}
28+
29+
newGatewayFunc := func(namespace, name string, opts ...func(*gatewayv1.Gateway)) gatewayv1.Gateway {
30+
return *newGateway(operatorConfig, namespace, name, opts...)
31+
}
32+
33+
type testContext struct {
34+
*testing.T
35+
reconciler *TrafficProtectionPolicyReconciler
36+
gateways []gatewayv1.Gateway
37+
httpRoutes []gatewayv1.HTTPRoute
38+
}
39+
40+
tests := []struct {
41+
name string
42+
gateways []gatewayv1.Gateway
43+
httpRoutes []gatewayv1.HTTPRoute
44+
trafficProtectionPolicies []networkingv1alpha.TrafficProtectionPolicy
45+
assert func(t *testContext, policyAttachments []policyAttachment)
46+
}{
47+
{
48+
name: "direct gateway attachment",
49+
gateways: []gatewayv1.Gateway{
50+
newGatewayFunc("default", "gateway-1"),
51+
},
52+
trafficProtectionPolicies: []networkingv1alpha.TrafficProtectionPolicy{
53+
newTrafficProtectionPolicy("default", "tpp-1", func(tpp *networkingv1alpha.TrafficProtectionPolicy) {
54+
tpp.Spec.TargetRefs = []gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName{
55+
{
56+
LocalPolicyTargetReference: gatewayv1alpha2.LocalPolicyTargetReference{
57+
Kind: "Gateway",
58+
Name: "gateway-1",
59+
},
60+
},
61+
}
62+
}),
63+
},
64+
assert: func(t *testContext, policyAttachments []policyAttachment) {
65+
if assert.Len(t, policyAttachments, 1, "expected one policy attachment") {
66+
attachment := policyAttachments[0]
67+
68+
assert.Equal(t, t.gateways[0].Name, attachment.Gateway.Name, "expected attachment to gateway-1")
69+
assert.Nil(t, attachment.Listener)
70+
assert.Nil(t, attachment.Route)
71+
assert.Nil(t, attachment.RuleSectionName)
72+
assert.Greater(t, len(attachment.CorazaDirectives), 0)
73+
}
74+
},
75+
},
76+
{
77+
name: "gateway listener attachment",
78+
gateways: []gatewayv1.Gateway{
79+
newGatewayFunc("default", "gateway-1"),
80+
},
81+
trafficProtectionPolicies: []networkingv1alpha.TrafficProtectionPolicy{
82+
newTrafficProtectionPolicy("default", "tpp-1", func(tpp *networkingv1alpha.TrafficProtectionPolicy) {
83+
tpp.Spec.TargetRefs = []gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName{
84+
{
85+
LocalPolicyTargetReference: gatewayv1alpha2.LocalPolicyTargetReference{
86+
Kind: "Gateway",
87+
Name: "gateway-1",
88+
},
89+
SectionName: ptr.To(gatewayv1.SectionName(gatewayutil.DefaultHTTPListenerName)),
90+
},
91+
}
92+
}),
93+
},
94+
assert: func(t *testContext, policyAttachments []policyAttachment) {
95+
if assert.Len(t, policyAttachments, 1, "expected one policy attachment") {
96+
attachment := policyAttachments[0]
97+
98+
assert.Equal(t, t.gateways[0].Name, attachment.Gateway.Name, "expected attachment to gateway-1")
99+
assert.Equal(t, gatewayv1.SectionName(gatewayutil.DefaultHTTPListenerName), ptr.Deref(attachment.Listener, ""))
100+
assert.Nil(t, attachment.Route)
101+
assert.Nil(t, attachment.RuleSectionName)
102+
assert.Greater(t, len(attachment.CorazaDirectives), 0)
103+
}
104+
},
105+
},
106+
{
107+
name: "direct httproute attachment",
108+
gateways: []gatewayv1.Gateway{
109+
newGatewayFunc("default", "gateway-1"),
110+
},
111+
httpRoutes: []gatewayv1.HTTPRoute{
112+
*newHTTPRoute("default", "route-1", func(route *gatewayv1.HTTPRoute) {
113+
route.Spec.ParentRefs = []gatewayv1.ParentReference{
114+
{
115+
Name: gatewayv1.ObjectName("gateway-1"),
116+
},
117+
}
118+
}),
119+
},
120+
trafficProtectionPolicies: []networkingv1alpha.TrafficProtectionPolicy{
121+
newTrafficProtectionPolicy("default", "tpp-1", func(tpp *networkingv1alpha.TrafficProtectionPolicy) {
122+
tpp.Spec.TargetRefs = []gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName{
123+
{
124+
LocalPolicyTargetReference: gatewayv1alpha2.LocalPolicyTargetReference{
125+
Kind: "HTTPRoute",
126+
Name: "route-1",
127+
},
128+
},
129+
}
130+
}),
131+
},
132+
assert: func(t *testContext, policyAttachments []policyAttachment) {
133+
if assert.Len(t, policyAttachments, 1, "expected one policy attachment") {
134+
attachment := policyAttachments[0]
135+
136+
assert.Equal(t, t.gateways[0].Name, attachment.Gateway.Name, "expected attachment to gateway-1")
137+
assert.Equal(t, t.httpRoutes[0].Name, attachment.Route.Name, "expected attachment to route-1")
138+
assert.Nil(t, attachment.Listener)
139+
assert.Nil(t, attachment.RuleSectionName)
140+
assert.Greater(t, len(attachment.CorazaDirectives), 0)
141+
}
142+
},
143+
},
144+
{
145+
name: "httproute rule attachment",
146+
gateways: []gatewayv1.Gateway{
147+
newGatewayFunc("default", "gateway-1"),
148+
},
149+
httpRoutes: []gatewayv1.HTTPRoute{
150+
*newHTTPRoute("default", "route-1", func(route *gatewayv1.HTTPRoute) {
151+
route.Spec.ParentRefs = []gatewayv1.ParentReference{
152+
{
153+
Name: gatewayv1.ObjectName("gateway-1"),
154+
},
155+
}
156+
route.Spec.Rules = []gatewayv1.HTTPRouteRule{
157+
{
158+
Name: ptr.To(gatewayv1.SectionName("rule-1")),
159+
},
160+
}
161+
}),
162+
},
163+
trafficProtectionPolicies: []networkingv1alpha.TrafficProtectionPolicy{
164+
newTrafficProtectionPolicy("default", "tpp-1", func(tpp *networkingv1alpha.TrafficProtectionPolicy) {
165+
tpp.Spec.TargetRefs = []gatewayv1alpha2.LocalPolicyTargetReferenceWithSectionName{
166+
{
167+
LocalPolicyTargetReference: gatewayv1alpha2.LocalPolicyTargetReference{
168+
Kind: "HTTPRoute",
169+
Name: "route-1",
170+
},
171+
SectionName: ptr.To(gatewayv1.SectionName("rule-1")),
172+
},
173+
}
174+
}),
175+
},
176+
assert: func(t *testContext, policyAttachments []policyAttachment) {
177+
if assert.Len(t, policyAttachments, 1, "expected one policy attachment") {
178+
attachment := policyAttachments[0]
179+
180+
assert.Equal(t, t.gateways[0].Name, attachment.Gateway.Name, "expected attachment to gateway-1")
181+
assert.Equal(t, t.httpRoutes[0].Name, attachment.Route.Name, "expected attachment to route-1")
182+
assert.Nil(t, attachment.Listener)
183+
assert.Equal(t, gatewayv1.SectionName("rule-1"), ptr.Deref(attachment.RuleSectionName, ""))
184+
assert.Greater(t, len(attachment.CorazaDirectives), 0)
185+
}
186+
},
187+
},
188+
}
189+
for _, tt := range tests {
190+
t.Run(tt.name, func(t *testing.T) {
191+
192+
reconciler := &TrafficProtectionPolicyReconciler{Config: operatorConfig}
193+
attachments := reconciler.collectTrafficProtectionPolicyAttachments(
194+
t.Context(),
195+
reconciler.getTrafficProtectionPolicyContexts(tt.trafficProtectionPolicies),
196+
tt.gateways,
197+
tt.httpRoutes,
198+
)
199+
200+
testCtx := &testContext{
201+
T: t,
202+
reconciler: reconciler,
203+
gateways: tt.gateways,
204+
httpRoutes: tt.httpRoutes,
205+
}
206+
207+
tt.assert(testCtx, attachments)
208+
209+
})
210+
}
211+
}
212+
213+
func newTrafficProtectionPolicy(
214+
namespace,
215+
name string,
216+
opts ...func(*networkingv1alpha.TrafficProtectionPolicy),
217+
) networkingv1alpha.TrafficProtectionPolicy {
218+
tpp := networkingv1alpha.TrafficProtectionPolicy{
219+
ObjectMeta: metav1.ObjectMeta{
220+
Namespace: namespace,
221+
Name: name,
222+
UID: uuid.NewUUID(),
223+
},
224+
Spec: networkingv1alpha.TrafficProtectionPolicySpec{
225+
Mode: networkingv1alpha.TrafficProtectionPolicyObserve,
226+
SamplingPercentage: 100,
227+
RuleSets: []networkingv1alpha.TrafficProtectionPolicyRuleSet{
228+
{
229+
Type: "OWASPCoreRuleSet",
230+
OWASPCoreRuleSet: networkingv1alpha.OWASPCRS{
231+
ParanoiaLevels: networkingv1alpha.ParanoiaLevels{
232+
Blocking: 1,
233+
Detection: 1,
234+
},
235+
ScoreThresholds: networkingv1alpha.OWASPScoreThresholds{
236+
Inbound: 5,
237+
Outbound: 4,
238+
},
239+
},
240+
},
241+
},
242+
},
243+
}
244+
245+
for _, opt := range opts {
246+
opt(&tpp)
247+
}
248+
249+
return tpp
250+
}

0 commit comments

Comments
 (0)