Templati
La specifica sintassi dei templati proviene dalla libreria standard dei templati del linguaggio Go, in cui Helm è scritto.
La stessa sintassi si usa anche in kubectl
, Hugo
e tanti altri applicativi scritti in Go.
Non è però necessario conoscere il linguaggio Go per usarla (anche se è un linguaggio di programmazione moderno, performante e bello da usare).
Azioni
Lo switch dentro il templating engine avviene quando si incontrano due graffe aperte, {{
. Lo switch fuori dal templating engine avviene quando si incontrano due graffe chiuse, }}
.
Ciò che si trova entro le graffe sono azioni, e possono essere comandi, strutture dati, costrutti di controllo, sottotemplati o funzioni.
Un trattino, -
, prima o dopo le graffe, separato con spazio dal contenuto, sopprime spazi nel testo risultante. Per esempio:
{{ "Hello" -}} , {{- "World" }}
genera
Hello,World
Informazioni Esterne
Informazioni acquisite nel templating engine dall'ambiente esterno sono rappresentate come .Nome
, oppure .Nome,nome
oppure .Nome.nome.nome
, ecc.
Tutte le informazioni iniziano col carattere punto, .
.
Per motivi derivanti da Go il primo Nome
deve essere con iniziale maiuscola. In Go i simboli che iniziano con la maiuscola sono di visibilità pubblica, non privata.
La struttura è gerarchica e il separatore di gerarchia è il carattere punto, .
. Corrispondono alla gerarchia presente nei files Yaml di specifica.
Le informazioni possono provenire dal file values.yaml
, tantissimi parametri potenziali, e hanno il nome .Values.xxx
.
Possono provenire da informazioni sulla release, col nome .Release.xxx
:
.Release.Name
.Release.Namespace
.Release.IsInstall
- .Release.IsUpgrade
.Release.Service
- il servizio che genera la release, settato a "Helm" nel nostro caso
Possono provenire dal file Chart.yaml
, col nome .Chart.xxx
:
.Chart.Name
.Chart.Version
.Chart.AppVersion
.Chart.Annotations
- una lista di annotazioni in formatokey=value
Possono provenire da informazioni ricevute dal cluster Kubernetes, col nome .Capabilities.xxx
:
.Capabilities.APIVersions
.Capabilities.KubeVersion.Version
.Capabilities.KubeVersion.Major
.Capabilities.KubeVersion.Minor
Possono provenire da files dopo una direttiva di inclusione. col nome .Files.xxx
.
Possono riferirsi al template in corso di processamento, col nome .Template.xxx
:
.Template.Name
.Template.BasePath
Pipelines e Tipi
Una pipeline è una sequenza di comandi, funzioni e variabili concatenati, separati dal simbolo di pipe |
-
Il funzionamento è molto simile a quello standard di Unix/Linux: l'output del comando precedente la pipe è passato come input al comando seguente. Per esempio:
character: {{ .Values.character | default "Sylvester" | quote }}
Il valore del parametro .Values.character
è passato alla funzione default
. Questa lo controlla e se è la stringa vuota lo sostituisce con la stringa "Sylvester", quidi lo passa alla funzione quote
. Tale funzione racchiude la stringa tra doppi apici.
Attenzione nella specifica di parametri, poichè il linguaggio sottostante Go è fortemente tipizzato e le funzioni lavorano solo su tipi prestabiliti.
Per esempio se .Values.character
vale 127
, questo è un numero intero; se vale ab112
può venir interpretato come intero esadecimale. Solo se vale "ab112"
è una stringa.
Non scordarsi i doppi apici per i valori stringa nei vari files sottoposti al templating engine.
Funzioni
Le funzioni provengono dalla libreria Go chiamata Sprig, e mantenuta a https://github.com/Masterminds/sprig
. Sono più di 100.
La documentazione delle funzioni è a https://helm.sh/docs/chart_template_guide/function_list/
.
Esempio:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
- viene preso il parametro
podSecurityContext
dal filevalues.yaml
(o passato tramite l'opzione--set
) - vengono tolti eventuali spazi all'inizio, dalla funzione
-
- la stringa risultante viene convertita a formato Yaml, dalla funzione
toYaml
- la funzione
nindent
indenta la stringa di 8 spazi
Go ha tre tipi di collezioni semplici: array (liste immutabili), slice (liste mutabili) e maps (coppie chiave-valore).
Se la funzione nindent
riceve in input una collezione, opera su ogni elemento della collezione su nuova riga.
Metodi
In Go un metodo è una funzione che si applica ad un certo tipo di oggetto.
La notazione è (oggetto).metodo
.
Se oggetto
è una collezione, metodo
si applica ad ogni suo elemento.
Vi sono le funzioni:
.Files.Get name
- ritorna il contenuto del file il cui nome èname
, espresso come percorso relativo alla directory radice del chart. Il contenuto è una slice di caratteri, quella che si chiamerebbe una stringa..Files.GetBytes name
- ritorna il contenuto del file il cui nome èname
, espresso come percorso relativo alla directory radice del chart. Il contenuto è una slice di byte, espressa in Go come[]byte
.Files.Glob pattern
- ritorna il contenuto dei file conformi allo schemapattern
, come stringa (slice di caratteri). Nel pattern si usano gli stessi simboli?
e*
della shell di Unix.
Go tratta normalmente le stringhe usando la codifica Unicode UTF-8. Ciò vuol dire che ogni carattere (in Go chiamato runa) è potenzialmente rappresentato da più bytes. Il Go ha corrispondentemente funzioni che operano su stringhe (lunghezza, sottostringhe, ecc.) e funzioni che operano su bytes (JSON, Base64, ecc.).
Queste tre funzioni soprs ritornano collezioni.
Vi sono dei metodi che si applicano alle collezioni risultanti:
.Files.AsConfig
- trasforma l'input per renderlo compatibile ad un manifest di ConfigMap.Files.AsSecrets
- trasforma l'input per renderlo compatibile ad un manifest di Secrets, codificato Base64.Files.Lines
- ritorna il contenuto dell'input come array, compiendo uno split degli elementi col carattere\n
Esempio. La stringa di template:
{{ (.Files.Glob "config/*").AsSecrets | indent 2 }}
potrebbe generare:
jetpack.ini: ZW5hYmxlZCA9IHRydWU=
rocket.yaml: ZW5hYmxlZDogdHJ1ZQ==
Funzione lookup
Compie una query al cluster Kubernetes alla ricerca di risorse.
Per esempio il seguente ricerca un D chiamato runner
nel namespace anvil
, e ne ritorna le annotazioni nella sezione metadati:
{{ (lookup "apps/v1" "Deployment" "anvil" "runner").metadata.annotations }}
Gli argomenti di lookup
sono:
- versione API
- tipo di oggetto
- namespace
- nome dell'oggetto
Nell'esempio seguente viene ritornata la lista di tutti i ConfigMap nel namespace anvil
:
{{ (lookup "v1" "ConfigMap" "anvil" "").items }}
La lista è iterabile in un loop.
Naturalmente lookup
non funziona in un Dry Run o col comando helm template
quando non viene contattato il server Kubernetes.
Costrutto if/else
Con solo la branca if
:
{{- if .Values.ingress.enabled -}}
...
{{- end }}
Con anche la branca else
:
{{- if .Values.ingress.enabled -}}
...
{{- else -}}
# Ingress not enabled
{{- end }}
and
e or
Non sono operatori, ma funzioni che prendono i due argomenti da paragonare.
Esempi:
{{- if and .Values.characters .Values.products -}}
...
{{- end }}
{{- if or (eq .Values.character "Wile E. Coyote") .Values.products -}}
...
{{- end }}
Costrutto with
Simile ad un foreach
di altri linguaggi.
Esempio:
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
Se ad un'iterazione l'argomento corrente del with
è vuoto, il blocco viene saltato e si passa all'argomento successivo.
Variabili
Si possono creare delle variabili. Il loro nome inizia col carattere $
.
Hanno un tipo, che è dedotto in fase di assegnazione dal tipo del valore.
L'operatore di creazione ed assegnazione - la prima volta - è :=
.
Il cambiamento di valore quando la variabile esiste già è =
.
Esempi:
{{ $var := .Values.character }}
{{ $var = "Tweety" }}
Lo scopo delle variabili è il templato in cui vivono, dal punto di assegnazione in avanti.
Il riferimento ad una variabile che non esiste è un errore.
Costrutto range
Opera in loop su una collezione, che può essere una lista o ona map (anche chiamata un dict).
Esempio:
Date le collezioni:
# An example list in YAML
characters:
- Sylvester
- Tweety
- Road Runner
- Wile E. Coyote
# An example map in YAML
products:
anvil: They ring like a bell
grease: 50% slippery
boomerang: Guaranteed to return
Iterando sulla lista si può avere:
characters:
{{- range .Values.characters }}
- {{ . | quote }}
{{- end }}
che fornisce in output:
characters:
- "Sylvester"
- "Tweety"
- "Road Runner"
- "Wile E. Coyote"
Il punto, .
, è l'argomento corrente del loop di range
.
Iterando sul dict si può avere:
products:
{{- range $key, $value := .Values.products }}
- {{ $key }}: {{ $value | quote }}
{{- end }}
che produce in output:
products:
- anvil: "They ring like a bell"
- boomerang: "Guaranteed to return"
- grease: "50% slippery"
$key
e $value
sono variabili predefinite per l'utilizzo di dict.
Notare che, derivato dal Go, è possibile la doppia assegnazione: $key, $value := .Values.products
.
Template con Nome
E' possibile definire un template dandogli un nome, e così richiamarlo da un altro template.
Esempio.
{{/*
Selector labels
*/}}
{{- define "anvil.selectorLabels" -}}
app.kubernetes.io/name: {{ include "anvil.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{/* ... */}}
racchiudono un commento{{- define "anvil.selectorLabels" -}}
è il nome che si da al template corrente. Può essere un nome gerarchico, separato da.
, e questo è utile per evitare collizioni di nomi- segue il corpo del template
{{- end -}}
termina la definizione di template
include
Richiama un template specificato, includendolo in quello corrente.
Esempio:
{{- include "anvil.selectorLabels" . | nindent 8 }}
- il primo argomento è il nome del template chiamato
- il secondo argomento è un oggetto da passare al template. Il carattere
.
indica l'oggetto globale, cioè tutti gli oggetti che sono in scopo al punto della chiamata. E' l'uso più comune. - l'output del template richiamato è eventualmente, come qui, passato alla pipeline