From b389c83444f29b60d55dd40a2c43bb6356bc3a42 Mon Sep 17 00:00:00 2001 From: Alice Wasko Date: Thu, 26 Oct 2023 22:57:10 -0700 Subject: [PATCH] add more crd validation Signed-off-by: Alice Wasko --- api/v1alpha1/backendtrafficpolicy_types.go | 7 +- api/v1alpha1/clienttrafficpolicy_types.go | 5 + api/v1alpha1/securitypolicy_types.go | 6 + ....envoyproxy.io_backendtrafficpolicies.yaml | 4 + ...y.envoyproxy.io_clienttrafficpolicies.yaml | 5 + ...ateway.envoyproxy.io_securitypolicies.yaml | 5 + .../backendtrafficpolicy_test.go | 202 ++++++++++++++++++ .../clienttrafficpolicy_test.go | 151 +++++++++++++ test/cel-validation/securitypolicy_test.go | 151 +++++++++++++ 9 files changed, 534 insertions(+), 2 deletions(-) create mode 100644 test/cel-validation/backendtrafficpolicy_test.go create mode 100644 test/cel-validation/clienttrafficpolicy_test.go create mode 100644 test/cel-validation/securitypolicy_test.go diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go index 44fa5688715..7a360348f12 100644 --- a/api/v1alpha1/backendtrafficpolicy_types.go +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -18,7 +18,6 @@ const ( // +kubebuilder:object:root=true // +kubebuilder:resource:shortName=btp // +kubebuilder:subresource:status -// +kubebuilder:subresource:overrideStrategy // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].reason` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // @@ -28,6 +27,8 @@ type BackendTrafficPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` + // +kubebuilder:validation:Required + // // spec defines the desired state of BackendTrafficPolicy. Spec BackendTrafficPolicySpec `json:"spec"` @@ -37,8 +38,10 @@ type BackendTrafficPolicy struct { // spec defines the desired state of BackendTrafficPolicy. type BackendTrafficPolicySpec struct { - + // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" // +kubebuilder:validation:XValidation:rule="self.kind == 'Gateway' || self.kind == 'HTTPRoute' || self.kind == 'GRPCRoute' || self.kind == 'UDPRoute' || self.kind == 'TCPRoute' || self.kind == 'TLSRoute'", message="this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" + // +kubebuilder:validation:XValidation:rule="has(self.sectionName) ? self.kind == 'Gateway': true", message="sectionName can only be set when kind is 'Gateway'." + // +kubebuilder:validation:Required // // targetRef is the name of the resource this policy // is being attached to. diff --git a/api/v1alpha1/clienttrafficpolicy_types.go b/api/v1alpha1/clienttrafficpolicy_types.go index 4e8cdd29a51..d9fe346533f 100644 --- a/api/v1alpha1/clienttrafficpolicy_types.go +++ b/api/v1alpha1/clienttrafficpolicy_types.go @@ -28,6 +28,8 @@ type ClientTrafficPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` + // +kubebuilder:validation:Required + // // Spec defines the desired state of ClientTrafficPolicy. Spec ClientTrafficPolicySpec `json:"spec"` @@ -37,6 +39,9 @@ type ClientTrafficPolicy struct { // ClientTrafficPolicySpec defines the desired state of ClientTrafficPolicy. type ClientTrafficPolicySpec struct { + // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" + // +kubebuilder:validation:XValidation:rule="self.kind == 'Gateway'", message="this policy can only have a targetRef.kind of Gateway" + // +kubebuilder:validation:Required // TargetRef is the name of the Gateway resource this policy // is being attached to. // This Policy and the TargetRef MUST be in the same namespace diff --git a/api/v1alpha1/securitypolicy_types.go b/api/v1alpha1/securitypolicy_types.go index 7adece4cbca..bd1cf42162e 100644 --- a/api/v1alpha1/securitypolicy_types.go +++ b/api/v1alpha1/securitypolicy_types.go @@ -27,6 +27,8 @@ type SecurityPolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` + // +kubebuilder:validation:Required + // // Spec defines the desired state of SecurityPolicy. Spec SecurityPolicySpec `json:"spec"` @@ -36,6 +38,10 @@ type SecurityPolicy struct { // SecurityPolicySpec defines the desired state of SecurityPolicy. type SecurityPolicySpec struct { + // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" + // +kubebuilder:validation:XValidation:rule="self.kind == 'Gateway'", message="this policy can only have a targetRef.kind of Gateway" + // +kubebuilder:validation:Required + // // TargetRef is the name of the Gateway resource this policy // is being attached to. // This Policy and the TargetRef MUST be in the same namespace diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 8a8e2283ddb..1d852143e65 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -264,10 +264,14 @@ spec: - name type: object x-kubernetes-validations: + - message: this policy can only have a targetRef.group of gateway.networking.k8s.io + rule: self.group == 'gateway.networking.k8s.io' - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute rule: self.kind == 'Gateway' || self.kind == 'HTTPRoute' || self.kind == 'GRPCRoute' || self.kind == 'UDPRoute' || self.kind == 'TCPRoute' || self.kind == 'TLSRoute' + - message: sectionName can only be set when kind is 'Gateway'. + rule: 'has(self.sectionName) ? self.kind == ''Gateway'': true' required: - targetRef type: object diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index c34aba25627..75373379052 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -93,6 +93,11 @@ spec: - kind - name type: object + x-kubernetes-validations: + - message: this policy can only have a targetRef.group of gateway.networking.k8s.io + rule: self.group == 'gateway.networking.k8s.io' + - message: this policy can only have a targetRef.kind of Gateway + rule: self.kind == 'Gateway' tcpKeepalive: description: TcpKeepalive settings associated with the downstream client connection. If defined, sets SO_KEEPALIVE on the listener diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 2c7c91aa529..19aa954bb14 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -235,6 +235,11 @@ spec: - kind - name type: object + x-kubernetes-validations: + - message: this policy can only have a targetRef.group of gateway.networking.k8s.io + rule: self.group == 'gateway.networking.k8s.io' + - message: this policy can only have a targetRef.kind of Gateway + rule: self.kind == 'Gateway' required: - targetRef type: object diff --git a/test/cel-validation/backendtrafficpolicy_test.go b/test/cel-validation/backendtrafficpolicy_test.go new file mode 100644 index 00000000000..e62f06552ff --- /dev/null +++ b/test/cel-validation/backendtrafficpolicy_test.go @@ -0,0 +1,202 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func TestBackendTrafficPolicyTarget(t *testing.T) { + ctx := context.Background() + baseBTP := egv1a1.BackendTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "btp", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.BackendTrafficPolicySpec{}, + } + + sectionName := gwapiv1a2.SectionName("foo") + + cases := []struct { + desc string + mutate func(btp *egv1a1.BackendTrafficPolicy) + mutateStatus func(btp *egv1a1.BackendTrafficPolicy) + wantErrors []string + }{ + { + desc: "valid gateway targetRef", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "valid httproute targetRef", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("HTTPRoute"), + Name: gwapiv1a2.ObjectName("httpbin-route"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "no targetRef", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{} + }, + wantErrors: []string{ + "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", + "spec.targetRef.name: Invalid value: \"\": spec.targetRef.name in body should be at least 1 chars long", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "targetRef unsupported kind", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "targetRef unsupported group", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + }, + }, + { + desc: "targetRef unsupported group and kind", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("bar"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "targetRef section name with gateway", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + SectionName: §ionName, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "targetRef section name with httproute", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("HTTPRoute"), + Name: gwapiv1a2.ObjectName("eg"), + }, + SectionName: §ionName, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": sectionName can only be set when kind is 'Gateway'.", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + btp := baseBTP.DeepCopy() + btp.Name = fmt.Sprintf("btp-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(btp) + } + err := c.Create(ctx, btp) + + if tc.mutateStatus != nil { + tc.mutateStatus(btp) + err = c.Status().Update(ctx, btp) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating BackendTrafficPolicy; got err=\n%v\n;want error=%v", err, tc.wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range tc.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 BackendTrafficPolicy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +} diff --git a/test/cel-validation/clienttrafficpolicy_test.go b/test/cel-validation/clienttrafficpolicy_test.go new file mode 100644 index 00000000000..14c32cfb865 --- /dev/null +++ b/test/cel-validation/clienttrafficpolicy_test.go @@ -0,0 +1,151 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func TestClientTrafficPolicyTarget(t *testing.T) { + ctx := context.Background() + baseCTP := egv1a1.ClientTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ctp", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.ClientTrafficPolicySpec{}, + } + + cases := []struct { + desc string + mutate func(ctp *egv1a1.ClientTrafficPolicy) + mutateStatus func(ctp *egv1a1.ClientTrafficPolicy) + wantErrors []string + }{ + { + desc: "valid targetRef", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "no targetRef", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{} + }, + wantErrors: []string{ + "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", + "spec.targetRef.name: Invalid value: \"\": spec.targetRef.name in body should be at least 1 chars long", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported kind", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported group", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + }, + }, + { + desc: "targetRef unsupported group and kind", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("bar"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + ctp := baseCTP.DeepCopy() + ctp.Name = fmt.Sprintf("ctp-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(ctp) + } + err := c.Create(ctx, ctp) + + if tc.mutateStatus != nil { + tc.mutateStatus(ctp) + err = c.Status().Update(ctx, ctp) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating ClientTrafficPolicy; got err=\n%v\n;want error=%v", err, tc.wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range tc.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 ClientTrafficPolicy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +} diff --git a/test/cel-validation/securitypolicy_test.go b/test/cel-validation/securitypolicy_test.go new file mode 100644 index 00000000000..89959a292da --- /dev/null +++ b/test/cel-validation/securitypolicy_test.go @@ -0,0 +1,151 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func TestSecurityPolicyTarget(t *testing.T) { + ctx := context.Background() + baseSP := egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sp", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.SecurityPolicySpec{}, + } + + cases := []struct { + desc string + mutate func(sp *egv1a1.SecurityPolicy) + mutateStatus func(sp *egv1a1.SecurityPolicy) + wantErrors []string + }{ + { + desc: "valid targetRef", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "no targetRef", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{} + }, + wantErrors: []string{ + "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", + "spec.targetRef.name: Invalid value: \"\": spec.targetRef.name in body should be at least 1 chars long", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported kind", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + { + desc: "targetRef unsupported group", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + }, + }, + { + desc: "targetRef unsupported group and kind", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("bar"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + sp := baseSP.DeepCopy() + sp.Name = fmt.Sprintf("sp-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(sp) + } + err := c.Create(ctx, sp) + + if tc.mutateStatus != nil { + tc.mutateStatus(sp) + err = c.Status().Update(ctx, sp) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating SecurityPolicy; got err=\n%v\n;want error=%v", err, tc.wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range tc.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 SecurityPolicy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +}