This is an automated email from the ASF dual-hosted git repository.
adutra pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git
The following commit(s) were added to refs/heads/main by this push:
new 9fc2e0d7f Helm chart: fix logic for deduplicating ports (#3790)
9fc2e0d7f is described below
commit 9fc2e0d7f2c6ca381faef3599f866a4a58754295
Author: Alexandre Dutra <[email protected]>
AuthorDate: Tue Feb 17 19:39:31 2026 +0100
Helm chart: fix logic for deduplicating ports (#3790)
The logic in `polaris.containerPorts` has been improved to prevent
duplicate ports in a `Deployment`.
Summary of changes:
- Container ports are keyed by port number instead of name to prevent
duplicate `containerPort` entries (invalid in Kubernetes)
- Validate that both port numbers and port names are unique
- Extract validation logic into `polaris.validateContainerPort` helper
template
- Fix documentation of `targetPort`: this field must be a number
- Update tests to reflect new validation behavior
- Fixed `extra-service-values.yaml` fixture (had invalid port declarations)
---
helm/polaris/ci/extra-service-values.yaml | 4 +-
helm/polaris/templates/_helpers.tpl | 72 ++++++-----
helm/polaris/tests/deployment_test.yaml | 208 ++++++++++++++++++++++++------
helm/polaris/values.yaml | 6 +-
site/content/in-dev/unreleased/helm.md | 4 +-
5 files changed, 219 insertions(+), 75 deletions(-)
diff --git a/helm/polaris/ci/extra-service-values.yaml
b/helm/polaris/ci/extra-service-values.yaml
index e19835a83..eadd13c61 100644
--- a/helm/polaris/ci/extra-service-values.yaml
+++ b/helm/polaris/ci/extra-service-values.yaml
@@ -32,7 +32,7 @@ service:
protocol: TCP
- name: polaris-http2
port: 28181
- targetPort: 8181
+ targetPort: 8183
protocol: TCP
extraServices:
@@ -44,4 +44,4 @@ extraServices:
ports:
- name: polaris-http2
port: 38181
- targetPort: 8181
+ targetPort: 8183
diff --git a/helm/polaris/templates/_helpers.tpl
b/helm/polaris/templates/_helpers.tpl
index 584158210..bd226abf3 100644
--- a/helm/polaris/templates/_helpers.tpl
+++ b/helm/polaris/templates/_helpers.tpl
@@ -267,53 +267,63 @@
https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/quantity/
{{- end -}}
{{/*
-Prints the ports section of the container spec. Also validates all port names
to ensure
-that they are unique.
+Helper template to validate and register a container port.
+Arguments (passed as a list):
+ 0: $ports dict (mutated) - maps port number to {name, protocol}
+ 1: $names dict (mutated) - maps port name to port number
+ 2: $errorPrefix string - prefix for error messages (e.g. "service.ports[0]")
+ 3: $port object - the port definition from the values.yaml with name, port,
targetPort, protocol
+*/}}
+{{- define "polaris.validateContainerPort" -}}
+{{- $ports := index . 0 -}}
+{{- $names := index . 1 -}}
+{{- $errorPrefix := index . 2 -}}
+{{- $port := index . 3 -}}
+{{- $portNumber := coalesce $port.targetPort $port.port | toString -}}
+{{- $protocol := $port.protocol | default "TCP" -}}
+{{- if hasKey $ports $portNumber -}}
+{{- $existing := get $ports $portNumber -}}
+{{- if ne $port.name (index $existing "name") -}}
+{{- fail (printf "%s: port number %s has conflicting name, expected %v, got
%v" $errorPrefix $portNumber (index $existing "name") $port.name) -}}
+{{- end -}}
+{{- if ne $protocol (index $existing "protocol") -}}
+{{- fail (printf "%s: port number %s has conflicting protocol, expected %v,
got %v" $errorPrefix $portNumber (index $existing "protocol") $protocol) -}}
+{{- end -}}
+{{- else if hasKey $names $port.name -}}
+{{- fail (printf "%s: port name %s has conflicting number, expected %v, got
%v" $errorPrefix $port.name (get $names $port.name) $portNumber) -}}
+{{- else -}}
+{{- $_ := set $ports $portNumber (dict "name" $port.name "protocol" $protocol)
-}}
+{{- $_ = set $names $port.name $portNumber -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Prints the ports section of the container spec. Iterates over all service port
declarations
+and determines which ports the container should expose, ensuring no duplicate
port numbers
+or port names.
*/}}
{{- define "polaris.containerPorts" -}}
{{- $ports := dict -}}
-{{- $protocols := dict -}}
+{{- $names := dict -}}
{{- /* Main service ports */ -}}
{{- range $i, $port := .Values.service.ports -}}
-{{- if hasKey $ports $port.name -}}
-{{- fail (printf "service.ports[%d]: port name already taken: %v" $i
$port.name) -}}
-{{- end -}}
-{{- $portNumber := coalesce $port.targetPort $port.port -}}
-{{- $_ := set $ports $port.name $portNumber -}}
-{{- $_ = set $protocols $port.name ($port.protocol | default "TCP") -}}
+{{- include "polaris.validateContainerPort" (list $ports $names (printf
"service.ports[%d]" $i) $port) -}}
{{- end -}}
{{- /* Management service ports */ -}}
{{- range $i, $port := .Values.managementService.ports -}}
-{{- if hasKey $ports $port.name -}}
-{{- fail (printf "managementService.ports[%d]: port name already taken: %v" $i
$port.name) -}}
-{{- end -}}
-{{- $portNumber := coalesce $port.targetPort $port.port -}}
-{{- $_ := set $ports $port.name $portNumber }}
-{{- $_ = set $protocols $port.name ($port.protocol | default "TCP") -}}
+{{- include "polaris.validateContainerPort" (list $ports $names (printf
"managementService.ports[%d]" $i) $port) -}}
{{- end -}}
{{- /* Extra service ports */ -}}
{{- range $i, $svc := .Values.extraServices -}}
{{- range $j, $port := $svc.ports -}}
-{{- $portNumber := coalesce $port.targetPort $port.port -}}
-{{- if hasKey $ports $port.name -}}
-{{- if ne $portNumber (get $ports $port.name) -}}
-{{- fail (printf "extraServices[%d].ports[%d]: wrong port number for port %s,
expected %v, got %v" $i $j $port.name (get $ports $port.name) $portNumber) -}}
-{{- end -}}
-{{- end -}}
-{{- if hasKey $protocols $port.name -}}
-{{- if ne ($port.protocol | default "TCP") (get $protocols $port.name) -}}
-{{- fail (printf "extraServices[%d].ports[%d]: wrong protocol for port %s,
expected %v, got %v" $i $j $port.name (get $protocols $port.name)
$port.protocol) -}}
-{{- end -}}
-{{- end -}}
-{{- $_ := set $ports $port.name $portNumber -}}
-{{- $_ = set $protocols $port.name ($port.protocol | default "TCP") -}}
+{{- include "polaris.validateContainerPort" (list $ports $names (printf
"extraServices[%d].ports[%d]" $i $j) $port) -}}
{{- end -}}
{{- end }}
ports:
-{{- range $portName, $portNumber := $ports }}
- - name: {{ $portName }}
+{{- range $portNumber, $portInfo := $ports }}
+ - name: {{ index $portInfo "name" }}
containerPort: {{ $portNumber }}
- protocol: {{ get $protocols $portName }}
+ protocol: {{ index $portInfo "protocol" }}
{{- end }}
{{- end -}}
diff --git a/helm/polaris/tests/deployment_test.yaml
b/helm/polaris/tests/deployment_test.yaml
index 71c84ac86..d0c1d7f41 100644
--- a/helm/polaris/tests/deployment_test.yaml
+++ b/helm/polaris/tests/deployment_test.yaml
@@ -514,7 +514,7 @@ tests:
containerPort: 8183
protocol: TCP
- - it: should fail if port name is not unique (#1)
+ - it: should fail when duplicate port names have different port numbers in
service
template: deployment.yaml
set:
service:
@@ -525,9 +525,9 @@ tests:
port: 18181
asserts:
- failedTemplate:
- errorPattern: "service.ports\\[\\d\\]: port name already taken:
polaris-http"
+ errorMessage: "service.ports[1]: port name polaris-http has
conflicting number, expected 8181, got 18181"
- - it: should fail if port name is not unique (#2)
+ - it: should fail when duplicate port names have different port numbers in
managementService
template: deployment.yaml
set:
managementService:
@@ -538,9 +538,39 @@ tests:
port: 18182
asserts:
- failedTemplate:
- errorPattern: "managementService.ports\\[\\d\\]: port name already
taken: polaris-mgmt"
+ errorMessage: "managementService.ports[1]: port name polaris-mgmt
has conflicting number, expected 8182, got 18182"
- - it: should fail if port name is not unique (#3)
+ - it: should fail when duplicate port names have different port numbers in
extraServices (#1)
+ template: deployment.yaml
+ set:
+ extraServices:
+ - nameSuffix: ext
+ ports:
+ - name: polaris-extra
+ port: 8183
+ - name: polaris-extra
+ port: 18183
+ asserts:
+ - failedTemplate:
+ errorMessage: "extraServices[0].ports[1]: port name polaris-extra
has conflicting number, expected 8183, got 18183"
+
+ - it: should fail when duplicate port names have different port numbers in
extraServices (#2)
+ template: deployment.yaml
+ set:
+ extraServices:
+ - nameSuffix: ext1
+ ports:
+ - name: polaris-extra
+ port: 8183
+ - nameSuffix: ext
+ ports:
+ - name: polaris-extra
+ port: 18183
+ asserts:
+ - failedTemplate:
+ errorMessage: "extraServices[1].ports[0]: port name polaris-extra
has conflicting number, expected 8183, got 18183"
+
+ - it: should fail when duplicate port names have different port numbers
across services (#1)
template: deployment.yaml
set:
service:
@@ -553,9 +583,25 @@ tests:
port: 8182
asserts:
- failedTemplate:
- errorPattern: "managementService.ports\\[\\d\\]: port name already
taken: polaris"
+ errorMessage: "managementService.ports[0]: port name polaris has
conflicting number, expected 8181, got 8182"
+
+ - it: should fail when duplicate port names have different port numbers
across services (#2)
+ template: deployment.yaml
+ set:
+ service:
+ ports:
+ - name: polaris
+ port: 8181
+ extraServices:
+ - nameSuffix: ext
+ ports:
+ - name: polaris
+ port: 8182
+ asserts:
+ - failedTemplate:
+ errorMessage: "extraServices[0].ports[0]: port number 8182 has
conflicting name, expected polaris-mgmt, got polaris"
- - it: should not fail when extra service references the same port name and
number
+ - it: should deduplicate when extra service references the same port number
and name
template: deployment.yaml
set:
extraServices:
@@ -581,6 +627,26 @@ tests:
containerPort: 8182
protocol: TCP
+ - it: should add extra service port with different number and name
+ template: deployment.yaml
+ set:
+ extraServices:
+ - nameSuffix: "-extra"
+ type: LoadBalancer
+ ports:
+ - name: polaris-extra
+ port: 9999
+ asserts:
+ - lengthEqual:
+ path: spec.template.spec.containers[0].ports
+ count: 3
+ - contains:
+ path: spec.template.spec.containers[0].ports
+ content:
+ name: polaris-extra
+ containerPort: 9999
+ protocol: TCP
+
- it: should fail when extra service references the same port name with
different number (#1)
template: deployment.yaml
set:
@@ -591,8 +657,8 @@ tests:
- name: polaris-http
port: 9999
asserts:
- - failedTemplate:
- errorPattern: "extraServices\\[\\d\\].ports\\[\\d\\]: wrong port
number for port polaris-http, expected 8181, got 9999"
+ - failedTemplate:
+ errorMessage: "extraServices[0].ports[0]: port name polaris-http has
conflicting number, expected 8181, got 9999"
- it: should fail when extra service references the same port name with
different number (#2)
template: deployment.yaml
@@ -603,30 +669,112 @@ tests:
ports:
- name: polaris-mgmt
port: 9999
+ asserts:
+ - failedTemplate:
+ errorMessage: "extraServices[0].ports[0]: port name polaris-mgmt has
conflicting number, expected 8182, got 9999"
+
+ - it: should fail when same port number has conflicting name in service
+ template: deployment.yaml
+ set:
+ service:
+ ports:
+ - name: polaris-http
+ port: 8181
+ - name: polaris-http2
+ port: 8181
asserts:
- failedTemplate:
- errorPattern: "extraServices\\[\\d\\].ports\\[\\d\\]: wrong port
number for port polaris-mgmt, expected 8182, got 9999"
+ errorMessage: "service.ports[1]: port number 8181 has conflicting
name, expected polaris-http, got polaris-http2"
- - it: should fail when extra service references the same port name with
different number (#3)
+ - it: should fail when same port number has conflicting name in
managementService
+ template: deployment.yaml
+ set:
+ managementService:
+ ports:
+ - name: polaris-mgmt
+ port: 8182
+ - name: polaris-mgmt2
+ port: 8182
+ asserts:
+ - failedTemplate:
+ errorMessage: "managementService.ports[1]: port number 8182 has
conflicting name, expected polaris-mgmt, got polaris-mgmt2"
+
+ - it: should fail when same port number has conflicting name in
extraServices (#1)
+ template: deployment.yaml
+ set:
+ extraServices:
+ - nameSuffix: ext
+ ports:
+ - name: polaris-extra
+ port: 8183
+ - name: polaris-extra2
+ port: 8183
+ asserts:
+ - failedTemplate:
+ errorMessage: "extraServices[0].ports[1]: port number 8183 has
conflicting name, expected polaris-extra, got polaris-extra2"
+
+ - it: should fail when same port number has conflicting name in
extraServices (#2)
+ template: deployment.yaml
+ set:
+ extraServices:
+ - nameSuffix: ext1
+ ports:
+ - name: polaris-extra
+ port: 8183
+ - nameSuffix: ext2
+ ports:
+ - name: polaris-extra2
+ port: 8183
+ asserts:
+ - failedTemplate:
+ errorMessage: "extraServices[1].ports[0]: port number 8183 has
conflicting name, expected polaris-extra, got polaris-extra2"
+
+ - it: should fail when extra service has conflicting name for same port
number
template: deployment.yaml
set:
service:
ports:
- name: polaris-http
port: 8181
- - name: polaris-https
- port: 8043
extraServices:
- nameSuffix: "-extra"
type: LoadBalancer
ports:
- - name: polaris-https
- port: 9999
+ - name: polaris-extra
+ port: 8181
+ asserts:
+ - failedTemplate:
+ errorMessage: "extraServices[0].ports[0]: port number 8181 has
conflicting name, expected polaris-http, got polaris-extra"
+
+ - it: should fail when same port number has conflicting protocol in service
+ template: deployment.yaml
+ set:
+ service:
+ ports:
+ - name: polaris-http
+ port: 8181
+ - name: polaris-http
+ port: 8181
+ protocol: UDP
+ asserts:
+ - failedTemplate:
+ errorMessage: "service.ports[1]: port number 8181 has conflicting
protocol, expected TCP, got UDP"
+
+ - it: should fail when same port number has conflicting protocol in
managementService
+ template: deployment.yaml
+ set:
+ managementService:
+ ports:
+ - name: polaris-mgmt
+ port: 8182
+ - name: polaris-mgmt
+ port: 8182
+ protocol: UDP
asserts:
- failedTemplate:
- errorPattern: "extraServices\\[\\d\\].ports\\[\\d\\]: wrong port
number for port polaris-https, expected 8043, got 9999"
+ errorMessage: "managementService.ports[1]: port number 8182 has
conflicting protocol, expected TCP, got UDP"
- - it: should fail when extra service references the same port name with
different protocol
+ - it: should fail when same port number has conflicting protocol in
extraServices
template: deployment.yaml
set:
service:
@@ -642,34 +790,28 @@ tests:
protocol: UDP
asserts:
- failedTemplate:
- errorPattern: "extraServices\\[\\d\\].ports\\[\\d\\]: wrong
protocol for port polaris-http, expected TCP, got UDP"
+ errorMessage: "extraServices[0].ports[0]: port number 8181 has
conflicting protocol, expected TCP, got UDP"
- - it: should create 2 ports with same number
+ - it: should deduplicate ports with same number and name
template: deployment.yaml
set:
service:
ports:
- name: polaris-http
port: 8181
- - name: polaris-http2
+ - name: polaris-http
port: 8181
asserts:
- lengthEqual:
path: spec.template.spec.containers[0].ports
- count: 3
+ count: 2
- contains:
path: spec.template.spec.containers[0].ports
content:
name: polaris-http
containerPort: 8181
protocol: TCP
- - contains:
- path: spec.template.spec.containers[0].ports
- content:
- name: polaris-http2
- containerPort: 8181
- protocol: TCP
- - it: should create 2 ports with same number using targetPort
+ - it: should deduplicate ports with same number and name using targetPort
template: deployment.yaml
set:
service:
@@ -677,25 +819,19 @@ tests:
- name: polaris-http
port: 18181
targetPort: 8181
- - name: polaris-http2
+ - name: polaris-http
port: 18181
targetPort: 8181
asserts:
- lengthEqual:
path: spec.template.spec.containers[0].ports
- count: 3
+ count: 2
- contains:
path: spec.template.spec.containers[0].ports
content:
name: polaris-http
containerPort: 8181
protocol: TCP
- - contains:
- path: spec.template.spec.containers[0].ports
- content:
- name: polaris-http2
- containerPort: 8181
- protocol: TCP
- it: should set port protocols
template: deployment.yaml
set:
diff --git a/helm/polaris/values.yaml b/helm/polaris/values.yaml
index 281d77cec..d110dff5e 100644
--- a/helm/polaris/values.yaml
+++ b/helm/polaris/values.yaml
@@ -110,8 +110,7 @@ service:
- name: polaris-http
# -- The port the service listens on. By default, the HTTP port is 8181.
port: 8181
- # -- Number or name of the port to access on the pods targeted by the
service.
- # If this is a string, it will be looked up as a named port in the
target Pod's container ports.
+ # -- Number of the port to access on the pods targeted by the service.
# If this is not specified, the value of the 'port' field is used.
targetPort: ~ # polaris-service
# -- The port on each node on which this service is exposed when type is
NodePort or LoadBalancer.
@@ -171,8 +170,7 @@ managementService:
# -- The port the management service listens on. By default, the
management interface is exposed
# on HTTP port 8182.
port: 8182
- # -- Number or name of the port to access on the pods targeted by the
service.
- # If this is a string, it will be looked up as a named port in the
target Pod's container ports.
+ # -- Number of the port to access on the pods targeted by the service.
# If this is not specified, the value of the 'port' field is used.
targetPort: ~ # polaris-service
# -- The port on each node on which this service is exposed when type is
NodePort or LoadBalancer.
diff --git a/site/content/in-dev/unreleased/helm.md
b/site/content/in-dev/unreleased/helm.md
index 14215a88a..c5b4c75c4 100644
--- a/site/content/in-dev/unreleased/helm.md
+++ b/site/content/in-dev/unreleased/helm.md
@@ -361,7 +361,7 @@ ct install --namespace polaris --charts ./helm/polaris
| managementService.ports[0].nodePort | string | `nil` | The port on each node
on which this service is exposed when type is NodePort or LoadBalancer. Usually
assigned by the system. If not specified, a port will be allocated if this
Service requires one. If this field is specified when creating a Service which
does not need it, creation will fail. |
| managementService.ports[0].port | int | `8182` | The port the management
service listens on. By default, the management interface is exposed on HTTP
port 8182. |
| managementService.ports[0].protocol | string | `nil` | The IP protocol for
this port. Supports "TCP", "UDP", and "SCTP". Default is TCP. |
-| managementService.ports[0].targetPort | string | `nil` | Number or name of
the port to access on the pods targeted by the service. If this is a string, it
will be looked up as a named port in the target Pod's container ports. If this
is not specified, the value of the 'port' field is used. |
+| managementService.ports[0].targetPort | string | `nil` | Number of the port
to access on the pods targeted by the service. If this is not specified, the
value of the 'port' field is used. |
| managementService.type | string | `"ClusterIP"` | The type of service to
create. Valid values are: ExternalName, ClusterIP, NodePort, and LoadBalancer.
The default value is ClusterIP. |
| metrics.enabled | bool | `true` | Specifies whether metrics for the polaris
server should be enabled. |
| metrics.tags | object | `{}` | Additional tags (dimensional labels) to add
to the metrics. |
@@ -431,7 +431,7 @@ ct install --namespace polaris --charts ./helm/polaris
| service.ports[0].nodePort | string | `nil` | The port on each node on which
this service is exposed when type is NodePort or LoadBalancer. Usually assigned
by the system. If not specified, a port will be allocated if this Service
requires one. If this field is specified when creating a Service which does not
need it, creation will fail. |
| service.ports[0].port | int | `8181` | The port the service listens on. By
default, the HTTP port is 8181. |
| service.ports[0].protocol | string | `nil` | The IP protocol for this port.
Supports "TCP", "UDP", and "SCTP". Default is TCP. |
-| service.ports[0].targetPort | string | `nil` | Number or name of the port to
access on the pods targeted by the service. If this is a string, it will be
looked up as a named port in the target Pod's container ports. If this is not
specified, the value of the 'port' field is used. |
+| service.ports[0].targetPort | string | `nil` | Number of the port to access
on the pods targeted by the service. If this is not specified, the value of the
'port' field is used. |
| service.sessionAffinity | string | `nil` | The session affinity for the
service. Valid values are: None, ClientIP. The default value is None. ClientIP
enables sticky sessions based on the client's IP address. This is generally
beneficial to Polaris deployments, but some testing may be required in order to
make sure that the load is distributed evenly among the pods. Also, this
setting affects only internal clients, not external ones. If Ingress is
enabled, it is recommended to set sess [...]
| service.trafficDistribution | string | `nil` | The traffic distribution
field provides another way to influence traffic routing within a Kubernetes
Service. While traffic policies focus on strict semantic guarantees, traffic
distribution allows you to express preferences such as routing to topologically
closer endpoints. The only valid value is: PreferClose. The default value is
implementation-specific. |
| service.type | string | `"ClusterIP"` | The type of service to create. Valid
values are: ExternalName, ClusterIP, NodePort, and LoadBalancer. The default
value is ClusterIP. |