feat(common): Release v2.1.0 (#221)

Co-authored-by: Christopher Larivière <lariviere.c@gmail.com>
This commit is contained in:
Bᴇʀɴᴅ Sᴄʜᴏʀɢᴇʀs 2023-11-10 15:46:55 +01:00 committed by GitHub
parent 272dbef383
commit b360c9885a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 288 additions and 44 deletions

View file

@ -1,6 +1,8 @@
controllers:
main:
type: cronjob
cronjob:
timeZone: UTC
containers:
main:
image:

View file

@ -154,3 +154,51 @@ tests:
value:
name: STATIC_ENV
value: static
- it: Env vars with dependsOn should pass
set:
controllers.main.containers.main.env:
STATIC_ENV: 1
DYNAMIC_ENV:
valueFrom:
fieldRef:
fieldPath: spec.nodeName
dependsOn: STATIC_ENV
ORDERED_ENV:
value: true
dependsOn: STATIC_ENV
DEPENDENT_ENV:
value: moo_two
dependsOn:
- DYNAMIC_ENV
- ORDERED_ENV
asserts:
- documentIndex: &DeploymentDoc 0
isKind:
of: Deployment
- documentIndex: *DeploymentDoc
equal:
path: spec.template.spec.containers[0].env[0]
value:
name: STATIC_ENV
value: "1"
- documentIndex: *DeploymentDoc
equal:
path: spec.template.spec.containers[0].env[1]
value:
name: DYNAMIC_ENV
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- documentIndex: *DeploymentDoc
equal:
path: spec.template.spec.containers[0].env[2]
value:
name: ORDERED_ENV
value: "true"
- documentIndex: *DeploymentDoc
equal:
path: spec.template.spec.containers[0].env[3]
value:
name: DEPENDENT_ENV
value: moo_two

View file

@ -71,3 +71,34 @@ tests:
- documentIndex: 3
isKind:
of: Ingress
- it: multiple with default enabled should pass
set:
ingress.main.enabled: true
ingress.test: {}
asserts:
- hasDocuments:
count: 4
- documentIndex: 0
not: true
isKind:
of: Ingress
- documentIndex: 1
not: true
isKind:
of: Ingress
- documentIndex: &FirstIngressDocument 2
isKind:
of: Ingress
- documentIndex: *FirstIngressDocument
equal:
path: metadata.name
value: RELEASE-NAME
- documentIndex: &SecondIngressDocument 3
isKind:
of: Ingress
- documentIndex: *SecondIngressDocument
equal:
path: metadata.name
value: RELEASE-NAME-test

View file

@ -77,3 +77,20 @@ tests:
equal:
path: spec.rules[0].http.paths[0].path
value: "/RELEASE-NAME.path"
- it: with defaultBackend should pass
set:
ingress.main:
enabled: true
defaultBackend: test
asserts:
- documentIndex: &IngressDocument 2
isKind:
of: Ingress
- documentIndex: *IngressDocument
equal:
path: spec.defaultBackend
value: test
- documentIndex: *IngressDocument
notExists:
path: spec.rules

View file

@ -42,6 +42,24 @@ tests:
path: spec.hostnames[0]
value: chart-test.local
- it: custom host with template
set:
route.main:
enabled: true
parentRefs:
- name: parentName
namespace: parentNamespace
hostnames:
- "{{ .Release.Name }}.local"
asserts:
- documentIndex: &HTTPRouteDocument 2
isKind:
of: HTTPRoute
- documentIndex: *HTTPRouteDocument
equal:
path: spec.hostnames[0]
value: RELEASE-NAME.local
- it: path matches should only be used for HTTPRoutes
set:
route:

View file

@ -3,7 +3,7 @@ apiVersion: v2
name: common
description: Function library for Helm charts
type: library
version: 2.0.3
version: 2.1.0
kubeVersion: ">=1.22.0-0"
keywords:
- common
@ -14,6 +14,29 @@ maintainers:
email: me@bjw-s.dev
annotations:
artifacthub.io/changes: |-
- kind: added
description: |-
Support v1 of Gateway APIs Route types
- kind: added
description: |-
Support setting CronJob timezones in Kubernetes versions >= 1.27
- kind: added
description: |-
Support setting a defaultBackend per ingress
- kind: added
description: |-
Support using templates in Route hostnames
- kind: added
description: |-
Added support for dependsOn in environment variables
links:
- name: GitHub repo
url: https://github.com/dastrobu/helm-charts/tree/main/environment-variables
- name: Related blogpost
url: https://dastrobu.medium.com/an-advanced-api-for-environment-variables-in-helm-charts-e0bb1e0aa58a
- kind: fixed
description: |-
Volumes did not render correctly across multiple controllers
Improved "isEmpty" checks to be compatible with more Helm versions
- kind: fixed
description: |-
No longer quote numeric ports in probes

View file

@ -1,6 +1,6 @@
# common
![Version: 2.0.3](https://img.shields.io/badge/Version-2.0.3-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square)
![Version: 2.1.0](https://img.shields.io/badge/Version-2.1.0-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square)
Function library for Helm charts
@ -27,7 +27,7 @@ Include this chart as a dependency in your `Chart.yaml` e.g.
# Chart.yaml
dependencies:
- name: common
version: 2.0.3
version: 2.1.0
repository: https://bjw-s.github.io/helm-charts/
```
@ -56,7 +56,7 @@ The following table contains an overview of available values and their descripti
| controllers.main.annotations | object | `{}` | Set annotations on the deployment/statefulset/daemonset/cronjob |
| controllers.main.containers.main.args | list | `[]` | Override the args for the default container |
| controllers.main.containers.main.command | list | `[]` | Override the command(s) for the default container |
| controllers.main.containers.main.env | string | `nil` | Environment variables. Template enabled. Syntax options: A) TZ: UTC B) PASSWD: '{{ .Release.Name }}' C) PASSWD: configMapKeyRef: name: config-map-name key: key-name D) PASSWD: valueFrom: secretKeyRef: name: secret-name key: key-name ... E) - name: TZ value: UTC F) - name: TZ value: '{{ .Release.Name }}' |
| controllers.main.containers.main.env | string | `nil` | Environment variables. Template enabled. Syntax options: A) TZ: UTC B) PASSWD: '{{ .Release.Name }}' B) TZ: value: UTC dependsOn: otherVar D) PASSWD: configMapKeyRef: name: config-map-name key: key-name E) PASSWD: dependsOn: - otherVar1 - otherVar2 valueFrom: secretKeyRef: name: secret-name key: key-name ... F) - name: TZ value: UTC G) - name: TZ value: '{{ .Release.Name }}' |
| controllers.main.containers.main.envFrom | list | `[]` | Secrets and/or ConfigMaps that will be loaded as environment variables. [[ref]](https://unofficial-kubernetes.readthedocs.io/en/latest/tasks/configure-pod-container/configmap/#use-case-consume-configmap-in-environment-variables) |
| controllers.main.containers.main.image.pullPolicy | string | `nil` | image pull policy |
| controllers.main.containers.main.image.repository | string | `nil` | image repository |
@ -91,6 +91,7 @@ The following table contains an overview of available values and their descripti
| controllers.main.cronjob.schedule | string | `"*/20 * * * *"` | Sets the CronJob time when to execute your jobs |
| controllers.main.cronjob.startingDeadlineSeconds | int | `30` | The deadline in seconds for starting the job if it misses its scheduled time for any reason |
| controllers.main.cronjob.successfulJobsHistory | int | `1` | The number of succesful Jobs to keep |
| controllers.main.cronjob.timeZone | string | `nil` | Sets the CronJob timezone (only works in Kubernetes >= 1.27) |
| controllers.main.cronjob.ttlSecondsAfterFinished | string | `nil` | If this field is set, ttlSecondsAfterFinished after the Job finishes, it is eligible to be automatically deleted. |
| controllers.main.enabled | bool | `true` | enable the controller. |
| controllers.main.initContainers | object | `{}` | Specify any initContainers here as dictionary items. Each initContainer should have its own key initContainers get sorted alphanumerically by the `<order>-<identifier>` combination. |
@ -136,6 +137,7 @@ The following table contains an overview of available values and their descripti
| ingress | object | See below | Configure the ingresses for the chart here. Additional ingresses can be added by adding a dictionary key similar to the 'main' ingress. |
| ingress.main.annotations | object | `{}` | Provide additional annotations which may be required. |
| ingress.main.className | string | `nil` | Set the ingressClass that is used for this ingress. |
| ingress.main.defaultBackend | string | `nil` | Configure the defaultBackend for this ingress. This will disable any other rules for the ingress. |
| ingress.main.enabled | bool | `false` | Enables or disables the ingress |
| ingress.main.hosts[0].host | string | `"chart-example.local"` | Host address. Helm template can be passed. |
| ingress.main.hosts[0].paths[0].path | string | `"/"` | Path. Helm template can be passed. |
@ -165,7 +167,7 @@ The following table contains an overview of available values and their descripti
| route | object | See below | Configure the gateway routes for the chart here. Additional routes can be added by adding a dictionary key similar to the 'main' route. [[ref]](https://gateway-api.sigs.k8s.io/references/spec/) |
| route.main.annotations | object | `{}` | Provide additional annotations which may be required. |
| route.main.enabled | bool | `false` | Enables or disables the route |
| route.main.hostnames | list | `[]` | Host addresses |
| route.main.hostnames | list | `[]` | Host addresses. Helm template can be passed. |
| route.main.kind | string | `"HTTPRoute"` | Set the route kind Valid options are GRPCRoute, HTTPRoute, TCPRoute, TLSRoute, UDPRoute |
| route.main.labels | object | `{}` | Provide additional labels which may be required. |
| route.main.nameOverride | string | `nil` | Override the name suffix that is used for this route. |

View file

@ -6,6 +6,11 @@ using the common library.
{{- $rootContext := .rootContext -}}
{{- $cronjobObject := .object -}}
{{- $timeZone := "" -}}
{{- if ge (int $rootContext.Capabilities.KubeVersion.Minor) 27 }}
{{- $timeZone = dig "cronjob" "timeZone" "" $cronjobObject -}}
{{- end -}}
{{- $labels := merge
(dict "app.kubernetes.io/component" $cronjobObject.identifier)
($cronjobObject.labels | default dict)
@ -29,6 +34,9 @@ metadata:
spec:
concurrencyPolicy: "{{ $cronjobObject.cronjob.concurrencyPolicy }}"
startingDeadlineSeconds: {{ $cronjobObject.cronjob.startingDeadlineSeconds }}
{{- with $timeZone }}
timeZone: "{{ . }}"
{{- end }}
schedule: "{{ $cronjobObject.cronjob.schedule }}"
successfulJobsHistoryLimit: {{ $cronjobObject.cronjob.successfulJobsHistory }}
failedJobsHistoryLimit: {{ $cronjobObject.cronjob.failedJobsHistory }}

View file

@ -43,6 +43,9 @@ spec:
{{- end }}
{{- end }}
{{- end }}
{{- if $ingressObject.defaultBackend }}
defaultBackend: {{ $ingressObject.defaultBackend }}
{{- else }}
rules:
{{- range $ingressObject.hosts }}
- host: {{ tpl .host $rootContext | quote }}
@ -56,7 +59,7 @@ spec:
{{ $service := include "bjw-s.common.lib.service.getByIdentifier" (dict "rootContext" $rootContext "id" .service.name) | fromYaml -}}
{{ $servicePort := 0 -}}
{{ if eq (dig "port" nil .service) nil -}}
{{ if empty (dig "port" nil .service) -}}
{{/* Default to the Service primary port if no port has been specified */ -}}
{{ if $service -}}
{{ $defaultServicePort := include "bjw-s.common.lib.service.primaryPort" (dict "rootContext" $rootContext "serviceObject" $service) | fromYaml -}}
@ -78,4 +81,5 @@ spec:
number: {{ $servicePort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -11,6 +11,9 @@ within the common library.
{{- if $rootContext.Capabilities.APIVersions.Has (printf "gateway.networking.k8s.io/v1beta1/%s" $routeKind) }}
{{- $apiVersion = "gateway.networking.k8s.io/v1beta1" -}}
{{- end -}}
{{- if $rootContext.Capabilities.APIVersions.Has (printf "gateway.networking.k8s.io/v1/%s" $routeKind) }}
{{- $apiVersion = "gateway.networking.k8s.io/v1" -}}
{{- end -}}
{{- $labels := merge
($routeObject.labels | default dict)
(include "bjw-s.common.lib.metadata.allLabels" $rootContext | fromYaml)
@ -46,9 +49,9 @@ spec:
{{- end }}
{{- if and (ne $routeKind "TCPRoute") (ne $routeKind "UDPRoute") $routeObject.hostnames }}
hostnames:
{{- with $routeObject.hostnames }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- range $routeObject.hostnames }}
- {{ tpl . $rootContext | quote }}
{{- end }}
{{- end }}
rules:
{{- range $routeObject.rules }}

View file

@ -0,0 +1,35 @@
{{/*
Implementation of Kahn's algorithm based on
https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
source: https://github.com/dastrobu/helm-charts/blob/main/environment-variables/templates/_kahn.tpl
*/}}
{{- define "bjw-s.common.lib.kahn" -}}
{{- $graph := .graph -}}
{{- if empty $graph -}}
{{- $_ := set . "out" list -}}
{{- else -}}
{{- $S := list -}}
{{- range $node, $edges := $graph -}}
{{- if empty $edges -}}
{{- $S = append $S $node -}}
{{- end -}}
{{- end -}}
{{- if empty $S -}}
{{- fail (printf "graph is cyclic or has bad edge definitions. Remaining graph is:\n%s" ( .graph | toYaml ) ) }}
{{- end -}}
{{- $n := first $S -}}
{{- $_ := unset $graph $n -}}
{{- range $node, $edges := $graph -}}
{{- $_ := set $graph $node ( without $edges $n ) -}}
{{- end -}}
{{- $args := dict "graph" $graph "out" list -}}
{{- include "bjw-s.common.lib.kahn" $args -}}
{{- $_ = set . "out" ( concat ( list $n ) $args.out ) -}}
{{- end -}}
{{- end }}

View file

@ -10,11 +10,11 @@ Validate container values
{{- fail (printf "Image required to be a dictionary with repository and tag fields. (controller %s, container %s)" $controllerObject.identifier $containerObject.identifier) }}
{{- end -}}
{{- if eq (dig "image" "repository" "" $containerObject) "" -}}
{{- if empty (dig "image" "repository" nil $containerObject) -}}
{{- fail (printf "No image repository specified for container. (controller %s, container %s)" $controllerObject.identifier $containerObject.identifier) }}
{{- end -}}
{{- if eq (dig "image" "tag" "" $containerObject) "" -}}
{{- if empty (dig "image" "tag" nil $containerObject) -}}
{{- fail (printf "No image tag specified for container. (controller %s, container %s)" $controllerObject.identifier $containerObject.identifier) }}
{{- end -}}
{{- end -}}

View file

@ -7,39 +7,77 @@ Env field used by the container.
{{- $containerObject := $ctx.containerObject -}}
{{- /* Default to empty list */ -}}
{{- $env := list -}}
{{- $envList := list -}}
{{- /* See if an override is desired */ -}}
{{- if not (empty (get $containerObject "env")) -}}
{{- with $containerObject.env -}}
{{- range $name, $value := . -}}
{{- if kindIs "slice" $containerObject.env -}}
{{- /* Env is a list so we assume the order is already as desired */ -}}
{{- range $name, $var := $containerObject.env -}}
{{- if kindIs "int" $name -}}
{{- $name = required "environment variables as a list of maps require a name field" $value.name -}}
{{- $name = required "environment variables as a list of maps require a name field" $var.name -}}
{{- end -}}
{{- end -}}
{{- $envList = $containerObject.env -}}
{{- else -}}
{{- /* Env is a map so we must check if ordering is desired */ -}}
{{- $graph := dict -}}
{{- if kindIs "map" $value -}}
{{- if hasKey $value "value" -}}
{{- $envValue := $value.value | toString -}}
{{- $env = append $env (dict "name" $name "value" (tpl $envValue $rootContext)) -}}
{{- else if hasKey $value "valueFrom" -}}
{{- $env = append $env (dict "name" $name "valueFrom" $value.valueFrom) -}}
{{- range $name, $var := $containerObject.env -}}
{{- if $var -}}
{{- if kindIs "map" $var -}}
{{- /* Value is a map so ordering can be specified */ -}}
{{- if empty (dig "dependsOn" nil $var) -}}
{{- $_ := set $graph $name ( list ) -}}
{{- else if kindIs "string" $var.dependsOn -}}
{{- $_ := set $graph $name ( list $var.dependsOn ) -}}
{{- else if kindIs "slice" $var.dependsOn -}}
{{- $_ := set $graph $name $var.dependsOn -}}
{{- end -}}
{{- else -}}
{{- $env = append $env (dict "name" $name "valueFrom" $value) -}}
{{- end -}}
{{- else -}}
{{- if kindIs "string" $value -}}
{{- $env = append $env (dict "name" $name "value" (tpl $value $rootContext)) -}}
{{- else if or (kindIs "float64" $value) (kindIs "bool" $value) -}}
{{- $env = append $env (dict "name" $name "value" ($value | toString)) -}}
{{- else -}}
{{- $env = append $env (dict "name" $name "value" $value) -}}
{{- /* Value is not a map so no ordering can be specified */ -}}
{{- $_ := set $graph $name ( list ) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $args := dict "graph" $graph "out" list -}}
{{- include "bjw-s.common.lib.kahn" $args -}}
{{- range $name := $args.out -}}
{{- $envItem := dict "name" $name -}}
{{- $envValue := get $containerObject.env $name -}}
{{- if kindIs "map" $envValue -}}
{{- $envItem := merge $envItem (omit $envValue "dependsOn") -}}
{{- else -}}
{{- $_ := set $envItem "value" $envValue -}}
{{- end -}}
{{- $envList = append $envList $envItem -}}
{{- end -}}
{{- $args = dict -}}
{{- end -}}
{{- end -}}
{{- if not (empty $env) -}}
{{- $env | toYaml -}}
{{- if not (empty $envList) -}}
{{- $output := list -}}
{{- range $envList -}}
{{- if hasKey . "value" -}}
{{- if kindIs "string" .value -}}
{{- $output = append $output (dict "name" .name "value" (tpl .value $rootContext)) -}}
{{- else if or (kindIs "float64" .value) (kindIs "bool" .value) -}}
{{- $output = append $output (dict "name" .name "value" (.value | toString)) -}}
{{- else -}}
{{- $output = append $output (dict "name" .name "value" .value) -}}
{{- end -}}
{{- else if hasKey . "valueFrom" -}}
{{- $output = append $output (dict "name" .name "valueFrom" .valueFrom) -}}
{{- else -}}
{{- $output = append $output (dict "name" .name "valueFrom" (omit . "name")) -}}
{{- end -}}
{{- end -}}
{{- $output | toYaml -}}
{{- end -}}
{{- end -}}

View file

@ -58,7 +58,11 @@ Probes used by the container.
{{- end -}}
{{- if $probeValues.port -}}
{{- $_ := set (index $probeDefinition $probeHeader) "port" (tpl ( $probeValues.port | toString ) $rootContext) -}}
{{- if kindIs "float64" $probeValues.port -}}
{{- $_ := set (index $probeDefinition $probeHeader) "port" $probeValues.port -}}
{{- else if kindIs "string" $probeValues.port -}}
{{- $_ := set (index $probeDefinition $probeHeader) "port" (tpl ( $probeValues.port | toString ) $rootContext) -}}
{{- end -}}
{{- else if $primaryServiceDefaultPort.targetPort -}}
{{- $_ := set (index $probeDefinition $probeHeader) "port" $primaryServiceDefaultPort.targetPort -}}
{{- else -}}

View file

@ -25,7 +25,7 @@ volumeMounts used by the container.
{{- end -}}
{{- /* Collect volumeClaimTemplates */ -}}
{{- if not (eq (dig "statefulset" "volumeClaimTemplates" nil $controllerObject) nil) -}}
{{- if not (empty (dig "statefulset" "volumeClaimTemplates" nil $controllerObject)) -}}
{{- range $persistenceValues := $controllerObject.statefulset.volumeClaimTemplates -}}
{{- /* Enable persistence item by default, but allow override */ -}}
{{- $persistenceEnabled := true -}}
@ -35,10 +35,10 @@ volumeMounts used by the container.
{{- if $persistenceEnabled -}}
{{- $mountValues := dict -}}
{{- if not (eq (dig "globalMounts" nil $persistenceValues) nil) -}}
{{- if not (empty (dig "globalMounts" nil $persistenceValues)) -}}
{{- $_ := set $mountValues "globalMounts" $persistenceValues.globalMounts -}}
{{- end -}}
{{- if not (eq (dig "advancedMounts" nil $persistenceValues) nil) -}}
{{- if not (empty (dig "advancedMounts" nil $persistenceValues)) -}}
{{- $_ := set $mountValues "advancedMounts" (dict $controllerObject.identifier $persistenceValues.advancedMounts) -}}
{{- end -}}
{{- $_ := set $persistenceItemsToProcess $persistenceValues.name $mountValues -}}

View file

@ -7,7 +7,7 @@ Convert controller values to an object
{{- $objectValues := .values -}}
{{- /* Default the controller type to Deployment */ -}}
{{- if eq (dig "type" "" $objectValues) "" -}}
{{- if empty (dig "type" nil $objectValues) -}}
{{- $_ := set $objectValues "type" "deployment" -}}
{{- end -}}

View file

@ -7,7 +7,7 @@ Validate Ingress values
{{- range $ingressValues.hosts -}}
{{- range .paths -}}
{{- if or (eq (dig "service" "name" "" .) "") (not .service.name) -}}
{{- if empty (dig "service" "name" nil .) -}}
{{- fail (printf "No service name configured. (ingress: %s, path: %s)" $ingressValues.identifier .path) -}}
{{- end -}}
{{- end -}}

View file

@ -135,6 +135,8 @@ controllers:
# -- Specifies how to treat concurrent executions of a job that is created by this cron job
# valid values are Allow, Forbid or Replace
concurrencyPolicy: Forbid
# -- Sets the CronJob timezone (only works in Kubernetes >= 1.27)
timeZone:
# -- Sets the CronJob time when to execute your jobs
schedule: "*/20 * * * *"
# -- The deadline in seconds for starting the job if it misses its scheduled time for any reason
@ -201,19 +203,25 @@ controllers:
# Syntax options:
# A) TZ: UTC
# B) PASSWD: '{{ .Release.Name }}'
# C) PASSWD:
# B) TZ:
# value: UTC
# dependsOn: otherVar
# D) PASSWD:
# configMapKeyRef:
# name: config-map-name
# key: key-name
# D) PASSWD:
# E) PASSWD:
# dependsOn:
# - otherVar1
# - otherVar2
# valueFrom:
# secretKeyRef:
# name: secret-name
# key: key-name
# ...
# E) - name: TZ
# value: UTC
# F) - name: TZ
# value: UTC
# G) - name: TZ
# value: '{{ .Release.Name }}'
env:
@ -500,6 +508,9 @@ ingress:
# -- Set the ingressClass that is used for this ingress.
className: # "nginx"
# -- Configure the defaultBackend for this ingress. This will disable any other rules for the ingress.
defaultBackend:
## Configure the hosts for the ingress
hosts:
- # -- Host address. Helm template can be passed.
@ -558,7 +569,7 @@ route:
# Name of the section within the target resource.
sectionName:
# -- Host addresses
# -- Host addresses. Helm template can be passed.
hostnames: []
# -- Configure rules for routing. Defaults to the primary service.