Керування потоком
Структури керування (називаються "діями" в термінології шаблонів) надають вам, автору шаблонів, можливість контролювати потік генерації шаблону. Мова шаблонів Helm надає такі структури керування:
if/elseдля створення умовних блоківwithдля визначення області видимостіrange, який надає цикл "for each"
Окрім цих, є кілька дій для оголошення та використання іменованих сегментів шаблону:
defineоголошує новий іменований шаблон всередині вашого шаблонуtemplateімплементує іменований шаблонblockоголошує спеціальний тип заповнювальної області шаблону
У цьому розділі ми розглянемо if, with та range. Інші дії будуть розглянуті в розділі "Іменовані шаблони" пізніше в цьому посібнику.
If/Else
Перша структура керування, яку ми розглянемо, використовується для умовного включення блоків тексту в шаблоні. Це блоки if/else.
Основна структура блоку з умовою виглядає так:
{{ if PIPELINE }}
# Щось зробити
{{ else if OTHER PIPELINE }}
# Зробити щось інше
{{ else }}
# Стандартне значення
{{ end }}
Зверніть увагу, що тепер ми говоримо про пайплайни замість значень. Причина в цьому полягає в тому, щоб уточнити, що структури керування можуть виконувати цілий пайплайн, а не лише оцінювати значення.
Пайплайн вважається хибним, якщо значення є:
- булевим хибним
- числовим нулем
- порожнім рядком
nil(порожнім або null)- порожньою колекцією (
map,slice,tuple,dict,array)
У всіх інших умовах умова є істинною.
Додамо просту умовну конструкцію до нашого ConfigMap. Ми додамо ще одне налаштування, якщо напій встановлений на каву:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{ if eq .Values.favorite.drink "coffee" }}mug: "true"{{ end }}
Оскільки ми закоментували drink: coffee у нашому останньому прикладі, вихідний файл не повинен містити прапорець mug: "true". Але якщо ми додамо цей рядок назад у наш файл values.yaml, вихід виглядатиме так:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: eyewitness-elk-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"
Контроль пробілів
Під час роботи з умовами варто звернути увагу на те, як контролюється кількість пробілів у шаблонах. Розглянемо попередній приклад і відформатуємо його для зручнішого читання:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{ if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{ end }}
Спочатку це має гарний вигляд. Але якщо ми пропустимо його через рушій шаблонів, отримаємо неприємний результат:
$ helm install --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/helm.sh/helm/_scratch/mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key
Що сталося? Ми згенерували некоректний YAML через пробіли.
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: eyewitness-elk-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"
mug має невірний відступ. Виправимо це, зменшивши відступ цього рядка, і запустимо знову:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{ if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{ end }}
Коли ми запустимо це, отримаємо валідний YAML, але з кількома порожніми рядками:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: telling-chimp-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"
Помітно, що у нас є кілька пустих рядків у YAML. Чому? Коли рушій шаблонів виконує шаблон, він видаляє вміст всередині {{ і }}, але залишає пробіли без змін.
YAML надає значення пробілам, тому управління пробілами стає важливим. На щастя, шаблони Helm мають кілька інструментів у поміч.
По-перше, синтаксис фігурних дужок шаблонів можна модифікувати за допомогою спеціальних символів, щоб вказати движку шаблонів обрізати пробіли. {{- (з тире і пробілом) вказує, що пробіли зліва повинні бути видалені, тоді як -}} означає, що пробіли справа повинні бути видалені. Будьте обережні! Нові рядки — це пробіли!
Переконайтеся, що є пробіл між
-і рештою вашої директиви.{{- 3 }}означає "вирізати ліві пробіли та вивести 3", тоді як{{-3 }}означає "вивести -3".
Використовуючи цей синтаксис, ми можемо змінити наш шаблон, щоб позбутися від тих нових рядків:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{- if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{- end }}
Щоб прояснити це, відзначимо кожен пробіл, який буде видалено відповідно до цього правила. * в кінці рядка вказує на символ нового рядка, який буде видалений:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}*
**{{- if eq .Values.favorite.drink "coffee" }}
mug: "true"*
**{{- end }}
Зважаючи на це, ми можемо запустити наш шаблон через Helm і побачити результат:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: clunky-cat-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"
Будьте обережні з модифікаторами обрізання пробілів. Легко випадково зробити ось так:
food: {{ .Values.favorite.food | upper | quote }}
{{- if eq .Values.favorite.drink "coffee" -}}
mug: "true"
{{- end -}}
Це створить food: "PIZZA"mug: "true", оскільки пробіли з обох сторін будуть видалені.
Для деталей про контроль пробілів у шаблонах дивіться Офіційну документацію Go шаблонів
Нарешті, іноді легше сказати системі шаблонів, як вам потрібно робити відступи, замість того, щоб намагатися освоїти розташування пробілів у директивах шаблону. З цієї причини іноді корисно використовувати функцію indent ({{ indent 2 "mug:true" }}).
Модифікація області видимості за допомогою with
Наступна структура управління, яку розглянемо, це дія with. Вона контролює область видимості змінних. Нагадаємо, що . є посиланням на поточну область видимості. Отже, .Values вказує шаблону знайти обʼєкт Values у поточній області видимості.
Синтаксис для with схожий на простий оператор if:
{{ with PIPELINE }}
# обмежена область видимості
{{ end }}
Області видимості можуть змінюватися. with дозволяє вам встановити поточну область видимості (.) на певний обʼєкт. Наприклад, ми працювали з .Values.favorite. Перепишемо наш ConfigMap, щоб змінити область видимості . на .Values.favorite:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
Зверніть увагу, що ми видалили умову if з попереднього прикладу, оскільки вона тепер непотрібна — блок після with виконується лише якщо значення PIPELINE не є порожнім.
Тепер ми можемо звертатися до .drink і .food без додаткових уточнень. Це відбувається тому, що оператор with встановлює . на .Values.favorite. . скидається до попередньої області видимості після {{ end }}.
Але є одне застереження! Усередині обмеженої області видимості ви не зможете отримати доступ до інших обʼєктів з батьківської області видимості за допомогою .. Наприклад, це не спрацює:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }}
{{- end }}
Це викличе помилку, оскільки Release.Name не знаходиться в межах обмеженої області видимості для .. Однак, якщо ми поміняємо місцями останні два рядки, все працюватиме як очікувалося, тому що область видимості скидається після {{ end }}.
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
release: {{ .Release.Name }}
Або ми можемо використовувати $ для доступу до обʼєкта Release.Name з батьківської області видимості. $ привʼязується до кореневої області видимості на початку виконання шаблону і не змінюється під час виконання шаблону. Ось таке рішення також спрацює:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $.Release.Name }}
{{- end }}
Після розгляду range ми перейдемо до змінних шаблону, які пропонують одне рішення для проблеми з областю видимості вище.
Цикли за допомогою дії range
Багато мов програмування підтримують цикли за допомогою for циклів, foreach циклів або подібних функціональних механізмів. У мові шаблонів Helm, для перебору колекції використовується оператор range.
Спочатку додамо список інгредієнтів для піци до нашого файлу values.yaml:
favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions
Тепер у нас є список (в шаблонах він називається slice) інгредієнтів для піци. Ми можемо змінити наш шаблон, щоб вивести цей список у наш ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
toppings: |-
{{- range .Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}
Ми можемо використовувати $ для доступу до списку Values.pizzaToppings з батьківської області видимості. $ привʼязується до кореневої області видимості на початку виконання шаблону і не змінюється під час виконання шаблону. Ось таке рішення також спрацює:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
toppings: |-
{{- range $.Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}
{{- end }}
Розглянемо детальніше список toppings:. Функція range буде "перебирати" список pizzaToppings. Але тепер відбувається щось цікаве. Так само як with встановлює область видимості для ., так і оператор range встановлює область видимості. Кожного разу під час циклу . встановлюється на поточний інгредієнт для піци. Тобто, під час першої ітерації . буде дорівнювати mushrooms. Під час другої ітерації він буде дорівнювати cheese, і так далі.
Ми можемо безпосередньо передавати значення . в конвеєр, тому коли ми використовуємо {{ . | title | quote }}, воно передається в title (функцію для перетворення на заголовні літери) і потім в quote. Якщо ми запустимо цей шаблон, результат буде:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: edgy-dragonfly-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
toppings: |-
- "Mushrooms"
- "Cheese"
- "Peppers"
- "Onions"
У цьому прикладі ми зробили дещо хитре. Лінія toppings: |- оголошує багаторядковий рядок. Отже, наш список інгредієнтів для піци насправді не є YAML списком. Це великий рядок. Чому ми так робимо? Тому що дані в ConfigMaps data складаються з пар ключ/значення, де і ключ, і значення є простими рядками. Щоб зрозуміти, чому це так, ознайомтеся з
документацією Kubernetes ConfigMap. Для нас цей нюанс не так важливий.
Маркер
|-в YAML приймає багаторядковий рядок. Це може бути корисною технікою для вбудовування великих блоків даних у ваші маніфести, як показано тут.
Іноді корисно швидко створити список у шаблоні, а потім перебирати цей список. Шаблони Helm мають функцію для спрощення цього завдання: tuple. У компʼютерних науках кортеж (tuple) — це список фіксованого розміру, але з довільними типами даних. Це приблизно передає те, як використовується tuple.
sizes: |-
{{- range tuple "small" "medium" "large" }}
- {{ . }}
{{- end }}
Вищезазначене створить:
sizes: |-
- small
- medium
- large
Окрім списків і кортежів, range можна використовувати для перебору колекцій, які мають ключ і значення (як map або dict). Ми розглянемо, як це зробити в наступному розділі, коли введемо змінні шаблону.