indent и nindent: укрощение отступов
Если чарт падает с «error converting YAML to JSON» — почти всегда виноваты отступы. Учимся управлять ими.
indent Nдобавляет N пробелов в начало каждой строки текста.nindent Nделает то же, но сначала добавляет перенос строки — отсюда буква n (newline).
YAML значим по отступам: сдвиг на пробел меняет структуру. Когда шаблон вставляет многострочный блок, его строки нужно сдвинуть на правильную глубину. Этим занимаются indent и nindent.
Корень проблемы в том, что шаблонизатор Go и YAML живут в разных мирах и не знают друг о друге. Для движка шаблонов манифест — это просто поток текста: он не понимает, что строка начинается на восьмом пробеле и что вставляемый блок должен лежать «внутри» ключа выше. Он слепо подставляет туда, где стоит выражение {{ ... }}, ровно тот текст, который вернула функция. А функция вроде toYaml отдаёт блок, выровненный по левому краю, с нулевым отступом. Совместить эти два мира — задача программиста, и единственный инструмент для этого — ручное добавление пробелов перед каждой строкой вставляемого блока. Отсюда и берутся indent/nindent: они не «понимают» YAML, они механически двигают текст, а ответственность за правильное число пробелов лежит на вас.
Именно поэтому отступы — самая частая причина «странных» падений чартов у новичков. Ошибка не в логике, не в значениях, а в одной неверной цифре после nindent. И сообщение об ошибке указывает не на шаблон, а на отрендеренный YAML, который вы даже не писали руками. Привыкнуть к этому стоит сразу: при любой ошибке вида «error converting YAML to JSON» или «did not find expected key» первым делом смотрите на отступы во вставленных блоках.
Проблема: многострочная вставка
Допустим, toYaml вернул блок:
requests:
cpu: 250m
limits:
cpu: "1"
Этот текст начинается с нулевого отступа. Но в манифесте он должен лежать под ключом resources: на глубине 8 пробелов. Просто подставить нельзя — поедет.
indent: сдвинуть каждую строку
resources:
{{ toYaml .Values.resources | indent 8 }}
Каждая строка блока получит +8 пробелов. Но есть подвох: строка с самим {{ ... }} уже стоит на нулевом отступе, и между resources: и вставкой не должно быть лишнего. Управлять этим вручную неудобно — поэтому чаще берут nindent.
nindent: перенос + отступ в одном
nindent 8 сам ставит перевод строки перед блоком и сдвигает на 8. Это позволяет писать вставку на той же строке, что и ключ:
resources: {{- toYaml .Values.resources | nindent 8 }}
Результат:
resources:
requests:
cpu: 250m
limits:
cpu: "1"
Здесь {{- срезает пробел после resources:, а nindent сам делает новую строку с нужным отступом. Это каноничный паттерн Helm — запомните его, он встречается в каждом втором чарте.
Разберём, почему именно nindent избавляет от мороки. Когда вставка стоит на отдельной строке (вариант с indent), нужно вручную следить, чтобы между ключом resources: и блоком не вклинилась пустая строка и чтобы сам блок начинался ровно там, где надо. nindent переносит контроль над переводом строки внутрь себя: он гарантированно ставит один перенос и затем отступ. Поэтому вставку можно писать прямо после двоеточия ключа — глаз видит «ключ: значение», а движок аккуратно разворачивает многострочный блок ниже. Меньше движущихся частей — меньше способов ошибиться.
Зачем дефис в {{- и -}}
Дефис у границ выражения — это управление пробелами и переносами, и в контексте отступов он критичен. {{- съедает весь пробельный «мусор» (пробелы и переносы) слева от выражения, а -}} — справа. Без {{- перед nindent на строке осталась бы и собственная разметка строки шаблона, и перенос от nindent — получилась бы лишняя пустая строка. Простое правило: {{- ставят, когда выражение занимает строку целиком и эту строку надо «убрать» из вывода (типичный случай для if/range/end), а также перед nindent после ключа, чтобы срезать унаследованный пробел. Злоупотреблять -}} справа не стоит: он умеет «склеить» вашу строку со следующей и неожиданно сломать YAML.
indent против nindent: когда какой
| Случай | Функция |
| Вставка на отдельной строке, ключ выше | indent |
| Вставка после ключа на той же строке | nindent (+ {{-) |
Типичный лейбл-блок
metadata:
labels: {{- include "webapp.labels" . | nindent 4 }}
Тот же приём с include (именованным шаблоном) — об этом следующий урок.
Как это работает под капотом
Обе функции — чистые строковые операции из Sprig. indent N s разбивает строку s по переносам и приклеивает N пробелов к каждой части. nindent N s = "\n" + indent N s. Никакой магии про YAML тут нет — это слепое добавление пробелов. Вот почему ошибка в числе отступа всплывает только на этапе YAML-парсинга после рендера, и сообщение указывает не на шаблон, а на номер строки в готовом манифесте. Отлаживать удобно через helm template, глядя на отрендеренный текст.
Частые ошибки
- Перепутать
indentиnindent. Сindentпосле ключа на той же строке нет переноса — блок прилипнет к ключу. Сnindentна отдельной строке появится лишняя пустая строка. - Неверное число отступа. Считайте глубину ключа: если ключ на 6 пробелах, содержимое — на 8 (обычно +2 за уровень для карты).
- Забыть
{{-передnindent. Останется пробел после двоеточия — иногда безобидно, иногда ломает.
Итог
indent Nдобавляет N пробелов к каждой строке;nindent N— то же плюс ведущий перенос.- Каноничный паттерн:
ключ: {{- toYaml ... | nindent N }}. - Это слепые строковые операции; ошибки отступа всплывают при YAML-парсинге — отлаживайте через
helm template.