diff --git a/apis/applyconfiguration/internal/internal.go b/apis/applyconfiguration/internal/internal.go index 56a2995ce2..cf2182195b 100644 --- a/apis/applyconfiguration/internal/internal.go +++ b/apis/applyconfiguration/internal/internal.go @@ -769,7 +769,6 @@ var schemaYAML = typed.YAMLObject(`types: - name: fraction type: namedType: io.k8s.sigs.gateway-api.apis.v1.Fraction - default: {} - name: percent type: scalar: numeric diff --git a/apis/v1/httproute_types.go b/apis/v1/httproute_types.go index 4c4f5457f8..dbae1fd864 100644 --- a/apis/v1/httproute_types.go +++ b/apis/v1/httproute_types.go @@ -1177,7 +1177,7 @@ type HTTPRequestMirrorFilter struct { // // +optional // - Fraction Fraction `json:"fraction,omitempty"` + Fraction *Fraction `json:"fraction,omitempty"` } // HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. diff --git a/apis/v1/zz_generated.deepcopy.go b/apis/v1/zz_generated.deepcopy.go index 6f54fec488..6ce1393c4d 100644 --- a/apis/v1/zz_generated.deepcopy.go +++ b/apis/v1/zz_generated.deepcopy.go @@ -996,7 +996,11 @@ func (in *HTTPRequestMirrorFilter) DeepCopyInto(out *HTTPRequestMirrorFilter) { *out = new(int32) **out = **in } - in.Fraction.DeepCopyInto(&out.Fraction) + if in.Fraction != nil { + in, out := &in.Fraction, &out.Fraction + *out = new(Fraction) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRequestMirrorFilter. diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index fc61854014..72809856fe 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -4408,7 +4408,6 @@ func schema_sigsk8sio_gateway_api_apis_v1_HTTPRequestMirrorFilter(ref common.Ref "fraction": { SchemaProps: spec.SchemaProps{ Description: "Fraction represents the fraction of requests that should be mirrored to BackendRef.\n\nOnly one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored.\n\n", - Default: map[string]interface{}{}, Ref: ref("sigs.k8s.io/gateway-api/apis/v1.Fraction"), }, }, diff --git a/pkg/test/cel/grcproute_experimental_test.go b/pkg/test/cel/grcproute_experimental_test.go deleted file mode 100644 index d7aca2abf5..0000000000 --- a/pkg/test/cel/grcproute_experimental_test.go +++ /dev/null @@ -1,90 +0,0 @@ -//go:build experimental -// +build experimental - -/* -Copyright 2024 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "testing" - "time" - - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// -// How are tests named? Where to add new tests? -// -// Ensure that tests for newly added CEL validations are added in the correctly -// named test function. For example, if you added a test at the -// `HTTPRouteFilter` hierarchy (i.e. either at the struct level, or on one of -// the immediate descendent fields), then the test will go in the -// TestHTTPRouteFilter function. If the appropriate test function does not -// exist, please create one. -// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -func TestHTTPRequestMirrorFilterForGRPCRouteExperimental(t *testing.T) { - var percent int32 = 42 - var denominator int32 = 1000 - testService := gatewayv1.ObjectName("test-service") - tests := []struct { - name string - wantErrors []string - rules []gatewayv1.GRPCRouteRule - }{ - { - name: "GRPCRoute - Invalid because both percent and fraction are specified", - wantErrors: []string{"Only one of percent or fraction may be specified in HTTPRequestMirrorFilter"}, - rules: []gatewayv1.GRPCRouteRule{{ - Filters: []gatewayv1.GRPCRouteFilter{{ - Type: gatewayv1.GRPCRouteFilterRequestMirror, - RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1.BackendObjectReference{ - Name: testService, - Port: ptrTo(gatewayv1.PortNumber(8081)), - }, - Percent: &percent, - Fraction: gatewayv1.Fraction{ - Numerator: 83, - Denominator: &denominator, - }, - }, - }}, - }}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - route := &gatewayv1.GRPCRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()), - Namespace: metav1.NamespaceDefault, - }, - Spec: gatewayv1.GRPCRouteSpec{Rules: tc.rules}, - } - validateGRPCRoute(t, route, tc.wantErrors) - }) - } -} diff --git a/pkg/test/cel/grpcroute_experimental_test.go b/pkg/test/cel/grpcroute_experimental_test.go new file mode 100644 index 0000000000..781a1947c0 --- /dev/null +++ b/pkg/test/cel/grpcroute_experimental_test.go @@ -0,0 +1,181 @@ +//go:build experimental +// +build experimental + +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "testing" + "time" + + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// How are tests named? Where to add new tests? +// +// Ensure that tests for newly added CEL validations are added in the correctly +// named test function. For example, if you added a test at the +// `GRPCRouteFilter` hierarchy (i.e. either at the struct level, or on one of +// the immediate descendent fields), then the test will go in the +// TestGRPCRouteFilter function. If the appropriate test function does not +// exist, please create one. +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +func TestGRPCRequestMirrorFilterExperimental(t *testing.T) { + var percent int32 = 42 + var denominator int32 = 1000 + var bad_denominator int32 = 0 + testService := gatewayv1.ObjectName("test-service") + tests := []struct { + name string + wantErrors []string + rules []gatewayv1.GRPCRouteRule + }{ + { + name: "GRPCRoute - Invalid because both percent and fraction are specified", + wantErrors: []string{"Only one of percent or fraction may be specified in HTTPRequestMirrorFilter"}, + rules: []gatewayv1.GRPCRouteRule{{ + Filters: []gatewayv1.GRPCRouteFilter{{ + Type: gatewayv1.GRPCRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Percent: &percent, + Fraction: &gatewayv1.Fraction{ + Numerator: 83, + Denominator: &denominator, + }, + }, + }}, + }}, + }, + { + name: "GRPCRoute - Invalid fraction - numerator greater than denominator", + wantErrors: []string{"numerator must be less than or equal to denominator"}, + rules: []gatewayv1.GRPCRouteRule{{ + Filters: []gatewayv1.GRPCRouteFilter{{ + Type: gatewayv1.GRPCRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ + Numerator: 1001, + Denominator: &denominator, + }, + }, + }}, + }}, + }, + { + name: "GRPCRoute - Invalid fraction - denominator is 0", + wantErrors: []string{"spec.rules[0].filters[0].requestMirror.fraction.denominator in body should be greater than or equal to 1"}, + rules: []gatewayv1.GRPCRouteRule{{ + Filters: []gatewayv1.GRPCRouteFilter{{ + Type: gatewayv1.GRPCRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ + Numerator: 0, + Denominator: &bad_denominator, + }, + }, + }}, + }}, + }, + { + name: "GRPCRoute - Invalid fraction - numerator is negative", + wantErrors: []string{"spec.rules[0].filters[0].requestMirror.fraction.numerator in body should be greater than or equal to 0"}, + rules: []gatewayv1.GRPCRouteRule{{ + Filters: []gatewayv1.GRPCRouteFilter{{ + Type: gatewayv1.GRPCRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ + Numerator: -1, + Denominator: &denominator, + }, + }, + }}, + }}, + }, + { + name: "GRPCRoute - Valid with percent", + rules: []gatewayv1.GRPCRouteRule{{ + Filters: []gatewayv1.GRPCRouteFilter{{ + Type: gatewayv1.GRPCRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Percent: &percent, + }, + }}, + }}, + }, + { + name: "GRPCRoute - Valid with fraction", + rules: []gatewayv1.GRPCRouteRule{{ + Filters: []gatewayv1.GRPCRouteFilter{{ + Type: gatewayv1.GRPCRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ + Numerator: 83, + Denominator: &denominator, + }, + }, + }}, + }}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + route := &gatewayv1.GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()), + Namespace: metav1.NamespaceDefault, + }, + Spec: gatewayv1.GRPCRouteSpec{Rules: tc.rules}, + } + validateGRPCRoute(t, route, tc.wantErrors) + }) + } +} diff --git a/pkg/test/cel/httproute_experimental_test.go b/pkg/test/cel/httproute_experimental_test.go index 7c09c9a0d2..a141899e37 100644 --- a/pkg/test/cel/httproute_experimental_test.go +++ b/pkg/test/cel/httproute_experimental_test.go @@ -436,6 +436,7 @@ func TestHTTPRouteRuleExperimental(t *testing.T) { func TestHTTPRequestMirrorFilterExperimental(t *testing.T) { var percent int32 = 42 var denominator int32 = 1000 + var bad_denominator int32 = 0 testService := gatewayv1.ObjectName("test-service") tests := []struct { name string @@ -454,7 +455,97 @@ func TestHTTPRequestMirrorFilterExperimental(t *testing.T) { Port: ptrTo(gatewayv1.PortNumber(8081)), }, Percent: &percent, - Fraction: gatewayv1.Fraction{ + Fraction: &gatewayv1.Fraction{ + Numerator: 83, + Denominator: &denominator, + }, + }, + }}, + }}, + }, + { + name: "HTTPRoute - Invalid fraction - numerator greater than denominator", + wantErrors: []string{"numerator must be less than or equal to denominator"}, + rules: []gatewayv1.HTTPRouteRule{{ + Filters: []gatewayv1.HTTPRouteFilter{{ + Type: gatewayv1.HTTPRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ + Numerator: 1001, + Denominator: &denominator, + }, + }, + }}, + }}, + }, + { + name: "HTTPRoute - Invalid fraction - denominator is 0", + wantErrors: []string{"spec.rules[0].filters[0].requestMirror.fraction.denominator in body should be greater than or equal to 1"}, + rules: []gatewayv1.HTTPRouteRule{{ + Filters: []gatewayv1.HTTPRouteFilter{{ + Type: gatewayv1.HTTPRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ + Numerator: 0, + Denominator: &bad_denominator, + }, + }, + }}, + }}, + }, + { + name: "HTTPRoute - Invalid fraction - numerator is negative", + wantErrors: []string{"spec.rules[0].filters[0].requestMirror.fraction.numerator in body should be greater than or equal to 0"}, + rules: []gatewayv1.HTTPRouteRule{{ + Filters: []gatewayv1.HTTPRouteFilter{{ + Type: gatewayv1.HTTPRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ + Numerator: -1, + Denominator: &denominator, + }, + }, + }}, + }}, + }, + { + name: "HTTPRoute - Valid with percent", + rules: []gatewayv1.HTTPRouteRule{{ + Filters: []gatewayv1.HTTPRouteFilter{{ + Type: gatewayv1.HTTPRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Percent: &percent, + }, + }}, + }}, + }, + { + name: "HTTPRoute - Valid with fraction", + rules: []gatewayv1.HTTPRouteRule{{ + Filters: []gatewayv1.HTTPRouteFilter{{ + Type: gatewayv1.HTTPRouteFilterRequestMirror, + RequestMirror: &gatewayv1.HTTPRequestMirrorFilter{ + BackendRef: gatewayv1.BackendObjectReference{ + Name: testService, + Port: ptrTo(gatewayv1.PortNumber(8081)), + }, + Fraction: &gatewayv1.Fraction{ Numerator: 83, Denominator: &denominator, },