Skip to content

Commit 6e724b9

Browse files
committed
conformance: add a cookie based session persistence test
Signed-off-by: Arko Dasgupta <[email protected]>
1 parent e00265b commit 6e724b9

File tree

5 files changed

+143
-5
lines changed

5 files changed

+143
-5
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
Copyright 2025 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+
"fmt"
21+
stdhttp "net/http"
22+
"testing"
23+
24+
"k8s.io/apimachinery/pkg/types"
25+
26+
"sigs.k8s.io/gateway-api/conformance/utils/http"
27+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
28+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
29+
"sigs.k8s.io/gateway-api/pkg/features"
30+
)
31+
32+
func init() {
33+
ConformanceTests = append(ConformanceTests, HTTPRouteSessionPersistenceCookie)
34+
}
35+
36+
var HTTPRouteSessionPersistenceCookie = suite.ConformanceTest{
37+
ShortName: "HTTPRouteSessionPersistenceCookie",
38+
Description: "An HTTPRoute with cookie-based session persistence routes requests with the same cookie to the same backend",
39+
Manifests: []string{"tests/httproute-session-persistence-cookie.yaml"},
40+
Features: []features.FeatureName{
41+
features.SupportGateway,
42+
features.SupportHTTPRoute,
43+
features.SupportHTTPRouteSessionPersistenceCookie,
44+
},
45+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
46+
const (
47+
ns = "gateway-conformance-infra"
48+
path = "/session-persistence"
49+
)
50+
routeNN := types.NamespacedName{Name: "session-persistence-cookie", Namespace: ns}
51+
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
52+
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
53+
kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)
54+
55+
expected := http.ExpectedResponse{
56+
Request: http.Request{
57+
Path: path,
58+
},
59+
Response: http.Response{
60+
StatusCode: 200,
61+
},
62+
Namespace: ns,
63+
}
64+
65+
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expected)
66+
67+
req := http.MakeRequest(t, &expected, gwAddr, "HTTP", "http")
68+
initialPod := ""
69+
for i := 0; i < 10; i++ {
70+
cReq, cRes, err := suite.RoundTripper.CaptureRoundTrip(req)
71+
if err != nil {
72+
t.Fatalf("request %d with cookie failed: %v", i+1, err)
73+
}
74+
if err := http.CompareRoundTrip(t, &req, cReq, cRes, expected); err != nil {
75+
t.Fatalf("request %d with cookie failed expectations: %v", i+1, err)
76+
}
77+
78+
if i == 0 {
79+
if cReq.Pod == "" {
80+
t.Fatalf("expected pod to be set")
81+
}
82+
cookie, err := parseCookie(cRes.Headers)
83+
if err != nil {
84+
t.Fatalf("failed to parse session persistence cookie: %v", err)
85+
}
86+
t.Logf("session persistence cookie: %s=%s", cookie.Name, cookie.Value)
87+
req.Headers["Cookie"] = []string{fmt.Sprintf("%s=%s", cookie.Name, cookie.Value)}
88+
initialPod = cReq.Pod
89+
continue
90+
}
91+
if cReq.Pod != initialPod {
92+
t.Fatalf("expected session persistence to keep routing to pod %q, got %q", initialPod, cReq.Pod)
93+
}
94+
}
95+
},
96+
}
97+
98+
func parseCookie(headers map[string][]string) (*stdhttp.Cookie, error) {
99+
parser := &stdhttp.Response{Header: stdhttp.Header(headers)}
100+
cookies := parser.Cookies()
101+
if len(cookies) == 0 {
102+
return nil, fmt.Errorf("cookie not found: headers: %v", headers)
103+
}
104+
return cookies[0], nil
105+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: HTTPRoute
3+
metadata:
4+
name: session-persistence-cookie
5+
namespace: gateway-conformance-infra
6+
spec:
7+
parentRefs:
8+
- name: same-namespace
9+
rules:
10+
- matches:
11+
- path:
12+
type: PathPrefix
13+
value: /session-persistence
14+
sessionPersistence:
15+
type: Cookie
16+
cookieConfig:
17+
lifetimeType: Session
18+
backendRefs:
19+
- name: infra-backend-v1
20+
port: 8080
21+
- name: infra-backend-v2
22+
port: 8080

conformance/utils/suite/conformance.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func (test *ConformanceTest) Run(t *testing.T, suite *ConformanceTestSuite) {
6868

6969
for _, manifestLocation := range test.Manifests {
7070
tlog.Logf(t, "Applying %s", manifestLocation)
71-
suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, true)
71+
suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, suite.Cleanup)
7272
}
7373

7474
if featuresInfo != "" {

hack/implementations/envoy-gateway/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ EOF
3737
## Step 4: Run a specific conformance test
3838

3939
```shell script
40-
go test -v ./conformance \
41-
--run TestConformance/TLSRouteSimpleSameNamespace \
42-
--gateway-class=envoy-gateway --supported-features=Gateway,TLSRoute \
43-
--allow-crds-mismatch
40+
go test -v ../../../conformance \
41+
--run TestConformance/HTTPRouteSessionPersistenceCookie \
42+
--gateway-class=envoy-gateway \
43+
--supported-features=Gateway,HTTPRoute,HTTPRouteSessionPersistenceCookie \
44+
--allow-crds-mismatch \
45+
--cleanup-base-resources=false --debug
4446
```
4547

4648
## (Optional) Step 4: Delete kind cluster

pkg/features/httproute.go

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

112112
// This option indicates support for HTTPRoute additional redirect status code 303 (extended conformance)
113113
SupportHTTPRoute308RedirectStatusCode FeatureName = "HTTPRoute308RedirectStatusCode"
114+
115+
// This option indicates support for HTTPRoute cookie-based session persistence (extended conformance).
116+
SupportHTTPRouteSessionPersistenceCookie FeatureName = "HTTPRouteSessionPersistenceCookie"
114117
)
115118

116119
var (
@@ -229,6 +232,11 @@ var (
229232
Name: SupportHTTPRoute308RedirectStatusCode,
230233
Channel: FeatureChannelStandard,
231234
}
235+
// HTTPRouteSessionPersistenceCookieFeature contains metadata for the SupportHTTPRouteSessionPersistenceCookie feature.
236+
HTTPRouteSessionPersistenceCookieFeature = Feature{
237+
Name: SupportHTTPRouteSessionPersistenceCookie,
238+
Channel: FeatureChannelExperimental,
239+
}
232240
)
233241

234242
// HTTPRouteExtendedFeatures includes all extended features for HTTPRoute
@@ -258,4 +266,5 @@ var HTTPRouteExtendedFeatures = sets.New(
258266
HTTPRoute303RedirectStatusCodeFeature,
259267
HTTPRoute307RedirectStatusCodeFeature,
260268
HTTPRoute308RedirectStatusCodeFeature,
269+
HTTPRouteSessionPersistenceCookieFeature,
261270
)

0 commit comments

Comments
 (0)