Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds "GATEWAY TCP" Conformance Profile and Initial TCPRoute Test #3495

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions conformance/tests/tcproute-invalid-reference-grant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tests

import (
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"sigs.k8s.io/gateway-api/apis/v1beta1"
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
"sigs.k8s.io/gateway-api/conformance/utils/suite"
"sigs.k8s.io/gateway-api/pkg/features"
)

func init() {
ConformanceTests = append(ConformanceTests, TCPRouteInvalidReferenceGrant)
}

var TCPRouteInvalidReferenceGrant = suite.ConformanceTest{
ShortName: "TCPRouteInvalidReferenceGrant",
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",
Features: []features.FeatureName{
features.SupportGateway,
features.SupportTCPRoute,
features.SupportReferenceGrant,
},
Manifests: []string{"tests/tcproute-invalid-reference-grant.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: "gateway-conformance-infra"}
gwNN := types.NamespacedName{Name: "gateway-tcproute-referencegrant", Namespace: "gateway-conformance-infra"}

kubernetes.GatewayAndTCPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

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) {
resolvedRefsCond := metav1.Condition{
Type: string(v1beta1.RouteConditionResolvedRefs),
Status: metav1.ConditionFalse,
Reason: string(v1beta1.RouteReasonRefNotPermitted),
}

kubernetes.TCPRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond)
})
},
}
136 changes: 136 additions & 0 deletions conformance/tests/tcproute-invalid-reference-grant.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: reference-grant-wrong-namespace
namespace: gateway-conformance-infra
spec:
from:
- group: gateway.networking.k8s.io
kind: TCPRoute
namespace: gateway-conformance-infra
to:
- group: ""
kind: Service
name: tcp-backend
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: reference-grant-wrong-from-group
namespace: gateway-conformance-app-backend
spec:
from:
- group: not-the-group-youre-looking-for
kind: TCPRoute
namespace: gateway-conformance-infra
to:
- group: ""
kind: Service
name: tcp-backend
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: reference-grant-wrong-from-kind
namespace: gateway-conformance-app-backend
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: gateway-conformance-infra
to:
- group: ""
kind: Service
name: tcp-backend
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: reference-grant-wrong-from-namespace
namespace: gateway-conformance-app-backend
spec:
from:
- group: gateway.networking.k8s.io
kind: TCPRoute
namespace: not-the-namespace-youre-looking-for
to:
- group: ""
kind: Service
name: tcp-backend
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: reference-grant-wrong-to-group
namespace: gateway-conformance-app-backend
spec:
from:
- group: gateway.networking.k8s.io
kind: TCPRoute
namespace: gateway-conformance-infra
to:
- group: not-the-group-youre-looking-for
kind: Service
name: tcp-backend
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: reference-grant-wrong-to-kind
namespace: gateway-conformance-app-backend
spec:
from:
- group: gateway.networking.k8s.io
kind: TCPRoute
namespace: gateway-conformance-infra
to:
- group: ""
kind: Secret
name: tcp-backend
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: reference-grant-wrong-to-name
namespace: gateway-conformance-app-backend
spec:
from:
- group: gateway.networking.k8s.io
kind: TCPRoute
namespace: gateway-conformance-infra
to:
- group: ""
kind: Service
name: not-the-service-youre-looking-for
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
name: gateway-conformance-infra-test
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-tcproute-referencegrant
rules:
- backendRefs:
- name: tcp-backend
namespace: gateway-conformance-app-backend
port: 1234
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: gateway-tcproute-referencegrant
namespace: gateway-conformance-infra
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: tcp
port: 1234
protocol: TCP
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: TCPRoute
8 changes: 8 additions & 0 deletions conformance/utils/config/timeout.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ type TimeoutConfig struct {
// Max value for conformant implementation: None
TLSRouteMustHaveCondition time.Duration

// TCPRouteMustHaveCondition represents the maximum time for a TCPRoute to have the supplied Condition.
// Max value for conformant implementation: None
TCPRouteMustHaveCondition time.Duration

// RouteMustHaveParents represents the maximum time for an xRoute to have parents in status that match the expected parents.
// Max value for conformant implementation: None
RouteMustHaveParents time.Duration
Expand Down Expand Up @@ -118,6 +122,7 @@ func DefaultTimeoutConfig() TimeoutConfig {
HTTPRouteMustNotHaveParents: 60 * time.Second,
HTTPRouteMustHaveCondition: 60 * time.Second,
TLSRouteMustHaveCondition: 60 * time.Second,
TCPRouteMustHaveCondition: 60 * time.Second,
RouteMustHaveParents: 60 * time.Second,
ManifestFetchTimeout: 10 * time.Second,
MaxTimeToConsistency: 30 * time.Second,
Expand Down Expand Up @@ -182,6 +187,9 @@ func SetupTimeoutConfig(timeoutConfig *TimeoutConfig) {
if timeoutConfig.TLSRouteMustHaveCondition == 0 {
timeoutConfig.TLSRouteMustHaveCondition = defaultTimeoutConfig.TLSRouteMustHaveCondition
}
if timeoutConfig.TCPRouteMustHaveCondition == 0 {
timeoutConfig.TCPRouteMustHaveCondition = defaultTimeoutConfig.TCPRouteMustHaveCondition
}
if timeoutConfig.DefaultTestTimeout == 0 {
timeoutConfig.DefaultTestTimeout = defaultTimeoutConfig.DefaultTestTimeout
}
Expand Down
104 changes: 104 additions & 0 deletions conformance/utils/kubernetes/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,30 @@ func TLSRouteMustHaveParents(t *testing.T, client client.Client, timeoutConfig c
return route
}

// TCPRouteMustHaveParents waits for the specified TCPRoute to have parents
// in status that match the expected parents, and also returns the TCPRoute.
// This will cause the test to halt if the specified timeout is exceeded.
func TCPRouteMustHaveParents(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeName types.NamespacedName, parents []v1alpha2.RouteParentStatus, namespaceRequired bool) v1alpha2.TCPRoute {
t.Helper()

var actual []gatewayv1.RouteParentStatus
var route v1alpha2.TCPRoute

waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.RouteMustHaveParents, true, func(ctx context.Context) (bool, error) {
err := client.Get(ctx, routeName, &route)
if err != nil {
return false, fmt.Errorf("error fetching TCPRoute: %w", err)
}
actual = route.Status.Parents
match := parentsForRouteMatch(t, routeName, parents, actual, namespaceRequired)

return match, nil
})
require.NoErrorf(t, waitErr, "error waiting for TCPRoute to have parents matching expectations")

return route
}

func parentsForRouteMatch(t *testing.T, routeName types.NamespacedName, expected, actual []gatewayv1.RouteParentStatus, namespaceRequired bool) bool {
t.Helper()

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

// GatewayAndTCPRoutesMustBeAccepted waits until the specified Gateway has an IP
// address assigned to it and the TCPRoute has a ParentRef referring to the
// Gateway. The test will fail if these conditions are not met before the
// timeouts.
func GatewayAndTCPRoutesMustBeAccepted(t *testing.T, c client.Client, timeoutConfig config.TimeoutConfig, controllerName string, gw GatewayRef, routeNNs ...types.NamespacedName) string {
t.Helper()

gwAddr, err := WaitForGatewayAddress(t, c, timeoutConfig, gw)
require.NoErrorf(t, err, "timed out waiting for Gateway address to be assigned")

ns := gatewayv1.Namespace(gw.Namespace)
kind := gatewayv1.Kind("Gateway")

for _, routeNN := range routeNNs {
namespaceRequired := true
if routeNN.Namespace == gw.Namespace {
namespaceRequired = false
}

var parents []gatewayv1.RouteParentStatus
for _, listener := range gw.listenerNames {
parents = append(parents, gatewayv1.RouteParentStatus{
ParentRef: gatewayv1.ParentReference{
Group: (*gatewayv1.Group)(&gatewayv1.GroupVersion.Group),
Kind: &kind,
Name: gatewayv1.ObjectName(gw.Name),
Namespace: &ns,
SectionName: listener,
},
ControllerName: gatewayv1.GatewayController(controllerName),
Conditions: []metav1.Condition{
{
Type: string(gatewayv1.RouteConditionAccepted),
Status: metav1.ConditionTrue,
Reason: string(gatewayv1.RouteReasonAccepted),
},
},
})
}
_ = TCPRouteMustHaveParents(t, c, timeoutConfig, routeNN, parents, namespaceRequired)
}

return gwAddr
}

// TCPRouteMustHaveCondition checks that the supplied TCPRoute has the supplied Condition,
// halting after the specified timeout is exceeded.
func TCPRouteMustHaveCondition(t *testing.T, client client.Client, timeoutConfig config.TimeoutConfig, routeNN types.NamespacedName, gwNN types.NamespacedName, condition metav1.Condition) {
t.Helper()

waitErr := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeoutConfig.TCPRouteMustHaveCondition, true, func(ctx context.Context) (bool, error) {
route := &v1alpha2.TCPRoute{}
err := client.Get(ctx, routeNN, route)
if err != nil {
return false, fmt.Errorf("error fetching TCPRoute: %w", err)
}

parents := route.Status.Parents
var conditionFound bool
for _, parent := range parents {
if err := ConditionsHaveLatestObservedGeneration(route, parent.Conditions); err != nil {
tlog.Logf(t, "TCPRoute %s (parentRef=%v) %v",
routeNN, parentRefToString(parent.ParentRef), err,
)
return false, nil
}

if parent.ParentRef.Name == gatewayv1.ObjectName(gwNN.Name) && (parent.ParentRef.Namespace == nil || string(*parent.ParentRef.Namespace) == gwNN.Namespace) {
if findConditionInList(t, parent.Conditions, condition.Type, string(condition.Status), condition.Reason) {
conditionFound = true
}
}
}

return conditionFound, nil
})

require.NoErrorf(t, waitErr, "error waiting for TCPRoute status to have a Condition matching expectations")
}

// TODO(mikemorris): this and parentsMatch could possibly be rewritten as a generic function?
func listenersMatch(t *testing.T, expected, actual []gatewayv1.ListenerStatus) bool {
t.Helper()
Expand Down
17 changes: 17 additions & 0 deletions conformance/utils/suite/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ const (
// which covers GRPC functionality with Gateways.
GatewayGRPCConformanceProfileName ConformanceProfileName = "GATEWAY-GRPC"

// GatewayTCPConformanceProfileName indicates the name of the conformance profile
// which covers TCP functionality with Gateways.
GatewayTCPConformanceProfileName ConformanceProfileName = "GATEWAY-TCP"

// MeshHTTPConformanceProfileName indicates the name of the conformance profile
// which covers HTTP functionality with service mesh.
MeshHTTPConformanceProfileName ConformanceProfileName = "MESH-HTTP"
Expand Down Expand Up @@ -108,6 +112,18 @@ var (
ExtendedFeatures: features.SetsToNamesSet(features.GatewayExtendedFeatures),
}

// GatewayTCPConformanceProfile is a ConformanceProfile that covers testing TCP
// related functionality with Gateways.
GatewayTCPConformanceProfile = ConformanceProfile{
Name: GatewayTCPConformanceProfileName,
CoreFeatures: sets.New(
features.SupportGateway,
features.SupportReferenceGrant,
features.SupportTCPRoute,
),
ExtendedFeatures: features.SetsToNamesSet(features.GatewayExtendedFeatures),
}

// MeshHTTPConformanceProfile is a ConformanceProfile that covers testing HTTP
// service mesh related functionality.
MeshHTTPConformanceProfile = ConformanceProfile{
Expand Down Expand Up @@ -155,6 +171,7 @@ var conformanceProfileMap = map[ConformanceProfileName]ConformanceProfile{
GatewayHTTPConformanceProfileName: GatewayHTTPConformanceProfile,
GatewayTLSConformanceProfileName: GatewayTLSConformanceProfile,
GatewayGRPCConformanceProfileName: GatewayGRPCConformanceProfile,
GatewayTCPConformanceProfileName: GatewayTCPConformanceProfile,
MeshHTTPConformanceProfileName: MeshHTTPConformanceProfile,
MeshGRPCConformanceProfileName: MeshGRPCConformanceProfile,
}
Expand Down
1 change: 1 addition & 0 deletions pkg/features/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ var (
Insert(HTTPRouteCoreFeatures.UnsortedList()...).
Insert(HTTPRouteExtendedFeatures.UnsortedList()...).
Insert(TLSRouteCoreFeatures.UnsortedList()...).
Insert(TCPRouteCoreFeatures.UnsortedList()...).
Insert(MeshCoreFeatures.UnsortedList()...).
Insert(MeshExtendedFeatures.UnsortedList()...).
Insert(GRPCRouteCoreFeatures.UnsortedList()...)
Expand Down
Loading