feat(common): Release common v4.1.0 (#416)

This commit is contained in:
Bernd Schorgers 2025-06-11 16:09:46 +02:00 committed by GitHub
parent f3660654f4
commit db552e2dee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 705 additions and 129 deletions

View file

@ -12,6 +12,9 @@ repo_url: https://github.com/bjw-s-labs/helm-charts
docs_dir: ../../docs
site_dir: ../../site
extra_css:
- stylesheets/extra.css
theme:
name: material
custom_dir: ../../docs/overrides

View file

@ -3,7 +3,7 @@ apiVersion: v2
name: common
description: Function library for Helm charts
type: library
version: 4.0.1
version: 4.1.0
kubeVersion: ">=1.28.0-0"
keywords:
- common
@ -16,51 +16,27 @@ sources:
- https://github.com/bjw-s-labs/helm-charts
annotations:
artifacthub.io/changes: |-
- kind: removed
description: |-
Individual `valuesToObject` functions have been removed in favor of a centralized `bjw-s.common.lib.valuesToObject` function.
- kind: fixed
description: |-
Fixed empty backendRefs in HTTPRoute leading to invalid spec
Fixed a bug where probes were not being configured correctly for Services that autodetect their controller.
- kind: fixed
description: |-
Fixed a bug where topologySpreadConstraints field did not properly render Helm templates.
- kind: added
description: |-
Added support for setting `parentRefs[].port` in HTTPRoute
Added support for configuring the `serviceName` field for StatefulSets.
- kind: added
description: |-
Added support for setting `sessionPersistence` in HTTPRoute
Added support for automatically selecting the ServiceAccount when only one is defined.
- kind: added
description: |-
Added support for setting `resources` on the Pod Level in Kubernetes >= 1.32
- Added support for referencing target service for ServiceMonitors by identifier.
- kind: added
description: |-
Added explicit JSON schema for `rbac` root key
Added support for automatically determining the target service for ServiceMonitors if there is only one enabled Service.
- kind: added
description: |-
Allow configuring the merge strategy for `defaultPodOptions`
- kind: added
description: |-
Added support for setting `subPathExpr` on globalMounts and advancedMounts persistence items
- kind: added
description: |-
Added support for automatically determining the target controller for Services if there is only one enabled controller
- kind: added
description: |-
Added support for automatically determining the target service for Ingress paths if there is only one enabled Service
- kind: added
description: |-
Added support for automatically determining the target service for Route backends if there is only one enabled Service
- kind: changed
description: |-
**Breaking**: Standardized resource name logic for all resources. This may cause changes in the generated resource names.
Added support for always adding the identifier suffix even if there is only a single resource.
links:
- name: Documentation
- name: Updated documentation
url: https://bjw-s-labs.github.io/helm-charts/docs/common-library/resources/names/
- kind: changed
description: |-
**Breaking**: Increased the minimum supported Kubernetes version to 1.28.0
- kind: changed
description: |-
**Breaking**: ServiceAccounts no longer create a static token by default. This is now controlled by the `staticToken` field in the `serviceAccount` object.
- kind: changed
description: |-
**Breaking**: Renamed the hardcoded app.kubernetes.io/component label to app.kubernetes.io/controller

View file

@ -1,6 +1,6 @@
# common
![Version: 4.0.1](https://img.shields.io/badge/Version-4.0.1-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square)
![Version: 4.1.0](https://img.shields.io/badge/Version-4.1.0-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square)
Function library for Helm charts
@ -31,7 +31,7 @@ Include this chart as a dependency in your `Chart.yaml` e.g.
# Chart.yaml
dependencies:
- name: common
version: 4.0.1
version: 4.1.0
repository: https://bjw-s-labs.github.io/helm-charts/
```

View file

@ -299,6 +299,22 @@
}
}
},
"serviceName": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"identifier": {
"type": "string"
}
}
}
]
},
"volumeClaimTemplates": {
"type": "array",
"items": {

View file

@ -87,7 +87,7 @@
},
"identifier": {
"type": "string",
"description": "A reference to a backend service that is defined within the chart values."
"description": "A reference to a service identifier that is defined within the chart values."
},
"port": {
"type": ["string", "integer"]

View file

@ -1,7 +1,7 @@
{
"instance": {
"allOf": [
{ "$ref": "definitions.json#/resourceIdentifier" },
{"$ref": "definitions.json#/resourceIdentifier"},
{
"type": "object",
"additionalProperties": false,
@ -10,6 +10,7 @@
"prefix": {},
"suffix": {},
"enabled": {
"description": "Whether this ServiceMonitor is enabled or not.",
"type": "boolean",
"default": true
},
@ -20,22 +21,26 @@
"$ref": "definitions.json#/labels"
},
"endpoints": {
"description": "A list of endpoints allowed as part of this ServiceMonitor.",
"type": "array",
"items": {
"type": "object"
}
},
"selector": {
"description": "Selector to select Endpoints objects.",
"type": "object",
"additionalProperties": false,
"properties": {
"matchLabels": {
"description": "matchLabels is a map of {key,value} pairs. The requirements are ANDed.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"matchExpressions": {
"description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
"type": "array",
"items": {
"type": "object",
@ -52,17 +57,47 @@
}
}
},
"service": {
"description": "Which service to monitor. Either 'serviceName' or 'service' must be specified.",
"oneOf": [
{
"description": "A reference to a Service name. Helm templates can be used.",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
}
},
"required": ["name"]
},
{
"description": "A reference to a service identifier that is defined within the chart values.",
"type": "object",
"additionalProperties": false,
"properties": {
"identifier": {
"type": "string"
}
},
"required": ["identifier"]
}
]
},
"serviceName": {
"type": "string"
"description": "A reference to a Service name to monitor. Helm templates can be used. Deprecated in favor of 'service'.",
"type": "string",
"deprecated": true
},
"targetLabels": {
"description": "TargetLabels transfers labels from the Kubernetes Service onto the created metrics.",
"type": "array"
}
},
"oneOf": [{"required": ["serviceName"]}, {"required": ["selector"]}],
"dependencies": {
"selector": {"not": {"required": ["serviceName"]}},
"serviceName": {"not": {"required": ["selector"]}}
"selector": {"not": {"required": ["serviceName", "service"]}},
"serviceName": {"not": {"required": ["selector", "service"]}},
"service": {"not": {"required": ["selector", "serviceName"]}}
}
}
]

View file

@ -9,6 +9,19 @@
($serviceMonitorObject.annotations | default dict)
(include "bjw-s.common.lib.metadata.globalAnnotations" $rootContext | fromYaml)
-}}
{{ $service := dict -}}
{{ $serviceName := "" -}}
{{ if $serviceMonitorObject.serviceName -}}
{{ $serviceName = tpl $serviceMonitorObject.serviceName $rootContext -}}
{{ else if not (empty (dig "service" "name" nil $serviceMonitorObject)) -}}
{{ $serviceName = tpl $serviceMonitorObject.service.name $rootContext -}}
{{ else if not (empty (dig "service" "identifier" nil $serviceMonitorObject)) -}}
{{ $service = (include "bjw-s.common.lib.service.getByIdentifier" (dict "rootContext" $rootContext "id" $serviceMonitorObject.service.identifier) | fromYaml ) -}}
{{ if not $service -}}
{{fail (printf "No enabled Service found with this identifier. (serviceMonitor: '%s', identifier: '%s')" $serviceMonitorObject.identifier $serviceMonitorObject.service.identifier)}}
{{ end -}}
{{ $serviceName = $service.name -}}
{{ end -}}
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
@ -37,7 +50,7 @@ spec:
{{- tpl ($serviceMonitorObject.selector | toYaml) $rootContext | nindent 4}}
{{- else }}
matchLabels:
app.kubernetes.io/service: {{ tpl $serviceMonitorObject.serviceName $rootContext }}
app.kubernetes.io/service: {{ $serviceName }}
{{- include "bjw-s.common.lib.metadata.selectorLabels" $rootContext | nindent 6 }}
{{- end }}
endpoints: {{- tpl (toYaml $serviceMonitorObject.endpoints) $rootContext | nindent 4 }}

View file

@ -47,7 +47,15 @@ spec:
matchLabels:
app.kubernetes.io/controller: {{ $statefulsetObject.identifier }}
{{- include "bjw-s.common.lib.metadata.selectorLabels" $rootContext | nindent 6 }}
serviceName: {{ include "bjw-s.common.lib.chart.names.fullname" $rootContext }}
{{- $serviceName := include "bjw-s.common.lib.chart.names.fullname" $rootContext }}
{{- with (dig "statefulset" "serviceName" nil $statefulsetObject) }}
{{- if kindIs "map" . }}
{{- $serviceName = (include "bjw-s.common.lib.service.getByIdentifier" (dict "rootContext" $rootContext "id" .identifier) | fromYaml ).name }}
{{- else }}
{{- $serviceName = tpl . $rootContext }}
{{- end }}
{{- end }}
serviceName: {{ $serviceName }}
{{- with (dig "statefulset" "persistentVolumeClaimRetentionPolicy" nil $statefulsetObject) }}
persistentVolumeClaimRetentionPolicy: {{ . | toYaml | nindent 4 }}
{{- end }}

View file

@ -20,8 +20,11 @@ Determine a recourse name based on Helm values
{{- end -}}
{{- if not (empty $itemCount) -}}
{{- if (gt $itemCount 1) -}}
{{- if not (hasSuffix (printf "-%s" $identifier) $objectName) -}}
{{- if or (gt $itemCount 1) ($rootContext.Values.global.alwaysAppendIdentifierToResourceName) -}}
{{- if and
(not (hasSuffix (printf "-%s" $identifier) $objectName))
(not (eq $identifier $objectName))
-}}
{{- $objectName = printf "%s-%s" $objectName $identifier -}}
{{- end -}}
{{- end -}}

View file

@ -15,4 +15,11 @@ Validate controller values
{{- if not $enabledContainers -}}
{{- fail (printf "No containers enabled for controller (%s)" $controllerValues.identifier) -}}
{{- end -}}
{{- $enabledServiceAccounts := (include "bjw-s.common.lib.serviceAccount.enabledServiceAccounts" (dict "rootContext" $rootContext) | fromYaml ) }}
{{- if not (has "serviceAccount" (keys $controllerValues)) -}}
{{- if (gt (len $enabledServiceAccounts) 1) -}}
{{- fail (printf "serviceAccount field is required because automatic Service Account detection is not possible. (controller: %s)" $controllerValues.identifier ) -}}
{{- end -}}
{{- end -}}
{{- end -}}

View file

@ -0,0 +1,22 @@
{{/*
Autodetects the service for an Ingress object
*/}}
{{- define "bjw-s.common.lib.ingress.autoDetectService" -}}
{{- $rootContext := .rootContext -}}
{{- $ingressObject := .object -}}
{{- $enabledServices := (include "bjw-s.common.lib.service.enabledServices" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{- if eq 1 (len $enabledServices) -}}
{{- range $ingressObject.hosts -}}
{{- range .paths -}}
{{- if not (has "service" (keys .)) -}}
{{- $_ := set . "service" (dict "identifier" ($enabledServices | keys | first)) -}}
{{- else if and (not .service.name) (not .service.identifier) -}}
{{- $_ := set .service "identifier" ($enabledServices | keys | first) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $ingressObject | toYaml -}}
{{- end -}}

View file

@ -19,5 +19,11 @@ Return the enabled Ingresses.
{{- end -}}
{{- end -}}
{{- range $identifier, $objectValues := $enabledIngress -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledIngress)) | fromYaml -}}
{{- $object = include "bjw-s.common.lib.ingress.autoDetectService" (dict "rootContext" $rootContext "object" $object) | fromYaml -}}
{{- $_ := set $enabledIngress $identifier $object -}}
{{- end -}}
{{- $enabledIngress | toYaml -}}
{{- end -}}

View file

@ -5,26 +5,9 @@ Return an Ingress Object by its Identifier.
{{- $rootContext := .rootContext -}}
{{- $identifier := .id -}}
{{- $enabledServices := (include "bjw-s.common.lib.service.enabledServices" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{- $enabledIngresses := (include "bjw-s.common.lib.ingress.enabledIngresses" (dict "rootContext" $rootContext) | fromYaml ) }}
{{- if (hasKey $enabledIngresses $identifier) -}}
{{- $objectValues := get $enabledIngresses $identifier -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledIngresses)) | fromYaml -}}
{{- /* Try to automatically determine the default Service identifier if needed and possible */ -}}
{{- if eq 1 (len $enabledServices) -}}
{{- range $object.hosts -}}
{{- range .paths -}}
{{- if not (has "service" (keys .)) -}}
{{- $_ := set . "service" (dict "identifier" ($enabledServices | keys | first)) -}}
{{- else if and (not .service.name) (not .service.identifier) -}}
{{- $_ := set .service "identifier" ($enabledServices | keys | first) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $object | toYaml -}}
{{- get $enabledIngresses $identifier | toYaml -}}
{{- end -}}
{{- end -}}

View file

@ -63,7 +63,7 @@ nodeSelector: {{ . | nindent 2 }}
affinity: {{- tpl . $rootContext | nindent 2 }}
{{- end -}}
{{- with (include "bjw-s.common.lib.pod.getOption" (dict "ctx" $ctx "option" "topologySpreadConstraints")) }}
topologySpreadConstraints: {{ . | nindent 2 }}
topologySpreadConstraints: {{- tpl . $rootContext | nindent 2 }}
{{- end -}}
{{- with (include "bjw-s.common.lib.pod.getOption" (dict "ctx" $ctx "option" "tolerations")) }}
tolerations: {{ . | nindent 2 }}

View file

@ -4,19 +4,25 @@ Returns the value for serviceAccountName
{{- define "bjw-s.common.lib.pod.field.serviceAccountName" -}}
{{- $rootContext := .ctx.rootContext -}}
{{- $controllerObject := .ctx.controllerObject -}}
{{- $enabledServiceAccounts := (include "bjw-s.common.lib.serviceAccount.enabledServiceAccounts" (dict "rootContext" $rootContext) | fromYaml ) }}
{{- $serviceAccountName := "default" -}}
{{- with $controllerObject.serviceAccount -}}
{{- if hasKey . "identifier" -}}
{{- $subject := (include "bjw-s.common.lib.serviceAccount.getByIdentifier" (dict "rootContext" $rootContext "id" .identifier) | fromYaml) -}}
{{- if not (has "serviceAccount" (keys $controllerObject)) -}}
{{- if (eq (len $enabledServiceAccounts) 1) -}}
{{- $serviceAccountName = ($enabledServiceAccounts | keys | first) -}}
{{- end -}}
{{- else -}}
{{- if hasKey $controllerObject.serviceAccount "identifier" -}}
{{- $subject := (include "bjw-s.common.lib.serviceAccount.getByIdentifier" (dict "rootContext" $rootContext "id" $controllerObject.serviceAccount.identifier) | fromYaml) -}}
{{- if not $subject }}
{{- fail (printf "No enabled ServiceAccount found with this identifier. (controller: '%s', identifier: '%s')" $controllerObject.identifier .identifier) -}}
{{- fail (printf "No enabled ServiceAccount found with this identifier. (controller: '%s', identifier: '%s')" $controllerObject.identifier $controllerObject.serviceAccount.identifier) -}}
{{- end -}}
{{- $serviceAccountName = get $subject "name" -}}
{{- else if hasKey . "name" -}}
{{- $serviceAccountName = .name -}}
{{- else if hasKey $controllerObject.serviceAccount "name" -}}
{{- $serviceAccountName = $controllerObject.serviceAccount.name -}}
{{- end -}}
{{- end -}}
{{- $serviceAccountName -}}

View file

@ -0,0 +1,21 @@
{{/*
Autodetects the service for a Route object
*/}}
{{- define "bjw-s.common.lib.route.autoDetectService" -}}
{{- $rootContext := .rootContext -}}
{{- $routeObject := .object -}}
{{- $enabledServices := (include "bjw-s.common.lib.service.enabledServices" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{- if eq 1 (len $enabledServices) -}}
{{- range $routeObject.rules -}}
{{- range .backendRefs }}
{{- $backendRef := . -}}
{{- if and (empty (dig "name" nil $backendRef)) (empty (dig "identifier" nil $backendRef)) -}}
{{- $_ := set $backendRef "identifier" ($enabledServices | keys | first) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $routeObject | toYaml -}}
{{- end -}}

View file

@ -19,5 +19,11 @@ Return the enabled routes.
{{- end -}}
{{- end -}}
{{- range $identifier, $objectValues := $enabledRoutes -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledRoutes)) | fromYaml -}}
{{- $object = include "bjw-s.common.lib.route.autoDetectService" (dict "rootContext" $rootContext "object" $object) | fromYaml -}}
{{- $_ := set $enabledRoutes $identifier $object -}}
{{- end -}}
{{- $enabledRoutes | toYaml -}}
{{- end -}}

View file

@ -5,25 +5,9 @@ Return a Route object by its Identifier.
{{- $rootContext := .rootContext -}}
{{- $identifier := .id -}}
{{- $enabledServices := (include "bjw-s.common.lib.service.enabledServices" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{- $enabledRoutes := (include "bjw-s.common.lib.route.enabledRoutes" (dict "rootContext" $rootContext) | fromYaml ) }}
{{- if (hasKey $enabledRoutes $identifier) -}}
{{- $objectValues := get $enabledRoutes $identifier -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledRoutes)) | fromYaml -}}
{{- /* Try to automatically determine the default Service identifier if needed and possible */ -}}
{{- if eq 1 (len $enabledServices) -}}
{{- range $object.rules -}}
{{- range .backendRefs }}
{{- $backendRef := . -}}
{{- if and (empty (dig "name" nil $backendRef)) (empty (dig "identifier" nil $backendRef)) -}}
{{- $_ := set $backendRef "identifier" ($enabledServices | keys | first) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $object | toYaml -}}
{{- get $enabledRoutes $identifier | toYaml -}}
{{- end -}}
{{- end -}}

View file

@ -0,0 +1,15 @@
{{/*
Autodetects the controller for a Service object
*/}}
{{- define "bjw-s.common.lib.service.autoDetectController" -}}
{{- $rootContext := .rootContext -}}
{{- $serviceObject := .object -}}
{{- $enabledControllers := (include "bjw-s.common.lib.controller.enabledControllers" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{- if eq 1 (len $enabledControllers) -}}
{{- if (empty (dig "controller" nil $serviceObject)) -}}
{{- $_ := set $serviceObject "controller" ($enabledControllers | keys | first) -}}
{{- end -}}
{{- end -}}
{{- $serviceObject | toYaml -}}
{{- end -}}

View file

@ -5,21 +5,25 @@ Return the enabled services.
{{- $rootContext := .rootContext -}}
{{- $enabledServices := dict -}}
{{- range $name, $service := $rootContext.Values.service -}}
{{- if kindIs "map" $service -}}
{{- range $identifier, $objectValues := $rootContext.Values.service -}}
{{- if kindIs "map" $objectValues -}}
{{- /* Enable Service by default, but allow override */ -}}
{{- $serviceEnabled := true -}}
{{- if hasKey $service "enabled" -}}
{{- $serviceEnabled = $service.enabled -}}
{{- if hasKey $objectValues "enabled" -}}
{{- $serviceEnabled = $objectValues.enabled -}}
{{- end -}}
{{- if $serviceEnabled -}}
{{- $_ := set $enabledServices $name . -}}
{{- $_ := set $enabledServices $identifier $objectValues -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- range $identifier, $objectValues := $enabledServices -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledServices)) | fromYaml -}}
{{- $object = include "bjw-s.common.lib.service.autoDetectController" (dict "rootContext" $rootContext "object" $object) | fromYaml -}}
{{- $_ := set $enabledServices $identifier $object -}}
{{- end -}}
{{- $enabledServices | toYaml -}}
{{- end -}}

View file

@ -5,18 +5,8 @@ Return a service Object by its Identifier.
{{- $rootContext := .rootContext -}}
{{- $identifier := .id -}}
{{- $enabledServices := (include "bjw-s.common.lib.service.enabledServices" (dict "rootContext" $rootContext) | fromYaml ) }}
{{- $enabledControllers := (include "bjw-s.common.lib.controller.enabledControllers" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{- if (hasKey $enabledServices $identifier) -}}
{{- $objectValues := get $enabledServices $identifier -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledServices)) | fromYaml -}}
{{- if eq 1 (len $enabledControllers) -}}
{{- if (empty (dig "controller" nil $object)) -}}
{{- $_ := set $object "controller" ($enabledControllers | keys | first) -}}
{{- end -}}
{{- end -}}
{{- $object | toYaml -}}
{{- get $enabledServices $identifier | toYaml -}}
{{- end -}}
{{- end -}}

View file

@ -19,5 +19,10 @@ Return the enabled serviceAccounts.
{{- end -}}
{{- end -}}
{{- range $identifier, $objectValues := $enabledServiceAccounts -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledServiceAccounts)) | fromYaml -}}
{{- $_ := set $enabledServiceAccounts $identifier $object -}}
{{- end -}}
{{- $enabledServiceAccounts | toYaml -}}
{{- end -}}

View file

@ -7,7 +7,6 @@ Return a ServiceAccount Object by its Identifier.
{{- $enabledServiceAccounts := (include "bjw-s.common.lib.serviceAccount.enabledServiceAccounts" (dict "rootContext" $rootContext) | fromYaml ) }}
{{- if (hasKey $enabledServiceAccounts $identifier) -}}
{{- $objectValues := get $enabledServiceAccounts $identifier -}}
{{- include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledServiceAccounts)) -}}
{{- get $enabledServiceAccounts $identifier | toYaml -}}
{{- end -}}
{{- end -}}

View file

@ -0,0 +1,21 @@
{{/*
Autodetects the service for a ServiceMonitors object
*/}}
{{- define "bjw-s.common.lib.serviceMonitor.autoDetectService" -}}
{{- $rootContext := .rootContext -}}
{{- $serviceMonitorObject := .object -}}
{{- $enabledServices := (include "bjw-s.common.lib.service.enabledServices" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{- if eq 1 (len $enabledServices) -}}
{{- if and
(empty (dig "selector" nil $serviceMonitorObject))
(empty (dig "serviceName" nil $serviceMonitorObject))
(empty (dig "service" "name" nil $serviceMonitorObject))
(empty (dig "service" "identifier" nil $serviceMonitorObject))
-}}
{{- $_ := set $serviceMonitorObject "service" (dict "identifier" ($enabledServices | keys | first)) -}}
{{- end -}}
{{- end -}}
{{- $serviceMonitorObject | toYaml -}}
{{- end -}}

View file

@ -7,7 +7,7 @@ Return the enabled serviceMonitors.
{{- range $identifier, $serviceMonitor := $rootContext.Values.serviceMonitor -}}
{{- if kindIs "map" $serviceMonitor -}}
{{- /* Enable Service by default, but allow override */ -}}
{{- /* Enable serviceMonitors by default, but allow override */ -}}
{{- $serviceMonitorEnabled := true -}}
{{- if hasKey $serviceMonitor "enabled" -}}
{{- $serviceMonitorEnabled = $serviceMonitor.enabled -}}
@ -19,5 +19,11 @@ Return the enabled serviceMonitors.
{{- end -}}
{{- end -}}
{{- range $identifier, $objectValues := $enabledServiceMonitors -}}
{{- $object := include "bjw-s.common.lib.valuesToObject" (dict "rootContext" $rootContext "id" $identifier "values" $objectValues "itemCount" (len $enabledServiceMonitors)) | fromYaml -}}
{{- $object = include "bjw-s.common.lib.serviceMonitor.autoDetectService" (dict "rootContext" $rootContext "object" $object) | fromYaml -}}
{{- $_ := set $enabledServiceMonitors $identifier $object -}}
{{- end -}}
{{- $enabledServiceMonitors | toYaml -}}
{{- end -}}

View file

@ -5,6 +5,20 @@ Validate serviceMonitor values
{{- $rootContext := .rootContext -}}
{{- $serviceMonitorObject := .object -}}
{{- $enabledServices := (include "bjw-s.common.lib.service.enabledServices" (dict "rootContext" $rootContext) | fromYaml ) -}}
{{/* Verify automatic controller detection */}}
{{- if not (eq 1 (len $enabledServices)) -}}
{{- if and
(empty (dig "selector" nil $serviceMonitorObject))
(empty (dig "serviceName" nil $serviceMonitorObject))
(empty (dig "service" "name" nil $serviceMonitorObject))
(empty (dig "service" "identifier" nil $serviceMonitorObject))
-}}
{{- fail (printf "Either service.name or service.identifier is required because automatic Service detection is not possible. (serviceMonitor: %s)" $serviceMonitorObject.identifier ) -}}
{{- end -}}
{{- end -}}
{{- if not $serviceMonitorObject.endpoints -}}
{{- fail (printf "endpoints are required for serviceMonitor with key \"%v\"" $serviceMonitorObject.identifier) -}}
{{- end -}}

View file

@ -18,6 +18,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: ConfigMap
apiVersion: v1
name: release-name-main
any: true
- it: forceRename
set:
configMaps.main.forceRename: forceRename

View file

@ -19,6 +19,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: CronJob
apiVersion: batch/v1
name: release-name-main
any: true
- it: forceRename
set:
controllers.main.forceRename: forceRename

View file

@ -16,6 +16,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: DaemonSet
apiVersion: apps/v1
name: release-name-main
any: true
- it: forceRename
set:
controllers.main.forceRename: forceRename

View file

@ -14,6 +14,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: Deployment
apiVersion: apps/v1
name: release-name-main
any: true
- it: forceRename
set:
controllers.main.forceRename: forceRename

View file

@ -16,6 +16,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: Ingress
apiVersion: networking.k8s.io/v1
name: release-name-main
any: true
- it: forceRename
set:
ingress.main.forceRename: forceRename

View file

@ -16,6 +16,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: Job
apiVersion: batch/v1
name: release-name-main
any: true
- it: forceRename
set:
controllers.main.forceRename: forceRename

View file

@ -21,6 +21,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
name: release-name-main
any: true
- it: forceRename
set:
networkpolicies.main.forceRename: forceRename

View file

@ -54,3 +54,15 @@ tests:
- equal:
path: spec.template.spec.serviceAccountName
value: mySA3
- it: with automatic serviceAccount detection should pass
set:
serviceAccount:
mySA: {}
documentSelector:
path: $[?(@.kind == "Deployment")].metadata.name
value: release-name
asserts:
- equal:
path: spec.template.spec.serviceAccountName
value: mySA

View file

@ -72,3 +72,29 @@ tests:
labelSelector:
matchLabels:
app: foo
- it: topologySpreadConstraints with template should pass
set:
controllers:
main:
pod:
topologySpreadConstraints:
- maxSkew: 2
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: "{{ .Release.Name }}"
documentSelector:
path: $[?(@.kind == "Deployment")].metadata.name
value: release-name
asserts:
- equal:
path: spec.template.spec.topologySpreadConstraints
value:
- maxSkew: 2
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: RELEASE-NAME

View file

@ -0,0 +1,16 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
suite: service - validations
templates:
- common.yaml
values:
- ../_values/controllers_main_default_container.yaml
tests:
- it: automatic Service Account determination should fail when >1 Service Account is enabled
set:
serviceAccount:
mySA: {}
mySA2: {}
asserts:
- failedTemplate:
errorMessage: "serviceAccount field is required because automatic Service Account detection is not possible. (controller: main)"

View file

@ -19,6 +19,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: PersistentVolumeClaim
apiVersion: v1
name: release-name-main
any: true
- it: forceRename
set:
persistence.main.forceRename: forceRename

View file

@ -20,6 +20,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: Endpoint
apiVersion: v1
name: release-name-main
any: true
- it: forceRename
set:
rawResources.main.forceRename: forceRename

View file

@ -19,6 +19,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1alpha2
name: release-name-main
any: true
- it: forceRename
set:
route.main.forceRename: forceRename

View file

@ -18,6 +18,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: Secret
apiVersion: v1
name: release-name-main
any: true
- it: forceRename
set:
secrets.main.forceRename: forceRename

View file

@ -15,6 +15,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: Service
apiVersion: v1
name: release-name-main
any: true
- it: forceRename
set:
service.main.forceRename: forceRename

View file

@ -16,6 +16,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: ServiceAccount
apiVersion: v1
name: release-name-main
any: true
- it: forceRename
set:
serviceAccount.main.forceRename: forceRename
@ -98,6 +108,7 @@ tests:
- it: multiple items
set:
controllers.main.serviceAccount.identifier: main
serviceAccount.second: {}
asserts:
- containsDocument:
@ -113,6 +124,7 @@ tests:
- it: multiple items with prefix
set:
controllers.main.serviceAccount.identifier: main
serviceAccount.second:
prefix: prefix
asserts:
@ -129,6 +141,7 @@ tests:
- it: multiple items with suffix
set:
controllers.main.serviceAccount.identifier: main
serviceAccount.second:
suffix: suffix
asserts:
@ -145,6 +158,7 @@ tests:
- it: multiple items with prefix and suffix
set:
controllers.main.serviceAccount.identifier: main
serviceAccount.second:
prefix: prefix
suffix: suffix
@ -162,6 +176,7 @@ tests:
- it: multiple items with prefix, suffix and forceRename (illegal combination)
set:
controllers.main.serviceAccount.identifier: main
serviceAccount.second:
forceRename: forceRename
prefix: prefix

View file

@ -40,6 +40,7 @@ tests:
- it: multiple serviceAccounts can be enabled
set:
controllers.main.serviceAccount.identifier: myAccount
serviceAccount:
myAccount: {}
mySA: {}

View file

@ -29,3 +29,26 @@ tests:
matchExpressions:
- key: k8s-app
operator: Exists
- it: selector can determine default service automatically
values:
- ../_values/service_main_default.yaml
set:
serviceMonitor:
main:
endpoints:
- port: http
scheme: http
path: /metrics
interval: 1m
scrapeTimeout: 10s
documentSelector:
path: $[?(@.kind == "ServiceMonitor")].metadata.name
value: release-name
asserts:
- equal:
path: spec.selector.matchLabels
value:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/name: RELEASE-NAME
app.kubernetes.io/service: release-name

View file

@ -0,0 +1,55 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
suite: serviceMonitor - fields - service
templates:
- common.yaml
values:
- ../_values/controllers_main_default_container.yaml
tests:
- it: a templated service name can be configured
set:
serviceMonitor:
main:
service:
name: "{{ .Release.Name }}"
endpoints:
- port: http
scheme: http
path: /metrics
interval: 1m
scrapeTimeout: 10s
documentSelector:
path: $[?(@.kind == "ServiceMonitor")].metadata.name
value: release-name
asserts:
- equal:
path: spec.selector.matchLabels
value:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/name: RELEASE-NAME
app.kubernetes.io/service: RELEASE-NAME
- it: a service identifier can be configured
values:
- ../_values/service_main_default.yaml
set:
serviceMonitor:
main:
service:
identifier: main
endpoints:
- port: http
scheme: http
path: /metrics
interval: 1m
scrapeTimeout: 10s
documentSelector:
path: $[?(@.kind == "ServiceMonitor")].metadata.name
value: release-name
asserts:
- equal:
path: spec.selector.matchLabels
value:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/name: RELEASE-NAME
app.kubernetes.io/service: release-name

View file

@ -23,6 +23,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: ServiceMonitor
apiVersion: monitoring.coreos.com/v1
name: release-name-main
any: true
- it: forceRename
set:
serviceMonitor.main.forceRename: forceRename

View file

@ -0,0 +1,62 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
suite: serviceMonitor - validations
templates:
- common.yaml
values:
- ../_values/controllers_main_default_container.yaml
tests:
- it: service reference to non-existing service identifier should fail
set:
serviceMonitor:
main:
service:
identifier: main
endpoints:
- port: http
scheme: http
path: /metrics
interval: 1m
scrapeTimeout: 10s
asserts:
- failedTemplate:
errorMessage: "No enabled Service found with this identifier. (serviceMonitor: 'main', identifier: 'main')"
- it: automatic service determination should fail when no service is enabled
set:
serviceMonitor:
main:
endpoints:
- port: http
scheme: http
path: /metrics
interval: 1m
scrapeTimeout: 10s
asserts:
- failedTemplate:
errorMessage: "Either service.name or service.identifier is required because automatic Service detection is not possible. (serviceMonitor: main)"
- it: automatic service determination should fail when >1 service is enabled
set:
service:
main:
controller: main
ports:
ui:
port: 8082
second:
controller: main
ports:
ui:
port: 8082
serviceMonitor:
main:
endpoints:
- port: http
scheme: http
path: /metrics
interval: 1m
scrapeTimeout: 10s
asserts:
- failedTemplate:
errorMessage: "Either service.name or service.identifier is required because automatic Service detection is not possible. (serviceMonitor: main)"

View file

@ -0,0 +1,70 @@
---
# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
suite: statefulset - fields - serviceName
templates:
- common.yaml
values:
- ../_values/controllers_main_default_container.yaml
set:
controllers.main.type: statefulset
tests:
- it: default should pass
documentSelector:
path: $[?(@.kind == "StatefulSet")].metadata.name
value: release-name
asserts:
- equal:
path: spec.serviceName
value: RELEASE-NAME
- it: custom serviceName
set:
controllers.main.statefulset:
serviceName: test
documentSelector:
path: $[?(@.kind == "StatefulSet")].metadata.name
value: release-name
asserts:
- equal:
path: spec.serviceName
value: test
- it: custom serviceName with template
set:
controllers.main.statefulset:
serviceName: "{{ .Release.Name | lower }}"
service:
main:
controller: main
ports:
http:
port: 8081
documentSelector:
path: $[?(@.kind == "StatefulSet")].metadata.name
value: release-name
asserts:
- equal:
path: spec.serviceName
value: release-name
- it: serviceName with identifier
set:
controllers.main.statefulset:
serviceName:
identifier: headless
service:
main:
ports:
http:
port: 8081
headless:
ports:
http:
port: 8081
documentSelector:
path: $[?(@.kind == "StatefulSet")].metadata.name
value: release-name
asserts:
- equal:
path: spec.serviceName
value: release-name-headless

View file

@ -16,6 +16,16 @@ tests:
name: release-name
any: true
- it: name includes identifier when alwaysAppendIdentifierToResourceName is enabled
set:
global.alwaysAppendIdentifierToResourceName: true
asserts:
- containsDocument:
kind: StatefulSet
apiVersion: apps/v1
name: release-name-main
any: true
- it: forceRename
set:
controllers.main.forceRename: forceRename

View file

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://raw.githubusercontent.com/bjw-s-labs/helm-charts/common-4.0.1/charts/library/common/values.schema.json",
"$id": "https://raw.githubusercontent.com/bjw-s-labs/helm-charts/common-4.1.0/charts/library/common/values.schema.json",
"type": "object",
"properties": {
"global": {
@ -21,6 +21,11 @@
"null"
]
},
"alwaysAppendIdentifierToResourceName": {
"description": "Always append identifier slugs to resource names, regardless of the enabled resource count.",
"type": "boolean",
"default": false
},
"propagateGlobalMetadataToPods": {
"description": "Propagate global metadata to Pod labels",
"type": "boolean",

View file

@ -15,32 +15,36 @@ Override the default resource name entirely.
#### prefix
Prefix to prepend to the resource name.
Prefix to prepend to the resource name. Will not be added if the resource name already starts with the prefix.
#### suffix
Suffix to append to the resource name. Defaults to the resource identifier if there are multiple enabled items, otherwise it defaults to an empty value.
Suffix to append to the resource name. Will not be added if the resource name already ends with the suffix. Defaults to the resource identifier if there are multiple enabled items, otherwise it defaults to an empty value.
### Behavior
By default, all resource names are based on the `bjw-s.common.lib.chart.names.fullname` template which defaults to the Helm Release name. This template can be further controlled by the `global.nameOverride` and `global.fullnameOverride` values.
The resource identifier (key) is only appended when there are multiple resources of the same kind or if the value `global.alwaysAppendIdentifierToResourceName` is set to `true`.
!!! info
All resource names are based on the `bjw-s.common.lib.chart.names.fullname` template. This defaults to the Helm Release name, but can be further controlled by the `global.nameOverride` and `global.fullnameOverride` values. The identifier only comes in to play when there are multiple resources of the same kind.
When the resource name already equals the identifier or if the identifier suffix is already present, it will not be added a second time. This is to prevent names like `vaultwarden-vaultwarden`
Assuming a Helm Release with the name `base_name`, the following table gives an overview of how the resource name is generated:
| item key | enabled items | `prefix` | `suffix` | `forceRename` | Generated name |
|----------|---------------|----------|----------|----------------------------|------------------------|
| `id` | 1 | | | `random_name` | `random_name` |
| `id` | 1 | | | `{{ .Release.Namespace }}` | `default` |
| `id` | 1 | | | | `base_name` |
| `id` | 1 | `pfx` | | | `pfx-base_name` |
| `id` | 1 | | `sfx` | | `base_name-sfx` |
| `id` | 2 | | | | `base_name-id` |
| `id` | 1 | `pfx` | `sfx` | | `pfx-base_name-sfx` |
| `id` | 2 | `pfx` | | | `pfx-base_name-id` |
| `id` | 2 | | `sfx` | | `base_name-id-sfx` |
| `id` | 2 | `pfx` | `sfx` | | `pfx-base_name-id-sfx` |
| `id` | 1 | | `sfx` | | `base_name-sfx` |
| `id` | 1 | `pfx` | | | `pfx-base_name` |
| `id` | 1 | `pfx` | `sfx` | | `pfx-base_name-sfx` |
| `forceRename` | Enabled items | Always append identifier | `identifier` | `prefix` | `suffix` | Expected name | Notes |
|------------------------------|---------------|--------------------------|---------------|---------------|-------------|-------------------------------|--------------------------------------------------|
| `custom-name` | | | | | | `custom-name` | Force rename overrides all logic |
| `"{{ .Release.Namespace }}"` | | | | | | `default` | Force rename with a template overrides all logic |
| | 1 | | `abc` | | | `base_name` | Base case, no additional configuration |
| | 1 | | `abc` | `team` | | `team-base_name` | Prefix added |
| | 1 | | `abc` | `base_name` | | `base_name` | Prefix equal to fullname skipped |
| | 1 | | `abc` | | `svc` | `base_name-svc` | Suffix added |
| | 1 | | `abc` | | `base_name` | `base_name` | Suffix equal to fullname - skipped |
| | 1 | | `abc` | `team` | `svc` | `team-base_name-svc` | Prefix + suffix, no identifier |
| | 1 | `true` | `abc` | | | `base_name-abc` | identifier suffix via global flag |
| | 1 | `true` | `abc` | `team` | `svc` | `team-base_name-abc-svc` | Prefix + identifier + suffix via global flag |
| | 2 | | `abc` | | | `base_name-abc` | Identifier due to itemCount > 1 |
| | 2 | | `abc` | `team` | `svc` | `team-base_name-abc-svc` | Prefix + Identifier + Suffix |
| | 2 | | `base_name` | | | `base_name` | Identifier equal to fullname skipped |
| | 2 | | `abc` | | `abc` | `base_name-abc` | Suffix skipped already included by identifer |

View file

@ -0,0 +1,4 @@
/* Maximum space for text block */
.md-grid {
max-width: 100%; /* 100% to stretch to full-width */
}