DEVOPS-117: чарт on-premise 3.0 — Nanabush Player, секреты в Secret, валидация values
Актуализация чарта по продовому .helm из HRBOX. Nanabush Player перенесён из прода и упрощён под one-release-топологию on-premise: без карт env/ci_dc, но с сохранёнными инвариантами (audience = https://<host>, basePath отдельно от aud, allowlist обратного канала только на внутрикластерный web, метрики на непубликуемом порту). Выделенного поддомена в on-premise нет, поэтому плеер монтируется на основной домен под /nanabush-player. Три переключателя для аварийного отката сохранены. Из прода также перенесены: пул конвертации видео, PodDisruptionBudget для web, OpenTelemetry-сайдкар, assetlinks, набор location в nginx (/healthz, /health-check, потоковый /api/v1/integration/commit, /ai/, sw.js, шрифты). Версии образов подтянуты к продовым. Приведение чарта в порядок: все образы в блоке image, imagePullSecrets в настройках, девять одинаковых define ресурсов заменены одним хелпером, удалено ~100 строк мёртвого кода в _helpers.tpl, пароли и ключи уехали в Secret и читаются через secretKeyRef, добавлены 00-validate.yaml и NOTES.txt. Исправлено в 2.x: - SERVICES_INTERNAL_SECRET имел захардкоженное значение по умолчанию, то есть все установки, где его не заполнили, работали на одном секрете межсервисной аутентификации; - cluster.domain игнорировался: маршруты кластера NATS были захардкожены на cluster.local; - CLUSTER_ADVERTISE без сегмента svc — ноды NATS объявляли соседям недостижимый адрес; - nats.replicas и nats.cluster.replicas задавались независимо, при расхождении часть нод не входила в кластер; - пробы web проверяли только TCP-порт 9000, поэтому под с неработающим PHP считался готовым; - внутренние URL были захардкожены строками hrbox-* и молча ломались при nameOverride. Имена объектов и селекторы Deployment не менялись — обновление 2.x -> 3.0 не пересоздаёт объекты. Переехавшие ключи values.yaml чарт распознаёт и останавливает установку с подсказкой; порядок перехода — в CHANGELOG.md. Проверено: helm lint чистый; отрендерены 4 конфигурации (дефолт, всё включено, плеер и инфра выключены, nameOverride); структурная проверка рендера без замечаний; 20 негативных сценариев срабатывают с ожидаемыми сообщениями; реалистичный values.yaml из 2.x последовательно ловится всеми шестью guard'ами. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+211
-90
@@ -1,110 +1,231 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
============================================================================
|
||||
Общие хелперы чарта HRBox
|
||||
============================================================================
|
||||
|
||||
Правило именования: все объекты называются "<имя чарта>-<компонент>"
|
||||
(hrbox-web, hrbox-worker, ...). Имя чарта можно переопределить через
|
||||
nameOverride, но по умолчанию оно равно "hrbox" — так же, как в чарте 2.x,
|
||||
поэтому обновление 2.x -> 3.x не пересоздаёт объекты.
|
||||
*/}}
|
||||
{{- define "nats.name" -}}
|
||||
{{- default .Chart.Name .Values.nats.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
|
||||
{{/* Базовое имя релиза: "hrbox" или nameOverride. */}}
|
||||
{{- define "hrbox.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Имя чарта с версией — для лейбла helm.sh/chart. */}}
|
||||
{{- define "hrbox.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "nats.fullname" -}}
|
||||
{{- if .Values.nats.fullnameOverride -}}
|
||||
{{- .Values.nats.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{/*
|
||||
Полное имя объекта компонента.
|
||||
|
||||
Usage: {{ include "hrbox.component" (dict "ctx" . "name" "web") }} -> hrbox-web
|
||||
*/}}
|
||||
{{- define "hrbox.component" -}}
|
||||
{{- printf "%s-%s" (include "hrbox.name" .ctx) .name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Селектор пода. ВАЖНО: spec.selector в Deployment неизменяем, поэтому набор
|
||||
лейблов здесь совпадает с чартом 2.x (один лейбл "service") и меняться не должен.
|
||||
|
||||
Usage: {{- include "hrbox.selectorLabels" (dict "ctx" . "name" "web") | nindent 6 }}
|
||||
*/}}
|
||||
{{- define "hrbox.selectorLabels" -}}
|
||||
service: {{ include "hrbox.component" (dict "ctx" .ctx "name" .name) }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Лейблы объекта. Лейбл "app" сохранён из чарта 2.x — на него опираются
|
||||
привычные команды вида "kubectl get pods -l app=worker".
|
||||
|
||||
Usage: {{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
|
||||
*/}}
|
||||
{{- define "hrbox.labels" -}}
|
||||
app: {{ .name }}
|
||||
app.kubernetes.io/name: {{ include "hrbox.name" .ctx }}
|
||||
app.kubernetes.io/component: {{ .name }}
|
||||
app.kubernetes.io/instance: {{ .ctx.Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .ctx.Release.Service }}
|
||||
app.kubernetes.io/version: {{ .ctx.Chart.AppVersion | quote }}
|
||||
helm.sh/chart: {{ include "hrbox.chart" .ctx }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Секреты для доступа к приватному Docker-реестру.
|
||||
|
||||
Usage: {{- include "hrbox.imagePullSecrets" . | nindent 6 }}
|
||||
*/}}
|
||||
{{- define "hrbox.imagePullSecrets" -}}
|
||||
{{- if .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- range .Values.imagePullSecrets }}
|
||||
- name: {{ .name | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Тег из ссылки на образ: "registry/repo:1.2.3" -> "1.2.3".
|
||||
Двоеточие ищется только в последнем сегменте пути, чтобы не спутать тег
|
||||
с портом реестра ("registry:5000/repo").
|
||||
*/}}
|
||||
{{- define "hrbox.imageTag" -}}
|
||||
{{- $lastSegment := splitList "/" . | last -}}
|
||||
{{- if contains ":" $lastSegment -}}
|
||||
{{- splitList ":" $lastSegment | last -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
============================================================================
|
||||
Секрет приложения
|
||||
============================================================================
|
||||
|
||||
Чувствительные значения (пароли БД и SMTP, ключи шифрования, токены) не
|
||||
попадают в env-блоки Deployment открытым текстом: чарт складывает их в один
|
||||
Secret, а контейнеры читают его через secretKeyRef. Полный список ключей —
|
||||
в README, раздел "Секреты".
|
||||
|
||||
secrets.existingSecret позволяет подставить Secret, созданный вне чарта
|
||||
(например, из внешнего vault-оператора) — тогда чарт свой Secret не создаёт.
|
||||
*/}}
|
||||
{{/*
|
||||
Адреса внутренних сервисов.
|
||||
|
||||
Сервисы релиза адресуются по именам, которые создаёт этот же чарт, поэтому
|
||||
пустое значение в values резолвится в имя из hrbox.component: адреса не
|
||||
разъезжаются при nameOverride и их не нужно править вручную. Непустое
|
||||
значение используется как есть — для сервиса за пределами релиза.
|
||||
*/}}
|
||||
{{- define "hrbox.geonamesUrl" -}}
|
||||
{{- .Values.app.geonames.url | default (printf "http://%s:8181" (include "hrbox.component" (dict "ctx" . "name" "geonames"))) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hrbox.goworkerUrl" -}}
|
||||
{{- .Values.app.goworker.url | default (printf "http://%s:8686" (include "hrbox.component" (dict "ctx" . "name" "goworker"))) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hrbox.kedocaUrl" -}}
|
||||
{{- .Values.app.kedoca.url | default (printf "http://%s:8558" (include "hrbox.component" (dict "ctx" . "name" "kedoca"))) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hrbox.redisHost" -}}
|
||||
{{- .Values.app.redis.host | default (include "hrbox.component" (dict "ctx" . "name" "dragonfly")) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hrbox.natsUrl" -}}
|
||||
{{- .Values.app.nats.url | default (printf "nats://%s:4222" (include "hrbox.component" (dict "ctx" . "name" "nats"))) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hrbox.secretName" -}}
|
||||
{{- if .Values.secrets.existingSecret -}}
|
||||
{{- .Values.secrets.existingSecret -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nats.nameOverride -}}
|
||||
{{- if contains $name .Release.Name -}}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- include "hrbox.component" (dict "ctx" . "name" "secrets") -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Одна переменная окружения из секрета приложения.
|
||||
|
||||
Usage: {{ include "hrbox.secretEnv" (dict "ctx" . "env" "POSTGRES_PASSWORD" "key" "postgres-password") }}
|
||||
*/}}
|
||||
{{- define "hrbox.secretEnv" -}}
|
||||
- name: {{ .env }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "hrbox.secretName" .ctx | quote }}
|
||||
key: {{ .key | quote }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
============================================================================
|
||||
Nanabush Player
|
||||
============================================================================
|
||||
|
||||
On-premise-топология Player'а отличается от облачной: выделенного поддомена
|
||||
нет, поэтому Player живёт на основном домене HRBox под путём basePath
|
||||
(по умолчанию /nanabush-player). Отсюда следуют три инварианта, которые
|
||||
проверяет 00-validate.yaml:
|
||||
|
||||
* audience тикета = https://<ingress.host> (origin без пути)
|
||||
* basePath = отдельная переменная, в aud не входит
|
||||
* issuer тикета = https://<ingress.host>/lms/player
|
||||
|
||||
Подробности контракта — docs/nanabush-player-deployment.md в репозитории HRBox.
|
||||
*/}}
|
||||
|
||||
{{- define "nanabushPlayer.host" -}}
|
||||
{{- required "Для Nanabush Player нужен непустой ingress.host" .Values.ingress.host -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "nanabushPlayer.publicOrigin" -}}
|
||||
{{- printf "https://%s" (include "nanabushPlayer.host" .) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Путь монтирования Player'а на основном домене. Пустое значение и "/" запрещены:
|
||||
это отдало бы Player'у корень, занятый самим HRBox.
|
||||
*/}}
|
||||
{{- define "nanabushPlayer.basePath" -}}
|
||||
{{- $path := .Values.app.nanabushPlayer.basePath | default "/nanabush-player" -}}
|
||||
{{- $path | trimSuffix "/" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "nanabushPlayer.issuer" -}}
|
||||
{{- if .Values.app.nanabushPlayer.issuer -}}
|
||||
{{- .Values.app.nanabushPlayer.issuer -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- printf "https://%s/lms/player" (include "nanabushPlayer.host" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "nats.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- define "nanabushPlayer.serviceName" -}}
|
||||
{{- .Values.app.nanabushPlayer.service.name | default (include "hrbox.component" (dict "ctx" . "name" "nanabush-player")) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
Хост обратного канала Player -> HRBox. Только внутрикластерное имя: по нему
|
||||
Player забирает пакеты курсов и отправляет отчёты, и оно же попадает в
|
||||
allowlist самого Player'а.
|
||||
*/}}
|
||||
{{- define "nats.labels" -}}
|
||||
app.kubernetes.io/name: nats
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
helm.sh/chart: {{ include "nats.chart" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "nats.selectorLabels" -}}
|
||||
app.kubernetes.io/name: nats
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
|
||||
{{/*
|
||||
Return the proper NATS image name
|
||||
*/}}
|
||||
{{- define "nats.clusterAdvertise" -}}
|
||||
{{- printf "$(POD_NAME).%s.$(POD_NAMESPACE).svc.%s" (include "nats.fullname" . ) $.Values.nats.k8sClusterDomain }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Return the NATS cluster routes.
|
||||
*/}}
|
||||
{{- define "nats.clusterRoutes" -}}
|
||||
{{- $name := (include "nats.fullname" . ) -}}
|
||||
{{- range $i, $e := until (.Values.nats.cluster.replicas | int) -}}
|
||||
{{- printf "nats://%s-%d.%s.%s.svc.%s:6222," $name $i $name $.Release.Namespace $.Values.nats.k8sClusterDomain -}}
|
||||
{{- define "nanabushPlayer.backchannelHost" -}}
|
||||
{{- .Values.app.nanabushPlayer.deployment.backchannel.host | default (include "hrbox.component" (dict "ctx" . "name" "web")) -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "nats.extraRoutes" -}}
|
||||
{{- range $i, $url := .Values.nats.cluster.extraRoutes -}}
|
||||
{{- printf "%s," $url -}}
|
||||
{{- define "nanabushPlayer.internalOrigin" -}}
|
||||
{{- printf "http://%s" (include "nanabushPlayer.backchannelHost" .) -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "nats.tlsConfig" -}}
|
||||
tls {
|
||||
{{- if .cert }}
|
||||
cert_file: {{ .secretPath }}/{{ .secret.name }}/{{ .cert }}
|
||||
{{- end }}
|
||||
{{- if .key }}
|
||||
key_file: {{ .secretPath }}/{{ .secret.name }}/{{ .key }}
|
||||
{{- end }}
|
||||
{{- if .ca }}
|
||||
ca_file: {{ .secretPath }}/{{ .secret.name }}/{{ .ca }}
|
||||
{{- end }}
|
||||
{{- if .insecure }}
|
||||
insecure: {{ .insecure }}
|
||||
{{- end }}
|
||||
{{- if .verify }}
|
||||
verify: {{ .verify }}
|
||||
{{- end }}
|
||||
{{- if .verifyAndMap }}
|
||||
verify_and_map: {{ .verifyAndMap }}
|
||||
{{- end }}
|
||||
{{- if .curvePreferences }}
|
||||
curve_preferences: {{ .curvePreferences }}
|
||||
{{- end }}
|
||||
{{- if .timeout }}
|
||||
timeout: {{ .timeout }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Renders a value that contains template.
|
||||
Usage:
|
||||
{{ include "tplvalues.render" ( dict "value" .Values.nats.path.to.the.Value "context" $) }}
|
||||
Версия Player'а берётся из тега образа, а не из отдельной настройки: тикет
|
||||
не должен объявлять версию, отличную от той, что реально запущена в кластере.
|
||||
*/}}
|
||||
{{- define "tplvalues.render" -}}
|
||||
{{- if typeIs "string" .value }}
|
||||
{{- tpl .value .context }}
|
||||
{{- else }}
|
||||
{{- tpl (.value | toYaml) .context }}
|
||||
{{- end }}
|
||||
{{- define "nanabushPlayer.version" -}}
|
||||
{{- $tag := include "hrbox.imageTag" .Values.image.nanabushPlayer -}}
|
||||
{{- if not $tag -}}
|
||||
{{- fail "image.nanabushPlayer должен содержать явный тег версии, например nanabush-player:1.0.11" -}}
|
||||
{{- end -}}
|
||||
{{- $tag -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "nanabushPlayer.secretName" -}}
|
||||
{{- required "app.nanabushPlayer.deployment.existingSecret.name обязателен, когда Player включён" .Values.app.nanabushPlayer.deployment.existingSecret.name -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* URL Redis, который чарт собирает из штатных настроек app.redis. */}}
|
||||
{{- define "nanabushPlayer.redisUrl" -}}
|
||||
{{- printf "redis://%s:%s/%d" (include "hrbox.redisHost" .) (.Values.app.redis.port | toString) (.Values.app.nanabushPlayer.deployment.redis.database | int) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Префикс ключей Redis. Namespace в префиксе разводит несколько релизов HRBox,
|
||||
живущих на одном Dragonfly.
|
||||
*/}}
|
||||
{{- define "nanabushPlayer.redisKeyPrefix" -}}
|
||||
{{- printf "hrbox:%s:%s" .Release.Namespace .Values.app.nanabushPlayer.deployment.redis.keyPrefixSuffix -}}
|
||||
{{- end -}}
|
||||
|
||||
Reference in New Issue
Block a user