From 86b6cbc66fd469937e9185449bc17b8a8649f6af Mon Sep 17 00:00:00 2001 From: sudipto baral Date: Wed, 18 Jun 2025 23:37:22 -0400 Subject: [PATCH 1/4] chore(installation): early validate presence of tag in `image` and `imageRepository` field in KubernetesContainerSpec Signed-off-by: sudipto baral --- .../infrastructure/kubernetes/proxy/resource.go | 8 ++++++++ .../kubernetes/proxy/resource_test.go | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/internal/infrastructure/kubernetes/proxy/resource.go b/internal/infrastructure/kubernetes/proxy/resource.go index 19e906404c..fb2747e51c 100644 --- a/internal/infrastructure/kubernetes/proxy/resource.go +++ b/internal/infrastructure/kubernetes/proxy/resource.go @@ -493,6 +493,10 @@ func resolveProxyImage(containerSpec *egv1a1.KubernetesContainerSpec) (string, e repo := ptr.Deref(containerSpec.ImageRepository, "") if repo != "" { + // if the imageRepository is set, it should not contain a tag + if existingTag, err := getImageTag(repo); err == nil && existingTag != "" { + return "", fmt.Errorf("imageRepository %q should not contain a tag: %q", repo, existingTag) + } tag, err := getImageTag(egv1a1.DefaultEnvoyProxyImage) if err != nil { return "", err @@ -502,6 +506,10 @@ func resolveProxyImage(containerSpec *egv1a1.KubernetesContainerSpec) (string, e image := ptr.Deref(containerSpec.Image, "") if image != "" { + // if the image field is set, it should contain a tag + if _, err := getImageTag(image); err != nil { + return "", fmt.Errorf("image %q should contain a tag: %w", image, err) + } return image, nil } diff --git a/internal/infrastructure/kubernetes/proxy/resource_test.go b/internal/infrastructure/kubernetes/proxy/resource_test.go index d295944fa6..c644f4782d 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_test.go @@ -105,6 +105,20 @@ func TestResolveProxyImage(t *testing.T) { }, expected: fmt.Sprintf("envoyproxy/envoy:%s", defaultTag), }, + { + name: "imageRepository contains tag (invalid)", + container: &egv1a1.KubernetesContainerSpec{ + ImageRepository: ptr.To("envoyproxy/envoy:v1.2.3"), + }, + expectError: true, + }, + { + name: "image without tag (invalid)", + container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("envoyproxy/envoy"), + }, + expectError: true, + }, } for _, tc := range tests { From b91fd11504a98b118a9f8a430c7286fe4b30262c Mon Sep 17 00:00:00 2001 From: sudipto baral Date: Sat, 21 Jun 2025 13:11:13 -0400 Subject: [PATCH 2/4] chore(installation): CEL validate presence of tag in image and imageRepository Signed-off-by: sudipto baral --- api/v1alpha1/shared_types.go | 2 + .../gateway.envoyproxy.io_envoyproxies.yaml | 14 +++++++ .../gateway.envoyproxy.io_envoyproxies.yaml | 14 +++++++ .../kubernetes/proxy/resource.go | 8 ---- .../kubernetes/proxy/resource_test.go | 14 ------- test/cel-validation/envoyproxy_test.go | 40 ++++++++++++++++++- test/helm/gateway-crds-helm/all.out.yaml | 14 +++++++ .../envoy-gateway-crds.out.yaml | 14 +++++++ 8 files changed, 96 insertions(+), 24 deletions(-) diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 2318c4ad57..c0b65a8594 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -209,6 +209,8 @@ type KubernetesPodSpec struct { // KubernetesContainerSpec defines the desired state of the Kubernetes container resource. // +kubebuilder:validation:XValidation:rule="!has(self.image) || !has(self.imageRepository)",message="Either image or imageRepository can be set." +// +kubebuilder:validation:XValidation:rule="has(self.image) ? self.image.matches('^[^:]+:[^:]+$') : true",message="Image must include a tag (e.g., 'image:tag')." +// +kubebuilder:validation:XValidation:rule="has(self.imageRepository) ? !self.imageRepository.contains(':') : true",message="ImageRepository must not include a tag or any colons." type KubernetesContainerSpec struct { // List of environment variables to set in the container. // diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml index c06dc41cb5..624b445aa4 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -941,6 +941,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' name: description: |- Name of the daemonSet. @@ -4778,6 +4785,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' initContainers: description: |- List of initialization containers belonging to the pod. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 5df38fb97e..7b690f0ef2 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -940,6 +940,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' name: description: |- Name of the daemonSet. @@ -4777,6 +4784,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' initContainers: description: |- List of initialization containers belonging to the pod. diff --git a/internal/infrastructure/kubernetes/proxy/resource.go b/internal/infrastructure/kubernetes/proxy/resource.go index fb2747e51c..19e906404c 100644 --- a/internal/infrastructure/kubernetes/proxy/resource.go +++ b/internal/infrastructure/kubernetes/proxy/resource.go @@ -493,10 +493,6 @@ func resolveProxyImage(containerSpec *egv1a1.KubernetesContainerSpec) (string, e repo := ptr.Deref(containerSpec.ImageRepository, "") if repo != "" { - // if the imageRepository is set, it should not contain a tag - if existingTag, err := getImageTag(repo); err == nil && existingTag != "" { - return "", fmt.Errorf("imageRepository %q should not contain a tag: %q", repo, existingTag) - } tag, err := getImageTag(egv1a1.DefaultEnvoyProxyImage) if err != nil { return "", err @@ -506,10 +502,6 @@ func resolveProxyImage(containerSpec *egv1a1.KubernetesContainerSpec) (string, e image := ptr.Deref(containerSpec.Image, "") if image != "" { - // if the image field is set, it should contain a tag - if _, err := getImageTag(image); err != nil { - return "", fmt.Errorf("image %q should contain a tag: %w", image, err) - } return image, nil } diff --git a/internal/infrastructure/kubernetes/proxy/resource_test.go b/internal/infrastructure/kubernetes/proxy/resource_test.go index c644f4782d..d295944fa6 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_test.go @@ -105,20 +105,6 @@ func TestResolveProxyImage(t *testing.T) { }, expected: fmt.Sprintf("envoyproxy/envoy:%s", defaultTag), }, - { - name: "imageRepository contains tag (invalid)", - container: &egv1a1.KubernetesContainerSpec{ - ImageRepository: ptr.To("envoyproxy/envoy:v1.2.3"), - }, - expectError: true, - }, - { - name: "image without tag (invalid)", - container: &egv1a1.KubernetesContainerSpec{ - Image: ptr.To("envoyproxy/envoy"), - }, - expectError: true, - }, } for _, tc := range tests { diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go index b952ec465b..0d87b3c2cf 100644 --- a/test/cel-validation/envoyproxy_test.go +++ b/test/cel-validation/envoyproxy_test.go @@ -1545,7 +1545,7 @@ func TestEnvoyProxyProvider(t *testing.T) { }, }, { - desc: "valid: image set, imageRepository not set", + desc: "valid: image set with tag, imageRepository not set", mutate: func(envoy *egv1a1.EnvoyProxy) { envoy.Spec = egv1a1.EnvoyProxySpec{ Provider: &egv1a1.EnvoyProxyProvider{ @@ -1563,7 +1563,7 @@ func TestEnvoyProxyProvider(t *testing.T) { wantErrors: []string{}, }, { - desc: "valid: imageRepository set, image not set", + desc: "valid: imageRepository set without tag, image not set", mutate: func(envoy *egv1a1.EnvoyProxy) { envoy.Spec = egv1a1.EnvoyProxySpec{ Provider: &egv1a1.EnvoyProxyProvider{ @@ -1599,6 +1599,42 @@ func TestEnvoyProxyProvider(t *testing.T) { }, wantErrors: []string{"Either image or imageRepository can be set."}, }, + { + desc: "invalid: image set without tag", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("envoyproxy/envoy"), + }, + }, + }, + }, + } + }, + wantErrors: []string{"Image must include a tag"}, + }, + { + desc: "invalid: imageRepository contains tag", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Container: &egv1a1.KubernetesContainerSpec{ + ImageRepository: ptr.To("envoyproxy/envoy:v1.2.3"), + }, + }, + }, + }, + } + }, + wantErrors: []string{"ImageRepository must not include a tag"}, + }, } for _, tc := range cases { diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 4a779b4df6..ddfee73204 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -24522,6 +24522,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' name: description: |- Name of the daemonSet. @@ -28359,6 +28366,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' initContainers: description: |- List of initialization containers belonging to the pod. diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 912213e9fe..de16372759 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -7210,6 +7210,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' name: description: |- Name of the daemonSet. @@ -11047,6 +11054,13 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' + - message: Image must include a tag (e.g., 'image:tag'). + rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') + : true' + - message: ImageRepository must not include a tag or any + colons. + rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') + : true' initContainers: description: |- List of initialization containers belonging to the pod. From e0adb524e447491d0c7a481f8202803343df714f Mon Sep 17 00:00:00 2001 From: sudipto baral Date: Sat, 21 Jun 2025 13:29:51 -0400 Subject: [PATCH 3/4] chore(installation): add more CEL test Signed-off-by: sudipto baral --- test/cel-validation/envoyproxy_test.go | 54 ++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go index 0d87b3c2cf..3badaa90df 100644 --- a/test/cel-validation/envoyproxy_test.go +++ b/test/cel-validation/envoyproxy_test.go @@ -1617,6 +1617,60 @@ func TestEnvoyProxyProvider(t *testing.T) { }, wantErrors: []string{"Image must include a tag"}, }, + { + desc: "invalid: image ends with colon", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("envoyproxy/envoy:"), + }, + }, + }, + }, + } + }, + wantErrors: []string{"Image must include a tag"}, + }, + { + desc: "invalid: image starts with colon", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To(":v1.25.2"), + }, + }, + }, + }, + } + }, + wantErrors: []string{"Image must include a tag"}, + }, + { + desc: "invalid: image with multiple colons", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.ProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Container: &egv1a1.KubernetesContainerSpec{ + Image: ptr.To("registry.com/envoy:v1.2.3:latest"), + }, + }, + }, + }, + } + }, + wantErrors: []string{"Image must include a tag"}, + }, { desc: "invalid: imageRepository contains tag", mutate: func(envoy *egv1a1.EnvoyProxy) { From c53ec2437b8d6b0b61f90cb789839d32eda16c50 Mon Sep 17 00:00:00 2001 From: sudipto baral Date: Mon, 23 Jun 2025 16:26:10 -0400 Subject: [PATCH 4/4] chore(installation): stricter CEL rules. Signed-off-by: sudipto baral --- api/v1alpha1/shared_types.go | 4 +-- .../gateway.envoyproxy.io_envoyproxies.yaml | 30 ++++++++++--------- .../gateway.envoyproxy.io_envoyproxies.yaml | 30 ++++++++++--------- test/cel-validation/envoyproxy_test.go | 10 +++---- test/helm/gateway-crds-helm/all.out.yaml | 30 ++++++++++--------- .../envoy-gateway-crds.out.yaml | 30 ++++++++++--------- 6 files changed, 71 insertions(+), 63 deletions(-) diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index c0b65a8594..0924896bbb 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -209,8 +209,6 @@ type KubernetesPodSpec struct { // KubernetesContainerSpec defines the desired state of the Kubernetes container resource. // +kubebuilder:validation:XValidation:rule="!has(self.image) || !has(self.imageRepository)",message="Either image or imageRepository can be set." -// +kubebuilder:validation:XValidation:rule="has(self.image) ? self.image.matches('^[^:]+:[^:]+$') : true",message="Image must include a tag (e.g., 'image:tag')." -// +kubebuilder:validation:XValidation:rule="has(self.imageRepository) ? !self.imageRepository.contains(':') : true",message="ImageRepository must not include a tag or any colons." type KubernetesContainerSpec struct { // List of environment variables to set in the container. // @@ -233,6 +231,7 @@ type KubernetesContainerSpec struct { // Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. // This field is mutually exclusive with ImageRepository. // + // +kubebuilder:validation:XValidation:rule="self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$')",message="Image must include a tag and allowed characters only (e.g., 'repo:tag')." // +optional Image *string `json:"image,omitempty"` @@ -240,6 +239,7 @@ type KubernetesContainerSpec struct { // The default tag will be used. // This field is mutually exclusive with Image. // + // +kubebuilder:validation:XValidation:rule="self.matches('^[a-zA-Z0-9._/-]+$')",message="ImageRepository must contain only allowed characters and must not include a tag or any colons." // +optional ImageRepository *string `json:"imageRepository,omitempty"` diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml index 624b445aa4..2d50550fa3 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -608,12 +608,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -941,13 +949,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' name: description: |- Name of the daemonSet. @@ -4452,12 +4453,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -4785,13 +4794,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' initContainers: description: |- List of initialization containers belonging to the pod. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 7b690f0ef2..6b1ef3baa0 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -607,12 +607,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -940,13 +948,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' name: description: |- Name of the daemonSet. @@ -4451,12 +4452,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -4784,13 +4793,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' initContainers: description: |- List of initialization containers belonging to the pod. diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go index 3badaa90df..3c85511fda 100644 --- a/test/cel-validation/envoyproxy_test.go +++ b/test/cel-validation/envoyproxy_test.go @@ -1615,7 +1615,7 @@ func TestEnvoyProxyProvider(t *testing.T) { }, } }, - wantErrors: []string{"Image must include a tag"}, + wantErrors: []string{"Image must include a tag and allowed characters only (e.g., 'repo:tag')."}, }, { desc: "invalid: image ends with colon", @@ -1633,7 +1633,7 @@ func TestEnvoyProxyProvider(t *testing.T) { }, } }, - wantErrors: []string{"Image must include a tag"}, + wantErrors: []string{"Image must include a tag and allowed characters only (e.g., 'repo:tag')."}, }, { desc: "invalid: image starts with colon", @@ -1651,7 +1651,7 @@ func TestEnvoyProxyProvider(t *testing.T) { }, } }, - wantErrors: []string{"Image must include a tag"}, + wantErrors: []string{"Image must include a tag and allowed characters only (e.g., 'repo:tag')."}, }, { desc: "invalid: image with multiple colons", @@ -1669,7 +1669,7 @@ func TestEnvoyProxyProvider(t *testing.T) { }, } }, - wantErrors: []string{"Image must include a tag"}, + wantErrors: []string{"Image must include a tag and allowed characters only (e.g., 'repo:tag')."}, }, { desc: "invalid: imageRepository contains tag", @@ -1687,7 +1687,7 @@ func TestEnvoyProxyProvider(t *testing.T) { }, } }, - wantErrors: []string{"ImageRepository must not include a tag"}, + wantErrors: []string{"ImageRepository must contain only allowed characters and must not include a tag or any colons."}, }, } diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index ddfee73204..4d6f274a79 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -24189,12 +24189,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -24522,13 +24530,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' name: description: |- Name of the daemonSet. @@ -28033,12 +28034,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -28366,13 +28375,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' initContainers: description: |- List of initialization containers belonging to the pod. diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index de16372759..66d4167c9e 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -6877,12 +6877,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -7210,13 +7218,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' name: description: |- Name of the daemonSet. @@ -10721,12 +10722,20 @@ spec: Image specifies the EnvoyProxy container image to be used including a tag, instead of the default image. This field is mutually exclusive with ImageRepository. type: string + x-kubernetes-validations: + - message: Image must include a tag and allowed characters + only (e.g., 'repo:tag'). + rule: self.matches('^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$') imageRepository: description: |- ImageRepository specifies the container image repository to be used without specifying a tag. The default tag will be used. This field is mutually exclusive with Image. type: string + x-kubernetes-validations: + - message: ImageRepository must contain only allowed + characters and must not include a tag or any colons. + rule: self.matches('^[a-zA-Z0-9._/-]+$') resources: description: |- Resources required by this container. @@ -11054,13 +11063,6 @@ spec: x-kubernetes-validations: - message: Either image or imageRepository can be set. rule: '!has(self.image) || !has(self.imageRepository)' - - message: Image must include a tag (e.g., 'image:tag'). - rule: 'has(self.image) ? self.image.matches(''^[^:]+:[^:]+$'') - : true' - - message: ImageRepository must not include a tag or any - colons. - rule: 'has(self.imageRepository) ? !self.imageRepository.contains('':'') - : true' initContainers: description: |- List of initialization containers belonging to the pod.