diff --git a/conformance/tests/tlsroute-invalid-reference-grant.go b/conformance/tests/tlsroute-invalid-reference-grant.go new file mode 100644 index 0000000000..3158af05ca --- /dev/null +++ b/conformance/tests/tlsroute-invalid-reference-grant.go @@ -0,0 +1,60 @@ +/* +Copyright 2022 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" +) + +func init() { + ConformanceTests = append(ConformanceTests, TLSRouteInvalidReferenceGrant) +} + +var TLSRouteInvalidReferenceGrant = suite.ConformanceTest{ + ShortName: "TLSRouteInvalidReferenceGrant", + Description: "A single TLSRoute in the gateway-conformance-infra namespace, with a backendRef in another namespace without valid ReferenceGrant, should have the ResolvedRefs condition set to False", + Features: []suite.SupportedFeature{ + suite.SupportGateway, + suite.SupportTLSRoute, + suite.SupportReferenceGrant, + }, + Manifests: []string{"tests/tlsroute-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-tlsroute", Namespace: "gateway-conformance-infra"} + + kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + t.Run("TLSRoute with BackendRef in another namespace and no ReferenceGrant covering the Service 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.TLSRouteMustHaveCondition(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN, resolvedRefsCond) + }) + }, +} diff --git a/conformance/tests/tlsroute-invalid-reference-grant.yaml b/conformance/tests/tlsroute-invalid-reference-grant.yaml new file mode 100644 index 0000000000..cf578ebc87 --- /dev/null +++ b/conformance/tests/tlsroute-invalid-reference-grant.yaml @@ -0,0 +1,141 @@ +--- +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: TLSRoute + namespace: gateway-conformance-infra + to: + - group: "" + kind: Service + name: tls-backend +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: reference-grant-wrong-from-group + namespace: gateway-conformance-tls-backend +spec: + from: + - group: not-the-group-youre-looking-for + kind: TLSRoute + namespace: gateway-conformance-infra + to: + - group: "" + kind: Service + name: tls-backend +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: reference-grant-wrong-from-kind + namespace: gateway-conformance-tls-backend +spec: + from: + - group: gateway.networking.k8s.io + kind: Gateway + namespace: gateway-conformance-infra + to: + - group: "" + kind: Service + name: tls-backend +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: reference-grant-wrong-from-namespace + namespace: gateway-conformance-tls-backend +spec: + from: + - group: gateway.networking.k8s.io + kind: TLSRoute + namespace: not-the-namespace-youre-looking-for + to: + - group: "" + kind: Service + name: tls-backend +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: reference-grant-wrong-to-group + namespace: gateway-conformance-tls-backend +spec: + from: + - group: gateway.networking.k8s.io + kind: TLSRoute + namespace: gateway-conformance-infra + to: + - group: not-the-group-youre-looking-for + kind: Service + name: tls-backend +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: reference-grant-wrong-to-kind + namespace: gateway-conformance-tls-backend +spec: + from: + - group: gateway.networking.k8s.io + kind: TLSRoute + namespace: gateway-conformance-infra + to: + - group: "" + kind: Secret + name: tls-backend +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: reference-grant-wrong-to-name + namespace: gateway-conformance-tls-backend +spec: + from: + - group: gateway.networking.k8s.io + kind: TLSRoute + namespace: gateway-conformance-infra + to: + - group: "" + kind: Service + name: not-the-service-youre-looking-for +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + name: gateway-conformance-infra-test + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: gateway-tlsroute + hostnames: + - abc.example.com + rules: + - backendRefs: + - name: tls-backend + namespace: gateway-conformance-tls-backend + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway-tlsroute + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: https + port: 443 + protocol: TLS + hostname: "*.example.com" + allowedRoutes: + namespaces: + from: Same + kinds: + - kind: TLSRoute + tls: + mode: Passthrough diff --git a/conformance/utils/config/timeout.go b/conformance/utils/config/timeout.go index 9d82a28088..dbaf2bec0f 100644 --- a/conformance/utils/config/timeout.go +++ b/conformance/utils/config/timeout.go @@ -154,4 +154,7 @@ func SetupTimeoutConfig(timeoutConfig *TimeoutConfig) { if timeoutConfig.LatestObservedGenerationSet == 0 { timeoutConfig.LatestObservedGenerationSet = defaultTimeoutConfig.LatestObservedGenerationSet } + if timeoutConfig.TLSRouteMustHaveCondition == 0 { + timeoutConfig.TLSRouteMustHaveCondition = defaultTimeoutConfig.TLSRouteMustHaveCondition + } } diff --git a/conformance/utils/kubernetes/helpers.go b/conformance/utils/kubernetes/helpers.go index dbf5e2ff1a..1056267e9d 100644 --- a/conformance/utils/kubernetes/helpers.go +++ b/conformance/utils/kubernetes/helpers.go @@ -611,6 +611,40 @@ func GatewayAndTLSRoutesMustBeAccepted(t *testing.T, c client.Client, timeoutCon return gwAddr, hostnames } +// TLSRouteMustHaveCondition checks that the supplied TLSRoute has the supplied Condition, +// halting after the specified timeout is exceeded. +func TLSRouteMustHaveCondition(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.TLSRouteMustHaveCondition, true, func(ctx context.Context) (bool, error) { + route := &v1alpha2.TLSRoute{} + err := client.Get(ctx, routeNN, route) + if err != nil { + return false, fmt.Errorf("error fetching TLSRoute: %w", err) + } + + parents := route.Status.Parents + var conditionFound bool + for _, parent := range parents { + if err := ConditionsHaveLatestObservedGeneration(route, parent.Conditions); err != nil { + + t.Logf("TLSRoute(parentRef=%v) %v", parentRefToString(parent.ParentRef), err) + return false, nil + } + + if parent.ParentRef.Name == v1beta1.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 TLSRoute 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 []v1beta1.ListenerStatus) bool { t.Helper()