ci: валидировать шаблоны ExternalSecret (ESO v2) — ошибки исполнения не видны до кластера #64

Open
opened 2026-09-20 13:00:14 +00:00 by hermes · 0 comments
hermes commented 2026-09-20 13:00:14 +00:00 (Migrated from git.grachevko.ru)

Что было

В #62 добавил шаблон ESO для HINDSIGHT_API_TENANT_USERS. Локально flate test all прошёл (77 passed), а в кластере ExternalSecret/ai/hindsight ушёл в Ready=False / SecretSyncedError:

unable to execute template at key HINDSIGHT_API_TENANT_USERS:
wrong type for value; expected map[string]interface {}; got map[string]string

Причина — sprig-функция keys (func keys(dicts ...map[string]interface{})) против map[string]string, который ESO отдаёт в шаблон. Починено в #63 (range по карте + sortAlpha по готовым парам).

Почему CI это не поймал

flate test проверяет рендер, ссылки, статусы и (после #56) схемы CR — но не исполняет шаблоны ESO: они исполняются в контроллере ESO уже в кластере, локального «рендера ExternalSecret» нет ни у flate, ни у kustomize. Класс ошибок «шаблон падает на типе/функции» проходит зелёным.

Чем это опасно: при ошибке шаблона ESO не перезаписывает целевой Secret, то есть новое значение молча не доезжает (в нашем случае спасло то, что старое значение было корректным и API продолжал работать). Заметить можно только глазами в статусе ES.

Варианты

  1. Линт шаблонов тем же движком в CI. Маленький Go-бинарь (go run) с text/template + sprig, на входе — map[string]string (как у ESO, именно в этом соль), на выходе — ошибка на первом невалидном ExternalSecret.spec.target.template.data из отрендеренного дерева. Версию ESO пинить, как и всё остальное (правило: сверять с установленной версией, см. #56). Рекомендую.
  2. Алерт по статусу. ExternalSecret с Ready=False — в мониторинг: ловит не только шаблоны, но и любые ошибки провайдера (Vault недоступен, свойство удалено — ровно тот случай, из-за которого #62 вообще понадобился). Рекомендую как дополнение к 1.
  3. Схемный линт (kubeconform + CRDs, #56) — этот класс не ловит: ошибка не в схеме, а в исполнении шаблона.
  4. E2E в kind с ESO и фейковым провайдером — поймает наверняка, но поднимать e2e-контур в CI ради этого класса пока избыточно.

Готовность

  • CI падает на заведомо битом шаблоне (регресс-фикстура: keys . вместо range) и зелёный на текущем hindsight.
  • Есть алерт (или хотя бы суточный отчёт) по ExternalSecret в состоянии Ready=False.

Пока этого нет, правило зафиксировано в скилле: home-ops-cluster/references/eso-templates.md (не использовать sprig-функции, требующие map[string]interface{}; сортировать значения для стабильности байтов; mergePolicy: Replace по умолчанию; при ошибке шаблона Secret не портится, а точный текст ошибки — только в логе контроллера).

## Что было В #62 добавил шаблон ESO для `HINDSIGHT_API_TENANT_USERS`. Локально `flate test all` прошёл (**77 passed**), а в кластере `ExternalSecret/ai/hindsight` ушёл в `Ready=False / SecretSyncedError`: ``` unable to execute template at key HINDSIGHT_API_TENANT_USERS: wrong type for value; expected map[string]interface {}; got map[string]string ``` Причина — sprig-функция `keys` (`func keys(dicts ...map[string]interface{})`) против `map[string]string`, который ESO отдаёт в шаблон. Починено в #63 (`range` по карте + `sortAlpha` по готовым парам). ## Почему CI это не поймал `flate test` проверяет рендер, ссылки, статусы и (после #56) схемы CR — но **не исполняет** шаблоны ESO: они исполняются в контроллере ESO уже в кластере, локального «рендера ExternalSecret» нет ни у flate, ни у kustomize. Класс ошибок «шаблон падает на типе/функции» проходит зелёным. Чем это опасно: при ошибке шаблона ESO **не перезаписывает** целевой Secret, то есть новое значение молча не доезжает (в нашем случае спасло то, что старое значение было корректным и API продолжал работать). Заметить можно только глазами в статусе ES. ## Варианты 1. **Линт шаблонов тем же движком в CI.** Маленький Go-бинарь (`go run`) с `text/template` + sprig, на входе — **`map[string]string`** (как у ESO, именно в этом соль), на выходе — ошибка на первом невалидном `ExternalSecret.spec.target.template.data` из отрендеренного дерева. Версию ESO пинить, как и всё остальное (правило: сверять с установленной версией, см. #56). Рекомендую. 2. **Алерт по статусу.** `ExternalSecret` с `Ready=False` — в мониторинг: ловит не только шаблоны, но и любые ошибки провайдера (Vault недоступен, свойство удалено — ровно тот случай, из-за которого #62 вообще понадобился). Рекомендую как дополнение к 1. 3. **Схемный линт** (kubeconform + CRDs, #56) — этот класс не ловит: ошибка не в схеме, а в исполнении шаблона. 4. **E2E в kind с ESO и фейковым провайдером** — поймает наверняка, но поднимать e2e-контур в CI ради этого класса пока избыточно. ## Готовность - CI падает на заведомо битом шаблоне (регресс-фикстура: `keys .` вместо `range`) и зелёный на текущем `hindsight`. - Есть алерт (или хотя бы суточный отчёт) по `ExternalSecret` в состоянии `Ready=False`. Пока этого нет, правило зафиксировано в скилле: `home-ops-cluster/references/eso-templates.md` (не использовать sprig-функции, требующие `map[string]interface{}`; сортировать значения для стабильности байтов; `mergePolicy: Replace` по умолчанию; при ошибке шаблона Secret не портится, а точный текст ошибки — только в логе контроллера).
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
grachevko/home-ops#64
No description provided.