Skip to content

Commit 4b4820e

Browse files
committed
Adds TCP Gateway conformance profile and initial test
Signed-off-by: Daneyon Hansen <[email protected]>
1 parent 1dc9d2e commit 4b4820e

File tree

7 files changed

+366
-0
lines changed

7 files changed

+366
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2023 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+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/apimachinery/pkg/types"
24+
25+
"sigs.k8s.io/gateway-api/apis/v1beta1"
26+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
27+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
28+
"sigs.k8s.io/gateway-api/pkg/features"
29+
)
30+
31+
func init() {
32+
ConformanceTests = append(ConformanceTests, TCPRouteInvalidReferenceGrant)
33+
}
34+
35+
var TCPRouteInvalidReferenceGrant = suite.ConformanceTest{
36+
ShortName: "TCPRouteInvalidReferenceGrant",
37+
Description: "A single TCPRoute in the gateway-conformance-infra namespace with a backendRef in another namespace without valid ReferenceGrant, should have the ResolvedRefs condition set to False",
38+
Features: []features.FeatureName{
39+
features.SupportGateway,
40+
features.SupportTCPRoute,
41+
features.SupportReferenceGrant,
42+
},
43+
Manifests: []string{"tests/tcproute-invalid-reference-grant.yaml"},
44+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
45+
routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: "gateway-conformance-infra"}
46+
gwNN := types.NamespacedName{Name: "gateway-tcproute-referencegrant", Namespace: "gateway-conformance-infra"}
47+
48+
kubernetes.GatewayAndTCPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
49+
50+
t.Run("TCPRoute with BackendRef to a Service in another namespace and no ReferenceGrant has a ResolvedRefs Condition with status False and Reason RefNotPermitted", func(t *testing.T) {
51+
resolvedRefsCond := metav1.Condition{
52+
Type: string(v1beta1.RouteConditionResolvedRefs),
53+
Status: metav1.ConditionFalse,
54+
Reason: string(v1beta1.RouteReasonRefNotPermitted),
55+
}
56+
57+
kubernetes.TCPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
58+
})
59+
},
60+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
apiVersion: gateway.networking.k8s.io/v1beta1
3+
kind: ReferenceGrant
4+
metadata:
5+
name: reference-grant-wrong-namespace
6+
namespace: gateway-conformance-infra
7+
spec:
8+
from:
9+
- group: gateway.networking.k8s.io
10+
kind: TCPRoute
11+
namespace: gateway-conformance-infra
12+
to:
13+
- group: ""
14+
kind: Service
15+
name: tcp-backend
16+
---
17+
apiVersion: gateway.networking.k8s.io/v1beta1
18+
kind: ReferenceGrant
19+
metadata:
20+
name: reference-grant-wrong-from-group
21+
namespace: gateway-conformance-app-backend
22+
spec:
23+
from:
24+
- group: not-the-group-youre-looking-for
25+
kind: TCPRoute
26+
namespace: gateway-conformance-infra
27+
to:
28+
- group: ""
29+
kind: Service
30+
name: tcp-backend
31+
---
32+
apiVersion: gateway.networking.k8s.io/v1beta1
33+
kind: ReferenceGrant
34+
metadata:
35+
name: reference-grant-wrong-from-kind
36+
namespace: gateway-conformance-app-backend
37+
spec:
38+
from:
39+
- group: gateway.networking.k8s.io
40+
kind: Gateway
41+
namespace: gateway-conformance-infra
42+
to:
43+
- group: ""
44+
kind: Service
45+
name: tcp-backend
46+
---
47+
apiVersion: gateway.networking.k8s.io/v1beta1
48+
kind: ReferenceGrant
49+
metadata:
50+
name: reference-grant-wrong-from-namespace
51+
namespace: gateway-conformance-app-backend
52+
spec:
53+
from:
54+
- group: gateway.networking.k8s.io
55+
kind: TCPRoute
56+
namespace: not-the-namespace-youre-looking-for
57+
to:
58+
- group: ""
59+
kind: Service
60+
name: tcp-backend
61+
---
62+
apiVersion: gateway.networking.k8s.io/v1beta1
63+
kind: ReferenceGrant
64+
metadata:
65+
name: reference-grant-wrong-to-group
66+
namespace: gateway-conformance-app-backend
67+
spec:
68+
from:
69+
- group: gateway.networking.k8s.io
70+
kind: TCPRoute
71+
namespace: gateway-conformance-infra
72+
to:
73+
- group: not-the-group-youre-looking-for
74+
kind: Service
75+
name: tcp-backend
76+
---
77+
apiVersion: gateway.networking.k8s.io/v1beta1
78+
kind: ReferenceGrant
79+
metadata:
80+
name: reference-grant-wrong-to-kind
81+
namespace: gateway-conformance-app-backend
82+
spec:
83+
from:
84+
- group: gateway.networking.k8s.io
85+
kind: TCPRoute
86+
namespace: gateway-conformance-infra
87+
to:
88+
- group: ""
89+
kind: Secret
90+
name: tcp-backend
91+
---
92+
apiVersion: gateway.networking.k8s.io/v1beta1
93+
kind: ReferenceGrant
94+
metadata:
95+
name: reference-grant-wrong-to-name
96+
namespace: gateway-conformance-app-backend
97+
spec:
98+
from:
99+
- group: gateway.networking.k8s.io
100+
kind: TCPRoute
101+
namespace: gateway-conformance-infra
102+
to:
103+
- group: ""
104+
kind: Service
105+
name: not-the-service-youre-looking-for
106+
---
107+
apiVersion: gateway.networking.k8s.io/v1alpha2
108+
kind: TCPRoute
109+
metadata:
110+
name: gateway-conformance-infra-test
111+
namespace: gateway-conformance-infra
112+
spec:
113+
parentRefs:
114+
- name: gateway-tcproute-referencegrant
115+
rules:
116+
- backendRefs:
117+
- name: tcp-backend
118+
namespace: gateway-conformance-app-backend
119+
port: 1234
120+
---
121+
apiVersion: gateway.networking.k8s.io/v1beta1
122+
kind: Gateway
123+
metadata:
124+
name: gateway-tcproute-referencegrant
125+
namespace: gateway-conformance-infra
126+
spec:
127+
gatewayClassName: "{GATEWAY_CLASS_NAME}"
128+
listeners:
129+
- name: tcp
130+
port: 1234
131+
protocol: TCP
132+
allowedRoutes:
133+
namespaces:
134+
from: Same
135+
kinds:
136+
- kind: TCPRoute

