Skip to content

Commit 6ba64d2

Browse files
committed
add e2e test case
Signed-off-by: hai.yue <20416005+yuehaii@users.noreply.github.com>
1 parent 9879f17 commit 6ba64d2

2 files changed

Lines changed: 174 additions & 0 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
apiVersion: gateway.networking.k8s.io/v1
3+
kind: Gateway
4+
metadata:
5+
name: stale-status-gw
6+
namespace: gateway-conformance-infra
7+
spec:
8+
gatewayClassName: "{GATEWAY_CLASS_NAME}"
9+
listeners:
10+
- name: http-first
11+
port: 8888
12+
protocol: HTTP
13+
hostname: "other.example.com"
14+
allowedRoutes:
15+
namespaces:
16+
from: Same
17+
---
18+
apiVersion: gateway.networking.k8s.io/v1
19+
kind: HTTPRoute
20+
metadata:
21+
name: stale-status-route
22+
namespace: gateway-conformance-infra
23+
spec:
24+
parentRefs:
25+
- name: stale-status-gw
26+
hostnames:
27+
- "stale.example.com"
28+
rules:
29+
- backendRefs:
30+
- name: infra-backend-v1
31+
port: 8080
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// Copyright Envoy Gateway Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
// The full text of the Apache license is available in the LICENSE file at
4+
// the root of the repo.
5+
6+
//go:build e2e
7+
8+
package tests
9+
10+
import (
11+
"context"
12+
"testing"
13+
"time"
14+
15+
"github.com/stretchr/testify/require"
16+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
"k8s.io/apimachinery/pkg/types"
18+
"k8s.io/apimachinery/pkg/util/wait"
19+
"sigs.k8s.io/controller-runtime/pkg/client"
20+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
21+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
22+
"sigs.k8s.io/gateway-api/conformance/utils/tlog"
23+
)
24+
25+
func init() {
26+
ConformanceTests = append(ConformanceTests, HTTPRouteStaleStatusTest)
27+
}
28+
29+
var HTTPRouteStaleStatusTest = suite.ConformanceTest{
30+
ShortName: "HTTPRouteStaleStatus",
31+
Description: "Stale Accepted=False/NoMatchingListenerHostname condition must be cleared after a matching listener is added to the Gateway",
32+
Manifests: []string{"testdata/httproute-stale-status.yaml"},
33+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
34+
ns := "gateway-conformance-infra"
35+
gwNN := types.NamespacedName{Name: "stale-status-gw", Namespace: ns}
36+
routeNN := types.NamespacedName{Name: "stale-status-route", Namespace: ns}
37+
38+
// Phase 1: Gateway listener hostname "other.example.com" does not match the
39+
// route's hostname "stale.example.com".
40+
// Expect Accepted=False / NoMatchingListenerHostname.
41+
t.Run("route rejected with NoMatchingListenerHostname when hostnames differ", func(t *testing.T) {
42+
tlog.Logf(t, "Waiting for HTTPRoute %s to be Accepted=False/NoMatchingListenerHostname", routeNN)
43+
require.NoError(t, wait.PollUntilContextTimeout(
44+
t.Context(), time.Second, suite.TimeoutConfig.MaxTimeToConsistency, true,
45+
func(ctx context.Context) (bool, error) {
46+
route := &gwapiv1.HTTPRoute{}
47+
if err := suite.Client.Get(ctx, routeNN, route); err != nil {
48+
tlog.Logf(t, "failed to get HTTPRoute: %v", err)
49+
return false, nil
50+
}
51+
for _, parent := range route.Status.Parents {
52+
for _, cond := range parent.Conditions {
53+
if cond.Type == string(gwapiv1.RouteConditionAccepted) {
54+
if cond.Status == metav1.ConditionFalse &&
55+
cond.Reason == string(gwapiv1.RouteReasonNoMatchingListenerHostname) {
56+
tlog.Logf(t, "HTTPRoute correctly Accepted=False/NoMatchingListenerHostname: %s", cond.Message)
57+
return true, nil
58+
}
59+
tlog.Logf(t, "Accepted condition not yet expected (status=%s reason=%s)", cond.Status, cond.Reason)
60+
}
61+
}
62+
}
63+
return false, nil
64+
},
65+
))
66+
})
67+
68+
// Phase 2: Add a second listener whose hostname matches the route's hostname.
69+
// The original non-matching listener is kept.
70+
t.Run("matching listener added to gateway", func(t *testing.T) {
71+
tlog.Logf(t, "Adding matching listener (stale.example.com) to gateway %s", gwNN)
72+
require.NoError(t, wait.PollUntilContextTimeout(
73+
t.Context(), time.Second, suite.TimeoutConfig.MaxTimeToConsistency, true,
74+
func(ctx context.Context) (bool, error) {
75+
gw := &gwapiv1.Gateway{}
76+
if err := suite.Client.Get(ctx, gwNN, gw); err != nil {
77+
tlog.Logf(t, "failed to get Gateway: %v", err)
78+
return false, nil
79+
}
80+
gw.Spec.Listeners = []gwapiv1.Listener{
81+
{
82+
Name: "http-first",
83+
Port: 8888,
84+
Protocol: gwapiv1.HTTPProtocolType,
85+
Hostname: ptrTo(gwapiv1.Hostname("other.example.com")),
86+
AllowedRoutes: &gwapiv1.AllowedRoutes{
87+
Namespaces: &gwapiv1.RouteNamespaces{From: ptrTo(gwapiv1.NamespacesFromSame)},
88+
},
89+
},
90+
{
91+
Name: "http-match",
92+
Port: 8889,
93+
Protocol: gwapiv1.HTTPProtocolType,
94+
Hostname: ptrTo(gwapiv1.Hostname("stale.example.com")),
95+
AllowedRoutes: &gwapiv1.AllowedRoutes{
96+
Namespaces: &gwapiv1.RouteNamespaces{From: ptrTo(gwapiv1.NamespacesFromSame)},
97+
},
98+
},
99+
}
100+
if err := suite.Client.Update(ctx, gw, &client.UpdateOptions{}); err != nil {
101+
tlog.Logf(t, "failed to update Gateway: %v", err)
102+
return false, nil
103+
}
104+
tlog.Logf(t, "Gateway updated, new generation: %d", gw.Generation)
105+
return true, nil
106+
},
107+
))
108+
})
109+
110+
// Phase 3: The NoMatchingListenerHostname condition must now be gone.
111+
// The route must be Accepted=True / Accepted.
112+
t.Run("NoMatchingListenerHostname cleared and route accepted after matching listener added", func(t *testing.T) {
113+
tlog.Logf(t, "Waiting for HTTPRoute %s to be Accepted=True/Accepted", routeNN)
114+
require.NoError(t, wait.PollUntilContextTimeout(
115+
t.Context(), time.Second, suite.TimeoutConfig.MaxTimeToConsistency, true,
116+
func(ctx context.Context) (bool, error) {
117+
route := &gwapiv1.HTTPRoute{}
118+
if err := suite.Client.Get(ctx, routeNN, route); err != nil {
119+
tlog.Logf(t, "failed to get HTTPRoute: %v", err)
120+
return false, nil
121+
}
122+
for _, parent := range route.Status.Parents {
123+
for _, cond := range parent.Conditions {
124+
if cond.Type == string(gwapiv1.RouteConditionAccepted) {
125+
if cond.Status == metav1.ConditionTrue &&
126+
cond.Reason == string(gwapiv1.RouteReasonAccepted) {
127+
tlog.Logf(t, "NoMatchingListenerHostname cleared, route is Accepted=True/Accepted: %s", cond.Message)
128+
return true, nil
129+
}
130+
tlog.Logf(t, "Condition not yet updated (status=%s reason=%s msg=%s)", cond.Status, cond.Reason, cond.Message)
131+
}
132+
}
133+
}
134+
return false, nil
135+
},
136+
))
137+
})
138+
},
139+
}
140+
141+
func ptrTo[T any](v T) *T {
142+
return &v
143+
}

0 commit comments

Comments
 (0)