Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
Copyright 2026 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"

v1 "sigs.k8s.io/gateway-api/apis/v1"
"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, TLSRouteInvalidNoMatchingListenerHostname)
}

var TLSRouteInvalidNoMatchingListenerHostname = suite.ConformanceTest{
ShortName: "TLSRouteInvalidNoMatchingListenerHostname",
Description: "A TLSRoute with a hostname that does not match the Gateway listener hostname should set Accepted=False with Reason=NoMatchingListenerHostname",
Features: []features.FeatureName{
features.SupportGateway,
features.SupportTLSRoute,
},
Manifests: []string{"tests/tlsroute-invalid-no-matching-listener-hostname.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
ns := "gateway-conformance-infra"
route1NN := types.NamespacedName{Name: "tlsroute-hostname-mismatch-1", Namespace: ns}
route2NN := types.NamespacedName{Name: "tlsroute-hostname-mismatch-2", Namespace: ns}
exactGwNN := types.NamespacedName{Name: "gateway-tls-exact-hostname", Namespace: ns}
wildcardGwNN := types.NamespacedName{Name: "gateway-tls-wildcard-hostname", Namespace: ns}
kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{ns})

acceptedCond := metav1.Condition{
Type: string(v1.RouteConditionAccepted),
Status: metav1.ConditionFalse,
Reason: string(v1.RouteReasonNoMatchingListenerHostname),
}

t.Run("TLSRoute 1 has Accepted=False for exact hostname Gateway", func(t *testing.T) {
kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, route1NN, exactGwNN, acceptedCond)
})

t.Run("TLSRoute 2 has Accepted=False for wildcard hostname Gateway", func(t *testing.T) {
kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, route2NN, wildcardGwNN, acceptedCond)
})

t.Run("TLSRoute 1 must have no accepted parents", func(t *testing.T) {
kubernetes.TLSRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, route1NN)
})

t.Run("TLSRoute 2 must have no accepted parents", func(t *testing.T) {
kubernetes.TLSRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, route2NN)
})

t.Run("Exact hostname Gateway must have 0 Routes attached", func(t *testing.T) {
kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, exactGwNN)
})

t.Run("Wildcard hostname Gateway must have 0 Routes attached", func(t *testing.T) {
kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, wildcardGwNN)
})
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-tls-exact-hostname
namespace: gateway-conformance-infra
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: tls
port: 443
protocol: TLS
hostname: "www.example.com"
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: TLSRoute
tls:
mode: Passthrough
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-tls-wildcard-hostname
namespace: gateway-conformance-infra
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: tls
port: 443
protocol: TLS
hostname: "*.example.com"
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: TLSRoute
tls:
mode: Passthrough
---
apiVersion: gateway.networking.k8s.io/v1alpha2
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind using v1alpha3 here and everywhere below?

kind: TLSRoute
metadata:
name: tlsroute-hostname-mismatch-1
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-tls-exact-hostname
hostnames:
- "www.nomatch.com"
- "*.nomatch.com"
rules:
- backendRefs:
- name: tls-backend
port: 443
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: tlsroute-hostname-mismatch-2
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-tls-wildcard-hostname
hostnames:
- "www.different.com"
- "*.different.com"
rules:
- backendRefs:
- name: tls-backend
port: 443
97 changes: 97 additions & 0 deletions conformance/tests/tlsroute-invalid-no-matching-listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
Copyright 2026 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"

v1 "sigs.k8s.io/gateway-api/apis/v1"
"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, TLSRouteInvalidNoMatchingListener)
}

