Skip to content

Commit 698d164

Browse files
committed
refactor: CEL for validation of RLP implicit and explicit default mutual exclusivity
1 parent c037de1 commit 698d164

8 files changed

+51
-62
lines changed

api/v1beta2/ratelimitpolicy_types.go

+2-10
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func (l Limit) CountersAsStringList() []string {
120120

121121
// RateLimitPolicySpec defines the desired state of RateLimitPolicy
122122
// +kubebuilder:validation:XValidation:rule="self.targetRef.kind != 'Gateway' || !has(self.limits) || !self.limits.exists(x, has(self.limits[x].routeSelectors))",message="route selectors not supported when targeting a Gateway"
123+
// +kubebuilder:validation:XValidation:rule="!(has(self.defaults.limits) && has(self.limits))",message="Implicit and explicit defaults are mutually exclusive"
123124
type RateLimitPolicySpec struct {
124125
// TargetRef identifies an API object to apply policy to.
125126
// +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'",message="Invalid targetRef.group. The only supported value is 'gateway.networking.k8s.io'"
@@ -128,7 +129,7 @@ type RateLimitPolicySpec struct {
128129

129130
// Defaults define explicit default values for this policy and for policies inheriting this policy
130131
// +optional
131-
Defaults CommonSpec `json:"defaults"`
132+
Defaults CommonSpec `json:"defaults,omitempty"`
132133

133134
// Implicit default
134135
CommonSpec `json:""`
@@ -142,11 +143,6 @@ type CommonSpec struct {
142143
Limits map[string]Limit `json:"limits,omitempty"`
143144
}
144145

145-
// IsUsed determines if any fields within CommonSpec is used
146-
func (c CommonSpec) IsUsed() bool {
147-
return c.Limits != nil
148-
}
149-
150146
// RateLimitPolicyStatus defines the observed state of RateLimitPolicy
151147
type RateLimitPolicyStatus struct {
152148
// ObservedGeneration reflects the generation of the most recently observed spec.
@@ -209,10 +205,6 @@ func (r *RateLimitPolicy) Validate() error {
209205
return fmt.Errorf("invalid targetRef.Namespace %s. Currently only supporting references to the same namespace", *r.Spec.TargetRef.Namespace)
210206
}
211207

212-
if r.Spec.Defaults.IsUsed() && r.Spec.CommonSpec.IsUsed() {
213-
return fmt.Errorf("cannot use implicit defaults if explicit defaults are defined")
214-
}
215-
216208
return nil
217209
}
218210

api/v1beta2/ratelimitpolicy_types_test.go

-47
Original file line numberDiff line numberDiff line change
@@ -62,53 +62,6 @@ func TestRateLimitPolicyValidation(t *testing.T) {
6262
subT.Fatalf(`rlp.Validate() did not return expected error. Instead: %v`, err)
6363
}
6464
})
65-
66-
t.Run("Valid - no implicit or explicit defaults", func(subT *testing.T) {
67-
rlp := testBuildBasicHTTPRouteRLP(name, nil)
68-
if err := rlp.Validate(); err != nil {
69-
subT.Fatalf(`rlp.Validate() did return error and should not: %v`, err)
70-
}
71-
})
72-
73-
t.Run("Valid - Implicit defaults only", func(subT *testing.T) {
74-
rlp := testBuildBasicHTTPRouteRLP(name, func(policy *RateLimitPolicy) {
75-
policy.Spec.Limits = map[string]Limit{
76-
"implicit": {Rates: []Rate{{Limit: 0}}},
77-
}
78-
})
79-
if err := rlp.Validate(); err != nil {
80-
subT.Fatalf(`rlp.Validate() did return error and should not: %v`, err)
81-
}
82-
})
83-
84-
t.Run("Valid - Explicit defaults only", func(subT *testing.T) {
85-
rlp := testBuildBasicHTTPRouteRLP(name, func(policy *RateLimitPolicy) {
86-
policy.Spec.Defaults.Limits = map[string]Limit{
87-
"explicit": {Rates: []Rate{{Limit: 1}}},
88-
}
89-
})
90-
if err := rlp.Validate(); err != nil {
91-
subT.Fatalf(`rlp.Validate() did return error and should not: %v`, err)
92-
}
93-
})
94-
95-
t.Run("Invalid - Implicit and explicit defaults ", func(subT *testing.T) {
96-
rlp := testBuildBasicHTTPRouteRLP(name, func(policy *RateLimitPolicy) {
97-
policy.Spec.Limits = map[string]Limit{
98-
"implicit": {Rates: []Rate{{Limit: 0}}},
99-
}
100-
policy.Spec.Defaults.Limits = map[string]Limit{
101-
"explicit": {Rates: []Rate{{Limit: 1}}},
102-
}
103-
})
104-
err := rlp.Validate()
105-
if err == nil {
106-
subT.Fatal(`rlp.Validate() did not return error and should`)
107-
}
108-
if !strings.Contains(err.Error(), "cannot use implicit defaults if explicit defaults are defined") {
109-
subT.Fatalf(`rlp.Validate() did not return expected error. Instead: %v`, err)
110-
}
111-
})
11265
}
11366

