diff --git a/apis/v1alpha2/validation/httproute_test.go b/apis/v1alpha2/validation/httproute_test.go index 80eb9f1d55..159ab4f587 100644 --- a/apis/v1alpha2/validation/httproute_test.go +++ b/apis/v1alpha2/validation/httproute_test.go @@ -29,7 +29,6 @@ import ( func TestValidateHTTPRoute(t *testing.T) { testService := gatewayv1a2.ObjectName("test-service") - specialService := gatewayv1a2.ObjectName("special-service") pathPrefixMatchType := gatewayv1b1.PathMatchPathPrefix tests := []struct { @@ -103,20 +102,20 @@ func TestValidateHTTPRoute(t *testing.T) { }, Filters: []gatewayv1a2.HTTPRouteFilter{ { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1a2.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1a2.BackendObjectReference{ - Name: testService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), + Type: gatewayv1b1.HTTPRouteFilterURLRewrite, + URLRewrite: &gatewayv1b1.HTTPURLRewriteFilter{ + Path: &gatewayv1b1.HTTPPathModifier{ + Type: gatewayv1b1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("foo"), }, }, }, { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1a2.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1a2.BackendObjectReference{ - Name: specialService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), + Type: gatewayv1b1.HTTPRouteFilterURLRewrite, + URLRewrite: &gatewayv1b1.HTTPURLRewriteFilter{ + Path: &gatewayv1b1.HTTPPathModifier{ + Type: gatewayv1b1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("bar"), }, }, }, @@ -186,11 +185,13 @@ func TestValidateHTTPRoute(t *testing.T) { }, Filters: []gatewayv1a2.HTTPRouteFilter{ { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1a2.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1a2.BackendObjectReference{ - Name: testService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), + Type: gatewayv1b1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1b1.HTTPHeaderFilter{ + Add: []gatewayv1b1.HTTPHeader{ + { + Name: "extra-header", + Value: "foo", + }, }, }, }, @@ -206,11 +207,13 @@ func TestValidateHTTPRoute(t *testing.T) { }, }, { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1a2.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1a2.BackendObjectReference{ - Name: testService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), + Type: gatewayv1b1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1b1.HTTPHeaderFilter{ + Set: []gatewayv1b1.HTTPHeader{ + { + Name: "other-header", + Value: "bat", + }, }, }, }, @@ -225,15 +228,6 @@ func TestValidateHTTPRoute(t *testing.T) { }, }, }, - { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1a2.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1a2.BackendObjectReference{ - Name: specialService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), - }, - }, - }, }, }, }, @@ -471,8 +465,8 @@ func TestValidateHTTPBackendUniqueFilters(t *testing.T) { }, }}, }, { - name: "invalid httpRoute Rules duplicate mirror filter", - errCount: 1, + name: "valid httpRoute Rules duplicate mirror filter", + errCount: 0, rules: []gatewayv1a2.HTTPRouteRule{{ BackendRefs: []gatewayv1a2.HTTPBackendRef{ { diff --git a/apis/v1beta1/httproute_types.go b/apis/v1beta1/httproute_types.go index fb7ab80c82..ff9e6a041b 100644 --- a/apis/v1beta1/httproute_types.go +++ b/apis/v1beta1/httproute_types.go @@ -201,8 +201,8 @@ type HTTPRouteRule struct { // - Implementation-specific custom filters have no API guarantees across // implementations. // - // Specifying a core filter multiple times has unspecified or - // implementation-specific conformance. + // Specifying the same filter multiple times is not supported unless explicitly + // indicated in the filter. // // All filters are expected to be compatible with each other except for the // URLRewrite and RequestRedirect filters, which may not be combined. If an @@ -618,6 +618,10 @@ type HTTPRouteFilter struct { // Requests are sent to the specified destination, but responses from // that destination are ignored. // + // This filter can be used multiple times within the same rule. Note that + // not all implementations will be able to support mirroring to multiple + // backends. + // // Support: Extended // // +optional @@ -643,6 +647,8 @@ type HTTPRouteFilter struct { // "networking.example.net"). ExtensionRef MUST NOT be used for core and // extended filters. // + // This filter can be used multiple times within the same rule. + // // Support: Implementation-specific // // +optional diff --git a/apis/v1beta1/validation/httproute.go b/apis/v1beta1/validation/httproute.go index 1502c1bed8..8dbe4e8f3d 100644 --- a/apis/v1beta1/validation/httproute.go +++ b/apis/v1beta1/validation/httproute.go @@ -32,6 +32,7 @@ var ( // repeated multiple times in a rule. repeatableHTTPRouteFilters = []gatewayv1b1.HTTPRouteFilterType{ gatewayv1b1.HTTPRouteFilterExtensionRef, + gatewayv1b1.HTTPRouteFilterRequestMirror, } // Invalid path sequences and suffixes, primarily related to directory traversal @@ -137,15 +138,16 @@ func validateHTTPRouteFilters(filters []gatewayv1b1.HTTPRouteFilter, matches []g } errs = append(errs, validateHTTPRouteFilterTypeMatchesValue(filter, path.Index(i))...) } - // custom filters don't have any validation - for _, key := range repeatableHTTPRouteFilters { - delete(counts, key) - } if counts[gatewayv1b1.HTTPRouteFilterRequestRedirect] > 0 && counts[gatewayv1b1.HTTPRouteFilterURLRewrite] > 0 { errs = append(errs, field.Invalid(path.Child("filters"), gatewayv1b1.HTTPRouteFilterRequestRedirect, "may specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both")) } + // repeatableHTTPRouteFilters filters can be used more than once + for _, key := range repeatableHTTPRouteFilters { + delete(counts, key) + } + for filterType, count := range counts { if count > 1 { errs = append(errs, field.Invalid(path.Child("filters"), filterType, "cannot be used multiple times in the same rule")) diff --git a/apis/v1beta1/validation/httproute_test.go b/apis/v1beta1/validation/httproute_test.go index bd23297064..c157dc3704 100644 --- a/apis/v1beta1/validation/httproute_test.go +++ b/apis/v1beta1/validation/httproute_test.go @@ -28,7 +28,6 @@ import ( func TestValidateHTTPRoute(t *testing.T) { testService := gatewayv1b1.ObjectName("test-service") - specialService := gatewayv1b1.ObjectName("special-service") pathPrefixMatchType := gatewayv1b1.PathMatchPathPrefix tests := []struct { @@ -102,20 +101,20 @@ func TestValidateHTTPRoute(t *testing.T) { }, Filters: []gatewayv1b1.HTTPRouteFilter{ { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1b1.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1b1.BackendObjectReference{ - Name: testService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), + Type: gatewayv1b1.HTTPRouteFilterURLRewrite, + URLRewrite: &gatewayv1b1.HTTPURLRewriteFilter{ + Path: &gatewayv1b1.HTTPPathModifier{ + Type: gatewayv1b1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("foo"), }, }, }, { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1b1.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1b1.BackendObjectReference{ - Name: specialService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), + Type: gatewayv1b1.HTTPRouteFilterURLRewrite, + URLRewrite: &gatewayv1b1.HTTPURLRewriteFilter{ + Path: &gatewayv1b1.HTTPPathModifier{ + Type: gatewayv1b1.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: ptrTo("bar"), }, }, }, @@ -172,7 +171,7 @@ func TestValidateHTTPRoute(t *testing.T) { }, }, { name: "invalid httpRoute with multiple duplicate filters", - errCount: 3, + errCount: 2, rules: []gatewayv1b1.HTTPRouteRule{ { Matches: []gatewayv1b1.HTTPRouteMatch{ @@ -184,15 +183,6 @@ func TestValidateHTTPRoute(t *testing.T) { }, }, Filters: []gatewayv1b1.HTTPRouteFilter{ - { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1b1.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1b1.BackendObjectReference{ - Name: testService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), - }, - }, - }, { Type: gatewayv1b1.HTTPRouteFilterRequestHeaderModifier, RequestHeaderModifier: &gatewayv1b1.HTTPHeaderFilter{ @@ -204,15 +194,6 @@ func TestValidateHTTPRoute(t *testing.T) { }, }, }, - { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1b1.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1b1.BackendObjectReference{ - Name: testService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), - }, - }, - }, { Type: gatewayv1b1.HTTPRouteFilterRequestHeaderModifier, RequestHeaderModifier: &gatewayv1b1.HTTPHeaderFilter{ @@ -235,15 +216,6 @@ func TestValidateHTTPRoute(t *testing.T) { }, }, }, - { - Type: gatewayv1b1.HTTPRouteFilterRequestMirror, - RequestMirror: &gatewayv1b1.HTTPRequestMirrorFilter{ - BackendRef: gatewayv1b1.BackendObjectReference{ - Name: specialService, - Port: ptrTo(gatewayv1b1.PortNumber(8080)), - }, - }, - }, { Type: gatewayv1b1.HTTPRouteFilterResponseHeaderModifier, ResponseHeaderModifier: &gatewayv1b1.HTTPHeaderFilter{ @@ -638,8 +610,8 @@ func TestValidateHTTPBackendUniqueFilters(t *testing.T) { }, }}, }, { - name: "invalid httpRoute Rules duplicate mirror filter", - errCount: 1, + name: "valid httpRoute Rules duplicate mirror filter", + errCount: 0, rules: []gatewayv1b1.HTTPRouteRule{{ BackendRefs: []gatewayv1b1.HTTPBackendRef{ { diff --git a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml index 2096fbb920..2d71ee1bee 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml @@ -319,7 +319,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times + within the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. @@ -457,7 +458,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from - that destination are ignored. \n Support: Extended" + that destination are ignored. \n This filter can + be used multiple times within the same rule. Note + that not all implementations will be able to support + mirroring to multiple backends. \n Support: Extended" properties: backendRef: description: "BackendRef references a resource @@ -1016,9 +1020,9 @@ spec: all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying - a core filter multiple times has unspecified or implementation-specific - conformance. \n All filters are expected to be compatible - with each other except for the URLRewrite and RequestRedirect + the same filter multiple times is not supported unless explicitly + indicated in the filter. \n All filters are expected to be + compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported @@ -1040,7 +1044,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times within + the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. For @@ -1170,7 +1175,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are - ignored. \n Support: Extended" + ignored. \n This filter can be used multiple times within + the same rule. Note that not all implementations will + be able to support mirroring to multiple backends. \n + Support: Extended" properties: backendRef: description: "BackendRef references a resource where @@ -2375,7 +2383,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times + within the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. @@ -2513,7 +2522,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from - that destination are ignored. \n Support: Extended" + that destination are ignored. \n This filter can + be used multiple times within the same rule. Note + that not all implementations will be able to support + mirroring to multiple backends. \n Support: Extended" properties: backendRef: description: "BackendRef references a resource @@ -3072,9 +3084,9 @@ spec: all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying - a core filter multiple times has unspecified or implementation-specific - conformance. \n All filters are expected to be compatible - with each other except for the URLRewrite and RequestRedirect + the same filter multiple times is not supported unless explicitly + indicated in the filter. \n All filters are expected to be + compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported @@ -3096,7 +3108,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times within + the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. For @@ -3226,7 +3239,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are - ignored. \n Support: Extended" + ignored. \n This filter can be used multiple times within + the same rule. Note that not all implementations will + be able to support mirroring to multiple backends. \n + Support: Extended" properties: backendRef: description: "BackendRef references a resource where diff --git a/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml b/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml index f51b3ee7b8..bd77feae15 100644 --- a/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml @@ -289,7 +289,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times + within the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. @@ -427,7 +428,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from - that destination are ignored. \n Support: Extended" + that destination are ignored. \n This filter can + be used multiple times within the same rule. Note + that not all implementations will be able to support + mirroring to multiple backends. \n Support: Extended" properties: backendRef: description: "BackendRef references a resource @@ -986,9 +990,9 @@ spec: all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying - a core filter multiple times has unspecified or implementation-specific - conformance. \n All filters are expected to be compatible - with each other except for the URLRewrite and RequestRedirect + the same filter multiple times is not supported unless explicitly + indicated in the filter. \n All filters are expected to be + compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported @@ -1010,7 +1014,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times within + the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. For @@ -1140,7 +1145,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are - ignored. \n Support: Extended" + ignored. \n This filter can be used multiple times within + the same rule. Note that not all implementations will + be able to support mirroring to multiple backends. \n + Support: Extended" properties: backendRef: description: "BackendRef references a resource where @@ -2283,7 +2291,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times + within the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. @@ -2421,7 +2430,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from - that destination are ignored. \n Support: Extended" + that destination are ignored. \n This filter can + be used multiple times within the same rule. Note + that not all implementations will be able to support + mirroring to multiple backends. \n Support: Extended" properties: backendRef: description: "BackendRef references a resource @@ -2980,9 +2992,9 @@ spec: all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying - a core filter multiple times has unspecified or implementation-specific - conformance. \n All filters are expected to be compatible - with each other except for the URLRewrite and RequestRedirect + the same filter multiple times is not supported unless explicitly + indicated in the filter. \n All filters are expected to be + compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported @@ -3004,7 +3016,8 @@ spec: extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended - filters. \n Support: Implementation-specific" + filters. \n This filter can be used multiple times within + the same rule. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. For @@ -3134,7 +3147,10 @@ spec: description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are - ignored. \n Support: Extended" + ignored. \n This filter can be used multiple times within + the same rule. Note that not all implementations will + be able to support mirroring to multiple backends. \n + Support: Extended" properties: backendRef: description: "BackendRef references a resource where diff --git a/pkg/admission/server_test.go b/pkg/admission/server_test.go index 37ae69a3f6..95e4d8ec04 100644 --- a/pkg/admission/server_test.go +++ b/pkg/admission/server_test.go @@ -235,7 +235,7 @@ func TestServeHTTPSubmissions(t *testing.T) { }, }, { - name: "invalid v1alpha2 HTTPRoute resource with two request mirror filters", + name: "valid v1alpha2 HTTPRoute resource with two request mirror filters", reqBody: dedent.Dedent(`{ "kind": "AdmissionReview", "apiVersion": "` + apiVersion + `", @@ -301,11 +301,8 @@ func TestServeHTTPSubmissions(t *testing.T) { wantRespCode: http.StatusOK, wantSuccessResponse: admission.AdmissionResponse{ UID: "7313cd05-eddc-4150-b88c-971a0d53b2ab", - Allowed: false, - Result: &metav1.Status{ - Code: 400, - Message: "spec.rules[0].filters: Invalid value: \"RequestMirror\": cannot be used multiple times in the same rule", - }, + Allowed: true, + Result: &metav1.Status{}, }, }, {