var TLSRouteInvalidNoMatchingListener = suite.ConformanceTest{
ShortName: "TLSRouteInvalidNoMatchingListener",
Description: "A TLSRoute should set Accepted=False when attaching to a Gateway with no compatible TLS listener",
Features: []features.FeatureName{
features.SupportGateway,
features.SupportTLSRoute,
},
Manifests: []string{"tests/tlsroute-invalid-no-matching-listener.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
routeNoMatchingPortNN := types.NamespacedName{Name: "tlsroute-no-matching-listener", Namespace: "gateway-conformance-infra"}
routeNotAllowedKindNN := types.NamespacedName{Name: "tlsroute-not-allowed-kind", Namespace: "gateway-conformance-infra"}
routeWrongProtocolNN := types.NamespacedName{Name: "tlsroute-wrong-protocol", Namespace: "gateway-conformance-infra"}
routeWrongSectionNN := types.NamespacedName{Name: "tlsroute-wrong-section-name", Namespace: "gateway-conformance-infra"}

gwHTTPOnlyNN := types.NamespacedName{Name: "gateway-http-only", Namespace: "gateway-conformance-infra"}
gwTLSHTTPRouteOnlyNN := types.NamespacedName{Name: "gateway-tls-httproute-only", Namespace: "gateway-conformance-infra"}
gwHTTPSOnlyNN := types.NamespacedName{Name: "gateway-https-only", Namespace: "gateway-conformance-infra"}

acceptedCondNoMatchingParent := metav1.Condition{
Type: string(v1.RouteConditionAccepted),
Status: metav1.ConditionFalse,
Reason: string(v1.RouteReasonNoMatchingParent),
}
acceptedCondNotAllowed := metav1.Condition{
Type: string(v1.RouteConditionAccepted),
Status: metav1.ConditionFalse,
Reason: string(v1.RouteReasonNotAllowedByListeners),
}
t.Run("TLSRoute rejected when listener protocol is HTTP", func(t *testing.T) {
kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNoMatchingPortNN, gwHTTPOnlyNN, acceptedCondNotAllowed)
})
t.Run("TLSRoute rejected when kind not allowed", func(t *testing.T) {
kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNotAllowedKindNN, gwTLSHTTPRouteOnlyNN, acceptedCondNotAllowed)
})
t.Run("TLSRoute rejected when listener protocol is HTTPS", func(t *testing.T) {
kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeWrongProtocolNN, gwHTTPSOnlyNN, acceptedCondNotAllowed)
})
t.Run("TLSRoute rejected when sectionName not found", func(t *testing.T) {
kubernetes.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeWrongSectionNN, gwHTTPOnlyNN, acceptedCondNoMatchingParent)
})
t.Run("TLSRoute (HTTP listener) should not have Parents accepted", func(t *testing.T) {
kubernetes.TLSRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeNoMatchingPortNN)
})
t.Run("TLSRoute (kind not allowed) should not have Parents accepted", func(t *testing.T) {
kubernetes.TLSRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeNotAllowedKindNN)
})
t.Run("TLSRoute (HTTPS listener) should not have Parents accepted", func(t *testing.T) {
kubernetes.TLSRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeWrongProtocolNN)
})
t.Run("TLSRoute (wrong sectionName) should not have Parents accepted", func(t *testing.T) {
kubernetes.TLSRouteMustHaveNoAcceptedParents(t, suite.Client, suite.TimeoutConfig, routeWrongSectionNN)
})
t.Run("Gateway HTTP-only should have 0 Routes attached", func(t *testing.T) {
kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, gwHTTPOnlyNN)
})
t.Run("Gateway TLS-HTTPRoute-only should have 0 Routes attached", func(t *testing.T) {
kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, gwTLSHTTPRouteOnlyNN)
})
t.Run("Gateway HTTPS-only should have 0 Routes attached", func(t *testing.T) {
kubernetes.GatewayMustHaveZeroRoutes(t, suite.Client, suite.TimeoutConfig, gwHTTPSOnlyNN)
})
},
}
117 changes: 117 additions & 0 deletions conformance/tests/tlsroute-invalid-no-matching-listener.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-http-only
namespace: gateway-conformance-infra
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: TLSRoute
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: tlsroute-no-matching-listener
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-http-only
hostnames:
- tls.example.com
rules:
- backendRefs:
- name: tls-backend
port: 443
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-tls-httproute-only
namespace: gateway-conformance-infra
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: HTTPRoute
Comment on lines +35 to +49
Copy link
Copy Markdown
Member

@rostislavbobo rostislavbobo Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this Gateway is used by "tlsroute-not-allowed-kind" to verify that the TLSRoute is not attached when allowedRoutes.kinds doesn't list it.

In that case, wouldn't it be better to configure the listener with protocol: TLS instead of HTTP? So that we have a valid attachment that is only prevented by the allowedRoutes.kinds.

---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway-https-only
namespace: gateway-conformance-infra
spec:
gatewayClassName: "{GATEWAY_CLASS_NAME}"
listeners:
- name: https
port: 443
protocol: HTTPS
allowedRoutes:
namespaces:
from: Same
kinds:
- kind: TLSRoute
tls:
mode: Terminate
certificateRefs:
- name: tls-validity-checks-certificate
namespace: gateway-conformance-infra
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: tlsroute-not-allowed-kind
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-tls-httproute-only
hostnames:
- tls.example.com
rules:
- backendRefs:
- name: tls-backend
port: 443
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: tlsroute-wrong-protocol
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-https-only
hostnames:
- tls.example.com
rules:
- backendRefs:
- name: tls-backend
port: 443
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: tlsroute-wrong-section-name
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: gateway-http-only
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I'd use a valid Gateway here for attachment to check that the wrong sectionName is the only preventing the attachment

sectionName: nonexistent-listener
hostnames:
- tls.example.com
rules:
- backendRefs:
- name: tls-backend
port: 443
Loading