Skip to content

Commit ffa0786

Browse files
committed
add conformance tests for ResponseHeaderModifier
Signed-off-by: Sanskar Jaiswal <[email protected]>
1 parent 06e9784 commit ffa0786

File tree

4 files changed

+294
-3
lines changed

4 files changed

+294
-3
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package tests
18+
19+
import (
20+
"testing"
21+
22+
"k8s.io/apimachinery/pkg/types"
23+
24+
"sigs.k8s.io/gateway-api/conformance/utils/http"
25+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
26+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
27+
)
28+
29+
func init() {
30+
ConformanceTests = append(ConformanceTests, HTTPRouteResponseHeaderModifier)
31+
}
32+
33+
var HTTPRouteResponseHeaderModifier = suite.ConformanceTest{
34+
ShortName: "HTTPRouteResponseHeaderModifier",
35+
Description: "An HTTPRoute has response header modifier filters applied correctly",
36+
Features: []suite.SupportedFeature{suite.SupportHTTPResponseHeaderModification},
37+
Manifests: []string{"tests/httproute-response-header-modifier.yaml"},
38+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
39+
ns := "gateway-conformance-infra"
40+
routeNN := types.NamespacedName{Name: "response-header-modifier", Namespace: ns}
41+
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
42+
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeReady(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
43+
44+
testCases := []http.ExpectedResponse{{
45+
Request: http.Request{
46+
Path: "/set",
47+
Headers: map[string]string{
48+
"Some-Other-Header": "val",
49+
},
50+
},
51+
Headers: map[string]string{
52+
"Some-Other-Header": "val",
53+
"X-Header-Set": "set-overwrites-values",
54+
},
55+
Backend: "infra-backend-v1",
56+
Namespace: ns,
57+
}, {
58+
Request: http.Request{
59+
Path: "/set",
60+
Headers: map[string]string{
61+
"Some-Other-Header": "val",
62+
"X-Header-Set": "some-other-value",
63+
},
64+
},
65+
Headers: map[string]string{
66+
"Some-Other-Header": "val",
67+
"X-Header-Set": "set-overwrites-values",
68+
},
69+
Backend: "infra-backend-v1",
70+
Namespace: ns,
71+
}, {
72+
Request: http.Request{
73+
Path: "/add",
74+
Headers: map[string]string{
75+
"Some-Other-Header": "val",
76+
},
77+
},
78+
Headers: map[string]string{
79+
"Some-Other-Header": "val",
80+
"X-Header-Add": "add-appends-values",
81+
},
82+
Backend: "infra-backend-v1",
83+
Namespace: ns,
84+
}, {
85+
Request: http.Request{
86+
Path: "/add",
87+
Headers: map[string]string{
88+
"Some-Other-Header": "val",
89+
"X-Header-Add": "some-other-value",
90+
},
91+
},
92+
Headers: map[string]string{
93+
"Some-Other-Header": "val",
94+
"X-Header-Add": "some-other-value,add-appends-values",
95+
},
96+
Backend: "infra-backend-v1",
97+
Namespace: ns,
98+
}, {
99+
Request: http.Request{
100+
Path: "/remove",
101+
Headers: map[string]string{
102+
"X-Header-Remove": "val",
103+
},
104+
},
105+
Headers: map[string]string{},
106+
AbsentHeaders: []string{"X-Header-Remove"},
107+
Backend: "infra-backend-v1",
108+
Namespace: ns,
109+
}, {
110+
Request: http.Request{
111+
Path: "/multiple",
112+
Headers: map[string]string{
113+
"X-Header-Set-2": "set-val-2",
114+
"X-Header-Add-2": "add-val-2",
115+
"X-Header-Remove-2": "remove-val-2",
116+
"Another-Header": "another-header-val",
117+
},
118+
},
119+
Headers: map[string]string{
120+
"X-Header-Set-1": "header-set-1",
121+
"X-Header-Set-2": "header-set-2",
122+
"X-Header-Add-1": "header-add-1",
123+
"X-Header-Add-2": "add-val-2,header-add-2",
124+
"X-Header-Add-3": "header-add-3",
125+
"Another-Header": "another-header-val",
126+
},
127+
AbsentHeaders: []string{"X-Header-Remove-1", "X-Header-Remove-2"},
128+
Backend: "infra-backend-v1",
129+
Namespace: ns,
130+
}, {
131+
Request: http.Request{
132+
Path: "/case-insensitivity",
133+
// The filter uses canonicalized header names,
134+
// the request uses lowercase names.
135+
Headers: map[string]string{
136+
"x-header-set": "original-val-set",
137+
"x-header-add": "original-val-add",
138+
"x-header-remove": "original-val-remove",
139+
"Another-Header": "another-header-val",
140+
},
141+
},
142+
Headers: map[string]string{
143+
"X-Header-Set": "header-set",
144+
"X-Header-Add": "original-val-add,header-add",
145+
"Another-Header": "another-header-val",
146+
},
147+
AbsentHeaders: []string{"x-header-remove", "X-Header-Remove"},
148+
Backend: "infra-backend-v1",
149+
Namespace: ns,
150+
}}
151+
152+
for i := range testCases {
153+
// Declare tc here to avoid loop variable
154+
// reuse issues across parallel tests.
155+
tc := testCases[i]
156+
t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
157+
t.Parallel()
158+
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
159+
})
160+
}
161+
},
162+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
apiVersion: gateway.networking.k8s.io/v1beta1
2+
kind: HTTPRoute
3+
metadata:
4+
name: request-header-modifier
5+
namespace: gateway-conformance-infra
6+
spec:
7+
parentRefs:
8+
- name: same-namespace
9+
rules:
10+
- matches:
11+
- path:
12+
type: PathPrefix
13+
value: /set
14+
filters:
15+
- type: ResponseHeaderModifier
16+
responseHeaderModifier:
17+
set:
18+
- name: X-Header-Set
19+
value: set-overwrites-values
20+
backendRefs:
21+
- name: infra-backend-v1
22+
port: 8080
23+
- matches:
24+
- path:
25+
type: PathPrefix
26+
value: /add
27+
filters:
28+
- type: ResponseHeaderModifier
29+
responseHeaderModifier:
30+
add:
31+
- name: X-Header-Add
32+
value: add-appends-values
33+
backendRefs:
34+
- name: infra-backend-v1
35+
port: 8080
36+
- matches:
37+
- path:
38+
type: PathPrefix
39+
value: /remove
40+
filters:
41+
- type: ResponseHeaderModifier
42+
responseHeaderModifier:
43+
remove:
44+
- X-Header-Remove
45+
backendRefs:
46+
- name: infra-backend-v1
47+
port: 8080
48+
- matches:
49+
- path:
50+
type: PathPrefix
51+
value: /multiple
52+
filters:
53+
- type: ResponseHeaderModifier
54+
responseHeaderModifier:
55+
set:
56+
- name: X-Header-Set-1
57+
value: header-set-1
58+
- name: X-Header-Set-2
59+
value: header-set-2
60+
add:
61+
- name: X-Header-Add-1
62+
value: header-add-1
63+
- name: X-Header-Add-2
64+
value: header-add-2
65+
- name: X-Header-Add-3
66+
value: header-add-3
67+
remove:
68+
- X-Header-Remove-1
69+
- X-Header-Remove-2
70+
backendRefs:
71+
- name: infra-backend-v1
72+
port: 8080
73+
- matches:
74+
- path:
75+
type: PathPrefix
76+
value: /case-insensitivity
77+
filters:
78+
- type: ResponseHeaderModifier
79+
responseHeaderModifier:
80+
set:
81+
- name: X-Header-Set
82+
value: header-set
83+
add:
84+
- name: X-Header-Add
85+
value: header-add
86+
remove:
87+
- X-Header-Remove
88+
backendRefs:
89+
- name: infra-backend-v1
90+
port: 8080

