diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 27c8f3d2d1..de0c662695 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -173,9 +173,12 @@ type KubernetesPodSpec struct { // Labels are the additional labels that should be tagged to the pods. // By default, no additional pod labels are tagged. + // Keys must be valid label keys (optionally prefixed qualified names) and + // values must satisfy the Kubernetes label value format, otherwise the + // resource is rejected. // // +optional - Labels map[string]string `json:"labels,omitempty"` + Labels map[gwapiv1.LabelKey]gwapiv1.LabelValue `json:"labels,omitempty"` // SecurityContext holds pod-level security attributes and common container settings. // Optional: Defaults to empty. See type description for default values of each field. @@ -329,9 +332,12 @@ type KubernetesServiceSpec struct { // Labels that should be appended to the service. // By default, no labels are appended. + // Keys must be valid label keys (optionally prefixed qualified names) and + // values must satisfy the Kubernetes label value format, otherwise the + // resource is rejected. // // +optional - Labels map[string]string `json:"labels,omitempty"` + Labels map[gwapiv1.LabelKey]gwapiv1.LabelValue `json:"labels,omitempty"` // Type determines how the Service is exposed. Defaults to LoadBalancer. // Valid options are ClusterIP, LoadBalancer and NodePort. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a566e3c1df..8374b7e8b0 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5514,7 +5514,7 @@ func (in *KubernetesPodSpec) DeepCopyInto(out *KubernetesPodSpec) { } if in.Labels != nil { in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) + *out = make(map[v1.LabelKey]v1.LabelValue, len(*in)) for key, val := range *in { (*out)[key] = val } @@ -5611,7 +5611,7 @@ func (in *KubernetesServiceSpec) DeepCopyInto(out *KubernetesServiceSpec) { } if in.Labels != nil { in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) + *out = make(map[v1.LabelKey]v1.LabelValue, len(*in)) for key, val := range *in { (*out)[key] = val } 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 93db35107b..454baac84f 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 @@ -2340,10 +2340,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -7897,10 +7916,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -11157,10 +11195,29 @@ spec: type: string labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that should be appended to the service. By default, no labels are appended. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object loadBalancerClass: description: |- diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index eafaa9486a..823e5461f4 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -2339,10 +2339,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -7896,10 +7915,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -11156,10 +11194,29 @@ spec: type: string labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that should be appended to the service. By default, no labels are appended. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object loadBalancerClass: description: |- diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider.go b/internal/infrastructure/kubernetes/proxy/resource_provider.go index f236de53c6..652eda0247 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider.go @@ -244,9 +244,7 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { // Get service-specific labels svcLabels := map[string]string{} maps.Copy(svcLabels, infraLabels) - if envoyServiceConfig.Labels != nil { - maps.Copy(svcLabels, envoyServiceConfig.Labels) - } + utils.CopyStringMap(svcLabels, envoyServiceConfig.Labels) if len(svcLabels) == 0 { svcLabels = nil } @@ -677,7 +675,7 @@ func (r *ResourceRender) getLabels() (map[string]string, error) { func (r *ResourceRender) getPodLabels(pod *egv1a1.KubernetesPodSpec) map[string]string { podLabels := r.infra.GetProxyMetadata().Labels - maps.Copy(podLabels, pod.Labels) + utils.CopyStringMap(podLabels, pod.Labels) return r.envoyLabels(podLabels) } diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go index 7106c3ee4c..92dcb6d8bd 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go @@ -224,7 +224,7 @@ func TestDeployment(t *testing.T) { Annotations: map[string]string{ "prometheus.io/scrape": "true", }, - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "foo.bar": "custom-label", }, SecurityContext: &corev1.PodSecurityContext{ @@ -502,7 +502,7 @@ func TestDeployment(t *testing.T) { Annotations: map[string]string{ "prometheus.io/scrape": "true", }, - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "foo.bar": "custom-label", }, SecurityContext: &corev1.PodSecurityContext{ @@ -577,7 +577,7 @@ func TestDeployment(t *testing.T) { Annotations: map[string]string{ "anno1": "value1-override", }, - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "label1": "value1-override", }, }, @@ -801,7 +801,7 @@ func TestDaemonSet(t *testing.T) { Annotations: map[string]string{ "prometheus.io/scrape": "true", }, - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "foo.bar": "custom-label", }, SecurityContext: &corev1.PodSecurityContext{ @@ -1068,7 +1068,7 @@ func TestDaemonSet(t *testing.T) { Annotations: map[string]string{ "anno1": "value1-override", }, - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "label1": "value1-override", }, }, @@ -1270,7 +1270,7 @@ func TestService(t *testing.T) { caseName: "custom", infra: newTestInfra(), service: &egv1a1.KubernetesServiceSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "key1": "value1", }, Annotations: map[string]string{ @@ -1305,7 +1305,7 @@ func TestService(t *testing.T) { caseName: "with-svc-labels", infra: newTestInfra(), service: &egv1a1.KubernetesServiceSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "label1": "value1", "label2": "value2", }, @@ -1321,7 +1321,7 @@ func TestService(t *testing.T) { "label2": "value2", }), service: &egv1a1.KubernetesServiceSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "label1": "value1-override", }, }, diff --git a/internal/infrastructure/kubernetes/proxy_daemonset_test.go b/internal/infrastructure/kubernetes/proxy_daemonset_test.go index a3a08a0bc6..680d1e4fc2 100644 --- a/internal/infrastructure/kubernetes/proxy_daemonset_test.go +++ b/internal/infrastructure/kubernetes/proxy_daemonset_test.go @@ -15,6 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" @@ -195,7 +196,7 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) { Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ EnvoyDaemonSet: &egv1a1.KubernetesDaemonSetSpec{ Pod: &egv1a1.KubernetesPodSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ // Add a new label to the custom label config. // It wouldn't break the daemonset because the selector would still match after this label update. "custom-label": "version1", @@ -236,7 +237,7 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) { Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ EnvoyDaemonSet: &egv1a1.KubernetesDaemonSetSpec{ Pod: &egv1a1.KubernetesPodSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "custom-label": "version1", "another-custom-label": "version1", // added. }, @@ -276,7 +277,7 @@ func TestCreateOrUpdateProxyDaemonSet(t *testing.T) { Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ EnvoyDaemonSet: &egv1a1.KubernetesDaemonSetSpec{ Pod: &egv1a1.KubernetesPodSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ // Update the label value which will break the daemonset // because the selector cannot be updated while the user wants to update the label value. // We cannot help this case, just emit an error and let the user recreate the envoy proxy by themselves. diff --git a/internal/infrastructure/kubernetes/proxy_deployment_test.go b/internal/infrastructure/kubernetes/proxy_deployment_test.go index 6fac00e062..bdc4d03916 100644 --- a/internal/infrastructure/kubernetes/proxy_deployment_test.go +++ b/internal/infrastructure/kubernetes/proxy_deployment_test.go @@ -15,6 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" @@ -188,7 +189,7 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) { Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ Pod: &egv1a1.KubernetesPodSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "custom-label": "version1", // added. }, }, @@ -227,7 +228,7 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) { Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ Pod: &egv1a1.KubernetesPodSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "custom-label": "version1", // Add a new label to the custom label config. // It wouldn't break the deployment because the selector would still match after this label update. @@ -269,7 +270,7 @@ func TestCreateOrUpdateProxyDeployment(t *testing.T) { Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ Pod: &egv1a1.KubernetesPodSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ // Update the label value which will break the deployment // because the selector cannot be updated while the user wants to update the label value. // We cannot help this case, just emit an error and let the user recreate the envoy proxy by themselves. diff --git a/internal/infrastructure/kubernetes/ratelimit/resource_provider.go b/internal/infrastructure/kubernetes/ratelimit/resource_provider.go index 7fea3d1a9f..37b9c69871 100644 --- a/internal/infrastructure/kubernetes/ratelimit/resource_provider.go +++ b/internal/infrastructure/kubernetes/ratelimit/resource_provider.go @@ -211,13 +211,11 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) { containers := expectedRateLimitContainers(r.rateLimit, r.rateLimitDeployment, r.Namespace()) selector := resource.GetSelector(rateLimitLabels()) - podLabels := rateLimitLabels() - if r.rateLimitDeployment.Pod.Labels != nil { - maps.Copy(podLabels, r.rateLimitDeployment.Pod.Labels) - // Copy overwrites values in the dest map if they exist in the src map https://pkg.go.dev/maps#Copy - // It's applied again with the rateLimitLabels that are used as deployment selector to ensure those are not overwritten by user input - maps.Copy(podLabels, rateLimitLabels()) - } + podLabels := map[string]string{} + utils.CopyStringMap(podLabels, r.rateLimitDeployment.Pod.Labels) + // Apply rateLimitLabels last so the labels used as the deployment selector always + // win over user input. + maps.Copy(podLabels, rateLimitLabels()) var podAnnotations map[string]string if enablePrometheus(r.rateLimit) { diff --git a/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go b/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go index 37f6979b7d..2390c8b40c 100644 --- a/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go +++ b/internal/infrastructure/kubernetes/ratelimit/resource_provider_test.go @@ -745,7 +745,7 @@ func TestDeployment(t *testing.T) { }, deploy: &egv1a1.KubernetesDeploymentSpec{ Pod: &egv1a1.KubernetesPodSpec{ - Labels: map[string]string{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ "app.kubernetes.io/name": InfraName, "app.kubernetes.io/component": "ratelimit", "app.kubernetes.io/managed-by": "envoy-gateway", diff --git a/internal/utils/maps.go b/internal/utils/maps.go new file mode 100644 index 0000000000..56cf178359 --- /dev/null +++ b/internal/utils/maps.go @@ -0,0 +1,17 @@ +// 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. + +package utils + +// CopyStringMap copies all key/value pairs from src into dst, converting the +// string-based key and value types of src to plain strings. It mirrors +// maps.Copy for maps whose key and value types have an underlying string type +// (e.g. Gateway API's LabelKey/LabelValue), which maps.Copy cannot handle +// because it requires identical key/value types. +func CopyStringMap[K, V ~string](dst map[string]string, src map[K]V) { + for k, v := range src { + dst[string(k)] = string(v) + } +} diff --git a/release-notes/current/breaking_changes/9413-envoyproxy-pod-service-labels-typed.md b/release-notes/current/breaking_changes/9413-envoyproxy-pod-service-labels-typed.md new file mode 100644 index 0000000000..67d8d2e023 --- /dev/null +++ b/release-notes/current/breaking_changes/9413-envoyproxy-pod-service-labels-typed.md @@ -0,0 +1 @@ +The `labels` fields on `KubernetesPodSpec` and `KubernetesServiceSpec` (under `spec.provider.kubernetes` of the EnvoyProxy resource) are now typed as `map[gwapiv1.LabelKey]gwapiv1.LabelValue` instead of `map[string]string`, matching Gateway API's own `Infrastructure.Labels`. The CRD/YAML wire format is unchanged, but Go code that imports `github.com/envoyproxy/gateway/api/v1alpha1` and constructs these structs must update the map literal to the typed form. The API server now also rejects label values that violate the Kubernetes label value format (empty, or up to 63 alphanumeric characters separated by `-`, `_`, or `.`), so any EnvoyProxy manifest with an invalid pod/service label value must be corrected before it can be created or updated. diff --git a/release-notes/current/bug_fixes/9413-envoyproxy-pod-service-labels-validation.md b/release-notes/current/bug_fixes/9413-envoyproxy-pod-service-labels-validation.md new file mode 100644 index 0000000000..4ede26f98a --- /dev/null +++ b/release-notes/current/bug_fixes/9413-envoyproxy-pod-service-labels-validation.md @@ -0,0 +1 @@ +Added validation for the pod and service `labels` under `spec.provider.kubernetes` on the EnvoyProxy resource so that invalid Kubernetes label values are rejected when the resource is created or updated, instead of being accepted and later causing reconciliation failures. diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 113b7d419a..3da4846d8a 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -3837,7 +3837,7 @@ _Appears in:_ | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `annotations` | _object (keys:string, values:string)_ | false | | Annotations are the annotations that should be appended to the pods.
By default, no pod annotations are appended. | -| `labels` | _object (keys:string, values:string)_ | false | | Labels are the additional labels that should be tagged to the pods.
By default, no additional pod labels are tagged. | +| `labels` | _object (keys:LabelKey, values:LabelValue)_ | false | | Labels are the additional labels that should be tagged to the pods.
By default, no additional pod labels are tagged.
Keys must be valid label keys (optionally prefixed qualified names) and
values must satisfy the Kubernetes label value format, otherwise the
resource is rejected. | | `securityContext` | _[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#podsecuritycontext-v1-core)_ | false | | SecurityContext holds pod-level security attributes and common container settings.
Optional: Defaults to empty. See type description for default values of each field. | | `affinity` | _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#affinity-v1-core)_ | false | | If specified, the pod's scheduling constraints. | | `tolerations` | _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#toleration-v1-core) array_ | false | | If specified, the pod's tolerations. | @@ -3874,7 +3874,7 @@ _Appears in:_ | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `annotations` | _object (keys:string, values:string)_ | false | | Annotations that should be appended to the service.
By default, no annotations are appended. | -| `labels` | _object (keys:string, values:string)_ | false | | Labels that should be appended to the service.
By default, no labels are appended. | +| `labels` | _object (keys:LabelKey, values:LabelValue)_ | false | | Labels that should be appended to the service.
By default, no labels are appended.
Keys must be valid label keys (optionally prefixed qualified names) and
values must satisfy the Kubernetes label value format, otherwise the
resource is rejected. | | `type` | _[ServiceType](#servicetype)_ | false | LoadBalancer | Type determines how the Service is exposed. Defaults to LoadBalancer.
Valid options are ClusterIP, LoadBalancer and NodePort.
"LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it).
"ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP.
"NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. | | `loadBalancerClass` | _string_ | false | | LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider
implementation if more than one are available or is otherwise expected to be specified | | `allocateLoadBalancerNodePorts` | _boolean_ | false | | AllocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for
services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster
load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a
value), those requests will be respected, regardless of this field. This field may only be set for
services with type LoadBalancer and will be cleared if the type is changed to any other type. | diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go index 20e425157c..51292f27e5 100644 --- a/test/cel-validation/envoyproxy_test.go +++ b/test/cel-validation/envoyproxy_test.go @@ -2485,6 +2485,104 @@ func TestEnvoyProxyProvider(t *testing.T) { }, wantErrors: []string{"If type is Remote, local field must not be set"}, }, + { + desc: "valid pod labels", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.EnvoyProxyProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Pod: &egv1a1.KubernetesPodSpec{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ + "app.kubernetes.io/name": "envoy", + "custom-label": "valid-value_1.2", + "empty-value": "", + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "invalid pod label value", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.EnvoyProxyProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDeployment: &egv1a1.KubernetesDeploymentSpec{ + Pod: &egv1a1.KubernetesPodSpec{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ + "name": "test-wec1, test-wec2", + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"in body should match '^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$'"}, + }, + { + desc: "invalid pod label value too long", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.EnvoyProxyProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyDaemonSet: &egv1a1.KubernetesDaemonSetSpec{ + Pod: &egv1a1.KubernetesPodSpec{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ + "custom-label": gwapiv1.LabelValue(strings.Repeat("a", 64)), + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{"Too long"}, + }, + { + desc: "valid service labels", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.EnvoyProxyProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ + "team": "blue", + }, + }, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "invalid service label value", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + Provider: &egv1a1.EnvoyProxyProvider{ + Type: egv1a1.EnvoyProxyProviderTypeKubernetes, + Kubernetes: &egv1a1.EnvoyProxyKubernetesProvider{ + EnvoyService: &egv1a1.KubernetesServiceSpec{ + Labels: map[gwapiv1.LabelKey]gwapiv1.LabelValue{ + "env": "not valid!", + }, + }, + }, + }, + } + }, + wantErrors: []string{"in body should match '^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$'"}, + }, } 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 3680608bb5..affb21294c 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -35954,10 +35954,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -41511,10 +41530,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -44771,10 +44809,29 @@ spec: type: string labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that should be appended to the service. By default, no labels are appended. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object loadBalancerClass: description: |- diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml index 7a383c7548..b9771f5da3 100644 --- a/test/helm/gateway-crds-helm/e2e.out.yaml +++ b/test/helm/gateway-crds-helm/e2e.out.yaml @@ -11892,10 +11892,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -17449,10 +17468,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -20709,10 +20747,29 @@ spec: type: string labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that should be appended to the service. By default, no labels are appended. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object loadBalancerClass: description: |- 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 836f9fd2cc..a6283305f9 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -11892,10 +11892,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -17449,10 +17468,29 @@ spec: type: array labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels are the additional labels that should be tagged to the pods. By default, no additional pod labels are tagged. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object nodeSelector: additionalProperties: @@ -20709,10 +20747,29 @@ spec: type: string labels: additionalProperties: + description: |- + LabelValue is the value of a label in the Gateway API. This is used for validation + of maps such as Gateway infrastructure labels. This matches the Kubernetes + label validation rules: + * must be 63 characters or less (can be empty), + * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), + * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. + + Valid values include: + + * MyValue + * my.name + * 123-my-value + maxLength: 63 + minLength: 0 + pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that should be appended to the service. By default, no labels are appended. + Keys must be valid label keys (optionally prefixed qualified names) and + values must satisfy the Kubernetes label value format, otherwise the + resource is rejected. type: object loadBalancerClass: description: |-