fix(docs): remove stand-specific hardcodes

This commit is contained in:
“Naeel”
2026-09-03 16:14:28 +03:00
parent 6bf514e03a
commit aa0f7f6402
7 changed files with 170 additions and 51 deletions
@@ -0,0 +1,67 @@
# 2026-09-03 — Устранение хардкодов документации и публикация DEV
## Найденная причина
Общие материалы `docs/30_registry/` и `docs/curated/` копировались в каждый `generated/<stand>/docs/`, но подстановка выполнялась только для части `getting-started.md`. Поэтому в DEV попадали TEST-значения:
- TEST provider source;
- `5.0.5`;
- TEST API endpoint;
- `deck-test.ngcloud.ru`.
Дополнительно `02_generate_resources_and_docs_v2.sh` не очищал старые generated-файлы. Ресурс, отсутствующий в текущем `services_list.txt`, мог остаться от предыдущей генерации.
## Изменения
- Общие документы используют placeholders:
- `{{NAMESPACE}}`;
- `{{VERSION}}`;
- `{{PROVIDER_SOURCE}}`;
- `{{NUBES_API_ENDPOINT}}`;
- `{{DASHBOARD_URL}}`.
- `04_build_and_publish_docs.sh` подставляет значения рекурсивно во все скопированные Markdown-файлы.
- Добавлена проверка чужих namespace, API/dashboard host и старого `registry.kube5s.ru` до сборки.
- Профиль стал обязательным; обязательные значения не берутся из PROD fallback.
- `02_generate_resources_and_docs_v2.sh` очищает только собственный `generated/<stand>/docs` перед генерацией.
- `docs-generator` больше не содержит DEV default для API/provider source.
- Базовый `mkdocs.yml` больше не содержит versioned URL.
## Проверки
- `bash -n` для обоих docs scripts — PASS.
- `go test ./...` и `go build ./...` в `TOOLS/docs-generator` — PASS.
- DEV regeneration — PASS.
- DEV MkDocs build — PASS; contamination check — PASS.
- В DEV отсутствуют `5.0.5`, TEST API, `deck-test.ngcloud.ru` и `registry.kube5s.ru`.
- Legacy generated `vc_vm_v2` удалён чистой генерацией, так как отсутствует в актуальном `services_list.txt`.
## Публикация
Локальный рекурсивный S3 mirror завершался `unexpected EOF`, поэтому exit code штатного скрипта нельзя считать достаточным подтверждением загрузки. Проверенный артефакт `site/` был передан на ВМ `5.172.178.213` по SSH и атомарно установлен в:
```text
/var/www/tf-docs/nubes-dev/
```
На ВМ проверены страницы getting-started и curated PostgreSQL:
- namespace `nubes-dev`;
- provider version `2.0.0`;
- DEV API endpoint;
- DEV dashboard URL;
- отсутствие TEST-значений.
Legacy versioned каталоги TEST ранее удалены и после публикации отсутствуют:
```text
/var/www/tf-docs/nubes-test/5.0.5
/var/www/tf-docs/nubes-test/5.0.57
```
Публичный путь документации:
```text
https://tf-docs.nodejsk8s.dev.nubes.ru/nubes-dev/
```
Публичный `curl` завершался timeout на большом HTML; содержимое активного зеркала ВМ проверено напрямую.
+8 -4
View File
@@ -24,10 +24,8 @@ func main() {
servicesListFlag := flag.String("services", "", "Path to TOOLS/config/{stand}/services_list.txt") servicesListFlag := flag.String("services", "", "Path to TOOLS/config/{stand}/services_list.txt")
excludeFlag := flag.String("exclude", "", "Comma-separated resource names to skip") excludeFlag := flag.String("exclude", "", "Comma-separated resource names to skip")
versionFlag := flag.String("version", "", "Provider version for example block") versionFlag := flag.String("version", "", "Provider version for example block")
// ⛔ LEGACY DEFAULT (index.cfm) — переопределяется через NUBES_API_ENDPOINT в profile.env. apiEndpointFlag := flag.String("api-endpoint", "", "API endpoint for example block (required)")
// Никогда не использовать deck-api.ngcloud.ru напрямую. providerSourceFlag := flag.String("provider-source", "", "Provider source for example block (required)")
apiEndpointFlag := flag.String("api-endpoint", "https://lk-api-gateway.ngcloud.ru/api/v1/svc", "API endpoint for example block")
providerSourceFlag := flag.String("provider-source", "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes", "Provider source for example block")
opsFlag := flag.Bool("ops", false, "Generate per-service operations docs (resources_ops_yaml → docs/.../operations)") opsFlag := flag.Bool("ops", false, "Generate per-service operations docs (resources_ops_yaml → docs/.../operations)")
flag.Parse() flag.Parse()
@@ -52,7 +50,13 @@ func main() {
panic("--version is required") panic("--version is required")
} }
apiEndpoint := *apiEndpointFlag apiEndpoint := *apiEndpointFlag
if apiEndpoint == "" {
panic("--api-endpoint is required")
}
providerSource := *providerSourceFlag providerSource := *providerSourceFlag
if providerSource == "" {
panic("--provider-source is required")
}
servicesOrder := loadServicesList(servicesList) servicesOrder := loadServicesList(servicesList)
specs := loadSpecs(resourcesDir, servicesOrder) specs := loadSpecs(resourcesDir, servicesOrder)
@@ -80,6 +80,13 @@ if [[ ! -f "$SERVICES_LIST_PATH" ]]; then
exit 2 exit 2
fi fi
for required_var in VERSION NAMESPACE PROVIDER_NAME NUBES_API_ENDPOINT REGISTRY_HOSTNAME; do
if [[ -z "${!required_var:-}" ]]; then
echo "Error: $required_var is required in profile.env or registry.env" >&2
exit 2
fi
done
cd "$PROVIDER_DIR" cd "$PROVIDER_DIR"
echo "Generating resources from unified YAML specs..." echo "Generating resources from unified YAML specs..."
@@ -96,11 +103,10 @@ cp -R "$TMP_GEN_DIR/." "$GO_OUTPUT_DIR/"
echo "Generating docs via template generator..." echo "Generating docs via template generator..."
mkdir -p "$DOCS_DIR" mkdir -p "$DOCS_DIR"
find "$DOCS_DIR" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
# Determine API endpoint for doc examples. # Determine API endpoint for doc examples from the selected profile.
# Use NUBES_API_ENDPOINT from profile.env, fall back to production default. DOCS_API_ENDPOINT="$NUBES_API_ENDPOINT"
# ⛔ LEGACY: deck-api.ngcloud.ru ЗАКРЫВАЕТСЯ. Default = Gateway. Override via NUBES_API_ENDPOINT.
DOCS_API_ENDPOINT="${NUBES_API_ENDPOINT:-https://lk-api-gateway.ngcloud.ru/api/v1/svc}"
# If endpoint looks like new REST gateway (no index.cfm), keep as-is. # If endpoint looks like new REST gateway (no index.cfm), keep as-is.
# If it's old-style without index.cfm, append it for backward compat in docs. # If it's old-style without index.cfm, append it for backward compat in docs.
if [[ "$DOCS_API_ENDPOINT" != *"/index.cfm"* ]] && [[ "$DOCS_API_ENDPOINT" != *"/svc"* ]]; then if [[ "$DOCS_API_ENDPOINT" != *"/index.cfm"* ]] && [[ "$DOCS_API_ENDPOINT" != *"/svc"* ]]; then
@@ -111,7 +117,7 @@ ${ROOT_DIR}/TOOLS/bin/docs-generator \
-resources "$RESOURCES_YAML_DIR" \ -resources "$RESOURCES_YAML_DIR" \
-docs "$DOCS_DIR" \ -docs "$DOCS_DIR" \
-services "$SERVICES_LIST_PATH" \ -services "$SERVICES_LIST_PATH" \
-version "${VERSION:-2.x}" \ -version "$VERSION" \
-api-endpoint "$DOCS_API_ENDPOINT" \ -api-endpoint "$DOCS_API_ENDPOINT" \
-provider-source "${REGISTRY_HOSTNAME}/${NAMESPACE}/${PROVIDER_NAME}" -provider-source "${REGISTRY_HOSTNAME}/${NAMESPACE}/${PROVIDER_NAME}"
+70 -28
View File
@@ -36,7 +36,26 @@ if [[ -n "$PROFILE_DIR" ]]; then
source "$PROFILE_ENV_FILE" source "$PROFILE_ENV_FILE"
set +a set +a
fi fi
REGISTRY_ENV_FILE="${ROOT_DIR}/TOOLS/config/registry.env"
if [[ -f "$REGISTRY_ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$REGISTRY_ENV_FILE"
set +a
fi fi
fi
if [[ -z "$PROFILE_DIR" ]]; then
echo "Error: --profile <path> is required" >&2
exit 2
fi
for required_var in VERSION NAMESPACE PROVIDER_NAME NUBES_API_ENDPOINT REGISTRY_HOSTNAME; do
if [[ -z "${!required_var:-}" ]]; then
echo "Error: $required_var is required in profile.env or registry.env" >&2
exit 2
fi
done
resolve_root_path() { resolve_root_path() {
local path_value="$1" local path_value="$1"
@@ -51,9 +70,9 @@ resolve_root_path() {
echo "${ROOT_DIR}/${path_value}" echo "${ROOT_DIR}/${path_value}"
} }
VERSION="${1:-}" VERSION_ARG="${1:-}"
if [[ -z "$VERSION" ]]; then if [[ -n "$VERSION_ARG" ]]; then
VERSION="${VERSION:-}" VERSION="$VERSION_ARG"
fi fi
if [[ -z "$VERSION" ]]; then if [[ -z "$VERSION" ]]; then
VERSION=$(grep -E 'version string' "$PROVIDER_MAIN" | sed -E 's/.*"([0-9.]+)".*/\1/') VERSION=$(grep -E 'version string' "$PROVIDER_MAIN" | sed -E 's/.*"([0-9.]+)".*/\1/')
@@ -65,8 +84,6 @@ if [[ -z "$VERSION" ]]; then
fi fi
REGISTRY_HOST="${REGISTRY_HOST:-tf-docs.nodejsk8s.dev.nubes.ru}" REGISTRY_HOST="${REGISTRY_HOST:-tf-docs.nodejsk8s.dev.nubes.ru}"
NAMESPACE="${NAMESPACE:-nubes}"
PROVIDER_NAME="${PROVIDER_NAME:-nubes}"
export REGISTRY_HOST NAMESPACE PROVIDER_NAME VERSION export REGISTRY_HOST NAMESPACE PROVIDER_NAME VERSION
S3CFG_REGISTRY="${S3CFG_REGISTRY:-${ROOT_DIR}/secrets/.s3cfg_registry}" S3CFG_REGISTRY="${S3CFG_REGISTRY:-${ROOT_DIR}/secrets/.s3cfg_registry}"
@@ -85,9 +102,15 @@ if [[ -n "$PROFILE_DIR" ]]; then
fi fi
fi fi
# ⛔ LEGACY: deck-api.ngcloud.ru ЗАКРЫВАЕТСЯ. Default = Gateway. DOCS_API_ENDPOINT="$NUBES_API_ENDPOINT"
DOCS_API_ENDPOINT="${NUBES_API_ENDPOINT:-https://lk-api-gateway.ngcloud.ru/api/v1/svc}"
DOCS_API_ENDPOINT="$(normalize_api_endpoint "$DOCS_API_ENDPOINT")" DOCS_API_ENDPOINT="$(normalize_api_endpoint "$DOCS_API_ENDPOINT")"
DASHBOARD_HOST="deck"
if [[ "$NAMESPACE" != "nubes" ]]; then
DASHBOARD_HOST="deck-${NAMESPACE#nubes-}"
fi
DASHBOARD_URL="https://${DASHBOARD_HOST}.ngcloud.ru"
PROVIDER_SOURCE="${REGISTRY_HOSTNAME}/${NAMESPACE}/${PROVIDER_NAME}"
export DOCS_API_ENDPOINT DASHBOARD_URL PROVIDER_SOURCE
load_s3cfg_registry() { load_s3cfg_registry() {
local cfg="$1" local cfg="$1"
@@ -143,37 +166,56 @@ if [[ -n "${MKDOCS_DOCS_DIR:-}" ]]; then
fi fi
fi fi
# Per-стенд подстановка в getting-started (после копирования 30_registry в docs_dir) # Per-стенд подстановка во все скопированные Markdown-файлы.
if [[ -n "${MKDOCS_DOCS_DIR:-}" ]]; then if [[ -n "${MKDOCS_DOCS_DIR:-}" ]]; then
export DOCS_GUIDE_VERSION="$VERSION" export DOCS_SUBSTITUTION_ROOT="$MKDOCS_DOCS_DIR"
export DOCS_GUIDE_API_ENDPOINT="$DOCS_API_ENDPOINT"
export DOCS_GUIDE_NAMESPACE="$NAMESPACE"
export DOCS_GUIDE_FILE="${MKDOCS_DOCS_DIR}/30_registry/guides/getting-started.md"
python3 - <<'PY' python3 - <<'PY'
import os import os
import re import re
from pathlib import Path from pathlib import Path
guide_path = Path(os.environ["DOCS_GUIDE_FILE"]) root = Path(os.environ["DOCS_SUBSTITUTION_ROOT"])
if guide_path.exists(): values = {
text = guide_path.read_text(encoding="utf-8") "{{NAMESPACE}}": os.environ["NAMESPACE"],
# source namespace: .../{{NAMESPACE}}/nubes -> .../<ns>/nubes "{{VERSION}}": os.environ["VERSION"],
text = text.replace("{{NAMESPACE}}", os.environ["DOCS_GUIDE_NAMESPACE"]) "{{NUBES_API_ENDPOINT}}": os.environ["DOCS_API_ENDPOINT"],
# version (первое вхождение version = "x.y.z" — блок required_providers) "{{DASHBOARD_URL}}": os.environ["DASHBOARD_URL"],
text = re.sub( "{{PROVIDER_SOURCE}}": os.environ["PROVIDER_SOURCE"],
r'(version\s*=\s*")([0-9.]+)(")', "{{REGISTRY_HOST}}": os.environ["REGISTRY_HOSTNAME"],
lambda m: f'{m.group(1)}{os.environ["DOCS_GUIDE_VERSION"]}{m.group(3)}', }
text, legacy_provider_source = re.compile(
count=1, r'(source\s*=\s*")registry\.kube5s\.ru/[^"\n]+(")'
) )
text = re.sub( for path in root.rglob("*.md"):
r'(api_endpoint\s*=\s*")([^"]+)(")', text = path.read_text(encoding="utf-8")
lambda m: f'{m.group(1)}{os.environ["DOCS_GUIDE_API_ENDPOINT"]}{m.group(3)}', for placeholder, value in values.items():
text = text.replace(placeholder, value)
text = legacy_provider_source.sub(
lambda match: f'{match.group(1)}{os.environ["PROVIDER_SOURCE"]}{match.group(2)}',
text, text,
count=1,
) )
guide_path.write_text(text, encoding="utf-8") path.write_text(text, encoding="utf-8")
for path in root.rglob("*.md"):
text = path.read_text(encoding="utf-8")
if "{{" in text or "}}" in text:
raise SystemExit(f"unresolved documentation placeholder: {path}")
PY PY
foreign_namespace=""
foreign_api=""
foreign_dashboard=""
case "$NAMESPACE" in
nubes) foreign_namespace="nubes-dev|nubes-test"; foreign_api="lk-api-gateway-(dev|test)"; foreign_dashboard="deck-(dev|test)" ;;
nubes-dev) foreign_namespace="nubes-test"; foreign_api="lk-api-gateway\.ngcloud\.ru|lk-api-gateway-test\.ngcloud\.ru"; foreign_dashboard="deck\.ngcloud\.ru|deck-test\.ngcloud\.ru" ;;
nubes-test) foreign_namespace="nubes-dev"; foreign_api="lk-api-gateway\.ngcloud\.ru|lk-api-gateway-dev\.ngcloud\.ru"; foreign_dashboard="deck\.ngcloud\.ru|deck-dev\.ngcloud\.ru" ;;
*) echo "Error: unsupported namespace for documentation validation: $NAMESPACE" >&2; exit 2 ;;
esac
if grep -RInE "$foreign_namespace|$foreign_api|$foreign_dashboard|registry\.kube5s\.ru" "$MKDOCS_DOCS_DIR" --include='*.md' >/tmp/docs-stand-contamination.txt 2>/dev/null; then
echo "Error: stand-specific contamination detected in generated docs:" >&2
cat /tmp/docs-stand-contamination.txt >&2
exit 2
fi
fi fi
python3 - <<'PY' python3 - <<'PY'
+8 -8
View File
@@ -24,14 +24,14 @@
terraform { terraform {
required_providers { required_providers {
nubes = { nubes = {
source = "tf-registry.containerk8s.services.ngcloud.ru/{{NAMESPACE}}/nubes" source = "{{PROVIDER_SOURCE}}"
version = "0.0.0" # автозамена на версию стенда при сборке (prod=1.x, dev=2.x, test=3.x) version = "{{VERSION}}"
} }
} }
} }
provider "nubes" { provider "nubes" {
api_endpoint = "https://lk-api-gateway.ngcloud.ru/api/v1/svc" api_endpoint = "{{NUBES_API_ENDPOINT}}"
api_token = var.api_token api_token = var.api_token
} }
@@ -41,11 +41,11 @@ variable "api_token" {
} }
``` ```
!!! info "TEST стенд (обязательные адреса)" !!! info "Стенд {{NAMESPACE}} (обязательные адреса)"
Эта документация относится к TEST стенду. Эта документация относится к стенду `{{NAMESPACE}}`.
- Личный кабинет: https://deck-test.ngcloud.ru/dashboard/ - Личный кабинет: {{DASHBOARD_URL}}/dashboard/
- API endpoint: https://lk-api-gateway-test.ngcloud.ru/api/v1/svc - API endpoint: {{NUBES_API_ENDPOINT}}
!!! tip "Безопасность" !!! tip "Безопасность"
Никогда не храните токен прямо в файле `main.tf`, если планируете загружать код в систему контроля версий (git). Используйте `variables.tf` или файл `terraform.tfvars`. Никогда не храните токен прямо в файле `main.tf`, если планируете загружать код в систему контроля версий (git). Используйте `variables.tf` или файл `terraform.tfvars`.
@@ -55,7 +55,7 @@ variable "api_token" {
Токен (Access Token) необходим провайдеру для авторизации ваших действий в облаке. Токен (Access Token) необходим провайдеру для авторизации ваших действий в облаке.
Если нет ТОКЕНА доступа или хотите создать новый - Если нет ТОКЕНА доступа или хотите создать новый -
В Личном Кабинете - на странице Профиля пользователя https://deck.ngcloud.ru/authorization/profile В Личном Кабинете - на странице Профиля пользователя {{DASHBOARD_URL}}/authorization/profile
во вкладке Токены - нажать "Выпустить тех-токен" во вкладке Токены - нажать "Выпустить тех-токен"
Значение токена показывается только при его создании, надо его сохранить Значение токена показывается только при его создании, надо его сохранить
+3 -3
View File
@@ -27,8 +27,8 @@ s3_name = "my-s3" # ИМЯ экземпляра S3 (не UUID
terraform { terraform {
required_providers { required_providers {
nubes = { nubes = {
source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-test/nubes" source = "{{PROVIDER_SOURCE}}"
version = "5.0.5" version = "{{VERSION}}"
} }
} }
} }
@@ -39,7 +39,7 @@ variable "s3_name" { type = string }
provider "nubes" { provider "nubes" {
api_token = var.api_token api_token = var.api_token
api_endpoint = "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc" api_endpoint = "{{NUBES_API_ENDPOINT}}"
} }
``` ```
+1 -1
View File
@@ -1,5 +1,5 @@
site_name: Провайдер Terraform Nubes site_name: Провайдер Terraform Nubes
site_url: https://tf-registry.containerk8s.services.ngcloud.ru/docs/nubes/nubes/2.0.1/ site_url: /
exclude_docs: | exclude_docs: |
README.md README.md