fix: redeploy template — merge modify-params into redeploy params (single apply), bump TEST 5.1.14
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
# LUCEE — схема взаимодействия с Postgres
|
||||
|
||||
Ниже статическая SVG-версия схемы (надёжно рендерится в VSCode):
|
||||
|
||||

|
||||
@@ -1,107 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="420" viewBox="0 0 1000 420" role="img" aria-label="LUCEE схема">
|
||||
<defs>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
|
||||
<path d="M0,0 L10,3.5 L0,7 Z" fill="#333"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<style>
|
||||
.node { fill:#fff; stroke:#333; stroke-width:1.4; }
|
||||
.chip { fill:#f8f9fa; stroke:#333; stroke-width:2; }
|
||||
.app { fill:#eef6ff; stroke:#1f78b4; stroke-width:1.5; }
|
||||
.label { font-family: Arial, Helvetica, sans-serif; font-size:13px; fill:#111; text-anchor:middle; dominant-baseline:middle; }
|
||||
.small { font-family: Arial, Helvetica, sans-serif; font-size:12px; fill:#111; }
|
||||
.line { stroke:#555; stroke-width:1.2; fill:none; marker-end:url(#arrow); }
|
||||
</style>
|
||||
|
||||
<!-- Inputs -->
|
||||
<g id="inputs">
|
||||
<circle cx="140" cy="112" r="8" class="node"/>
|
||||
<text x="170" y="116" class="small">TABLE_NAME</text>
|
||||
|
||||
<circle cx="140" cy="120" r="8" class="node"/>
|
||||
<text x="170" y="124" class="small">TABLE_NAME</text>
|
||||
|
||||
<circle cx="140" cy="184" r="8" class="node"/>
|
||||
<text x="170" y="188" class="small">testds_username</text>
|
||||
|
||||
<circle cx="140" cy="220" r="8" class="node"/>
|
||||
<text x="170" y="224" class="small">testds_password</text>
|
||||
|
||||
<circle cx="140" cy="154" r="8" class="node"/>
|
||||
<text x="170" y="158" class="small">testds_connectionString</text>
|
||||
|
||||
<circle cx="140" cy="188" r="8" class="node"/>
|
||||
<text x="170" y="192" class="small">testds_username</text>
|
||||
|
||||
<circle cx="140" cy="328" r="8" class="node"/>
|
||||
<text x="170" y="332" class="small">testds_validate</text>
|
||||
|
||||
<circle cx="140" cy="364" r="8" class="node"/>
|
||||
<text x="170" y="368" class="small">testds_class</text>
|
||||
</g>
|
||||
|
||||
<!-- Central Postgres chip -->
|
||||
<rect x="370" y="170" width="260" height="120" rx="10" class="chip"/>
|
||||
<text x="500" y="230" class="label">Postgres (БД)</text>
|
||||
|
||||
<text x="880" y="124" class="small">internalMaster</text>
|
||||
<g id="outputs">
|
||||
<circle cx="860" cy="120" r="8" class="node"/>
|
||||
<text x="880" y="158" class="small">vault_users</text>
|
||||
|
||||
<circle cx="860" cy="154" r="8" class="node"/>
|
||||
<text x="880" y="192" class="small">pg_db_name</text>
|
||||
|
||||
<circle cx="860" cy="188" r="8" class="node"/>
|
||||
<text x="880" y="226" class="small">DATABASE_URL</text>
|
||||
|
||||
<circle cx="860" cy="222" r="8" class="node"/>
|
||||
<text x="880" y="260" class="small">PGHOST</text>
|
||||
|
||||
<circle cx="860" cy="256" r="8" class="node"/>
|
||||
<text x="880" y="294" class="small">PGPORT</text>
|
||||
|
||||
<circle cx="860" cy="290" r="8" class="node"/>
|
||||
<text x="880" y="328" class="small">PGUSER</text>
|
||||
|
||||
<circle cx="860" cy="324" r="8" class="node"/>
|
||||
<text x="880" y="362" class="small">PGPASSWORD</text>
|
||||
|
||||
<circle cx="860" cy="358" r="8" class="node"/>
|
||||
<text x="880" y="362" class="small">notification</text>
|
||||
</g>
|
||||
|
||||
<!-- Lucee box -->
|
||||
<rect x="700" y="270" width="200" height="120" rx="10" class="app"/>
|
||||
<text x="800" y="295" class="label">Lucee</text>
|
||||
<text x="800" y="318" class="small">DATABASE_URL</text>
|
||||
<text x="800" y="334" class="small">PGHOST · PGPORT · PGUSER · PGPASSWORD</text>
|
||||
<text x="800" y="350" class="small">TABLE_NAME · testds_class/bundle/version</text>
|
||||
|
||||
<!-- Connections: inputs -> Postgres -->
|
||||
<path d="M140 120 C 260 120 250 170 370 170" class="line"/>
|
||||
<path d="M140 154 C 260 154 250 170 370 170" class="line"/>
|
||||
<path d="M140 188 C 260 188 250 188 370 188" class="line"/>
|
||||
<path d="M140 222 C 260 222 250 210 370 210" class="line"/>
|
||||
<path d="M140 256 C 260 256 250 240 370 240" class="line"/>
|
||||
<path d="M140 290 C 260 290 250 270 370 270" class="line"/>
|
||||
<path d="M140 324 C 260 324 250 300 370 300" class="line"/>
|
||||
<path d="M140 358 C 260 358 250 300 370 300" class="line"/>
|
||||
|
||||
<!-- Connections: Postgres -> outputs -->
|
||||
<path d="M630 170 C 730 170 760 120 860 120" class="line"/>
|
||||
<path d="M630 170 C 730 170 760 150 860 154" class="line"/>
|
||||
<path d="M630 188 C 730 188 760 180 860 188" class="line"/>
|
||||
<path d="M630 210 C 730 210 760 210 860 222" class="line"/>
|
||||
<path d="M630 240 C 730 240 760 240 860 256" class="line"/>
|
||||
<path d="M630 270 C 730 270 760 270 860 290" class="line"/>
|
||||
<path d="M630 300 C 730 300 760 310 860 324" class="line"/>
|
||||
<path d="M630 300 C 730 300 760 330 860 358" class="line"/>
|
||||
|
||||
<!-- Some outputs -> Lucee -->
|
||||
<path d="M860 154 C 820 154 760 240 700 325" class="line"/>
|
||||
<path d="M860 188 C 820 188 760 260 700 325" class="line"/>
|
||||
<path d="M860 290 C 820 290 760 310 700 325" class="line"/>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,45 @@
|
||||
# =============================================================================
|
||||
# locals.tf — все настраиваемые значения модуля LUCEE
|
||||
# Никакого хардкода в ресурсах — всё здесь.
|
||||
# =============================================================================
|
||||
|
||||
locals {
|
||||
# ─── PostgreSQL ──────────────────────────────────────────────────────────
|
||||
pg_resource_name = "pglucee001"
|
||||
pg_cpu = 500
|
||||
pg_memory = 512
|
||||
pg_replicas = 1
|
||||
pg_disk = 10
|
||||
pg_version = "17"
|
||||
pg_retain = 14
|
||||
pg_schedule = "0 0 * * *"
|
||||
pg_timeout = "11m"
|
||||
|
||||
# ─── PostgreSQL User ─────────────────────────────────────────────────────
|
||||
pg_username = "user4crudpg"
|
||||
pg_role = "ddl_user"
|
||||
pg_db_name = "db4crudpg"
|
||||
|
||||
# ─── Lucee ───────────────────────────────────────────────────────────────
|
||||
lucee_git_revision = "94e14"
|
||||
lucee_resource_name = "luceeresource"
|
||||
lucee_domain = "tflucee"
|
||||
lucee_version = "5.4"
|
||||
lucee_git_path = "https://gitea.services.ngcloud.ru/Nail/tfluceecrud.git"
|
||||
lucee_cpu = 300
|
||||
lucee_memory = 512
|
||||
lucee_replicas = 1
|
||||
|
||||
# ─── Таблица CRUD ────────────────────────────────────────────────────────
|
||||
crud_table_name = "crud_items"
|
||||
|
||||
# ─── JDBC / БД ───────────────────────────────────────────────────────────
|
||||
jdbc_class = "org.postgresql.Driver"
|
||||
jdbc_bundle_name = "org.postgresql.jdbc"
|
||||
jdbc_bundle_version = "42.6.0"
|
||||
jdbc_conn_limit = "5"
|
||||
jdbc_live_timeout = "15"
|
||||
jdbc_validate = "false"
|
||||
pg_port = "5432"
|
||||
pg_ssl_mode = "require"
|
||||
}
|
||||
+18
-18
@@ -1,51 +1,51 @@
|
||||
locals {
|
||||
pg_host = nubes_postgres.npg_lucee.state_out_flat["internalMaster"]
|
||||
pg_user = nubes_postgres_user.crud_user_0.username
|
||||
pg_pass = jsondecode(nubes_postgres.npg_lucee.vault_secrets["users"]).user4crudpg.password
|
||||
pg_pass = nonsensitive(jsondecode(nubes_postgres.npg_lucee.vault_secrets["users"]).user4crudpg.password)
|
||||
pg_db = nubes_postgres_database.pg_db.db_name
|
||||
}
|
||||
|
||||
resource "nubes_lucee" "applucee" {
|
||||
resource_name = "luceeresource"
|
||||
resource_name = local.lucee_resource_name
|
||||
|
||||
startup_configuration = {
|
||||
resource_realm = var.realm
|
||||
}
|
||||
|
||||
cluster_configuration = {
|
||||
cpu = 300
|
||||
memory = 512
|
||||
replicas = 1
|
||||
cpu = local.lucee_cpu
|
||||
memory = local.lucee_memory
|
||||
replicas = local.lucee_replicas
|
||||
}
|
||||
|
||||
access_configuration = {
|
||||
domain = "tflucee"
|
||||
domain = local.lucee_domain
|
||||
}
|
||||
|
||||
app_configuration = {
|
||||
version = "5.4"
|
||||
git_path = "https://gitea.services.ngcloud.ru/Nail/tfluceecrud.git"
|
||||
version = local.lucee_version
|
||||
git_path = local.lucee_git_path
|
||||
}
|
||||
|
||||
git_revision = "94ebf06"
|
||||
git_revision = local.lucee_git_revision
|
||||
|
||||
json_env = jsonencode({
|
||||
TABLE_NAME = "check_point"
|
||||
testds_class = "org.postgresql.Driver"
|
||||
testds_bundleName = "org.postgresql.jdbc"
|
||||
testds_bundleVersion = "42.6.0"
|
||||
TABLE_NAME = local.crud_table_name
|
||||
testds_class = local.jdbc_class
|
||||
testds_bundleName = local.jdbc_bundle_name
|
||||
testds_bundleVersion = local.jdbc_bundle_version
|
||||
testds_connectionString = "jdbc:postgresql://${local.pg_host}:5432/${local.pg_db}"
|
||||
testds_username = local.pg_user
|
||||
testds_password = local.pg_pass
|
||||
testds_connectionLimit = "5"
|
||||
testds_liveTimeout = "15"
|
||||
testds_validate = "false"
|
||||
testds_connectionLimit = local.jdbc_conn_limit
|
||||
testds_liveTimeout = local.jdbc_live_timeout
|
||||
testds_validate = local.jdbc_validate
|
||||
|
||||
PGHOST = local.pg_host
|
||||
PGPORT = "5432"
|
||||
PGPORT = local.pg_port
|
||||
PGUSER = local.pg_user
|
||||
PGPASSWORD = local.pg_pass
|
||||
PGSSLMODE = "require"
|
||||
PGSSLMODE = local.pg_ssl_mode
|
||||
DATABASE_URL = format(
|
||||
"postgresql://%s:%s@%s:5432/%s",
|
||||
local.pg_user,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
resource "nubes_postgres" "npg_lucee" {
|
||||
resource_name = "pglucee001"
|
||||
resource_name = local.pg_resource_name
|
||||
|
||||
startup_configuration = {
|
||||
resource_realm = var.realm
|
||||
}
|
||||
|
||||
cluster_configuration = {
|
||||
cpu = 500
|
||||
memory = 512
|
||||
replicas = 1
|
||||
disk = 10
|
||||
cpu = local.pg_cpu
|
||||
memory = local.pg_memory
|
||||
replicas = local.pg_replicas
|
||||
disk = local.pg_disk
|
||||
}
|
||||
|
||||
access_configuration = {
|
||||
@@ -20,7 +20,7 @@ resource "nubes_postgres" "npg_lucee" {
|
||||
}
|
||||
|
||||
postgres_configuration = {
|
||||
version = "17"
|
||||
version = local.pg_version
|
||||
ssl_required = true
|
||||
pooler_master = false
|
||||
pooler_slave = false
|
||||
@@ -33,9 +33,8 @@ resource "nubes_postgres" "npg_lucee" {
|
||||
|
||||
backup_configuration = {
|
||||
s3_uid = var.s3_name
|
||||
# s3_uid = var.s3_user_uid
|
||||
retain = 14
|
||||
schedule = "0 0 * * *"
|
||||
retain = local.pg_retain
|
||||
schedule = local.pg_schedule
|
||||
}
|
||||
|
||||
autoscale_configuration = {
|
||||
@@ -45,6 +44,6 @@ resource "nubes_postgres" "npg_lucee" {
|
||||
quota = 100
|
||||
}
|
||||
|
||||
operation_timeout = "11m"
|
||||
operation_timeout = local.pg_timeout
|
||||
adopt_existing_on_create = true
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# =============================================================================
|
||||
# PostgreSQL — пользователи (6 шт.)
|
||||
# PostgreSQL — пользователи и базы данных
|
||||
# =============================================================================
|
||||
resource "nubes_postgres_user" "crud_user_0" {
|
||||
postgres_id = nubes_postgres.npg_lucee.id
|
||||
username = "user4crudpg"
|
||||
role = "ddl_user"
|
||||
username = local.pg_username
|
||||
role = local.pg_role
|
||||
adopt_existing_on_create = true
|
||||
}
|
||||
|
||||
resource "nubes_postgres_database" "pg_db" {
|
||||
postgres_id = nubes_postgres.npg_lucee.id
|
||||
db_name = "db4crudpg"
|
||||
db_name = local.pg_db_name
|
||||
db_owner = nubes_postgres_user.crud_user_0.username
|
||||
adopt_existing_on_create = true
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ TOKEN_FILE="secrets/dev.token"
|
||||
|
||||
# Release versions
|
||||
# Version
|
||||
VERSION="3.1.11"
|
||||
VERSION="3.1.12"
|
||||
|
||||
# Registry/S3 settings
|
||||
REGISTRY_HOST="registry.kube5s.ru"
|
||||
|
||||
@@ -4,7 +4,7 @@ TOKEN_FILE="secrets/prod.token"
|
||||
|
||||
# Release versions
|
||||
# Version
|
||||
VERSION="2.1.10"
|
||||
VERSION="2.1.11"
|
||||
|
||||
# Registry/S3 settings
|
||||
REGISTRY_HOST="registry.kube5s.ru"
|
||||
|
||||
@@ -3,7 +3,7 @@ NUBES_API_ENDPOINT="https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
|
||||
TOKEN_FILE="secrets/test.token"
|
||||
|
||||
# Version
|
||||
VERSION="5.1.13"
|
||||
VERSION="5.1.14"
|
||||
|
||||
# Docs generation — ONLY from docs_gen/<stand>/ (never from docs/)
|
||||
DOCS_GEN_DIR="provider/docs_gen/test"
|
||||
|
||||
@@ -548,7 +548,6 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
|
||||
{{- if .HasRedeploy }}
|
||||
// --- Redeploy support (ARCHITECTURE.md) ---
|
||||
if redeployRequested {
|
||||
{{- if .RedeployParams }}
|
||||
redeployParams := map[int]string{}
|
||||
{{- range .RedeployParams }}
|
||||
{{- if (IsNested .) }}
|
||||
@@ -559,10 +558,17 @@ func (r *{{ToCamel .Name}}Resource) Update(ctx context.Context, req resource.Upd
|
||||
redeployParams[{{.ID}}] = {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout, redeployParams); err != nil {
|
||||
// Merge modify-params into redeploy to avoid state drift
|
||||
{{- range .ModifyParams }}
|
||||
{{- if (IsNested .) }}
|
||||
if plan.{{ToCamel .Code}} != nil {
|
||||
redeployParams[{{.ID}}] = {{NestedJSONExpr . "plan"}}
|
||||
}
|
||||
{{- else }}
|
||||
if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout, nil); err != nil {
|
||||
redeployParams[{{.ID}}] = {{ParamFormat . (printf "plan.%s" (ToCamel .Code))}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout, redeployParams); err != nil {
|
||||
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,574 @@
|
||||
# GATEWAY Service — Path-based DNS-less Router
|
||||
|
||||
> **Репозиторий:** `gateway` (шаблонный, создан 2026-07-20)
|
||||
> **URL:** `https://gateway.nodejsk8s.services.ngcloud.ru/`
|
||||
> **Назначение:** Замена всех DNS-записей `*.kube5s.ru` единым path-based роутером
|
||||
> **Статус:** СПЛАНИРОВАНО, НЕ НАЧАТО
|
||||
|
||||
---
|
||||
|
||||
## 1. ЗАЧЕМ ЭТО НУЖНО
|
||||
|
||||
### Проблема
|
||||
|
||||
Сейчас каждый сервис живёт на своём поддомене `*.kube5s.ru`:
|
||||
|
||||
| Поддомен | Сервис | Ingress | TLS-сертификат |
|
||||
|----------|--------|---------|-----------------|
|
||||
| `registry.kube5s.ru` | Terraform Registry | отдельный | отдельный |
|
||||
| `something1.kube5s.ru` | Сервис 1 | отдельный | отдельный |
|
||||
| `something2.kube5s.ru` | Сервис 2 | отдельный | отдельный |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
**Проблемы:**
|
||||
- Домен `kube5s.ru` — **срочный**, скоро истекает
|
||||
- Продление — **дорого**
|
||||
- Каждый поддомен = DNS-запись + Ingress + сертификат — **администрировать заебало**
|
||||
|
||||
### Решение
|
||||
|
||||
**Один домен, один Ingress, один сертификат.** Все сервисы — под path-префиксами:
|
||||
|
||||
```
|
||||
https://gateway.nodejsk8s.services.ngcloud.ru/record01/* → Сервис 1
|
||||
https://gateway.nodejsk8s.services.ngcloud.ru/record02/* → Сервис 2
|
||||
https://gateway.nodejsk8s.services.ngcloud.ru/record03/* → Сервис 3
|
||||
```
|
||||
|
||||
Домен `gateway.nodejsk8s.services.ngcloud.ru` — **облачный managed service**, назначается облаком, не требует покупки/продления.
|
||||
|
||||
### Выгода
|
||||
|
||||
| Было (DNS-based) | Стало (Path-based) |
|
||||
|---|---|
|
||||
| N DNS-записей | 0 дополнительных DNS-записей |
|
||||
| N Ingress'ов | 1 Ingress |
|
||||
| N TLS-сертификатов | 1 TLS-сертификат |
|
||||
| Домен `kube5s.ru` нужен | Домен `kube5s.ru` не нужен |
|
||||
|
||||
---
|
||||
|
||||
## 2. АРХИТЕКТУРА
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Внешний мир"
|
||||
T[Terraform CLI]
|
||||
C[Клиенты API]
|
||||
end
|
||||
|
||||
subgraph "Kubernetes (облако Nubes)"
|
||||
I["Ingress<br/>gateway.nodejsk8s.services.ngcloud.ru<br/>(cert-manager + LE)"]
|
||||
G["GATEWAY Service<br/>(NodeJS: Express + http-proxy-middleware)"]
|
||||
|
||||
I --> G
|
||||
|
||||
G -->|"/record01/*"| R1["registry-server<br/>(Terraform Registry)"]
|
||||
G -->|"/record02/*"| R2["Сервис 2<br/>(будущий)"]
|
||||
G -->|"/record03/*"| R3["Сервис 3<br/>(будущий)"]
|
||||
G -->|"/"| FALLBACK["Fallback / health-check"]
|
||||
end
|
||||
|
||||
T -->|"source = gateway...nubes.ru/nubes/nubes"| I
|
||||
```
|
||||
|
||||
### Как это работает (для Terraform Registry)
|
||||
|
||||
1. Terraform читает `.tf`:
|
||||
```hcl
|
||||
source = "gateway.nodejsk8s.services.ngcloud.ru/nubes-test/nubes"
|
||||
```
|
||||
|
||||
2. Terraform идёт в:
|
||||
```
|
||||
GET https://gateway.nodejsk8s.services.ngcloud.ru/.well-known/terraform.json
|
||||
```
|
||||
|
||||
3. GATEWAY проксирует на registry-server, который отвечает:
|
||||
```json
|
||||
{ "providers.v1": "/record01/v1/providers/" }
|
||||
```
|
||||
|
||||
4. Terraform дальше сам идёт по `/record01/v1/providers/nubes-test/nubes/versions`
|
||||
|
||||
5. Registry-server генерит download-url с префиксом `/record01/v1/proxy?...`
|
||||
|
||||
6. Profit. Terraform скачивает провайдер.
|
||||
|
||||
---
|
||||
|
||||
## 3. КОНФИГУРАЦИЯ GATEWAY
|
||||
|
||||
### Формат конфига (`config.yaml` или env-переменные)
|
||||
|
||||
```yaml
|
||||
# gateway/config.yaml
|
||||
routes:
|
||||
- id: "registry"
|
||||
prefix: "/record01"
|
||||
description: "Terraform Provider Registry"
|
||||
upstream: "http://registry-server.terra.svc.cluster.local:8080"
|
||||
stripPrefix: false # НЕ отрезать /record01 — registry сам знает свой префикс
|
||||
timeout: 30000 # 30s (registry может долго листить S3)
|
||||
|
||||
- id: "future-service-2"
|
||||
prefix: "/record02"
|
||||
description: "Будущий сервис — TODO"
|
||||
upstream: "http://service2.namespace.svc.cluster.local:8080"
|
||||
stripPrefix: true # Отрезать /record02 перед отправкой в upstream
|
||||
timeout: 10000
|
||||
|
||||
- id: "future-service-3"
|
||||
prefix: "/record03"
|
||||
description: "Будущий сервис — TODO"
|
||||
upstream: "http://service3.namespace.svc.cluster.local:3000"
|
||||
stripPrefix: true
|
||||
timeout: 10000
|
||||
```
|
||||
|
||||
### Конфиг через env (альтернатива, проще для K8s)
|
||||
|
||||
```bash
|
||||
# Формат: ROUTE_XX_ID, ROUTE_XX_PREFIX, ROUTE_XX_UPSTREAM, ROUTE_XX_STRIP, ROUTE_XX_TIMEOUT
|
||||
ROUTE_01_ID=registry
|
||||
ROUTE_01_PREFIX=/record01
|
||||
ROUTE_01_UPSTREAM=http://registry-server.terra.svc.cluster.local:8080
|
||||
ROUTE_01_STRIP=false
|
||||
ROUTE_01_TIMEOUT=30000
|
||||
|
||||
ROUTE_02_ID=future-svc
|
||||
ROUTE_02_PREFIX=/record02
|
||||
ROUTE_02_UPSTREAM=http://service2.namespace.svc.cluster.local:8080
|
||||
ROUTE_02_STRIP=true
|
||||
ROUTE_02_TIMEOUT=10000
|
||||
```
|
||||
|
||||
### Важно: `stripPrefix`
|
||||
|
||||
| Режим | Когда использовать |
|
||||
|-------|--------------------|
|
||||
| `stripPrefix: false` | Upstream **знает** о своём префиксе и сам его обрабатывает (как registry-server с `PATH_PREFIX=/record01`) |
|
||||
| `stripPrefix: true` | Upstream **не знает** о префиксе — GATEWAY отрезает его перед проксированием. `/record01/api/users` → `/api/users` |
|
||||
|
||||
---
|
||||
|
||||
## 4. КОД GATEWAY (план)
|
||||
|
||||
### Стек
|
||||
|
||||
- **NodeJS** + **Express** — HTTP-фреймворк
|
||||
- **http-proxy-middleware** — реверс-прокси
|
||||
- **Dotenv / js-yaml** — конфигурация
|
||||
|
||||
### Структура проекта
|
||||
|
||||
```
|
||||
gateway/
|
||||
├── package.json
|
||||
├── server.js # Главный файл
|
||||
├── config/
|
||||
│ ├── routes.yaml # Конфиг маршрутов
|
||||
│ └── index.js # Загрузчик конфига
|
||||
├── lib/
|
||||
│ ├── proxy.js # Фабрика proxy-middleware'ов из конфига
|
||||
│ ├── health.js # Health/readiness probes
|
||||
│ └── logger.js # Логирование
|
||||
├── Dockerfile
|
||||
├── .dockerignore
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### `server.js` (псевдокод)
|
||||
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
const { loadRoutes } = require('./config');
|
||||
const { healthHandler, readyHandler } = require('./lib/health');
|
||||
|
||||
const app = express();
|
||||
const routes = loadRoutes(); // из env или routes.yaml
|
||||
|
||||
// Health probes (без прокси)
|
||||
app.get('/healthz', healthHandler);
|
||||
app.get('/readyz', readyHandler);
|
||||
|
||||
// Root — информационная страница
|
||||
app.get('/', (req, res) => {
|
||||
res.json({
|
||||
service: 'gateway',
|
||||
version: process.env.APP_VERSION || '1.0.0',
|
||||
routes: routes.map(r => ({
|
||||
prefix: r.prefix,
|
||||
description: r.description
|
||||
}))
|
||||
});
|
||||
});
|
||||
|
||||
// Динамическая регистрация прокси-маршрутов
|
||||
for (const route of routes) {
|
||||
const proxy = createProxyMiddleware({
|
||||
target: route.upstream,
|
||||
changeOrigin: true,
|
||||
pathRewrite: route.stripPrefix
|
||||
? { [`^${route.prefix}`]: '' }
|
||||
: undefined,
|
||||
timeout: route.timeout,
|
||||
onError: (err, req, res) => {
|
||||
console.error(`[${route.id}] Proxy error:`, err.message);
|
||||
res.status(502).json({ error: 'Upstream unavailable', route: route.id });
|
||||
}
|
||||
});
|
||||
|
||||
app.use(route.prefix, proxy);
|
||||
console.log(`Route: ${route.prefix} → ${route.upstream} (strip: ${route.stripPrefix})`);
|
||||
}
|
||||
|
||||
// 404 для всего остального
|
||||
app.use((req, res) => {
|
||||
res.status(404).json({ error: 'Not Found', path: req.path });
|
||||
});
|
||||
|
||||
app.listen(process.env.PORT || 3000);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. КАК ДОБАВИТЬ НОВЫЙ СЕРВИС (RECORD)
|
||||
|
||||
### Шаг 1: Создать сервис в K8s
|
||||
|
||||
```yaml
|
||||
# Пример: новый сервис my-api
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-api
|
||||
namespace: default
|
||||
spec:
|
||||
ports:
|
||||
- port: 8080
|
||||
selector:
|
||||
app: my-api
|
||||
```
|
||||
|
||||
### Шаг 2: Добавить route в конфиг gateway
|
||||
|
||||
```yaml
|
||||
# Добавить в config/routes.yaml:
|
||||
- id: "my-api"
|
||||
prefix: "/record04"
|
||||
description: "Мой новый API"
|
||||
upstream: "http://my-api.default.svc.cluster.local:8080"
|
||||
stripPrefix: true
|
||||
timeout: 10000
|
||||
```
|
||||
|
||||
### Шаг 3: Передеплоить gateway
|
||||
|
||||
```bash
|
||||
kubectl rollout restart deployment/gateway -n terra
|
||||
```
|
||||
|
||||
### Шаг 4: Готово
|
||||
|
||||
Сервис доступен по адресу:
|
||||
```
|
||||
https://gateway.nodejsk8s.services.ngcloud.ru/record04/
|
||||
```
|
||||
|
||||
**Никаких DNS, Ingress'ов, сертификатов.** Только конфиг gateway.
|
||||
|
||||
---
|
||||
|
||||
## 6. ЧТО НУЖНО ПОМЕНЯТЬ В REGISTRY-SERVER
|
||||
|
||||
Файл: `~/tf_registry/server/main.go`
|
||||
|
||||
### Добавить env-переменную
|
||||
|
||||
```go
|
||||
var pathPrefix = os.Getenv("PATH_PREFIX") // "/record01" или "" (пустая строка = без префикса, обратная совместимость)
|
||||
```
|
||||
|
||||
### Изменить `main()` — регистрация handlers
|
||||
|
||||
```go
|
||||
// БЫЛО:
|
||||
http.HandleFunc("/.well-known/terraform.json", discoveryHandler)
|
||||
http.HandleFunc("/v1/providers/", router)
|
||||
http.HandleFunc("/v1/proxy", proxyHandler)
|
||||
http.HandleFunc("/docs/", docsHandler)
|
||||
|
||||
// СТАЛО:
|
||||
http.HandleFunc(pathPrefix+"/.well-known/terraform.json", discoveryHandler)
|
||||
http.HandleFunc(pathPrefix+"/v1/providers/", router)
|
||||
http.HandleFunc(pathPrefix+"/v1/proxy", proxyHandler)
|
||||
http.HandleFunc(pathPrefix+"/docs/", docsHandler)
|
||||
```
|
||||
|
||||
### Изменить `discoveryHandler()` — ответ с префиксом
|
||||
|
||||
```go
|
||||
// БЫЛО:
|
||||
json.NewEncoder(w).Encode(Discovery{ProvidersV1: "/v1/providers/"})
|
||||
|
||||
// СТАЛО:
|
||||
json.NewEncoder(w).Encode(Discovery{ProvidersV1: pathPrefix + "/v1/providers/"})
|
||||
```
|
||||
|
||||
### Изменить `downloadVersion()` — ссылки с префиксом
|
||||
|
||||
```go
|
||||
// БЫЛО:
|
||||
baseURL := "https://" + hostname
|
||||
downloadLink := fmt.Sprintf("%s/v1/proxy?bucket=%s&key=%s", baseURL, bucketName, url.QueryEscape(fullKey))
|
||||
|
||||
// СТАЛО:
|
||||
baseURL := "https://" + hostname + pathPrefix
|
||||
downloadLink := fmt.Sprintf("%s/v1/proxy?bucket=%s&key=%s", baseURL, bucketName, url.QueryEscape(fullKey))
|
||||
```
|
||||
|
||||
### Изменить `listVersions()` — S3-префикс
|
||||
|
||||
```go
|
||||
// БЫЛО:
|
||||
prefix := fmt.Sprintf("%s/%s/%s/", hostname, namespace, pType)
|
||||
|
||||
// СТАЛО (hostname теперь = gateway.nodejsk8s.services.ngcloud.ru):
|
||||
// Если PATH_PREFIX непустой, можно использовать его как основу для S3-пути,
|
||||
// либо продолжать использовать hostname. Зависит от того, как лежат файлы в S3.
|
||||
// Пока оставляем hostname — он просто поменяется на новый домен.
|
||||
prefix := fmt.Sprintf("%s/%s/%s/", hostname, namespace, pType)
|
||||
```
|
||||
|
||||
### Новые env-переменные для registry-server
|
||||
|
||||
| Переменная | Было | Стало |
|
||||
|------------|------|-------|
|
||||
| `REGISTRY_HOSTNAME` | `terra.k8c.ru` / `registry.kube5s.ru` | `gateway.nodejsk8s.services.ngcloud.ru` |
|
||||
| `PATH_PREFIX` | — | `/record01` |
|
||||
|
||||
---
|
||||
|
||||
## 7. КАК ЭТО ВЫГЛЯДИТ ДЛЯ КЛИЕНТОВ (terraform init)
|
||||
|
||||
### Было
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
nubes = {
|
||||
source = "registry.kube5s.ru/nubes-test/nubes"
|
||||
version = "5.1.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Стало
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
nubes = {
|
||||
source = "gateway.nodejsk8s.services.ngcloud.ru/nubes-test/nubes"
|
||||
version = "5.1.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Всё.** Terraform сам разберётся:
|
||||
1. Пойдёт на `.well-known/terraform.json`
|
||||
2. Получит путь `/record01/v1/providers/`
|
||||
3. Скачает провайдер через `/record01/v1/proxy?...`
|
||||
|
||||
---
|
||||
|
||||
## 8. ПЛАН ВНЕДРЕНИЯ
|
||||
|
||||
### Фаза 1: Подготовка GATEWAY
|
||||
|
||||
- [ ] Склонировать шаблонный репо
|
||||
- [ ] Написать `server.js` (прокси-роутер)
|
||||
- [ ] Написать `config/routes.yaml` с первым роутом (`/record01` → registry)
|
||||
- [ ] Dockerfile
|
||||
- [ ] Helm chart / K8s manifests (Deployment, Service)
|
||||
- [ ] Ingress: `gateway.nodejsk8s.services.ngcloud.ru` → gateway-service
|
||||
- [ ] TLS через cert-manager
|
||||
|
||||
### Фаза 2: Подготовка Registry-Server
|
||||
|
||||
- [ ] Добавить `PATH_PREFIX` в `server/main.go`
|
||||
- [ ] Изменить `REGISTRY_HOSTNAME` на `gateway.nodejsk8s.services.ngcloud.ru`
|
||||
- [ ] Собрать новый Docker-образ
|
||||
- [ ] Задеплоить registry-server с новыми env
|
||||
- [ ] Убедиться что registry отвечает на `/record01/.well-known/terraform.json`
|
||||
- [ ] Убедиться что download-url содержат `/record01/`
|
||||
|
||||
### Фаза 3: Тестирование
|
||||
|
||||
- [ ] `curl https://gateway.nodejsk8s.services.ngcloud.ru/record01/.well-known/terraform.json` → OK
|
||||
- [ ] `curl https://gateway.nodejsk8s.services.ngcloud.ru/record01/v1/providers/nubes-test/nubes/versions` → OK
|
||||
- [ ] `terraform init` с новым source → OK
|
||||
|
||||
### Фаза 4: Миграция клиентов
|
||||
|
||||
- [ ] Обновить `provider/main.go` — `address` по умолчанию на новый домен
|
||||
- [ ] Обновить profile.env (dev/test/prod) — `REGISTRY_HOSTNAME`
|
||||
- [ ] Обновить все `.tf` файлы в `PROD_STAND/`, `TEST_STAND/`, `DEV_STAND/`
|
||||
- [ ] Обновить CI/CD (`.github/workflows/`, build scripts)
|
||||
- [ ] Старый `registry.kube5s.ru` — 301 редирект или удалить
|
||||
|
||||
### Фаза 5: Добавление остальных сервисов
|
||||
|
||||
- [ ] Выявить все текущие `*.kube5s.ru` (через grep по всем репо)
|
||||
- [ ] Для каждого: добавить route в gateway
|
||||
- [ ] Перенести сервис за gateway
|
||||
- [ ] Удалить старые DNS/Ingress/сертификаты
|
||||
|
||||
---
|
||||
|
||||
## 9. ТЕКУЩИЕ ЗАПИСИ `kube5s.ru` (НАЙТИ ВСЕ)
|
||||
|
||||
```bash
|
||||
# Выполнить чтобы найти все использования kube5s.ru:
|
||||
grep -r "kube5s\.ru" /home/naeel/ --include="*.{tf,yaml,yml,go,sh,env,js,json,md,cfg,ejs,html}" -l
|
||||
```
|
||||
|
||||
**Известные на 2026-07-20:**
|
||||
|
||||
| Запись | Сервис | Приоритет миграции |
|
||||
|--------|--------|--------------------|
|
||||
| `registry.kube5s.ru` | Terraform Registry Server | **#1 (критично)** |
|
||||
| ??? | ??? | Найти grep'ом |
|
||||
|
||||
---
|
||||
|
||||
## 10. КОНФИГУРАЦИЯ KUBERNETES
|
||||
|
||||
### Deployment
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: gateway
|
||||
namespace: terra
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: gateway
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: gateway
|
||||
spec:
|
||||
containers:
|
||||
- name: gateway
|
||||
image: registry.kube5s.ru/gateway:1.0.0
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: PORT
|
||||
value: "3000"
|
||||
- name: APP_VERSION
|
||||
value: "1.0.0"
|
||||
# ROUTES
|
||||
- name: ROUTE_01_ID
|
||||
value: "registry"
|
||||
- name: ROUTE_01_PREFIX
|
||||
value: "/record01"
|
||||
- name: ROUTE_01_UPSTREAM
|
||||
value: "http://registry-server.terra.svc.cluster.local:8080"
|
||||
- name: ROUTE_01_STRIP
|
||||
value: "false"
|
||||
- name: ROUTE_01_TIMEOUT
|
||||
value: "30000"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 3000
|
||||
initialDelaySeconds: 5
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: 3000
|
||||
initialDelaySeconds: 3
|
||||
```
|
||||
|
||||
### Service
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gateway
|
||||
namespace: terra
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 3000
|
||||
selector:
|
||||
app: gateway
|
||||
```
|
||||
|
||||
### Ingress
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: gateway-ingress
|
||||
namespace: terra
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- gateway.nodejsk8s.services.ngcloud.ru
|
||||
secretName: gateway-tls
|
||||
rules:
|
||||
- host: gateway.nodejsk8s.services.ngcloud.ru
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gateway
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. ССЫЛКИ
|
||||
|
||||
| Ресурс | URL |
|
||||
|--------|-----|
|
||||
| GATEWAY (production) | `https://gateway.nodejsk8s.services.ngcloud.ru/` |
|
||||
| Репозиторий GATEWAY | `~/tf_provider/gateway/` (создать) или отдельный репо |
|
||||
| Registry Server | `~/tf_registry/server/main.go` |
|
||||
| Terraform Provider | `~/tf_provider/provider/main.go` |
|
||||
| S3 (registry files) | `s3.msk-1.ngcloud.ru`, bucket `terraform-registry` |
|
||||
|
||||
---
|
||||
|
||||
## 12. КОНТРОЛЬНЫЙ СПИСОК ПРИ ДОБАВЛЕНИИ НОВОГО RECORD
|
||||
|
||||
1. [ ] Сервис задеплоен в K8s, есть Service
|
||||
2. [ ] Сервис работает (проверить через `kubectl port-forward` или напрямую)
|
||||
3. [ ] Определён `stripPrefix`: upstream знает о префиксе? (как registry) или нет?
|
||||
4. [ ] Добавлен route в конфиг gateway
|
||||
5. [ ] Gateway передеплоен
|
||||
6. [ ] Проверен curl через gateway: `curl https://gateway...ngcloud.ru/recordXX/...`
|
||||
7. [ ] Клиенты обновлены на новый URL
|
||||
|
||||
---
|
||||
|
||||
*Документ создан 2026-07-20. Планирование завершено, реализация — позже.*
|
||||
+1
-1
Submodule tfluceecrud updated: 53e12ecddd...7b6ab4c8b7
Reference in New Issue
Block a user