diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 48e3471c77d..3f165cc6c2d 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -262,6 +262,12 @@ type KubernetesServiceSpec struct { // +optional Annotations map[string]string `json:"annotations,omitempty"` + // Labels that should be appended to the service. + // By default, no labels are appended. + // + // +optional + Labels map[string]string `json:"labels,omitempty"` + // 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). diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ed5df681ad2..a72706c33bb 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -3580,6 +3580,13 @@ func (in *KubernetesServiceSpec) DeepCopyInto(out *KubernetesServiceSpec) { (*out)[key] = val } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Type != nil { in, out := &in.Type, &out.Type *out = new(ServiceType) 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 1b18890cd27..487f436ab81 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -10134,6 +10134,13 @@ spec: - Local - Cluster type: string + labels: + additionalProperties: + type: string + description: |- + Labels that should be appended to the service. + By default, no labels are appended. + type: object loadBalancerClass: description: |- LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider.go b/internal/infrastructure/kubernetes/proxy/resource_provider.go index 768ed7514ba..233afddce73 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider.go @@ -101,10 +101,10 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { } } - // Set the labels based on the owning gatewayclass name. - labels := envoyLabels(r.infra.GetProxyMetadata().Labels) - if OwningGatewayLabelsAbsent(labels) { - return nil, fmt.Errorf("missing owning gateway labels") + // Set the infraLabels based on the owning gatewayclass name. + infraLabels := envoyLabels(r.infra.GetProxyMetadata().Labels) + if OwningGatewayLabelsAbsent(infraLabels) { + return nil, fmt.Errorf("missing owning gateway infraLabels") } // Get annotations @@ -120,10 +120,20 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { annotations = nil } + // Get service-specific labels + svcLabels := map[string]string{} + maps.Copy(svcLabels, infraLabels) + if envoyServiceConfig.Labels != nil { + maps.Copy(svcLabels, envoyServiceConfig.Labels) + } + if len(svcLabels) == 0 { + svcLabels = nil + } + // Set the spec of gateway service serviceSpec := resource.ExpectedServiceSpec(envoyServiceConfig) serviceSpec.Ports = ports - serviceSpec.Selector = resource.GetSelector(labels).MatchLabels + serviceSpec.Selector = resource.GetSelector(infraLabels).MatchLabels if (*envoyServiceConfig.Type) == egv1a1.ServiceTypeClusterIP { if len(r.infra.Addresses) > 0 { @@ -144,7 +154,7 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { }, ObjectMeta: metav1.ObjectMeta{ Namespace: r.Namespace, - Labels: labels, + Labels: svcLabels, Annotations: annotations, }, Spec: serviceSpec, diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go index c92d94d4b42..8c4138a3825 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go @@ -1051,6 +1051,9 @@ func TestService(t *testing.T) { caseName: "custom", infra: newTestInfra(), service: &egv1a1.KubernetesServiceSpec{ + Labels: map[string]string{ + "key1": "value1", + }, Annotations: map[string]string{ "key1": "value1", }, @@ -1079,6 +1082,31 @@ func TestService(t *testing.T) { }, }, }, + { + caseName: "with-svc-labels", + infra: newTestInfra(), + service: &egv1a1.KubernetesServiceSpec{ + Labels: map[string]string{ + "label1": "value1", + "label2": "value2", + }, + }, + }, + { + caseName: "override-labels", + infra: newTestInfraWithAnnotationsAndLabels(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }, map[string]string{ + "label1": "value1", + "label2": "value2", + }), + service: &egv1a1.KubernetesServiceSpec{ + Labels: map[string]string{ + "label1": "value1-override", + }, + }, + }, { caseName: "clusterIP-custom-addresses", infra: newTestInfraWithAddresses([]string{ diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml index e898ccb1aff..d087bf24bf6 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml @@ -4,6 +4,7 @@ metadata: annotations: key1: value1 labels: + key1: value1 app.kubernetes.io/name: envoy app.kubernetes.io/component: proxy app.kubernetes.io/managed-by: envoy-gateway diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/override-labels.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/override-labels.yaml new file mode 100644 index 00000000000..6f60f58176c --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/override-labels.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1-override + label2: value2 + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + externalTrafficPolicy: Local + ports: + - name: EnvoyHTTPPort + port: 0 + protocol: TCP + targetPort: 8080 + - name: EnvoyHTTPSPort + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1 + label2: value2 + sessionAffinity: None + type: LoadBalancer diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/with-svc-labels.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/with-svc-labels.yaml new file mode 100644 index 00000000000..8ff9e5bb319 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/with-svc-labels.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + label1: value1 + label2: value2 + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + externalTrafficPolicy: Local + ports: + - name: EnvoyHTTPPort + port: 0 + protocol: TCP + targetPort: 8080 + - name: EnvoyHTTPSPort + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + sessionAffinity: None + type: LoadBalancer diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index c26a8c713f8..76adfb15735 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -2564,6 +2564,7 @@ _Appears in:_ | Field | Type | Required | 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. | | `type` | _[ServiceType](#servicetype)_ | false | 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/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index c26a8c713f8..76adfb15735 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -2564,6 +2564,7 @@ _Appears in:_ | Field | Type | Required | 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. | | `type` | _[ServiceType](#servicetype)_ | false | 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. |