Skip to Content
Жора К

6 мин чтения


Helm-шпаргалка: как работать с контекстом в именованных шаблонах

Небольшая заметка про работу и управление контекстом в функциях include и template внутри именованных шаблонов Helm.


Обзор

Рассмотрим на простом примере определение именованных шаблонов через define, работу с контекстом в таких шаблонах, определение переменных и передачу их в кастомный контекст.

Начало

Для начала создадим чарт для экспериментов с помощью запуска команды

helm create helm-context-demo

Для тестовых целей создадим отдельный yaml файл по пути templates/test/test-helm.yaml. Теперь интересующие нас файлы проекта представленны следующей иерархией

mychart/
  Chart.yaml
  values.yaml
  charts/
  templates/
    _helpers.tpl
    test/
      test-helm.yaml
  ...

Вспомним быстро для чего предназначены каждый файл и папка:

  • Сhart.yaml - определение и метаданные чарта
  • values.yaml - файл содержит значения по умолчанию для чарта
  • templates/ - каталог предназначен для файлов шаблонов: все файлы из этого каталога рендерятся в выходной файл.
  • _helpers.tpl - файл может содержать именнованные шаблоны, которые могут повторно использоваться в templates

Вызывать рендеринг будем следующими командами

helm template . # собрать все шаблоны из /templates
helm template  -s templates/tests/test-helm.yaml . # собрать один файл

Создаем именованный шаблон

Создадим простой именнованный шаблон с спомощью define, а затем используем его в test-helm.yaml.

{{ define "my.first.template" }}
simple_key: simple_value
chart_name: {{ .Chart.name }}
{{end}}

Использовать можно так {{ include "my.first.template" . }}. В этом случае передаем текущий контекст. Также можно переопределить контекст, передав, например, пустую строку {{ include "my.first.template" "" }}. В данном случае вызов рендеринга приведет к ошибке. Можно в контекст передать любую переменную или любой объект и затем с ним работать, например:

{{ include "my.first.template" (dict "a" "a_val") }}

Или через переменную

{{ $custom_context := (dict "a" "a_val") }}
{{ include "my.first.template" $custom_context }}

Результата вызова шаблона также можно сохранить как строку в переменную, а затем вывести ее значение:

{{ $custom_context := (dict "a" "a_val") }}
{{ $result := (include "my.first.template" $custom_context) }}
{{ $result }}

Можно видоизменить шаблон, чтобы он отрабатывал без ошибок при каждом использовании добавив проверки на пустое значение в контексте

{{- define "my.first.template" -}}
simple_key: simple_value
{{- if and (not (empty .)) (not (empty .Chart))  }}
chart_name: {{ .Chart.Name }}
{{- end }}
{{- if and (not (empty .)) (not (empty .a))  }}
a: {{ .a }}
{{- end }}
{{- end -}}

Теперь при вызове примера с $custom_context будет сгенерирован следующий шаблон

---
# Source: helm-context/templates/tests/test-helm.yaml
simple_key: simple_value
a: a_val

NOTE: Синтаксис `{{-`

Cинтаксис фигурных скобок в объявлениях шаблонов можно изменить с помощью специальных символов, чтобы заставить механизм шаблонов удалять пробельные символы. {{- (с добавлением тире и пробела) указывает, что пробельные символы нужно удалить слева, а -}} означает, что справа. Так например, если бы мы не использовали дефис в предыдущем примере возле if, то мы бы получили вот такой вывод

---
# Source: helm-context/templates/tests/test-helm.yaml
simple_key: simple_value
 
a: a_val

Контекст, что он содержит, как переопределять

