diff --git a/apis/v1alpha2/backendtlspolicy_types.go b/apis/v1alpha2/backendtlspolicy_types.go index bb8b696dce..ee8c2771b9 100644 --- a/apis/v1alpha2/backendtlspolicy_types.go +++ b/apis/v1alpha2/backendtlspolicy_types.go @@ -75,8 +75,8 @@ type BackendTLSPolicySpec struct { } // BackendTLSPolicyConfig contains backend TLS policy configuration. -// +kubebuilder:validation:XValidation:message="must not contain both CertRefs and WellKnownCACerts",rule="(has(self.caCertRefs) && size(self.caCertRefs) > 0 && has(self.wellKnownCACerts) && self.wellKnownCACerts != \"\")" -// +kubebuilder:validation:XValidation:message="must specify either CertRefs or WellKnownCACerts",rule="!(has(self.caCertRefs) && size(self.caCertRefs) > 0 || has(self.wellKnownCACerts) && self.wellKnownCACerts != \"\")" +// +kubebuilder:validation:XValidation:message="must not contain both CACertRefs and WellKnownCACerts",rule="!(has(self.caCertRefs) && size(self.caCertRefs) > 0 && has(self.wellKnownCACerts) && self.wellKnownCACerts != \"\")" +// +kubebuilder:validation:XValidation:message="must specify either CACertRefs or WellKnownCACerts",rule="(has(self.caCertRefs) && size(self.caCertRefs) > 0 || has(self.wellKnownCACerts) && self.wellKnownCACerts != \"\")" type BackendTLSPolicyConfig struct { // CACertRefs contains one or more references to Kubernetes objects that // contain a PEM-encoded TLS CA certificate bundle, which is used to diff --git a/config/crd/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml b/config/crd/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml index e58f040570..4350edf409 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml @@ -178,12 +178,12 @@ spec: - hostname type: object x-kubernetes-validations: - - message: must not contain both CertRefs and WellKnownCACerts - rule: (has(self.caCertRefs) && size(self.caCertRefs) > 0 && has(self.wellKnownCACerts) - && self.wellKnownCACerts != "") - - message: must specify either CertRefs or WellKnownCACerts - rule: '!(has(self.caCertRefs) && size(self.caCertRefs) > 0 || has(self.wellKnownCACerts) + - message: must not contain both CACertRefs and WellKnownCACerts + rule: '!(has(self.caCertRefs) && size(self.caCertRefs) > 0 && has(self.wellKnownCACerts) && self.wellKnownCACerts != "")' + - message: must specify either CACertRefs or WellKnownCACerts + rule: (has(self.caCertRefs) && size(self.caCertRefs) > 0 || has(self.wellKnownCACerts) + && self.wellKnownCACerts != "") required: - targetRef - tls diff --git a/pkg/test/cel/backendtlspolicy_test.go b/pkg/test/cel/backendtlspolicy_test.go new file mode 100644 index 0000000000..f56447298d --- /dev/null +++ b/pkg/test/cel/backendtlspolicy_test.go @@ -0,0 +1,149 @@ +//go:build experimental +// +build experimental + +/* +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 main + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gatewayv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +func TestBackendTLSPolicyConfig(t *testing.T) { + tests := []struct { + name string + wantErrors []string + routeConfig gatewayv1a2.BackendTLSPolicyConfig + }{ + { + name: "valid BackendTLSPolicyConfig with WellKnownCACerts", + routeConfig: gatewayv1a2.BackendTLSPolicyConfig{ + WellKnownCACerts: ptrTo(gatewayv1a2.WellKnownCACertType("System")), + Hostname: "foo.example.com", + }, + wantErrors: []string{}, + }, + { + name: "valid BackendTLSPolicyConfig with CACertRefs", + routeConfig: gatewayv1a2.BackendTLSPolicyConfig{ + CACertRefs: []v1beta1.LocalObjectReference{ + { + Group: "group", + Kind: "kind", + Name: "name", + }, + }, + Hostname: "foo.example.com", + }, + wantErrors: []string{}, + }, + { + name: "invalid BackendTLSPolicyConfig with missing fields", + routeConfig: gatewayv1a2.BackendTLSPolicyConfig{}, + wantErrors: []string{"spec.tls.hostname in body should be at least 1 chars long", "must specify either CACertRefs or WellKnownCACerts"}, + }, + { + name: "invalid BackendTLSPolicyConfig with both CACertRefs and WellKnownCACerts", + routeConfig: gatewayv1a2.BackendTLSPolicyConfig{ + CACertRefs: []v1beta1.LocalObjectReference{ + { + Group: "group", + Kind: "kind", + Name: "name", + }, + }, + WellKnownCACerts: ptrTo(gatewayv1a2.WellKnownCACertType("System")), + Hostname: "foo.example.com", + }, + + wantErrors: []string{"must not contain both CACertRefs and WellKnownCACerts"}, + }, + { + name: "invalid BackendTLSPolicyConfig with Unsupported value for WellKnownCACerts", + routeConfig: gatewayv1a2.BackendTLSPolicyConfig{ + WellKnownCACerts: ptrTo(gatewayv1a2.WellKnownCACertType("bar")), + Hostname: "foo.example.com", + }, + wantErrors: []string{"supported values: \"System\""}, + }, + { + name: "invalid BackendTLSPolicyConfig with empty Hostname field", + routeConfig: gatewayv1a2.BackendTLSPolicyConfig{ + CACertRefs: []v1beta1.LocalObjectReference{ + { + Group: "group", + Kind: "kind", + Name: "name", + }, + }, + Hostname: "", + }, + wantErrors: []string{"spec.tls.hostname in body should be at least 1 chars long"}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + route := &gatewayv1a2.BackendTLSPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()), + Namespace: metav1.NamespaceDefault, + }, + Spec: gatewayv1a2.BackendTLSPolicySpec{ + TargetRef: gatewayv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gatewayv1a2.PolicyTargetReference{ + Group: "group", + Kind: "kind", + Name: "name", + Namespace: ptrTo(gatewayv1a2.Namespace("ns")), + }, + }, + TLS: tc.routeConfig, + }, + } + validateBackendTLSPolicy(t, route, tc.wantErrors) + }) + } +} + +func validateBackendTLSPolicy(t *testing.T, route *gatewayv1a2.BackendTLSPolicy, wantErrors []string) { + t.Helper() + + ctx := context.Background() + err := k8sClient.Create(ctx, route) + + if (len(wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating BackendTLSPolicy %q; got err=\n%v\n;want error=%v", fmt.Sprintf("%v/%v", route.Namespace, route.Name), err, wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range wantErrors { + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) { + missingErrorStrings = append(missingErrorStrings, wantError) + } + } + if len(missingErrorStrings) != 0 { + t.Errorf("Unexpected response while creating BackendTLSPolicy %q; got err=\n%v\n;missing strings within error=%q", fmt.Sprintf("%v/%v", route.Namespace, route.Name), err, missingErrorStrings) + } +}