k8s-manifests/jupyter/base/charts/jupyterhub/templates/_helpers.tpl

433 lines
15 KiB
Smarty

{{- /*
## About
This file contains helpers to systematically name, label and select Kubernetes
objects we define in the .yaml template files.
## How helpers work
Helm helper functions is a good way to avoid repeating something. They will
generate some output based on one single dictionary of input that we call the
helpers scope. When you are in helm, you access your current scope with a
single a single punctuation (.).
When you ask a helper to render its content, one often forward the current
scope to the helper in order to allow it to access .Release.Name,
.Values.rbac.create and similar values.
#### Example - Passing the current scope
{{ include "jupyterhub.commonLabels" . }}
It would be possible to pass something specific instead of the current scope
(.), but that would make .Release.Name etc. inaccessible by the helper which
is something we aim to avoid.
#### Example - Passing a new scope
{{ include "demo.bananaPancakes" (dict "pancakes" 5 "bananas" 3) }}
To let a helper access the current scope along with additional values we have
opted to create dictionary containing additional values that is then populated
with additional values from the current scope through a the merge function.
#### Example - Passing a new scope augmented with the old
{{- $_ := merge (dict "appLabel" "kube-lego") . }}
{{- include "jupyterhub.matchLabels" $_ | nindent 6 }}
In this way, the code within the definition of `jupyterhub.matchLabels` will
be able to access .Release.Name and .appLabel.
NOTE:
The ordering of merge is crucial, the latter argument is merged into the
former. So if you would swap the order you would influence the current scope
risking unintentional behavior. Therefore, always put the fresh unreferenced
dictionary (dict "key1" "value1") first and the current scope (.) last.
## Declared helpers
- appLabel |
- componentLabel |
- commonLabels | uses appLabel
- labels | uses commonLabels
- matchLabels | uses labels
## Example usage
```yaml
# Excerpt from proxy/autohttps/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "jupyterhub.autohttps.fullname" . }}
labels:
{{- include "jupyterhub.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "jupyterhub.matchLabels" $_ | nindent 6 }}
template:
metadata:
labels:
{{- include "jupyterhub.labels" $_ | nindent 8 }}
hub.jupyter.org/network-access-proxy-http: "true"
```
NOTE:
The "jupyterhub.matchLabels" and "jupyterhub.labels" is passed an augmented
scope that will influence the helpers' behavior. It get the current scope
"." but merged with a dictionary containing extra key/value pairs. In this
case the "." scope was merged with a small dictionary containing only one
key/value pair "appLabel: kube-lego". It is required for kube-lego to
function properly. It is a way to override the default app label's value.
*/}}
{{- /*
jupyterhub.appLabel:
Used by "jupyterhub.labels".
*/}}
{{- define "jupyterhub.appLabel" -}}
{{ .Values.nameOverride | default .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- /*
jupyterhub.componentLabel:
Used by "jupyterhub.labels".
NOTE: The component label is determined by either...
- 1: The provided scope's .componentLabel
- 2: The template's filename if living in the root folder
- 3: The template parent folder's name
- : ...and is combined with .componentPrefix and .componentSuffix
*/}}
{{- define "jupyterhub.componentLabel" -}}
{{- $file := .Template.Name | base | trimSuffix ".yaml" -}}
{{- $parent := .Template.Name | dir | base | trimPrefix "templates" -}}
{{- $component := .componentLabel | default $parent | default $file -}}
{{- $component := print (.componentPrefix | default "") $component (.componentSuffix | default "") -}}
{{ $component }}
{{- end }}
{{- /*
jupyterhub.commonLabels:
Foundation for "jupyterhub.labels".
Provides old labels:
app
release
chart (omitted for matchLabels)
heritage (omitted for matchLabels)
Provides modern labels (omitted for matchLabels):
app.kubernetes.io/name ("app")
app.kubernetes.io/instance ("release")
helm.sh/chart ("chart")
app.kubernetes.io/managed-by ("heritage")
*/}}
{{- define "jupyterhub.commonLabels" -}}
app: {{ .appLabel | default (include "jupyterhub.appLabel" .) | quote }}
release: {{ .Release.Name | quote }}
{{- if not .matchLabels }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
heritage: {{ .Release.Service }}
app.kubernetes.io/name: {{ .appLabel | default (include "jupyterhub.appLabel" .) | quote }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- end }}
{{- /*
jupyterhub.labels:
Provides old labels:
component
app
release
chart (omitted for matchLabels)
heritage (omitted for matchLabels)
Provides modern labels (omitted for matchLabels):
app.kubernetes.io/component ("component")
app.kubernetes.io/name ("app")
app.kubernetes.io/instance release ("release")
helm.sh/chart ("chart")
app.kubernetes.io/managed-by ("heritage")
*/}}
{{- define "jupyterhub.labels" -}}
component: {{ include "jupyterhub.componentLabel" . }}
{{- if not .matchLabels }}
app.kubernetes.io/component: {{ include "jupyterhub.componentLabel" . }}
{{- end }}
{{ include "jupyterhub.commonLabels" . }}
{{- end }}
{{- /*
jupyterhub.matchLabels:
Provides old labels:
component
app
release
*/}}
{{- define "jupyterhub.matchLabels" -}}
{{- $_ := merge (dict "matchLabels" true) . -}}
{{ include "jupyterhub.labels" $_ }}
{{- end }}
{{- /*
jupyterhub.dockerconfigjson:
Creates a base64 encoded docker registry json blob for use in a image pull
secret, just like the `kubectl create secret docker-registry` command does
for the generated secrets data.dockerconfigjson field. The output is
verified to be exactly the same even if you have a password spanning
multiple lines as you may need to use a private GCR registry.
- https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod
*/}}
{{- define "jupyterhub.dockerconfigjson" -}}
{{ include "jupyterhub.dockerconfigjson.yaml" . | b64enc }}
{{- end }}
{{- define "jupyterhub.dockerconfigjson.yaml" -}}
{{- with .Values.imagePullSecret -}}
{
"auths": {
{{ .registry | default "https://index.docker.io/v1/" | quote }}: {
"username": {{ .username | quote }},
"password": {{ .password | quote }},
{{- if .email }}
"email": {{ .email | quote }},
{{- end }}
"auth": {{ (print .username ":" .password) | b64enc | quote }}
}
}
}
{{- end }}
{{- end }}
{{- /*
jupyterhub.imagePullSecrets
Augments passed .pullSecrets with $.Values.imagePullSecrets
*/}}
{{- define "jupyterhub.imagePullSecrets" -}}
{{- /*
We have implemented a trick to allow a parent chart depending on this
chart to call this named templates.
Caveats and notes:
1. While parent charts can reference these, grandparent charts can't.
2. Parent charts must not use an alias for this chart.
3. There is no failsafe workaround to above due to
https://github.com/helm/helm/issues/9214.
4. .Chart is of its own type (*chart.Metadata) and needs to be casted
using "toYaml | fromYaml" in order to be able to use normal helm
template functions on it.
*/}}
{{- $jupyterhub_values := .root.Values }}
{{- if ne .root.Chart.Name "jupyterhub" }}
{{- if .root.Values.jupyterhub }}
{{- $jupyterhub_values = .root.Values.jupyterhub }}
{{- end }}
{{- end }}
{{- /* Populate $_.list with all relevant entries */}}
{{- $_ := dict "list" (concat .image.pullSecrets $jupyterhub_values.imagePullSecrets | uniq) }}
{{- if and $jupyterhub_values.imagePullSecret.create $jupyterhub_values.imagePullSecret.automaticReferenceInjection }}
{{- $__ := set $_ "list" (append $_.list (include "jupyterhub.image-pull-secret.fullname" .root) | uniq) }}
{{- end }}
{{- /* Decide if something should be written */}}
{{- if not (eq ($_.list | toJson) "[]") }}
{{- /* Process the $_.list where strings become dicts with a name key and the
strings become the name keys' values into $_.res */}}
{{- $_ := set $_ "res" list }}
{{- range $_.list }}
{{- if eq (typeOf .) "string" }}
{{- $__ := set $_ "res" (append $_.res (dict "name" .)) }}
{{- else }}
{{- $__ := set $_ "res" (append $_.res .) }}
{{- end }}
{{- end }}
{{- /* Write the results */}}
{{- $_.res | toJson }}
{{- end }}
{{- end }}
{{- /*
jupyterhub.singleuser.resources:
The resource request of a singleuser.
*/}}
{{- define "jupyterhub.singleuser.resources" -}}
{{- $r1 := .Values.singleuser.cpu.guarantee -}}
{{- $r2 := .Values.singleuser.memory.guarantee -}}
{{- $r3 := .Values.singleuser.extraResource.guarantees -}}
{{- $r := or $r1 $r2 $r3 -}}
{{- $l1 := .Values.singleuser.cpu.limit -}}
{{- $l2 := .Values.singleuser.memory.limit -}}
{{- $l3 := .Values.singleuser.extraResource.limits -}}
{{- $l := or $l1 $l2 $l3 -}}
{{- if $r -}}
requests:
{{- if $r1 }}
cpu: {{ .Values.singleuser.cpu.guarantee }}
{{- end }}
{{- if $r2 }}
memory: {{ .Values.singleuser.memory.guarantee }}
{{- end }}
{{- if $r3 }}
{{- range $key, $value := .Values.singleuser.extraResource.guarantees }}
{{ $key | quote }}: {{ $value | quote }}
{{- end }}
{{- end }}
{{- end }}
{{- if $l }}
limits:
{{- if $l1 }}
cpu: {{ .Values.singleuser.cpu.limit }}
{{- end }}
{{- if $l2 }}
memory: {{ .Values.singleuser.memory.limit }}
{{- end }}
{{- if $l3 }}
{{- range $key, $value := .Values.singleuser.extraResource.limits }}
{{ $key | quote }}: {{ $value | quote }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- /*
jupyterhub.extraEnv:
Output YAML formatted EnvVar entries for use in a containers env field.
*/}}
{{- define "jupyterhub.extraEnv" -}}
{{- include "jupyterhub.extraEnv.withTrailingNewLine" . | trimSuffix "\n" }}
{{- end }}
{{- define "jupyterhub.extraEnv.withTrailingNewLine" -}}
{{- if . }}
{{- /* If extraEnv is a list, we inject it as it is. */}}
{{- if eq (typeOf .) "[]interface {}" }}
{{- . | toYaml }}
{{- /* If extraEnv is a map, we differentiate two cases: */}}
{{- else if eq (typeOf .) "map[string]interface {}" }}
{{- range $key, $value := . }}
{{- /*
- If extraEnv.someKey has a map value, then we add the value as a YAML
parsed list element and use the key as the name value unless its
explicitly set.
*/}}
{{- if eq (typeOf $value) "map[string]interface {}" }}
{{- merge (dict) $value (dict "name" $key) | list | toYaml | println }}
{{- /*
- If extraEnv.someKey has a string value, then we use the key as the
environment variable name for the value.
*/}}
{{- else if eq (typeOf $value) "string" -}}
- name: {{ $key | quote }}
value: {{ $value | quote | println }}
{{- else }}
{{- printf "?.extraEnv.%s had an unexpected type (%s)" $key (typeOf $value) | fail }}
{{- end }}
{{- end }} {{- /* end of range */}}
{{- end }}
{{- end }} {{- /* end of: if . */}}
{{- end }} {{- /* end of definition */}}
{{- /*
jupyterhub.extraFiles.data:
Renders content for a k8s Secret's data field, coming from extraFiles with
binaryData entries.
*/}}
{{- define "jupyterhub.extraFiles.data.withNewLineSuffix" -}}
{{- range $file_key, $file_details := . }}
{{- include "jupyterhub.extraFiles.validate-file" (list $file_key $file_details) }}
{{- if $file_details.binaryData }}
{{- $file_key | quote }}: {{ $file_details.binaryData | nospace | quote }}{{ println }}
{{- end }}
{{- end }}
{{- end }}
{{- define "jupyterhub.extraFiles.data" -}}
{{- include "jupyterhub.extraFiles.data.withNewLineSuffix" . | trimSuffix "\n" }}
{{- end }}
{{- /*
jupyterhub.extraFiles.stringData:
Renders content for a k8s Secret's stringData field, coming from extraFiles
with either data or stringData entries.
*/}}
{{- define "jupyterhub.extraFiles.stringData.withNewLineSuffix" -}}
{{- range $file_key, $file_details := . }}
{{- include "jupyterhub.extraFiles.validate-file" (list $file_key $file_details) }}
{{- $file_name := $file_details.mountPath | base }}
{{- if $file_details.stringData }}
{{- $file_key | quote }}: |
{{- $file_details.stringData | trimSuffix "\n" | nindent 2 }}{{ println }}
{{- end }}
{{- if $file_details.data }}
{{- $file_key | quote }}: |
{{- if or (eq (ext $file_name) ".yaml") (eq (ext $file_name) ".yml") }}
{{- $file_details.data | toYaml | nindent 2 }}{{ println }}
{{- else if eq (ext $file_name) ".json" }}
{{- $file_details.data | toJson | nindent 2 }}{{ println }}
{{- else if eq (ext $file_name) ".toml" }}
{{- $file_details.data | toToml | trimSuffix "\n" | nindent 2 }}{{ println }}
{{- else }}
{{- print "\n\nextraFiles entries with 'data' (" $file_key " > " $file_details.mountPath ") needs to have a filename extension of .yaml, .yml, .json, or .toml!" | fail }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- define "jupyterhub.extraFiles.stringData" -}}
{{- include "jupyterhub.extraFiles.stringData.withNewLineSuffix" . | trimSuffix "\n" }}
{{- end }}
{{- define "jupyterhub.extraFiles.validate-file" -}}
{{- $file_key := index . 0 }}
{{- $file_details := index . 1 }}
{{- /* Use of mountPath. */}}
{{- if not ($file_details.mountPath) }}
{{- print "\n\nextraFiles entries (" $file_key ") must contain the field 'mountPath'." | fail }}
{{- end }}
{{- /* Use one of stringData, binaryData, data. */}}
{{- $field_count := 0 }}
{{- if $file_details.data }}
{{- $field_count = add1 $field_count }}
{{- end }}
{{- if $file_details.stringData }}
{{- $field_count = add1 $field_count }}
{{- end }}
{{- if $file_details.binaryData }}
{{- $field_count = add1 $field_count }}
{{- end }}
{{- if ne $field_count 1 }}
{{- print "\n\nextraFiles entries (" $file_key ") must only contain one of the fields: 'data', 'stringData', and 'binaryData'." | fail }}
{{- end }}
{{- end }}
{{- /*
jupyterhub.chart-version-to-git-ref:
Renders a valid git reference from a chartpress generated version string.
In practice, either a git tag or a git commit hash will be returned.
- The version string will follow a chartpress pattern, see
https://github.com/jupyterhub/chartpress#examples-chart-versions-and-image-tags.
- The regexReplaceAll function is a sprig library function, see
https://masterminds.github.io/sprig/strings.html.
- The regular expression is in golang syntax, but \d had to become \\d for
example.
*/}}
{{- define "jupyterhub.chart-version-to-git-ref" -}}
{{- regexReplaceAll ".*[.-]n\\d+[.]h(.*)" . "${1}" }}
{{- end }}