conformance/utils/http/http.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ type ExpectedResponse struct {
3838
// expected to match Request.
3939
ExpectedRequest *ExpectedRequest
4040

41-
StatusCode int
42-
Backend string
43-
Namespace string
41+
// TODO: move these into a dedicated type named Response
42+
// ref: https://github.com/kubernetes-sigs/gateway-api/issues/1384
43+
StatusCode int
44+
Headers map[string]string
45+
AbsentHeaders []string
46+
47+
Backend string
48+
Namespace string
4449

4550
// User Given TestCase name
4651
TestCaseName string
@@ -209,6 +214,37 @@ func CompareRequest(cReq *roundtripper.CapturedRequest, cRes *roundtripper.Captu
209214
}
210215
}
211216

217+
if expected.Headers != nil {
218+
if cRes.Headers == nil {
219+
return fmt.Errorf("no headers captured, expected %v", len(expected.ExpectedRequest.Headers))
220+
}
221+
for name, val := range cRes.Headers {
222+
cRes.Headers[strings.ToLower(name)] = val
223+
}
224+
225+
for name, expectedVal := range expected.Headers {
226+
actualVal, ok := cRes.Headers[strings.ToLower(name)]
227+
if !ok {
228+
return fmt.Errorf("expected %s header to be set, actual headers: %v", name, cRes.Headers)
229+
} else if strings.Join(actualVal, ",") != expectedVal {
230+
return fmt.Errorf("expected %s header to be set to %s, got %s", name, expectedVal, strings.Join(actualVal, ","))
231+
}
232+
}
233+
}
234+
235+
if len(expected.AbsentHeaders) > 0 {
236+
for name, val := range cRes.Headers {
237+
cRes.Headers[strings.ToLower(name)] = val
238+
}
239+
240+
for _, name := range expected.AbsentHeaders {
241+
val, ok := cRes.Headers[strings.ToLower(name)]
242+
if ok {
243+
return fmt.Errorf("expected %s header to not be set, got %s", name, val)
244+
}
245+
}
246+
}
247+
212248
// Verify that headers expected *not* to be present on the
213249
// request are actually not present.
214250
if len(expected.ExpectedRequest.AbsentHeaders) > 0 {

conformance/utils/suite/suite.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ const (
5252

5353
// This option indicates support for HTTPRoute method matching (extended conformance).
5454
SupportHTTPRouteMethodMatching SupportedFeature = "HTTPRouteMethodMatching"
55+
56+
// This option indicates support for HTTPRoute response header modification (extended conformance).
57+
SupportHTTPResponseHeaderModification SupportedFeature = "HTTPResponseHeaderModification"
5558
)
5659

5760
// ConformanceTestSuite defines the test suite used to run Gateway API

0 commit comments

Comments
 (0)