Контекст в шаблонах Helm представляет собой совокупность доступных переменных и объектов, которые можно использовать в шаблонах для генерации конфигурации Kubernetes. Он включает в себя глобальный контекст, который доступен во всех шаблонах чарта, а также локальные контексты, которые могут быть определены в пределах конкретных шаблонов. Глобальный контекст в helm доступен по умолчанию в каждом шаблоне. В этом контексте доступны по умолчанию следующие объекты: .Values, .Release, .Chart, .Capabilities, .Files, .Template. Через точку можно получить доступ к объектам текущего контекста. Передав . в шаблон мы передаем текущий контекст в шаблон на уровень ниже. Если контекст не изменяется в шаблоне, то при таком подходе через точку дочернему шаблону будет доступен верхний контекст. Контекст можно переопределить передав свою собственную переменную. В таком случае доступ ко всем дефолтным объектам глобального шаблона будет утерян. Исключением является контекст внутри функций with и range: в таком шаблоне глобальный контекст может быть виден через $. Как я показывал ранее переопределить контекст в шаблоне можно вот так: {{ include "my.first.template" (dict "a" "a_val") }}. После этого вызова объект .Chart будет не доступен шаблону.

Переменные в контекст

Вот пример определения переменной внутри шаблона.

{{- define "my.second.template" -}}
{{ $var := "my string var" }}
x: {{ $var | quote }}
{{- end -}}
---
# Source: helm-context/templates/tests/test-helm2.yaml
x: "my string var"

Переменные существуют только в контексте шаблона - внутри define. Нельзя использовать переменную, объявленную снаружи блока определения шаблона, однако можно передать переменную в контекст.

{{- define "my.second.template" -}}
{{ $var := "my string var" }}
x: {{ $var | quote }}
nested: {{ include "my.second.template.nested" (dict "var" $var) | nindent 2 }}
{{- end -}}
 
 
{{- define "my.second.template.nested" -}}
x: {{ .var | quote }}
isNested: true
{{- end -}}

Будет выведено

# Source: helm-context/templates/tests/test-helm2.yaml
x: "my string var"
nested:
  x: "my string var"
  isNested: true

Однако теперь в nested шаблоне не доступно никаких объектов: ни .Values, ни .Chart, ни других. Чтобы получить к ним доступ можно пробросить явно необходимые объекты в контекст, например:

{{- define "my.second.template" -}}
{{ $var := "my string var" }}
x: {{ $var | quote }}
nested: {{ include "my.second.template.nested" (dict "var" $var "Chart" .Chart) | nindent 2 }}
{{- end -}}
 
 
{{- define "my.second.template.nested" -}}
x: {{ .var | quote }}
isNested: true
chartName: {{ .Chart.Name }}
{{- end -}}
# Source: helm-context/templates/tests/test-helm2.yaml
x: "my string var"
nested:
  x: "my string var"
  isNested: true
  chartName: helm-context

Изменять существующий контекст нельзя - он является иммутабельным.

Рассмотрим пример с использованием .Values, для этого добавим такой список в values.yaml

hosts:
  - "www.example0.com"
  - "www.example1.com"
  - "www.example2.com"

Определим шаблон, который создает конфигурации серверов с использованием случайного идентификатора и списка хостов, указанных в переменной .Values.hosts. Каждая конфигурация сервера включает уникальный идентификатор, состоящий из сгенерированного идентификатора в родительском шаблоне и индекса хоста, а также сам хост.

{{- define "my.third.template" -}}
{{ $id := (randAlphaNum 5) }}
id: {{ $id | quote }}
servers: {{ include "my.third.template.servers" (dict "id" $id "Values" .Values) }}
{{- end -}}
 
{{- define "my.third.template.servers" -}}
{{- $hosts := .Values.hosts -}}
{{- range $i, $host := $hosts }}
- id: {{ $.id }}-{{ $i }}
  host: {{ $host }}
{{- end }}
{{- end -}}

Вывод:

# Source: helm-context/templates/tests/test-helm3.yaml
id: "pgm7b"
servers:
  - id: pgm7b-0
    host: www.example0.com
  - id: pgm7b-1
    host: www.example1.com
  - id: pgm7b-2
    host: www.example2.com

Profit!

Все примеры кода, приведенные в этой статье, можно найти в репозитории. Если вам понравилась эта статья, пожалуйста, поставьте звезду ⭐ репозиторию и подпишитесь на меня на GitHub, чтобы получать уведомления о новых уроках и контенте.

Полезные ссылки

Больше информации на: