Skip to content

Commit

Permalink
Add CEL validation for GRPCMethodMatch
Browse files Browse the repository at this point in the history
  • Loading branch information
gauravkghildiyal committed Aug 16, 2023
1 parent a975fd6 commit 6c80c8e
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 9 deletions.
5 changes: 4 additions & 1 deletion apis/v1alpha2/grpcroute_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ type GRPCRouteSpec struct {
//
// +optional
// +kubebuilder:validation:MaxItems=16
// +kubebuilder:default={{matches: {{method: {type: "Exact"}}}}}
Rules []GRPCRouteRule `json:"rules,omitempty"`
}

Expand Down Expand Up @@ -313,6 +312,10 @@ type GRPCRouteMatch struct {
// request service and/or method.
//
// At least one of Service and Method MUST be a non-empty string.
//
// +kubebuilder:validation:XValidation:message="One or both of 'service' or 'method' must be specified",rule="has(self.type) ? has(self.service) || has(self.method) : true"
// +kubebuilder:validation:XValidation:message="service must only contain valid characters (matching ^(?i)\\.?[a-z_][a-z_0-9]*(\\.[a-z_][a-z_0-9]*)*$)",rule="(!has(self.type) || self.type == 'Exact') && has(self.service) ? self.service.matches(r\"\"\"^(?i)\\.?[a-z_][a-z_0-9]*(\\.[a-z_][a-z_0-9]*)*$\"\"\"): true"
// +kubebuilder:validation:XValidation:message="method must only contain valid characters (matching ^[A-Za-z_][A-Za-z_0-9]*$)",rule="(!has(self.type) || self.type == 'Exact') && has(self.method) ? self.method.matches(r\"\"\"^[A-Za-z_][A-Za-z_0-9]*$\"\"\"): true"
type GRPCMethodMatch struct {
// Type specifies how to match against the service and/or method.
// Support: Core (Exact with service and method specified)
Expand Down
19 changes: 15 additions & 4 deletions config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

121 changes: 117 additions & 4 deletions pkg/test/cel/grpcroute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ package main
import (
"context"
"fmt"
"testing"
"strings"
"testing"
"time"

gatewayv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -175,7 +176,7 @@ func TestGRPCRouteRule(t *testing.T) {
Matches: []gatewayv1a2.GRPCRouteMatch{
{
Method: &gatewayv1a2.GRPCMethodMatch{
Type: ptrTo(gatewayv1a2.GRPCMethodMatchType("Exact")),
Type: ptrTo(gatewayv1a2.GRPCMethodMatchType("Exact")),
Service: ptrTo("helloworld.Greeter"),
},
},
Expand All @@ -201,7 +202,7 @@ func TestGRPCRouteRule(t *testing.T) {
Matches: []gatewayv1a2.GRPCRouteMatch{
{
Method: &gatewayv1a2.GRPCMethodMatch{
Type: ptrTo(gatewayv1a2.GRPCMethodMatchType("Exact")),
Type: ptrTo(gatewayv1a2.GRPCMethodMatchType("Exact")),
Method: ptrTo("SayHello"),
},
},
Expand All @@ -228,7 +229,7 @@ func TestGRPCRouteRule(t *testing.T) {
Matches: []gatewayv1a2.GRPCRouteMatch{
{
Method: &gatewayv1a2.GRPCMethodMatch{
Type: ptrTo(gatewayv1a2.GRPCMethodMatchType("Exact")),
Type: ptrTo(gatewayv1a2.GRPCMethodMatchType("Exact")),
Service: ptrTo("helloworld.Greeter"),
},
},
Expand Down Expand Up @@ -297,6 +298,118 @@ func TestGRPCRouteRule(t *testing.T) {
}
}

func TestGRPCMethodMatch(t *testing.T) {
service := "foo.Test.Example"
method := "Login"
regex := ".*"

tests := []struct {
name string
method gatewayv1a2.GRPCMethodMatch
wantErrors []string
}{
{
name: "valid GRPCRoute with 1 service in GRPCMethodMatch field",
method: gatewayv1a2.GRPCMethodMatch{
Service: &service,
},
},
{
name: "valid GRPCRoute with 1 method in GRPCMethodMatch field",
method: gatewayv1a2.GRPCMethodMatch{
Method: &method,
},
},
{
name: "invalid GRPCRoute missing service or method in GRPCMethodMatch field",
method: gatewayv1a2.GRPCMethodMatch{
Service: nil,
Method: nil,
},
wantErrors: []string{"One or both of 'service' or 'method"},
},
{
name: "GRPCRoute uses regex in service and method with undefined match type",
method: gatewayv1a2.GRPCMethodMatch{
Service: &regex,
Method: &regex,
},
wantErrors: []string{"service must only contain valid characters (matching ^(?i)\\.?[a-z_][a-z_0-9]*(\\.[a-z_][a-z_0-9]*)*$)", "method must only contain valid characters (matching ^[A-Za-z_][A-Za-z_0-9]*$)"},
},
{
name: "GRPCRoute uses regex in service and method with match type Exact",
method: gatewayv1a2.GRPCMethodMatch{
Type: ptrTo(gatewayv1a2.GRPCMethodMatchExact),
Service: &regex,
Method: &regex,
},
wantErrors: []string{"service must only contain valid characters (matching ^(?i)\\.?[a-z_][a-z_0-9]*(\\.[a-z_][a-z_0-9]*)*$)", "method must only contain valid characters (matching ^[A-Za-z_][A-Za-z_0-9]*$)"},
},
{
name: "GRPCRoute uses regex in method with undefined match type",
method: gatewayv1a2.GRPCMethodMatch{
Method: &regex,
},
wantErrors: []string{"method must only contain valid characters (matching ^[A-Za-z_][A-Za-z_0-9]*$)"},
},
{
name: "GRPCRoute uses regex in service with match type Exact",
method: gatewayv1a2.GRPCMethodMatch{
Type: ptrTo(gatewayv1a2.GRPCMethodMatchExact),
Service: &regex,
},
wantErrors: []string{"service must only contain valid characters (matching ^(?i)\\.?[a-z_][a-z_0-9]*(\\.[a-z_][a-z_0-9]*)*$)"},
},
{
name: "GRPCRoute uses regex in service and method with match type RegularExpression",
method: gatewayv1a2.GRPCMethodMatch{
Type: ptrTo(gatewayv1a2.GRPCMethodMatchRegularExpression),
Service: &regex,
Method: &regex,
},
},
{
name: "GRPCRoute uses valid service and method with undefined match type",
method: gatewayv1a2.GRPCMethodMatch{
Service: &service,
Method: &method,
},
},
{
name: "GRPCRoute uses valid service and method with match type Exact",
method: gatewayv1a2.GRPCMethodMatch{
Type: ptrTo(gatewayv1a2.GRPCMethodMatchExact),
Service: &service,
Method: &method,
},
},
}

for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
route := gatewayv1a2.GRPCRoute{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
Namespace: metav1.NamespaceDefault,
},
Spec: gatewayv1a2.GRPCRouteSpec{
Rules: []gatewayv1a2.GRPCRouteRule{
{
Matches: []gatewayv1a2.GRPCRouteMatch{
{
Method: &tc.method,
},
},
},
},
},
}
validateGRPCRoute(t, &route, tc.wantErrors)
})
}
}

func validateGRPCRoute(t *testing.T, route *gatewayv1a2.GRPCRoute, wantErrors []string) {
t.Helper()

Expand Down

0 comments on commit 6c80c8e

Please sign in to comment.