11467
func TestRateLimitPolicyListGetItems(t *testing.T) {

bundle/manifests/kuadrant-operator.clusterserviceversion.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ metadata:
106106
capabilities: Basic Install
107107
categories: Integration & Delivery
108108
containerImage: quay.io/kuadrant/kuadrant-operator:latest
109-
createdAt: "2024-03-12T12:20:22Z"
109+
createdAt: "2024-03-13T11:55:14Z"
110110
operators.operatorframework.io/builder: operator-sdk-v1.32.0
111111
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
112112
repository: https://github.com/Kuadrant/kuadrant-operator

bundle/manifests/kuadrant.io_ratelimitpolicies.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,8 @@ spec:
868868
- message: route selectors not supported when targeting a Gateway
869869
rule: self.targetRef.kind != 'Gateway' || !has(self.limits) || !self.limits.exists(x,
870870
has(self.limits[x].routeSelectors))
871+
- message: Implicit and explicit defaults are mutually exclusive
872+
rule: '!(has(self.defaults.limits) && has(self.limits))'
871873
status:
872874
description: RateLimitPolicyStatus defines the observed state of RateLimitPolicy
873875
properties:

config/crd/bases/kuadrant.io_ratelimitpolicies.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,8 @@ spec:
867867
- message: route selectors not supported when targeting a Gateway
868868
rule: self.targetRef.kind != 'Gateway' || !has(self.limits) || !self.limits.exists(x,
869869
has(self.limits[x].routeSelectors))
870+
- message: Implicit and explicit defaults are mutually exclusive
871+
rule: '!(has(self.defaults.limits) && has(self.limits))'
870872
status:
871873
description: RateLimitPolicyStatus defines the observed state of RateLimitPolicy
872874
properties:

controllers/ratelimitpolicy_controller_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,48 @@ var _ = Describe("RateLimitPolicy CEL Validations", func() {
706706
Expect(err).To(Not(BeNil()))
707707
Expect(strings.Contains(err.Error(), "Invalid targetRef.kind. The only supported values are 'HTTPRoute' and 'Gateway'")).To(BeTrue())
708708
})
709+
710+
It("Valid only implicit defaults", func() {
711+
policy := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) {
712+
policy.Spec.Limits = map[string]kuadrantv1beta2.Limit{
713+
"implicit": {
714+
Rates: []kuadrantv1beta2.Rate{{Limit: 2, Duration: 20, Unit: "second"}},
715+
},
716+
}
717+
})
718+
err := k8sClient.Create(context.Background(), policy)
719+
Expect(err).To(BeNil())
720+
})
721+
722+
It("Valid only explicit defaults", func() {
723+
policy := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) {
724+
policy.Spec.Defaults.Limits = map[string]kuadrantv1beta2.Limit{
725+
"explicit": {
726+
Rates: []kuadrantv1beta2.Rate{{Limit: 1, Duration: 10, Unit: "second"}},
727+
},
728+
}
729+
})
730+
err := k8sClient.Create(context.Background(), policy)
731+
Expect(err).To(BeNil())
732+
})
733+
734+
It("Invalid implicit and explicit defaults are mutually exclusive", func() {
735+
policy := policyFactory(func(policy *kuadrantv1beta2.RateLimitPolicy) {
736+
policy.Spec.Defaults.Limits = map[string]kuadrantv1beta2.Limit{
737+
"explicit": {
738+
Rates: []kuadrantv1beta2.Rate{{Limit: 1, Duration: 10, Unit: "second"}},
739+
},
740+
}
741+
policy.Spec.Limits = map[string]kuadrantv1beta2.Limit{
742+
"implicit": {
743+
Rates: []kuadrantv1beta2.Rate{{Limit: 2, Duration: 20, Unit: "second"}},
744+
},
745+
}
746+
})
747+
err := k8sClient.Create(context.Background(), policy)
748+
Expect(err).To(Not(BeNil()))
749+
Expect(strings.Contains(err.Error(), "Implicit and explicit defaults are mutually exclusive")).To(BeTrue())
750+
})
709751
})
710752

711753
Context("Route Selector Validation", func() {

pkg/rlptools/utils.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ import (
88

99
limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1"
1010

11-
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
12-
1311
kuadrantv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2"
12+
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
1413
)
1514

1615
const (

pkg/rlptools/utils_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import (
1010
limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1212

13-
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
14-
1513
kuadrantv1beta2 "github.com/kuadrant/kuadrant-operator/api/v1beta2"
14+
"github.com/kuadrant/kuadrant-operator/pkg/library/utils"
1615
)
1716

1817
func testRLP_1Limit_1Rate(ns, name string) *kuadrantv1beta2.RateLimitPolicy {

0 commit comments

Comments
 (0)