conformance/utils/config/timeout.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ type TimeoutConfig struct {
6868
// Max value for conformant implementation: None
6969
TLSRouteMustHaveCondition time.Duration
7070

71+
// TCPRouteMustHaveCondition represents the maximum time for a TCPRoute to have the supplied Condition.
72+
// Max value for conformant implementation: None
73+
TCPRouteMustHaveCondition time.Duration
74+
7175
// RouteMustHaveParents represents the maximum time for an xRoute to have parents in status that match the expected parents.
7276
// Max value for conformant implementation: None
7377
RouteMustHaveParents time.Duration
@@ -118,6 +122,7 @@ func DefaultTimeoutConfig() TimeoutConfig {
118122
HTTPRouteMustNotHaveParents: 60 * time.Second,
119123
HTTPRouteMustHaveCondition: 60 * time.Second,
120124
TLSRouteMustHaveCondition: 60 * time.Second,
125+
TCPRouteMustHaveCondition: 60 * time.Second,
121126
RouteMustHaveParents: 60 * time.Second,
122127
ManifestFetchTimeout: 10 * time.Second,
123128
MaxTimeToConsistency: 30 * time.Second,
@@ -182,6 +187,9 @@ func SetupTimeoutConfig(timeoutConfig *TimeoutConfig) {
182187
if timeoutConfig.TLSRouteMustHaveCondition == 0 {
183188
timeoutConfig.TLSRouteMustHaveCondition = defaultTimeoutConfig.TLSRouteMustHaveCondition
184189
}
190+
if timeoutConfig.TCPRouteMustHaveCondition == 0 {
191+
timeoutConfig.TCPRouteMustHaveCondition = defaultTimeoutConfig.TCPRouteMustHaveCondition
192+
}
185193
if timeoutConfig.DefaultTestTimeout == 0 {
186194
timeoutConfig.DefaultTestTimeout = defaultTimeoutConfig.DefaultTestTimeout
187195
}

conformance/utils/kubernetes/helpers.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,30 @@ func TLSRouteMustHaveParents(t *testing.T, client client.Client, timeoutConfig c
649649
return route
650650
}
651651

652+
// TCPRouteMustHaveParents waits for the specified TCPRoute to have parents
653+
// in status that match the expected parents, and also returns the TCPRoute.
654+
// This will cause the test to halt if the specified timeout is exceeded.
655+
func TCPRouteMustHaveParents(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeName types.NamespacedName, parents []v1alpha2.RouteParentStatus, namespaceRequired bool) v1alpha2.TCPRoute {
656+
t.Helper()
657+
658+
var actual []gatewayv1.RouteParentStatus
659+
var route v1alpha2.TCPRoute
660+
661+
waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.RouteMustHaveParents, true, func(ctx context.Context) (bool, error) {
662+
err := client.Get(ctx, routeName, &route)
663+
if err != nil {
664+
return false, fmt.Errorf("error fetching TCPRoute: %w", err)
665+
}
666+
actual = route.Status.Parents
667+
match := parentsForRouteMatch(t, routeName, parents, actual, namespaceRequired)
668+
669+
return match, nil
670+
})
671+
require.NoErrorf(t, waitErr, "error waiting for TCPRoute to have parents matching expectations")
672+
673+
return route
674+
}
675+
652676
func parentsForRouteMatch(t *testing.T, routeName types.NamespacedName, expected, actual []gatewayv1.RouteParentStatus, namespaceRequired bool) bool {
653677
t.Helper()
654678

@@ -851,6 +875,86 @@ func TLSRouteMustHaveCondition(t *testing.T, client client.Client, timeoutConfig
851875
require.NoErrorf(t, waitErr, "error waiting for TLSRoute status to have a Condition matching expectations")
852876
}
853877

878+
// GatewayAndTCPRoutesMustBeAccepted waits until the specified Gateway has an IP
879+
// address assigned to it and the TCPRoute has a ParentRef referring to the
880+
// Gateway. The test will fail if these conditions are not met before the
881+
// timeouts.
882+
func GatewayAndTCPRoutesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig config.TimeoutConfig, controllerName string, gw GatewayRef, routeNNs ...types.NamespacedName) string {
883+
t.Helper()
884+
885+
gwAddr, err := WaitForGatewayAddress(t, c, timeoutConfig, gw)
886+
require.NoErrorf(t, err, "timed out waiting for Gateway address to be assigned")
887+
888+
ns := gatewayv1.Namespace(gw.Namespace)
889+
kind := gatewayv1.Kind("Gateway")
890+
891+
for _, routeNN := range routeNNs {
892+
namespaceRequired := true
893+
if routeNN.Namespace == gw.Namespace {
894+
namespaceRequired = false
895+
}
896+
897+
var parents []gatewayv1.RouteParentStatus
898+
for _, listener := range gw.listenerNames {
899+
parents = append(parents, gatewayv1.RouteParentStatus{
900+
ParentRef: gatewayv1.ParentReference{
901+
Group: (*gatewayv1.Group)(&gatewayv1.GroupVersion.Group),
902+
Kind: &kind,
903+
Name: gatewayv1.ObjectName(gw.Name),
904+
Namespace: &ns,
905+
SectionName: listener,
906+
},
907+
ControllerName: gatewayv1.GatewayController(controllerName),
908+
Conditions: []metav1.Condition{
909+
{
910+
Type: string(gatewayv1.RouteConditionAccepted),
911+
Status: metav1.ConditionTrue,
912+
Reason: string(gatewayv1.RouteReasonAccepted),
913+
},
914+
},
915+
})
916+
}
917+
_ = TCPRouteMustHaveParents(t, c, timeoutConfig, routeNN, parents, namespaceRequired)
918+
}
919+
920+
return gwAddr
921+
}
922+
923+
// TCPRouteMustHaveCondition checks that the supplied TCPRoute has the supplied Condition,
924+
// halting after the specified timeout is exceeded.
925+
func TCPRouteMustHaveCondition(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeNN types.NamespacedName, gwNN types.NamespacedName, condition metav1.Condition) {
926+
t.Helper()
927+
928+
waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.TCPRouteMustHaveCondition, true, func(ctx context.Context) (bool, error) {
929+
route := &v1alpha2.TCPRoute{}
930+
err := client.Get(ctx, routeNN, route)
931+
if err != nil {
932+
return false, fmt.Errorf("error fetching TCPRoute: %w", err)
933+
}
934+
935+
parents := route.Status.Parents
936+
var conditionFound bool
937+
for _, parent := range parents {
938+
if err := ConditionsHaveLatestObservedGeneration(route, parent.Conditions); err != nil {
939+
tlog.Logf(t, "TCPRoute %s (parentRef=%v) %v",
940+
routeNN, parentRefToString(parent.ParentRef), err,
941+
)
942+
return false, nil
943+
}
944+
945+
if parent.ParentRef.Name == gatewayv1.ObjectName(gwNN.Name) && (parent.ParentRef.Namespace == nil || string(*parent.ParentRef.Namespace) == gwNN.Namespace) {
946+
if findConditionInList(t, parent.Conditions, condition.Type, string(condition.Status), condition.Reason) {
947+
conditionFound = true
948+
}
949+
}
950+
}
951+
952+
return conditionFound, nil
953+
})
954+
955+
require.NoErrorf(t, waitErr, "error waiting for TCPRoute status to have a Condition matching expectations")
956+
}
957+
854958
// TODO(mikemorris): this and parentsMatch could possibly be rewritten as a generic function?
855959
func listenersMatch(t *testing.T, expected, actual []gatewayv1.ListenerStatus) bool {
856960
t.Helper()

conformance/utils/suite/profiles.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ const (
5454
// which covers GRPC functionality with Gateways.
5555
GatewayGRPCConformanceProfileName ConformanceProfileName = "GATEWAY-GRPC"
5656

57+
// GatewayTCPConformanceProfileName indicates the name of the conformance profile
58+
// which covers TCP functionality with Gateways.
59+
GatewayTCPConformanceProfileName ConformanceProfileName = "GATEWAY-TCP"
60+
5761
// MeshHTTPConformanceProfileName indicates the name of the conformance profile
5862
// which covers HTTP functionality with service mesh.
5963
MeshHTTPConformanceProfileName ConformanceProfileName = "MESH-HTTP"
@@ -108,6 +112,18 @@ var (
108112
ExtendedFeatures: features.SetsToNamesSet(features.GatewayExtendedFeatures),
109113
}
110114

115+
// GatewayTCPConformanceProfile is a ConformanceProfile that covers testing TCP
116+
// related functionality with Gateways.
117+
GatewayTCPConformanceProfile = ConformanceProfile{
118+
Name: GatewayTCPConformanceProfileName,
119+
CoreFeatures: sets.New(
120+
features.SupportGateway,
121+
features.SupportReferenceGrant,
122+
features.SupportTCPRoute,
123+
),
124+
ExtendedFeatures: features.SetsToNamesSet(features.GatewayExtendedFeatures),
125+
}
126+
111127
// MeshHTTPConformanceProfile is a ConformanceProfile that covers testing HTTP
112128
// service mesh related functionality.
113129
MeshHTTPConformanceProfile = ConformanceProfile{
@@ -155,6 +171,7 @@ var conformanceProfileMap = map[ConformanceProfileName]ConformanceProfile{
155171
GatewayHTTPConformanceProfileName: GatewayHTTPConformanceProfile,
156172
GatewayTLSConformanceProfileName: GatewayTLSConformanceProfile,
157173
GatewayGRPCConformanceProfileName: GatewayGRPCConformanceProfile,
174+
GatewayTCPConformanceProfileName: GatewayTCPConformanceProfile,
158175
MeshHTTPConformanceProfileName: MeshHTTPConformanceProfile,
159176
MeshGRPCConformanceProfileName: MeshGRPCConformanceProfile,
160177
}

pkg/features/features.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ var (
5858
Insert(HTTPRouteCoreFeatures.UnsortedList()...).
5959
Insert(HTTPRouteExtendedFeatures.UnsortedList()...).
6060
Insert(TLSRouteCoreFeatures.UnsortedList()...).
61+
Insert(TCPRouteCoreFeatures.UnsortedList()...).
6162
Insert(MeshCoreFeatures.UnsortedList()...).
6263
Insert(MeshExtendedFeatures.UnsortedList()...).
6364
Insert(GRPCRouteCoreFeatures.UnsortedList()...)

0 commit comments

Comments
 (0)